]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/xserver.git/blob - config/dbus.c
Imported Upstream version 1.11.4
[glsdk/xserver.git] / config / dbus.c
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;
194 static int
195 remove_device(DBusMessage *message, DBusMessage *reply, DBusError *error)
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;
238 static int
239 list_devices(DBusMessage *message, DBusMessage *reply, DBusError *error)
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;
271 static int
272 get_version(DBusMessage *message, DBusMessage *reply, DBusError *error)
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;
286 static DBusHandlerResult
287 message_handler(DBusConnection *connection, DBusMessage *message, void *data)
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;
344 static void
345 connect_hook(DBusConnection *connection, void *data)
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);
393 static void
394 disconnect_hook(void *data)
398 #if 0
399 void
400 pre_disconnect_hook(void)
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);
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)
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);
433 void
434 config_dbus_fini(void)
436     config_dbus_core_remove_hook(&core_hook);
437     connection_data.busname[0] = '\0';
438     connection_data.busobject[0] = '\0';