View | Details | Raw Unified | Return to bug 196678 | Differences between
and this patch

Collapse All | Expand All

(-)config/devd.c (+578 lines)
Line 0 Link Here
1
/*
2
 * Copyright (c) 2012 Baptiste Daroussin
3
 * Copyright (c) 2013, 2014 Alex Kozlov
4
 * Copyright (c) 2014 Robert Millan
5
 * Copyright (c) 2014 Jean-Sebastien Pedron
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/kbio.h>
35
#include <sys/socket.h>
36
#include <sys/stat.h>
37
#include <sys/sysctl.h>
38
#include <sys/un.h>
39
40
#include <ctype.h>
41
#include <dirent.h>
42
#include <errno.h>
43
#include <fcntl.h>
44
#include <stdlib.h>
45
#include <stdio.h>
46
#include <stdbool.h>
47
#include <unistd.h>
48
#include <paths.h>
49
50
#include "input.h"
51
#include "inputstr.h"
52
#include "hotplug.h"
53
#include "config-backends.h"
54
#include "os.h"
55
56
#define DEVD_SOCK_PATH "/var/run/devd.pipe"
57
58
#define RECONNECT_DELAY		(5 * 1000)
59
60
static int sock_devd;
61
static bool is_kbdmux = false;
62
OsTimerPtr rtimer;
63
64
struct hw_type {
65
	const char *driver;
66
	int flag;
67
	const char *xdriver;
68
	const char *sysctldesc;
69
};
70
71
static const struct hw_type hw_types0[] = {
72
	{ _PATH_DEV "sysmouse", ATTR_POINTER, "mouse", NULL },
73
	{ NULL, 0, NULL, NULL },
74
};
75
76
static const struct hw_type hw_types1[] = {
77
	{ _PATH_DEV "ukbd%d", ATTR_KEYBOARD, "kbd", "dev.ukbd.%d.%%desc" },
78
	{ _PATH_DEV "atkbd%d", ATTR_KEYBOARD, "kbd", "dev.atkbd.%d.%%desc" },
79
	{ _PATH_DEV "kbdmux%d", ATTR_KEYBOARD, "kbd", NULL },
80
	{ _PATH_DEV "ums%d", ATTR_POINTER, "mouse", "dev.ums.%d.%%desc" },
81
	{ _PATH_DEV "psm%d", ATTR_POINTER, "mouse", "dev.psm.%d.%%desc" },
82
	{ _PATH_DEV "joy%d", ATTR_JOYSTICK, "mouse", "dev.joy.%d.%%desc" },
83
	{ _PATH_DEV "atp%d", ATTR_TOUCHPAD, "mouse", "dev.atp.%d.%%desc" },
84
	{ _PATH_DEV "wsp%d", ATTR_TOUCHPAD, "mouse", "dev.wsp.%d.%%desc" },
85
	{ _PATH_DEV "uep%d", ATTR_TOUCHSCREEN, "mouse", "dev.uep.%d.%%desc" },
86
	{ _PATH_DEV "vboxguest", ATTR_POINTER, "vboxmouse", NULL },
87
	{ _PATH_DEV "input/event%d", ATTR_TOUCHPAD, "evdev", NULL },
88
	{ NULL, 0, NULL, NULL },
89
};
90
91
static void
92
get_usb_id(char **pptr, int fd)
93
{
94
	unsigned short vendor;
95
	unsigned short product;
96
	unsigned int speed;
97
#define	WEBCAMD_IOCTL_GET_USB_VENDOR_ID _IOR('q', 250, unsigned short)
98
#define	WEBCAMD_IOCTL_GET_USB_PRODUCT_ID _IOR('q', 251, unsigned short)
99
#define	WEBCAMD_IOCTL_GET_USB_SPEED _IOR('q', 252, unsigned int)
100
	if (ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vendor) == 0 &&
101
	    ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &product) == 0 &&
102
	    ioctl(fd, WEBCAMD_IOCTL_GET_USB_SPEED, &speed) == 0) {
103
		if (asprintf(pptr, "%04x:%04x", vendor, product) == -1)
104
			*pptr = NULL;
105
	}
106
}
107
108
static const char *
109
skip_path_dev(const char *ptr)
110
{
111
	if (strstr(ptr, _PATH_DEV) == ptr)
112
		ptr += strlen(_PATH_DEV);
113
	return (ptr);
114
}
115
116
static char *
117
sysctl_get_str(const char *sysctlname)
118
{
119
	char *dest = NULL;
120
	size_t len;
121
122
	if (sysctlname == NULL)
123
		return NULL;
124
125
	if (sysctlbyname(sysctlname, NULL, &len, NULL, 0) == 0) {
126
		dest = malloc(len + 1);
127
		if (dest) {
128
			if (sysctlbyname(sysctlname, dest, &len, NULL, 0) == 0)
129
				dest[len] = '\0';
130
			else {
131
				free(dest);
132
				dest = NULL;
133
			}
134
		}
135
	}
136
137
	return dest;
138
}
139
140
static void
141
device_added(const char *devicename, bool allow_no_device)
142
{
143
	InputAttributes attrs = { };
144
	InputOption *options = NULL;
145
	char *config_info = NULL;
146
	DeviceIntPtr dev = NULL;
147
	struct hw_type hw_type;
148
	char *product = NULL;
149
	char sysctlname[64];
150
	char *vendor = NULL;
151
	int unit = 0;
152
	int fd = -1;
153
	char *walk;
154
	int i;
155
156
	for (i = 0; hw_types0[i].driver != NULL; i++) {
157
		if (strcmp(devicename, hw_types0[i].driver) == 0) {
158
			hw_type = hw_types0[i];
159
			goto found;
160
		}
161
	}
162
	for (i = 0; hw_types1[i].driver != NULL; i++) {
163
		if (sscanf(devicename, hw_types1[i].driver, &unit) == 1) {
164
			hw_type = hw_types1[i];
165
			goto found;
166
		}
167
	}
168
	goto ignore;
169
170
found:
171
	if (hw_type.xdriver == NULL)
172
		goto ignore;
173
174
	/* set flags, if any */
175
	attrs.flags |= hw_type.flag;
176
177
	if (strcmp(hw_type.xdriver, "kbd") == 0) {
178
		bool match = (strstr(hw_type.driver,
179
		    _PATH_DEV "kbdmux") == hw_type.driver);
180
181
		if (is_kbdmux) {
182
			if (!match)
183
				goto ignore;
184
		} else {
185
			if (match)
186
				goto ignore;
187
		}
188
	}
189
190
	options = input_option_new(NULL, "_source", "server/devd");
191
	if (options == NULL)
192
		goto error;
193
194
	if (hw_type.sysctldesc != NULL) {
195
		snprintf(sysctlname, sizeof(sysctlname),
196
		    hw_type.sysctldesc, unit);
197
		vendor = sysctl_get_str(sysctlname);
198
	}
199
200
	if (vendor == NULL) {
201
		options = input_option_new(options, "name",
202
		    skip_path_dev(devicename));
203
	} else {
204
		if ((walk = strchr(vendor, ' ')) != NULL) {
205
			walk[0] = '\0';
206
			walk++;
207
			product = walk;
208
			if ((walk = strchr(product, ',')) != NULL)
209
				walk[0] = '\0';
210
		}
211
212
		attrs.vendor = strdup(vendor);
213
		if (product != NULL) {
214
			attrs.product = strdup(product);
215
			options = input_option_new(options,
216
			    "name", product);
217
		} else {
218
			options = input_option_new(options,
219
			    "name", "Unknown");
220
		}
221
	}
222
	attrs.device = strdup(devicename);
223
224
	fd = open(devicename, O_RDONLY);
225
	if (fd > -1) {
226
	  	get_usb_id(&attrs.usb_id, fd);
227
		close(fd);
228
		options = input_option_new(options, "device", devicename);
229
		if (options == NULL)
230
			goto error;
231
	} else if (allow_no_device) {
232
		/*
233
		 * Don't pass "device" option if the keyboard is
234
		 * already attached to the console (ie. open() fails).
235
		 * This would activate a special logic in
236
		 * xf86-input-keyboard. Prevent any other attached to
237
		 * console keyboards being processed. There can be
238
		 * only one such device.
239
		 */
240
	} else {
241
		goto ignore;
242
	}
243
244
	options = input_option_new(options, "driver", hw_type.xdriver);
245
	if (options == NULL)
246
		goto error;
247
248
	if (asprintf(&config_info, "devd:%s",
249
	    skip_path_dev(devicename)) == -1) {
250
		config_info = NULL;
251
		goto error;
252
	}
253
254
	if (device_is_duplicate(config_info))
255
		goto duplicate;
256
257
	options = input_option_new(options, "config_info", config_info);
258
	if (options == NULL)
259
		goto error;
260
261
	LogMessage(X_INFO, "config/devd: adding input device '%s'\n",
262
	    devicename);
263
264
	NewInputDeviceRequest(options, &attrs, &dev);
265
	goto done;
266
267
duplicate:
268
	LogMessage(X_WARNING, "config/devd: device '%s' already "
269
	    "added. Ignoring\n", devicename);
270
	goto done;
271
272
error:
273
	LogMessage(X_INFO, "config/devd: error adding device '%s'\n",
274
	    devicename);
275
	goto done;
276
277
ignore:
278
	LogMessage(X_INFO, "config/devd: ignoring device '%s'\n",
279
	    devicename);
280
	goto done;
281
282
done:
283
	free(config_info);
284
	input_option_free_list(&options);
285
	free(attrs.usb_id);
286
	free(attrs.product);
287
	free(attrs.device);
288
	free(attrs.vendor);
289
	free(vendor);
290
}
291
292
static void
293
devpath_scan_sub(char *path, int off, int rem)
294
{
295
	struct dirent *entry;
296
	DIR *dp;
297
298
	if ((dp = opendir(path)) == NULL) {
299
		LogMessage(X_INFO, "Cannot open directory '%s'\n", path);
300
		return;
301
	}
302
	while ((entry = readdir(dp)) != NULL) {
303
		int len = strlen(entry->d_name);
304
		if (len > rem)
305
			continue;
306
		strcpy(path + off, entry->d_name);
307
		off += len;
308
		rem -= len;
309
		switch (entry->d_type) {
310
		case DT_DIR:
311
			if (strcmp(entry->d_name, ".") == 0 ||
312
			    strcmp(entry->d_name, "..") == 0)
313
				break;
314
			if (rem < 1)
315
				break;
316
			path[off] = '/';
317
			path[off+1] = '\0';
318
			off++;
319
			rem--;
320
			/* recurse */
321
			devpath_scan_sub(path, off, rem);
322
			off--;
323
			rem++;
324
			break;
325
		case DT_SOCK:
326
		case DT_FIFO:
327
		case DT_LNK:
328
		case DT_CHR:
329
			/* add device, if any */			
330
			device_added(path, false);
331
			break;
332
		default:
333
			break;
334
		}
335
		off -= len;
336
		rem += len;
337
	}
338
	closedir(dp);
339
}
340
341
static void
342
devpath_scan(void)
343
{
344
	char path[PATH_MAX + 1];
345
346
	strlcpy(path, _PATH_DEV, sizeof(path));
347
348
	devpath_scan_sub(path, strlen(path), PATH_MAX - strlen(path));
349
}
350
351
static void
352
device_removed(char *devicename)
353
{
354
	char *config_info;
355
356
	if (asprintf(&config_info, "devd:%s",
357
	    skip_path_dev(devicename)) == -1)
358
		return;
359
360
	if (device_is_duplicate(config_info)) {
361
		LogMessage(X_INFO, "config/devd: removing input device '%s'\n",
362
		    devicename);
363
	}
364
	remove_devices("devd", config_info);
365
366
	free(config_info);
367
}
368
369
static bool is_kbdmux_enabled(void)
370
{
371
	/* Xorg uses /dev/ttyv0 as a console device */
372
	static const char device[]= { _PATH_DEV "ttyv0" };
373
	keyboard_info_t info;
374
	int fd;
375
376
	fd = open(device, O_RDONLY);
377
378
	if (fd < 0)
379
		return false;
380
381
	if (ioctl(fd, KDGKBINFO, &info) == -1) {
382
		close(fd);
383
		return false;
384
	}
385
386
	close(fd);
387
388
	if (!strncmp(info.kb_name, "kbdmux", 6))
389
		return true;
390
391
	return false;
392
}
393
394
static void
395
disconnect_devd(int sock)
396
{
397
	if (sock >= 0) {
398
		RemoveGeneralSocket(sock);
399
		close(sock);
400
	}
401
}
402
403
static int
404
connect_devd(void)
405
{
406
	struct sockaddr_un devd;
407
	int sock;
408
409
	sock = socket(AF_UNIX, SOCK_STREAM, 0);
410
	if (sock < 0) {
411
		LogMessage(X_ERROR, "config/devd: fail opening stream socket\n");
412
		return -1;
413
	}
414
415
	devd.sun_family = AF_UNIX;
416
	strlcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(devd.sun_path));
417
418
	if (connect(sock, (struct sockaddr *) &devd, sizeof(devd)) < 0) {
419
		close(sock);
420
		LogMessage(X_ERROR, "config/devd: fail to connect to devd\n");
421
		return -1;
422
	}
423
424
	AddGeneralSocket(sock);
425
426
	return	sock;
427
}
428
429
static CARD32
430
reconnect_handler(OsTimerPtr timer, CARD32 time, pointer arg)
431
{
432
	int newsock;
433
434
	if ((newsock = connect_devd()) > 0) {
435
		sock_devd = newsock;
436
		TimerFree(rtimer);
437
		rtimer = NULL;
438
		LogMessage(X_INFO, "config/devd: reopening devd socket\n");
439
		return 0;
440
	}
441
442
	/* Try again after RECONNECT_DELAY */
443
	return RECONNECT_DELAY;
444
}
445
446
static ssize_t
447
socket_getline(int fd, char **out)
448
{
449
	char *buf, *newbuf;
450
	ssize_t ret, cap, sz = 0;
451
	char c;
452
453
	cap = 1024;
454
	buf = malloc(cap * sizeof(char));
455
	if (!buf)
456
		return -1;
457
458
	for (;;) {
459
		ret = read(sock_devd, &c, 1);
460
		if (ret < 0) {
461
			if (errno == EINTR)
462
				continue;
463
			free(buf);
464
			return -1;
465
		/* EOF - devd socket is lost */
466
		} else if (ret == 0) {
467
			disconnect_devd(sock_devd);
468
			rtimer = TimerSet(NULL, 0, 1, reconnect_handler, NULL);
469
			LogMessage(X_WARNING, "config/devd: devd socket is lost\n");
470
			return -1;
471
		}
472
		if (c == '\n')
473
			break;
474
475
		if (sz + 1 >= cap) {
476
			cap *= 2;
477
			newbuf = realloc(buf, cap * sizeof(char));
478
			if (!newbuf) {
479
				free(buf);
480
				return -1;
481
			}
482
			buf = newbuf;
483
		}
484
		buf[sz] = c;
485
		sz++;
486
	}
487
488
	buf[sz] = '\0';
489
	if (sz >= 0)
490
		*out = buf;
491
	else
492
		free(buf);
493
494
	/* Number of bytes in the line, not counting the line break */
495
	return sz;
496
}
497
498
static void
499
wakeup_handler(void *data, int err, void *read_mask)
500
{
501
	static const char cdev_create[] = { "!system=DEVFS subsystem=CDEV type=CREATE cdev=" };
502
	static const char cdev_destroy[] = { "!system=DEVFS subsystem=CDEV type=DESTROY cdev=" };
503
	static const char cdev_path[] = { _PATH_DEV };
504
	char *line = NULL;
505
	char *devicename;
506
	char *walk;
507
508
	if (err < 0)
509
		return;
510
511
	if (FD_ISSET(sock_devd, (fd_set *) read_mask)) {
512
		if (socket_getline(sock_devd, &line) < 0)
513
			return;
514
		if (strstr(line, cdev_create) == line) {
515
			devicename = line + strlen(cdev_create) - strlen(cdev_path);
516
			memcpy(devicename, cdev_path, strlen(cdev_path));
517
			walk = strchr(devicename, ' ');
518
			if (walk != NULL)
519
				walk[0] = '\0';
520
			device_added(devicename, false);
521
		} else if (strstr(line, cdev_destroy) == line) {
522
			devicename = line + strlen(cdev_destroy) - strlen(cdev_path);
523
			memcpy(devicename, cdev_path, strlen(cdev_path));
524
			walk = strchr(devicename, ' ');
525
			if (walk != NULL)
526
				walk[0] = '\0';
527
			device_removed(devicename);
528
		}
529
		free(line);
530
	}
531
}
532
533
static void
534
block_handler(void *data, struct timeval **tv, void *read_mask)
535
{
536
}
537
538
int
539
config_devd_init(void)
540
{
541
	LogMessage(X_INFO, "config/devd: probing input devices...\n");
542
543
	/* Check if kbdmux is enabled */
544
	is_kbdmux = is_kbdmux_enabled();
545
546
	/* Try to add kbdmux device first */
547
	if (is_kbdmux)
548
		device_added(_PATH_DEV "kbdmux0", true);
549
550
	/* Connect to devd, so that we don't loose any events */
551
	if ((sock_devd = connect_devd()) < 0)
552
		return 0;
553
554
	/* Scan what is currently connected */
555
	devpath_scan();
556
557
	/* Register wakeup handler */
558
	RegisterBlockAndWakeupHandlers(block_handler,
559
	    wakeup_handler, NULL);
560
561
	return 1;
562
}
563
564
void
565
config_devd_fini(void)
566
{
567
	LogMessage(X_INFO, "config/devd: terminating backend...\n");
568
569
	if (rtimer) {
570
		TimerFree(rtimer);
571
		rtimer = NULL;
572
	}
573
574
	disconnect_devd(sock_devd);
575
576
	RemoveBlockAndWakeupHandlers(block_handler,
577
	    wakeup_handler, NULL);
578
}

Return to bug 196678