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

Collapse All | Expand All

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

Return to bug 196678