Imported Upstream version 1.15.1
[deb_xorg-server.git] / config / dbus.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright © 2006-2007 Daniel Stone
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Author: Daniel Stone <daniel@fooishbar.org>
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include <dbus/dbus.h>
31#include <string.h>
32
33#include <X11/X.h>
34
35#include "config-backends.h"
36#include "opaque.h" /* for 'display': there should be a better way. */
37#include "input.h"
38#include "inputstr.h"
39
40#define API_VERSION 2
41
42#define MATCH_RULE "type='method_call',interface='org.x.config.input'"
43
44#define MALFORMED_MSG "[config/dbus] malformed message, dropping"
45#define MALFORMED_MESSAGE() { DebugF(MALFORMED_MSG "\n"); \
46 ret = BadValue; \
47 goto unwind; }
48#define MALFORMED_MESSAGE_ERROR() { DebugF(MALFORMED_MSG ": %s, %s", \
49 error->name, error->message); \
50 ret = BadValue; \
51 goto unwind; }
52
53struct connection_info {
54 char busobject[32];
55 char busname[64];
56 DBusConnection *connection;
57};
58
59static void
60reset_info(struct connection_info *info)
61{
62 info->connection = NULL;
63 info->busname[0] = '\0';
64 info->busobject[0] = '\0';
65}
66
67static int
68add_device(DBusMessage * message, DBusMessage * reply, DBusError * error)
69{
70 DBusMessageIter iter, reply_iter, subiter;
71 InputOption *input_options = NULL;
72 int ret, err;
73 DeviceIntPtr dev = NULL;
74
75 dbus_message_iter_init_append(reply, &reply_iter);
76
77 if (!dbus_message_iter_init(message, &iter)) {
78 ErrorF("[config/dbus] couldn't initialise iterator\n");
79 MALFORMED_MESSAGE();
80 }
81
82 input_options = input_option_new(input_options, "_source", "client/dbus");
83 if (!input_options) {
84 ErrorF("[config/dbus] couldn't allocate first key/value pair\n");
85 ret = BadAlloc;
86 goto unwind;
87 }
88
89 /* signature should be [ss][ss]... */
90 while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
91 char *key, *value;
92
93 dbus_message_iter_recurse(&iter, &subiter);
94
95 if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING)
96 MALFORMED_MESSAGE();
97
98 dbus_message_iter_get_basic(&subiter, &key);
99 if (!key)
100 MALFORMED_MESSAGE();
101 /* The _ prefix refers to internal settings, and may not be given by
102 * the client. */
103 if (key[0] == '_') {
104 ErrorF("[config/dbus] attempted subterfuge: option name %s given\n",
105 key);
106 MALFORMED_MESSAGE();
107 }
108
109 if (!dbus_message_iter_has_next(&subiter))
110 MALFORMED_MESSAGE();
111 dbus_message_iter_next(&subiter);
112 if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING)
113 MALFORMED_MESSAGE();
114
115 dbus_message_iter_get_basic(&subiter, &value);
116 if (!value)
117 MALFORMED_MESSAGE();
118
119 input_options = input_option_new(input_options, key, value);
120
121 dbus_message_iter_next(&iter);
122 }
123
124 ret = NewInputDeviceRequest(input_options, NULL, &dev);
125 if (ret != Success) {
126 DebugF("[config/dbus] NewInputDeviceRequest failed\n");
127 goto unwind;
128 }
129
130 if (!dev) {
131 DebugF("[config/dbus] NewInputDeviceRequest provided no device\n");
132 ret = BadImplementation;
133 goto unwind;
134 }
135
136 /* XXX: If we fail halfway through, we don't seem to have any way to
137 * empty the iterator, so you'll end up with some device IDs,
138 * plus an error. This seems to be a shortcoming in the D-Bus
139 * API. */
140 for (; dev; dev = dev->next) {
141 if (!dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32,
142 &dev->id)) {
143 ErrorF("[config/dbus] couldn't append to iterator\n");
144 ret = BadAlloc;
145 goto unwind;
146 }
147 }
148
149 unwind:
150 if (ret != Success) {
151 if (dev)
152 RemoveDevice(dev, TRUE);
153
154 err = -ret;
155 dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err);
156 }
157
158 input_option_free_list(&input_options);
159
160 return ret;
161}
162
163static int
164remove_device(DBusMessage * message, DBusMessage * reply, DBusError * error)
165{
166 int deviceid, ret, err;
167 DeviceIntPtr dev;
168 DBusMessageIter iter, reply_iter;
169
170 dbus_message_iter_init_append(reply, &reply_iter);
171
172 if (!dbus_message_iter_init(message, &iter)) {
173 ErrorF("[config/dbus] failed to init iterator\n");
174 MALFORMED_MESSAGE();
175 }
176
177 if (!dbus_message_get_args(message, error, DBUS_TYPE_UINT32,
178 &deviceid, DBUS_TYPE_INVALID)) {
179 MALFORMED_MESSAGE_ERROR();
180 }
181
182 dixLookupDevice(&dev, deviceid, serverClient, DixDestroyAccess);
183 if (!dev) {
184 DebugF("[config/dbus] bogus device id %d given\n", deviceid);
185 ret = BadMatch;
186 goto unwind;
187 }
188
189 DebugF("[config/dbus] removing device %s (id %d)\n", dev->name, deviceid);
190
191 /* Call PIE here so we don't try to dereference a device that's
192 * already been removed. */
193 OsBlockSignals();
194 ProcessInputEvents();
195 DeleteInputDeviceRequest(dev);
196 OsReleaseSignals();
197
198 ret = Success;
199
200 unwind:
201 err = (ret == Success) ? ret : -ret;
202 dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err);
203
204 return ret;
205}
206
207static int
208list_devices(DBusMessage * message, DBusMessage * reply, DBusError * error)
209{
210 DeviceIntPtr dev;
211 DBusMessageIter iter, subiter;
212
213 dbus_message_iter_init_append(reply, &iter);
214
215 for (dev = inputInfo.devices; dev; dev = dev->next) {
216 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL,
217 &subiter)) {
218 ErrorF("[config/dbus] couldn't init container\n");
219 return BadAlloc;
220 }
221 if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_UINT32,
222 &dev->id)) {
223 ErrorF("[config/dbus] couldn't append to iterator\n");
224 return BadAlloc;
225 }
226 if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING,
227 &dev->name)) {
228 ErrorF("[config/dbus] couldn't append to iterator\n");
229 return BadAlloc;
230 }
231 if (!dbus_message_iter_close_container(&iter, &subiter)) {
232 ErrorF("[config/dbus] couldn't close container\n");
233 return BadAlloc;
234 }
235 }
236
237 return Success;
238}
239
240static int
241get_version(DBusMessage * message, DBusMessage * reply, DBusError * error)
242{
243 DBusMessageIter iter;
244 unsigned int version = API_VERSION;
245
246 dbus_message_iter_init_append(reply, &iter);
247 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &version)) {
248 ErrorF("[config/dbus] couldn't append version\n");
249 return BadAlloc;
250 }
251
252 return Success;
253}
254
255static DBusHandlerResult
256message_handler(DBusConnection * connection, DBusMessage * message, void *data)
257{
258 DBusError error;
259 DBusMessage *reply;
260 struct connection_info *info = data;
261
262 /* ret is the overall D-Bus handler result, whereas err is the internal
263 * X error from our individual functions. */
264 int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
265 int err;
266
267 DebugF("[config/dbus] received a message for %s\n",
268 dbus_message_get_interface(message));
269
270 dbus_error_init(&error);
271
272 reply = dbus_message_new_method_return(message);
273 if (!reply) {
274 ErrorF("[config/dbus] failed to create reply\n");
275 ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
276 goto err_start;
277 }
278
279 if (strcmp(dbus_message_get_member(message), "add") == 0)
280 err = add_device(message, reply, &error);
281 else if (strcmp(dbus_message_get_member(message), "remove") == 0)
282 err = remove_device(message, reply, &error);
283 else if (strcmp(dbus_message_get_member(message), "listDevices") == 0)
284 err = list_devices(message, reply, &error);
285 else if (strcmp(dbus_message_get_member(message), "version") == 0)
286 err = get_version(message, reply, &error);
287 else
288 goto err_reply;
289
290 /* Failure to allocate is a special case. */
291 if (err == BadAlloc) {
292 ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
293 goto err_reply;
294 }
295
296 /* While failure here is always an OOM, we don't return that,
297 * since that would result in devices being double-added/removed. */
298 if (dbus_connection_send(info->connection, reply, NULL))
299 dbus_connection_flush(info->connection);
300 else
301 ErrorF("[config/dbus] failed to send reply\n");
302
303 ret = DBUS_HANDLER_RESULT_HANDLED;
304
305 err_reply:
306 dbus_message_unref(reply);
307 err_start:
308 dbus_error_free(&error);
309
310 return ret;
311}
312
313static void
314connect_hook(DBusConnection * connection, void *data)
315{
316 DBusError error;
317 DBusObjectPathVTable vtable = {.message_function = message_handler, };
318 struct connection_info *info = data;
319
320 info->connection = connection;
321
322 dbus_error_init(&error);
323
324 dbus_bus_request_name(info->connection, info->busname, 0, &error);
325 if (dbus_error_is_set(&error)) {
326 ErrorF("[config/dbus] couldn't take over org.x.config: %s (%s)\n",
327 error.name, error.message);
328 goto err_start;
329 }
330
331 /* blocks until we get a reply. */
332 dbus_bus_add_match(info->connection, MATCH_RULE, &error);
333 if (dbus_error_is_set(&error)) {
334 ErrorF("[config/dbus] couldn't add match: %s (%s)\n", error.name,
335 error.message);
336 goto err_name;
337 }
338
339 if (!dbus_connection_register_object_path(info->connection,
340 info->busobject, &vtable, info)) {
341 ErrorF("[config/dbus] couldn't register object path\n");
342 goto err_match;
343 }
344
345 DebugF("[dbus] registered %s, %s\n", info->busname, info->busobject);
346
347 dbus_error_free(&error);
348
349 return;
350
351 err_match:
352 dbus_bus_remove_match(info->connection, MATCH_RULE, &error);
353 err_name:
354 dbus_bus_release_name(info->connection, info->busname, &error);
355 err_start:
356 dbus_error_free(&error);
357
358 reset_info(info);
359}
360
361static void
362disconnect_hook(void *data)
363{
364}
365
366#if 0
367void
368pre_disconnect_hook(void)
369{
370 DBusError error;
371
372 dbus_error_init(&error);
373 dbus_connection_unregister_object_path(connection_data->connection,
374 connection_data->busobject);
375 dbus_bus_remove_match(connection_data->connection, MATCH_RULE, &error);
376 dbus_bus_release_name(connection_data->connection,
377 connection_data->busname, &error);
378 dbus_error_free(&error);
379}
380#endif
381
382static struct connection_info connection_data;
383
384static struct config_dbus_core_hook core_hook = {
385 .connect = connect_hook,
386 .disconnect = disconnect_hook,
387 .data = &connection_data,
388};
389
390int
391config_dbus_init(void)
392{
393 snprintf(connection_data.busname, sizeof(connection_data.busname),
394 "org.x.config.display%d", atoi(display));
395 snprintf(connection_data.busobject, sizeof(connection_data.busobject),
396 "/org/x/config/%d", atoi(display));
397
398 return config_dbus_core_add_hook(&core_hook);
399}
400
401void
402config_dbus_fini(void)
403{
404 config_dbus_core_remove_hook(&core_hook);
405 connection_data.busname[0] = '\0';
406 connection_data.busobject[0] = '\0';
407}