Link Here
|
1 |
--- /dev/null 2013-08-03 00:44:23.000000000 +0200 |
|
|
2 |
+++ config/devd.c 2013-08-03 00:45:15.162836806 +0200 |
3 |
@@ -0,0 +1,478 @@ |
4 |
+/* |
5 |
+ * Copyright © 2012 Baptiste Daroussin |
6 |
+ * |
7 |
+ * Permission is hereby granted, free of charge, to any person obtaining a |
8 |
+ * copy of this software and associated documentation files (the "Software"), |
9 |
+ * to deal in the Software without restriction, including without limitation |
10 |
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
11 |
+ * and/or sell copies of the Software, and to permit persons to whom the |
12 |
+ * Software is furnished to do so, subject to the following conditions: |
13 |
+ * |
14 |
+ * The above copyright notice and this permission notice (including the next |
15 |
+ * paragraph) shall be included in all copies or substantial portions of the |
16 |
+ * Software. |
17 |
+ * |
18 |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
21 |
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
23 |
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
24 |
+ * DEALINGS IN THE SOFTWARE. |
25 |
+ * |
26 |
+ * Author: Baptiste Daroussin <bapt@FreeBSD.org> |
27 |
+ */ |
28 |
+ |
29 |
+#ifdef HAVE_DIX_CONFIG_H |
30 |
+#include <dix-config.h> |
31 |
+#endif |
32 |
+ |
33 |
+#include <sys/types.h> |
34 |
+#include <sys/socket.h> |
35 |
+#include <sys/sysctl.h> |
36 |
+#include <sys/un.h> |
37 |
+ |
38 |
+#include <ctype.h> |
39 |
+#include <stdlib.h> |
40 |
+#include <stdio.h> |
41 |
+#include <stdarg.h> |
42 |
+#include <stdbool.h> |
43 |
+#include <unistd.h> |
44 |
+ |
45 |
+#include "input.h" |
46 |
+#include "inputstr.h" |
47 |
+#include "hotplug.h" |
48 |
+#include "config-backends.h" |
49 |
+#include "os.h" |
50 |
+ |
51 |
+#define DEVD_SOCK_PATH "/var/run/devd.pipe" |
52 |
+ |
53 |
+#define DEVD_EVENT_ADD '+' |
54 |
+#define DEVD_EVENT_REMOVE '-' |
55 |
+ |
56 |
+static int sock_devd = -1; |
57 |
+ |
58 |
+#if XORG_VERSION_CURRENT < 10800000 |
59 |
+enum { |
60 |
+ ATTR_KEYBOARD, |
61 |
+ ATTR_POINTER, |
62 |
+ ATTR_JOYSTICK, |
63 |
+ ATTR_TOUCHPAD, |
64 |
+ ATTR_TOUCHSCREEN, |
65 |
+}; |
66 |
+#endif |
67 |
+ |
68 |
+struct hw_type { |
69 |
+ const char *driver; |
70 |
+ int flag; |
71 |
+ const char *xdriver; |
72 |
+}; |
73 |
+ |
74 |
+static struct hw_type hw_types[] = { |
75 |
+ { "ukbd", ATTR_KEYBOARD, "kdb" }, |
76 |
+ { "atkbd", ATTR_KEYBOARD, "kdb" }, |
77 |
+ { "ums", ATTR_POINTER, "mouse" }, |
78 |
+ { "psm", ATTR_POINTER, "mouse" }, |
79 |
+ { "uhid", ATTR_POINTER, "mouse" }, |
80 |
+ { "joy", ATTR_JOYSTICK, NULL }, |
81 |
+ { "atp", ATTR_TOUCHPAD, NULL }, |
82 |
+ { "uep", ATTR_TOUCHSCREEN, NULL }, |
83 |
+ { NULL, -1, NULL }, |
84 |
+}; |
85 |
+ |
86 |
+#if XORG_VERSION_CURRENT < 10800000 |
87 |
+static void |
88 |
+add_option(InputOption **options, const char *key, const char *value) |
89 |
+{ |
90 |
+ if (!value || *value == '\0') |
91 |
+ return; |
92 |
+ |
93 |
+ for (; *options; options = &(*options)->next) |
94 |
+ ; |
95 |
+ *options = calloc(sizeof(**options), 1); |
96 |
+ if (!*options) /* Yeesh. */ |
97 |
+ return; |
98 |
+ (*options)->key = xstrdup(key); |
99 |
+ (*options)->value = xstrdup(value); |
100 |
+ (*options)->next = NULL; |
101 |
+} |
102 |
+ |
103 |
+static void |
104 |
+remove_device(DeviceIntPtr dev) |
105 |
+{ |
106 |
+ /* this only gets called for devices that have already been added */ |
107 |
+ LogMessage(X_INFO, "config/devd: removing device %s\n", dev->name); |
108 |
+ |
109 |
+ /* Call PIE here so we don't try to dereference a device that's |
110 |
+ * already been removed. */ |
111 |
+ OsBlockSignals(); |
112 |
+ ProcessInputEvents(); |
113 |
+ DeleteInputDeviceRequest(dev); |
114 |
+ OsReleaseSignals(); |
115 |
+} |
116 |
+ |
117 |
+static bool |
118 |
+device_is_duplicate(char *config_info) |
119 |
+{ |
120 |
+ DeviceIntPtr dev; |
121 |
+ |
122 |
+ for (dev = inputInfo.devices; dev; dev = dev->next) |
123 |
+ if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) |
124 |
+ return true; |
125 |
+ |
126 |
+ for (dev = inputInfo.off_devices; dev; dev = dev->next) |
127 |
+ if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) |
128 |
+ return true; |
129 |
+ |
130 |
+ return false; |
131 |
+} |
132 |
+ |
133 |
+#endif |
134 |
+ |
135 |
+static bool |
136 |
+sysctl_exists(const char *format, ...) |
137 |
+{ |
138 |
+ va_list args; |
139 |
+ char *name = NULL; |
140 |
+ size_t len; |
141 |
+ int ret; |
142 |
+ |
143 |
+ if (format == NULL) |
144 |
+ return false; |
145 |
+ |
146 |
+ va_start(args, format); |
147 |
+ vasprintf(&name, format, args); |
148 |
+ va_end(args); |
149 |
+ |
150 |
+ ret = sysctlbyname(name, NULL, &len, NULL, 0); |
151 |
+ |
152 |
+ if (ret == -1) |
153 |
+ len = 0; |
154 |
+ |
155 |
+ free(name); |
156 |
+ return (len > 0); |
157 |
+} |
158 |
+ |
159 |
+static char * |
160 |
+sysctl_get_str(const char *format, ...) |
161 |
+{ |
162 |
+ va_list args; |
163 |
+ char *name = NULL; |
164 |
+ char *dest = NULL; |
165 |
+ size_t len; |
166 |
+ |
167 |
+ if (format == NULL) |
168 |
+ return NULL; |
169 |
+ |
170 |
+ va_start(args, format); |
171 |
+ vasprintf(&name, format, args); |
172 |
+ va_end(args); |
173 |
+ |
174 |
+ if (sysctlbyname(name, NULL, &len, NULL, 0) == 0) { |
175 |
+ dest = malloc(len + 1); |
176 |
+ if (sysctlbyname(name, dest, &len, NULL, 0) == 0) |
177 |
+ dest[len] = '\0'; |
178 |
+ else { |
179 |
+ free(dest); |
180 |
+ dest = NULL; |
181 |
+ } |
182 |
+ } |
183 |
+ |
184 |
+ free(name); |
185 |
+ return dest; |
186 |
+} |
187 |
+ |
188 |
+static void |
189 |
+device_added(char *line) |
190 |
+{ |
191 |
+ char *walk; |
192 |
+ char *path; |
193 |
+ char *vendor; |
194 |
+ char *product = NULL; |
195 |
+ char *config_info = NULL; |
196 |
+ InputOption *options = NULL; |
197 |
+#if XORG_VERSION_CURRENT > 10800000 |
198 |
+ InputAttributes attrs = {}; |
199 |
+#else |
200 |
+ InputOption *tmpo; |
201 |
+#endif |
202 |
+ DeviceIntPtr dev = NULL; |
203 |
+ int i, rc; |
204 |
+ |
205 |
+ walk = strchr(line, ' '); |
206 |
+ if (walk != NULL) |
207 |
+ walk[0] = '\0'; |
208 |
+ |
209 |
+ for (i = 0; hw_types[i].driver != NULL; i++) { |
210 |
+ if (strncmp(line, hw_types[i].driver, |
211 |
+ strlen(hw_types[i].driver)) == 0 && |
212 |
+ isnumber(*(line + strlen(hw_types[i].driver)))) { |
213 |
+#if XORG_VERSION_CURRENT > 10800000 |
214 |
+ attrs.flags |= hw_types[i].flag; |
215 |
+#endif |
216 |
+ break; |
217 |
+ } |
218 |
+ } |
219 |
+ if (hw_types[i].driver == NULL) { |
220 |
+ LogMessageVerb(X_INFO, 10, "config/devd: ignoring device %s\n", line); |
221 |
+ return; |
222 |
+ } |
223 |
+ |
224 |
+#if XORG_VERSION_CURRENT < 10800000 |
225 |
+ if (hw_types[i].xdriver == NULL) { |
226 |
+ LogMessageVerb(X_INFO, 10, "config/devd: ignoring device %s\n", line); |
227 |
+ return; |
228 |
+ } |
229 |
+#endif |
230 |
+ if (asprintf(&path, "/dev/%s", line) == -1) |
231 |
+ return; |
232 |
+ |
233 |
+#if XORG_VERSION_CURRENT < 10800000 |
234 |
+ options = calloc(sizeof(*options), 1); |
235 |
+ if (!options) |
236 |
+ return; |
237 |
+ |
238 |
+ add_option(&options, "_source", "server/devd"); |
239 |
+#else |
240 |
+ options = input_option_new(NULL, "_source", "server/devd"); |
241 |
+ if (!options) |
242 |
+ return; |
243 |
+#endif |
244 |
+ |
245 |
+ vendor = sysctl_get_str("dev.%s.%s.%%desc", hw_types[i].driver, line + strlen(hw_types[i].driver)); |
246 |
+ if (vendor == NULL) { |
247 |
+#if XORG_VERSION_CURRENT > 10800000 |
248 |
+ attrs.vendor = strdup("(unnamed)"); |
249 |
+#endif |
250 |
+ } else { |
251 |
+ if ((product = strchr(vendor, ' ')) != NULL) { |
252 |
+ product[0] = '\0'; |
253 |
+ product++; |
254 |
+ } |
255 |
+#if XORG_VERSION_CURRENT > 10800000 |
256 |
+ attrs.vendor = strdup(vendor); |
257 |
+#endif |
258 |
+ if (product != NULL && (walk = strchr(product, ',')) != NULL) |
259 |
+ walk[0] = '\0'; |
260 |
+#if XORG_VERSION_CURRENT > 10800000 |
261 |
+ attrs.product = strdup(product != NULL ? product : "(unnamed)"); |
262 |
+ options = input_option_new(options, "name", product != NULL ? product : "(unnamed)"); |
263 |
+#else |
264 |
+ add_option(&options, "name", product != NULL ? product : "(unnamed)"); |
265 |
+#endif |
266 |
+ } |
267 |
+#if XORG_VERSION_CURRENT > 10800000 |
268 |
+ attrs.usb_id = NULL; |
269 |
+ options = input_option_new(options, "path", path); |
270 |
+ options = input_option_new(options, "device", path); |
271 |
+#else |
272 |
+ add_option(&options, "path", path); |
273 |
+ add_option(&options, "device", path); |
274 |
+#endif |
275 |
+ |
276 |
+#if XORG_VERSION_CURRENT < 10800000 |
277 |
+ add_option(&options, "driver", hw_types[i].xdriver); |
278 |
+#endif |
279 |
+ |
280 |
+ if (asprintf(&config_info, "devd:%s", line) == -1) { |
281 |
+ config_info = NULL; |
282 |
+ goto unwind; |
283 |
+ } |
284 |
+ |
285 |
+ if (device_is_duplicate(config_info)) { |
286 |
+ LogMessage(X_WARNING, "config/devd: device %s already added. " |
287 |
+ "Ignoring.\n", product != NULL ? product : "(unnamed)"); |
288 |
+ goto unwind; |
289 |
+ } |
290 |
+ |
291 |
+#if XORG_VERSION_CURRENT < 10800000 |
292 |
+ add_option(&options, "config_info", config_info); |
293 |
+#else |
294 |
+ options = input_option_new(options, "config_info", config_info); |
295 |
+#endif |
296 |
+ LogMessage(X_INFO, "config/devd: Adding input device %s (%s)\n", |
297 |
+ product != NULL ? product : "(unnamed)", path); |
298 |
+ |
299 |
+#if XORG_VERSION_CURRENT > 10800000 |
300 |
+ rc = NewInputDeviceRequest(options, &attrs, &dev); |
301 |
+#else |
302 |
+ rc = NewInputDeviceRequest(options, &dev); |
303 |
+#endif |
304 |
+ |
305 |
+ if (rc != Success) |
306 |
+ goto unwind; |
307 |
+ |
308 |
+ unwind: |
309 |
+ free(config_info); |
310 |
+#if XORG_VERSION_CURRENT < 10800000 |
311 |
+ while ((tmpo = options)) { |
312 |
+ options = tmpo->next; |
313 |
+ free(tmpo->key); /* NULL if dev != NULL */ |
314 |
+ free(tmpo->value); /* NULL if dev != NULL */ |
315 |
+ free(tmpo); |
316 |
+ } |
317 |
+#else |
318 |
+ input_option_free_list(&options); |
319 |
+#endif |
320 |
+ |
321 |
+#if XORG_VERSION_CURRENT > 10800000 |
322 |
+ free(attrs.usb_id); |
323 |
+ free(attrs.product); |
324 |
+ free(attrs.device); |
325 |
+ free(attrs.vendor); |
326 |
+#endif |
327 |
+ |
328 |
+ return; |
329 |
+} |
330 |
+ |
331 |
+static void |
332 |
+device_removed(char *line) |
333 |
+{ |
334 |
+ char *walk; |
335 |
+ char *value; |
336 |
+#if XORG_VERSION_CURRENT < 10800000 |
337 |
+ DeviceIntPtr dev, next; |
338 |
+#endif |
339 |
+ |
340 |
+ walk = strchr(line, ' '); |
341 |
+ if (walk != NULL) |
342 |
+ walk[0] = '\0'; |
343 |
+ |
344 |
+ if (asprintf(&value, "devd:%s", line) == -1) |
345 |
+ return; |
346 |
+ |
347 |
+#if XORG_VERSION_CURRENT > 10800000 |
348 |
+ remove_devices("dev", value); |
349 |
+#else |
350 |
+ for (dev = inputInfo.devices; dev; dev = next) { |
351 |
+ next = dev->next; |
352 |
+ if (dev->config_info && strcmp(dev->config_info, value) == 0) |
353 |
+ remove_device(dev); |
354 |
+ } |
355 |
+ for (dev = inputInfo.off_devices; dev; dev = next) { |
356 |
+ next = dev->next; |
357 |
+ if (dev->config_info && strcmp(dev->config_info, value) == 0) |
358 |
+ remove_device(dev); |
359 |
+ } |
360 |
+#endif |
361 |
+ |
362 |
+ free(value); |
363 |
+} |
364 |
+ |
365 |
+static ssize_t |
366 |
+socket_getline(int fd, char **out) |
367 |
+{ |
368 |
+ char *buf; |
369 |
+ ssize_t ret, cap, sz = 0; |
370 |
+ char c; |
371 |
+ |
372 |
+ cap = 1024; |
373 |
+ buf = malloc(cap * sizeof(char)); |
374 |
+ if (!buf) |
375 |
+ return -1; |
376 |
+ |
377 |
+ for (;;) { |
378 |
+ ret = read(sock_devd, &c, 1); |
379 |
+ if (ret < 1) { |
380 |
+ free(buf); |
381 |
+ return -1; |
382 |
+ } |
383 |
+ |
384 |
+ if (c == '\n') |
385 |
+ break; |
386 |
+ |
387 |
+ if (sz + 1 >= cap) { |
388 |
+ cap *= 2; |
389 |
+ buf = realloc(buf, cap *sizeof(char)); |
390 |
+ } |
391 |
+ buf[sz] = c; |
392 |
+ sz++; |
393 |
+ } |
394 |
+ |
395 |
+ buf[sz] = '\0'; |
396 |
+ if (sz > 0) |
397 |
+ *out = buf; |
398 |
+ else |
399 |
+ free(buf); |
400 |
+ |
401 |
+ return sz; /* number of bytes in the line, not counting the line break*/ |
402 |
+} |
403 |
+ |
404 |
+static void |
405 |
+wakeup_handler(pointer data, int err, pointer read_mask) |
406 |
+{ |
407 |
+ char *line = NULL; |
408 |
+ |
409 |
+ if (err < 0) |
410 |
+ return; |
411 |
+ |
412 |
+ if (FD_ISSET(sock_devd, (fd_set *)read_mask)) { |
413 |
+ if (socket_getline(sock_devd, &line) < 0) |
414 |
+ return; |
415 |
+ |
416 |
+ switch(*line) { |
417 |
+ case DEVD_EVENT_ADD: |
418 |
+ device_added(line++); |
419 |
+ break; |
420 |
+ case DEVD_EVENT_REMOVE: |
421 |
+ device_removed(line++); |
422 |
+ break; |
423 |
+ default: |
424 |
+ break; |
425 |
+ } |
426 |
+ free(line); |
427 |
+ } |
428 |
+} |
429 |
+ |
430 |
+static void |
431 |
+block_handler(pointer data, struct timeval **tv, pointer read_mask) |
432 |
+{ |
433 |
+} |
434 |
+ |
435 |
+int |
436 |
+config_devd_init(void) |
437 |
+{ |
438 |
+ struct sockaddr_un devd; |
439 |
+ char devicename[1024]; |
440 |
+ int i, j; |
441 |
+ |
442 |
+ /* first scan the sysctl to determine the hardware if needed */ |
443 |
+ |
444 |
+ for (i = 0; hw_types[i].driver != NULL; i++) { |
445 |
+ for (j = 0; sysctl_exists("dev.%s.%i.%%desc", hw_types[i].driver, j); j++) { |
446 |
+ snprintf(devicename, 1024, "%s%i", hw_types[i].driver, j); |
447 |
+ device_added(devicename); |
448 |
+ } |
449 |
+ |
450 |
+ } |
451 |
+ sock_devd = socket(AF_UNIX, SOCK_STREAM, 0); |
452 |
+ if (sock_devd < 0) { |
453 |
+ ErrorF("config/devd: Fail opening stream socket"); |
454 |
+ return 0; |
455 |
+ } |
456 |
+ |
457 |
+ devd.sun_family = AF_UNIX; |
458 |
+ strlcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(devd.sun_path)); |
459 |
+ |
460 |
+ if (connect(sock_devd, (struct sockaddr *) &devd, sizeof(struct sockaddr_un)) < 0) { |
461 |
+ close(sock_devd); |
462 |
+ ErrorF("config/devd: Fail to connect to devd"); |
463 |
+ return 0; |
464 |
+ } |
465 |
+ |
466 |
+ RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); |
467 |
+ AddGeneralSocket(sock_devd); |
468 |
+ |
469 |
+ return 1; |
470 |
+} |
471 |
+ |
472 |
+void |
473 |
+config_devd_fini(void) |
474 |
+{ |
475 |
+ if (sock_devd < 0) |
476 |
+ return; |
477 |
+ |
478 |
+ RemoveGeneralSocket(sock_devd); |
479 |
+ RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); |
480 |
+ close(sock_devd); |
481 |
+} |