34e3caade9a0405b4442724ced00d4081ce8c47e
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 */
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
28 #endif
30 #include <dbus/dbus.h>
31 #include <string.h>
33 #include <X11/X.h>
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"
40 #define API_VERSION 2
42 #define MATCH_RULE "type='method_call',interface='org.x.config.input'"
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; }
53 struct connection_info {
54 char busobject[32];
55 char busname[64];
56 DBusConnection *connection;
57 };
59 static void
60 reset_info(struct connection_info *info)
61 {
62 info->connection = NULL;
63 info->busname[0] = '\0';
64 info->busobject[0] = '\0';
65 }
67 static int
68 add_device(DBusMessage *message, DBusMessage *reply, DBusError *error)
69 {
70 DBusMessageIter iter, reply_iter, subiter;
71 InputOption *tmpo = NULL, *options = NULL;
72 char *tmp = NULL;
73 int ret, err;
74 DeviceIntPtr dev = NULL;
76 dbus_message_iter_init_append(reply, &reply_iter);
78 if (!dbus_message_iter_init(message, &iter)) {
79 ErrorF("[config/dbus] couldn't initialise iterator\n");
80 MALFORMED_MESSAGE();
81 }
83 options = calloc(sizeof(*options), 1);
84 if (!options) {
85 ErrorF("[config/dbus] couldn't allocate option\n");
86 return BadAlloc;
87 }
89 options->key = strdup("_source");
90 options->value = strdup("client/dbus");
91 if (!options->key || !options->value) {
92 ErrorF("[config/dbus] couldn't allocate first key/value pair\n");
93 ret = BadAlloc;
94 goto unwind;
95 }
97 /* signature should be [ss][ss]... */
98 while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
99 tmpo = calloc(sizeof(*tmpo), 1);
100 if (!tmpo) {
101 ErrorF("[config/dbus] couldn't allocate option\n");
102 ret = BadAlloc;
103 goto unwind;
104 }
105 tmpo->next = options;
106 options = tmpo;
108 dbus_message_iter_recurse(&iter, &subiter);
110 if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING)
111 MALFORMED_MESSAGE();
113 dbus_message_iter_get_basic(&subiter, &tmp);
114 if (!tmp)
115 MALFORMED_MESSAGE();
116 /* The _ prefix refers to internal settings, and may not be given by
117 * the client. */
118 if (tmp[0] == '_') {
119 ErrorF("[config/dbus] attempted subterfuge: option name %s given\n",
120 tmp);
121 MALFORMED_MESSAGE();
122 }
123 options->key = strdup(tmp);
124 if (!options->key) {
125 ErrorF("[config/dbus] couldn't duplicate key!\n");
126 ret = BadAlloc;
127 goto unwind;
128 }
130 if (!dbus_message_iter_has_next(&subiter))
131 MALFORMED_MESSAGE();
132 dbus_message_iter_next(&subiter);
133 if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING)
134 MALFORMED_MESSAGE();
136 dbus_message_iter_get_basic(&subiter, &tmp);
137 if (!tmp)
138 MALFORMED_MESSAGE();
139 options->value = strdup(tmp);
140 if (!options->value) {
141 ErrorF("[config/dbus] couldn't duplicate option!\n");
142 ret = BadAlloc;
143 goto unwind;
144 }
146 dbus_message_iter_next(&iter);
147 }
149 ret = NewInputDeviceRequest(options, NULL, &dev);
150 if (ret != Success) {
151 DebugF("[config/dbus] NewInputDeviceRequest failed\n");
152 goto unwind;
153 }
155 if (!dev) {
156 DebugF("[config/dbus] NewInputDeviceRequest provided no device\n");
157 ret = BadImplementation;
158 goto unwind;
159 }
161 /* XXX: If we fail halfway through, we don't seem to have any way to
162 * empty the iterator, so you'll end up with some device IDs,
163 * plus an error. This seems to be a shortcoming in the D-Bus
164 * API. */
165 for (; dev; dev = dev->next) {
166 if (!dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32,
167 &dev->id)) {
168 ErrorF("[config/dbus] couldn't append to iterator\n");
169 ret = BadAlloc;
170 goto unwind;
171 }
172 }
174 unwind:
175 if (ret != Success) {
176 if (dev)
177 RemoveDevice(dev, TRUE);
179 err = -ret;
180 dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err);
181 }
183 while (options) {
184 tmpo = options;
185 options = options->next;
186 free(tmpo->key);
187 free(tmpo->value);
188 free(tmpo);
189 }
191 return ret;
192 }
194 static int
195 remove_device(DBusMessage *message, DBusMessage *reply, DBusError *error)
196 {
197 int deviceid, ret, err;
198 DeviceIntPtr dev;
199 DBusMessageIter iter, reply_iter;
201 dbus_message_iter_init_append(reply, &reply_iter);
203 if (!dbus_message_iter_init(message, &iter)) {
204 ErrorF("[config/dbus] failed to init iterator\n");
205 MALFORMED_MESSAGE();
206 }
208 if (!dbus_message_get_args(message, error, DBUS_TYPE_UINT32,
209 &deviceid, DBUS_TYPE_INVALID)) {
210 MALFORMED_MESSAGE_ERROR();
211 }
213 dixLookupDevice(&dev, deviceid, serverClient, DixDestroyAccess);
214 if (!dev) {
215 DebugF("[config/dbus] bogus device id %d given\n", deviceid);
216 ret = BadMatch;
217 goto unwind;
218 }
220 DebugF("[config/dbus] removing device %s (id %d)\n", dev->name, deviceid);
222 /* Call PIE here so we don't try to dereference a device that's
223 * already been removed. */
224 OsBlockSignals();
225 ProcessInputEvents();
226 DeleteInputDeviceRequest(dev);
227 OsReleaseSignals();
229 ret = Success;
231 unwind:
232 err = (ret == Success) ? ret : -ret;
233 dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err);
235 return ret;
236 }
238 static int
239 list_devices(DBusMessage *message, DBusMessage *reply, DBusError *error)
240 {
241 DeviceIntPtr dev;
242 DBusMessageIter iter, subiter;
244 dbus_message_iter_init_append(reply, &iter);
246 for (dev = inputInfo.devices; dev; dev = dev->next) {
247 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL,
248 &subiter)) {
249 ErrorF("[config/dbus] couldn't init container\n");
250 return BadAlloc;
251 }
252 if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_UINT32,
253 &dev->id)) {
254 ErrorF("[config/dbus] couldn't append to iterator\n");
255 return BadAlloc;
256 }
257 if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING,
258 &dev->name)) {
259 ErrorF("[config/dbus] couldn't append to iterator\n");
260 return BadAlloc;
261 }
262 if (!dbus_message_iter_close_container(&iter, &subiter)) {
263 ErrorF("[config/dbus] couldn't close container\n");
264 return BadAlloc;
265 }
266 }
268 return Success;
269 }
271 static int
272 get_version(DBusMessage *message, DBusMessage *reply, DBusError *error)
273 {
274 DBusMessageIter iter;
275 unsigned int version = API_VERSION;
277 dbus_message_iter_init_append(reply, &iter);
278 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &version)) {
279 ErrorF("[config/dbus] couldn't append version\n");
280 return BadAlloc;
281 }
283 return Success;
284 }
286 static DBusHandlerResult
287 message_handler(DBusConnection *connection, DBusMessage *message, void *data)
288 {
289 DBusError error;
290 DBusMessage *reply;
291 struct connection_info *info = data;
293 /* ret is the overall D-Bus handler result, whereas err is the internal
294 * X error from our individual functions. */
295 int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
296 int err;
298 DebugF("[config/dbus] received a message for %s\n",
299 dbus_message_get_interface(message));
301 dbus_error_init(&error);
303 reply = dbus_message_new_method_return(message);
304 if (!reply) {
305 ErrorF("[config/dbus] failed to create reply\n");
306 ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
307 goto err_start;
308 }
310 if (strcmp(dbus_message_get_member(message), "add") == 0)
311 err = add_device(message, reply, &error);
312 else if (strcmp(dbus_message_get_member(message), "remove") == 0)
313 err = remove_device(message, reply, &error);
314 else if (strcmp(dbus_message_get_member(message), "listDevices") == 0)
315 err = list_devices(message, reply, &error);
316 else if (strcmp(dbus_message_get_member(message), "version") == 0)
317 err = get_version(message, reply, &error);
318 else
319 goto err_reply;
321 /* Failure to allocate is a special case. */
322 if (err == BadAlloc) {
323 ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
324 goto err_reply;
325 }
327 /* While failure here is always an OOM, we don't return that,
328 * since that would result in devices being double-added/removed. */
329 if (dbus_connection_send(info->connection, reply, NULL))
330 dbus_connection_flush(info->connection);
331 else
332 ErrorF("[config/dbus] failed to send reply\n");
334 ret = DBUS_HANDLER_RESULT_HANDLED;
336 err_reply:
337 dbus_message_unref(reply);
338 err_start:
339 dbus_error_free(&error);
341 return ret;
342 }
344 static void
345 connect_hook(DBusConnection *connection, void *data)
346 {
347 DBusError error;
348 DBusObjectPathVTable vtable = { .message_function = message_handler, };
349 struct connection_info *info = data;
351 info->connection = connection;
353 dbus_error_init(&error);
355 dbus_bus_request_name(info->connection, info->busname, 0, &error);
356 if (dbus_error_is_set(&error)) {
357 ErrorF("[config/dbus] couldn't take over org.x.config: %s (%s)\n",
358 error.name, error.message);
359 goto err_start;
360 }
362 /* blocks until we get a reply. */
363 dbus_bus_add_match(info->connection, MATCH_RULE, &error);
364 if (dbus_error_is_set(&error)) {
365 ErrorF("[config/dbus] couldn't add match: %s (%s)\n", error.name,
366 error.message);
367 goto err_name;
368 }
370 if (!dbus_connection_register_object_path(info->connection,
371 info->busobject, &vtable,
372 info)) {
373 ErrorF("[config/dbus] couldn't register object path\n");
374 goto err_match;
375 }
377 DebugF("[dbus] registered %s, %s\n", info->busname, info->busobject);
379 dbus_error_free(&error);
381 return;
383 err_match:
384 dbus_bus_remove_match(info->connection, MATCH_RULE, &error);
385 err_name:
386 dbus_bus_release_name(info->connection, info->busname, &error);
387 err_start:
388 dbus_error_free(&error);
390 reset_info(info);
391 }
393 static void
394 disconnect_hook(void *data)
395 {
396 }
398 #if 0
399 void
400 pre_disconnect_hook(void)
401 {
402 DBusError error;
404 dbus_error_init(&error);
405 dbus_connection_unregister_object_path(connection_data->connection,
406 connection_data->busobject);
407 dbus_bus_remove_match(connection_data->connection, MATCH_RULE,
408 &error);
409 dbus_bus_release_name(connection_data->connection,
410 connection_data->busname, &error);
411 dbus_error_free(&error);
412 }
413 #endif
415 static struct connection_info connection_data;
416 static struct config_dbus_core_hook core_hook = {
417 .connect = connect_hook,
418 .disconnect = disconnect_hook,
419 .data = &connection_data,
420 };
422 int
423 config_dbus_init(void)
424 {
425 snprintf(connection_data.busname, sizeof(connection_data.busname),
426 "org.x.config.display%d", atoi(display));
427 snprintf(connection_data.busobject, sizeof(connection_data.busobject),
428 "/org/x/config/%d", atoi(display));
430 return config_dbus_core_add_hook(&core_hook);
431 }
433 void
434 config_dbus_fini(void)
435 {
436 config_dbus_core_remove_hook(&core_hook);
437 connection_data.busname[0] = '\0';
438 connection_data.busobject[0] = '\0';
439 }