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

Collapse All | Expand All

(-)config/devd.c (+748 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
 * Copyright (c) 2015 - 2016 Rozhuk Ivan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a
9
 * copy of this software and associated documentation files (the "Software"),
10
 * to deal in the Software without restriction, including without limitation
11
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
 * and/or sell copies of the Software, and to permit persons to whom the
13
 * Software is furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice (including the next
16
 * paragraph) shall be included in all copies or substantial portions of the
17
 * Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
 * DEALINGS IN THE SOFTWARE.
26
 *
27
 * Author: Baptiste Daroussin <bapt@FreeBSD.org>
28
 */
29
30
#ifdef HAVE_DIX_CONFIG_H
31
#include <dix-config.h>
32
#endif
33
34
#include <sys/types.h>
35
#include <sys/kbio.h>
36
#include <sys/socket.h>
37
#include <sys/stat.h>
38
#include <sys/sysctl.h>
39
#include <sys/un.h>
40
#include <sys/mouse.h>
41
#include <sys/consio.h>
42
43
#include <ctype.h>
44
#include <dirent.h>
45
#include <errno.h>
46
#include <fcntl.h>
47
#include <stdlib.h>
48
#include <stdio.h>
49
#include <stdbool.h>
50
#include <unistd.h>
51
#include <string.h>
52
#include <paths.h>
53
54
#include "input.h"
55
#include "inputstr.h"
56
#include "hotplug.h"
57
#include "config-backends.h"
58
#include "os.h"
59
60
61
/* from: <linux/input.h> */
62
#define	_IOC_READ   IOC_OUT
63
struct input_id {
64
	uint16_t bustype;
65
	uint16_t vendor;
66
	uint16_t product;
67
	uint16_t version;
68
};
69
70
#define EVIOCGID		_IOR('E', 0x02, struct input_id)	/* get device ID */
71
#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
72
73
/* from: <linux/joystick.h> */
74
#define JSIOCGNAME(len)		_IOC(_IOC_READ, 'j', 0x13, len)		/* get identifier string */
75
76
/* WebCamD specific. */
77
#define WEBCAMD_IOCTL_GET_USB_VENDOR_ID _IOR('q', 250, unsigned short)
78
#define WEBCAMD_IOCTL_GET_USB_PRODUCT_ID _IOR('q', 251, unsigned short)
79
#define WEBCAMD_IOCTL_GET_USB_SPEED	_IOR('q', 252, unsigned int)
80
81
82
#define _PATH_DEV_LEN		(sizeof(_PATH_DEV) - 1)
83
#define DEVD_PATH_DEV		"devd:" _PATH_DEV
84
#define DEVD_PATH_DEV_LEN	(sizeof(DEVD_PATH_DEV) - 1)
85
86
#define DEVD_SOCK_PATH		_PATH_VARRUN "devd.pipe"
87
88
#define DEVD_EVENT_ADD		'+'
89
#define DEVD_EVENT_REMOVE	'-'
90
#define DEVD_EVENT_NOTIFY	'!'
91
92
#define RECONNECT_DELAY		(5 * 1000)
93
94
95
#define is_meuqual(__v1, __v1sz, __v2, __v2sz)				\
96
	((__v1sz) == (__v2sz) && NULL != (__v1) && NULL != (__v2) &&	\
97
	 0 == memcmp((__v1), (__v2), (__v1sz)))
98
99
#define is_meuqual_cstr(__cstr, __v, __vsz)				\
100
	is_meuqual(__cstr, (sizeof(__cstr) - 1), __v, __vsz)
101
102
#define is_de_euqual_cstr(__de, __cstr)					\
103
	(NULL != (__de) &&						\
104
	 is_meuqual((__de)->d_name, (__de)->d_namlen, __cstr, (sizeof(__cstr) - 1)))
105
106
#define devd_get_val_cstr(__cstr, __buf, __bufsz, __valsz)		\
107
	devd_get_val((__buf), (__bufsz), __cstr, (sizeof(__cstr) - 1),	\
108
	(__valsz))
109
110
111
static int devd_skt;
112
static char devd_buf[4096];
113
static size_t devd_buf_used = 0;
114
static int is_kbdmux = 0;
115
OsTimerPtr rtimer;
116
117
118
/* Input devices. */
119
typedef struct hw_type_s {
120
	const char	*dev_name;
121
	size_t		dev_name_size;
122
	size_t		path_offset;
123
	int		flags;
124
	const char	*xdriver; 
125
} hw_type_t, *hw_type_p;
126
127
/* xdriver can be set via config "InputClass" section.
128
 * Do not set xdriver name if device have more than one
129
 * xf86-input-* drivers.
130
 * "input/event" can be hadled by: xf86-input-evdev and
131
 * xf86-input-wacom, let user chooses.
132
 */
133
static hw_type_t hw_types[] = {
134
	{ "ukbd",	4, 0, ATTR_KEYBOARD, "kbd" },
135
	{ "atkbd",	5, 0, ATTR_KEYBOARD, "kbd" },
136
	{ "kbdmux",	6, 0, ATTR_KEYBOARD, "kbd" },
137
	{ "sysmouse",	8, 0, ATTR_POINTER, "mouse" },
138
	{ "ums",	3, 0, ATTR_POINTER, "mouse" },
139
	{ "psm",	3, 0, ATTR_POINTER, "mouse" },
140
	{ "vboxguest",	9, 0, ATTR_POINTER, "vboxmouse" },
141
	{ "joy",	3, 0, ATTR_JOYSTICK, NULL },
142
	{ "atp",	3, 0, ATTR_TOUCHPAD, NULL },
143
	{ "uep",	3, 0, ATTR_TOUCHSCREEN, NULL },
144
	{ "input/event",5, 6, ATTR_TOUCHPAD, NULL },
145
	{ "input/js",	2, 6, ATTR_JOYSTICK, NULL },
146
	{ NULL,		0, 0, 0, NULL },
147
};
148
149
/* Input devices paths. */
150
static hw_type_t hw_type_path[] = {
151
	{ "input/",	0, 6, 0, NULL },
152
	{ NULL,		0, 0, 0, NULL },
153
};
154
155
156
157
static hw_type_p
158
get_dev_type_by_name(const char *dev_name, size_t dev_name_size)
159
{
160
	size_t i;
161
162
	if (NULL == dev_name || 0 == dev_name_size)
163
		return (NULL);
164
	for (i = 0; NULL != hw_types[i].dev_name; i ++) {
165
		if (dev_name_size >= (hw_types[i].dev_name_size + hw_types[i].path_offset) &&
166
		    0 == memcmp(dev_name, hw_types[i].dev_name,
167
		    (hw_types[i].path_offset + hw_types[i].dev_name_size))) {
168
			return (&hw_types[i]);
169
		}
170
	}
171
	return (NULL);
172
}
173
174
static hw_type_p
175
get_dev_type_by_path(const char *dev_name, size_t dev_name_size,
176
    hw_type_p hw_type_cust)
177
{
178
	size_t i;
179
180
	if (NULL == dev_name || 0 == dev_name_size || NULL == hw_type_cust)
181
		return (NULL);
182
	for (i = 0; NULL != hw_type_path[i].dev_name; i ++) {
183
		if (dev_name_size <= hw_type_path[i].path_offset ||
184
		    0 != memcmp(dev_name, hw_type_path[i].dev_name,
185
		    hw_type_path[i].path_offset))
186
			continue;
187
		/* Path in white list. */
188
		hw_type_cust->dev_name = dev_name;
189
		hw_type_cust->path_offset = hw_type_path[i].path_offset;
190
		for (i = hw_type_cust->path_offset; i < dev_name_size; i ++) {
191
			if (isdigit(dev_name[i]))
192
				break;
193
		}
194
		hw_type_cust->dev_name_size = (i - hw_type_cust->path_offset);
195
		hw_type_cust->flags = hw_type_path[i].flags;
196
		hw_type_cust->xdriver = hw_type_path[i].xdriver;
197
		return (hw_type_cust);
198
	}
199
	return (NULL);
200
}
201
202
static int
203
is_kbdmux_enabled(void)
204
{
205
	/* Xorg uses /dev/ttyv0 as a console device */
206
	/* const char device[]="/dev/console"; */
207
	int fd;
208
	const char *device = _PATH_TTY "v0";
209
	keyboard_info_t info;
210
211
	fd = open(device, O_RDONLY);
212
	if (0 > fd)
213
		return (0);
214
	if (-1 == ioctl(fd, KDGKBINFO, &info) ||
215
	    0 != memcmp(info.kb_name, "kbdmux", 6)) {
216
		close(fd);
217
		return (0);
218
	}
219
	close(fd);
220
	return (1);
221
}
222
223
static char *
224
sysctl_get_str(const char *sysctlname, size_t *size_ret)
225
{
226
	char *dest;
227
	size_t len;
228
229
	if (NULL == sysctlname)
230
		return (NULL);
231
	if (0 != sysctlbyname(sysctlname, NULL, &len, NULL, 0))
232
		return (NULL);
233
	dest = malloc(len + 4);
234
	if (NULL == dest)
235
		return (NULL);
236
	if (0 != sysctlbyname(sysctlname, dest, &len, NULL, 0)) {
237
		free(dest);
238
		return (NULL);
239
	}
240
	dest[len] = 0;
241
	if (NULL != size_ret)
242
		(*size_ret) = len;
243
	return (dest);
244
}
245
246
static char *
247
devd_get_val(char *buf, size_t buf_size, const char *val_name,
248
    size_t val_name_size, size_t *val_size)
249
{
250
	char *ret, *buf_end, *ptr;
251
252
	buf_end = (buf + buf_size);
253
	for (ret = buf; ret != NULL && ret < buf_end;) {
254
		ret = memmem(ret, (buf_end - ret), val_name, val_name_size);
255
		if (ret == NULL)
256
			return (NULL);
257
		/* Found. */
258
		/* Check: space before or buf+1. */
259
		if ((buf + 1) < ret && ret[-1] != ' ') {
260
			ret += val_name_size;
261
			continue;
262
		}
263
		/* Check: = after name and size for value. */
264
		ret += val_name_size;
265
		if ((ret + 1) >= buf_end)
266
			return (NULL);
267
		if ('=' != ret[0])
268
			continue;
269
		ret ++;
270
		break;
271
	}
272
	if (ret == NULL || val_size == NULL)
273
		return (ret);
274
	/* Calc value data size. */
275
	ptr = memchr(ret, ' ', (buf_end - ret));
276
	if (ptr == NULL) /* End of string/last value. */
277
		ptr = buf_end;
278
	(*val_size) = (ptr - ret);
279
	return (ret);
280
}
281
282
static void
283
device_added(const char *dev_name, size_t dev_name_size, int allow_no_device)
284
{
285
	int fd;
286
	InputOption *options = NULL;
287
	InputAttributes attrs;
288
	DeviceIntPtr dev_iptr = NULL;
289
	keyboard_info_t kbdi;
290
	mousehw_t mshw;
291
	struct input_id iid;
292
	char *dev_path, config_info[(PATH_MAX + 32)];
293
	char sysctlname[PATH_MAX], *sdata, *ptr_pid, *ptr_vid;
294
	char pnp_usb_id[PATH_MAX], vendor[PATH_MAX], product[PATH_MAX];
295
	size_t sdata_size, pid_size, vid_size;
296
	hw_type_t *hwtype, hwtype_cust;
297
	unsigned short vid, pid;
298
299
300
	if (NULL == dev_name || 0 == dev_name_size || PATH_MAX < dev_name_size)
301
		return;
302
	/* Make dev_name null ended string. */
303
	memcpy(config_info, DEVD_PATH_DEV, DEVD_PATH_DEV_LEN);
304
	memcpy((config_info + DEVD_PATH_DEV_LEN), dev_name, dev_name_size);
305
	config_info[(DEVD_PATH_DEV_LEN + dev_name_size)] = 0;
306
	/* Set / update pointers to dev_name and dev_path. */
307
	dev_path = (config_info + 5); /* Skip: "devd:" */
308
	dev_name = (dev_path + _PATH_DEV_LEN); /* Skip: "/dev/" */
309
310
	/* Is known input device or path? */
311
	hwtype = get_dev_type_by_name(dev_name, dev_name_size);
312
	if (NULL == hwtype) {
313
		hwtype = get_dev_type_by_path(dev_name, dev_name_size,
314
		    &hwtype_cust);
315
	}
316
	if (NULL == hwtype) /* Not found in white list. */
317
		return;
318
	/* Init and set attributes. */
319
	memset(&attrs, 0, sizeof(attrs));
320
	attrs.device = dev_path;
321
	attrs.flags = hwtype->flags;
322
323
	/* Skip keyboard devices if kbdmux is enabled */
324
	if (is_kbdmux && 0 == allow_no_device && (hwtype->flags & ATTR_KEYBOARD)) {
325
		LogMessage(X_INFO, "config/devd: kbdmux is enabled, ignoring device %s\n",
326
		    dev_name);
327
		return;
328
	}
329
	/* Skip duplicate devices. */
330
	if (device_is_duplicate(config_info)) {
331
		LogMessage(X_WARNING, "config/devd: device %s already added. ignoring\n",
332
		    dev_path);
333
		return;
334
	}
335
336
	/* Try to open device. */
337
	fd = open(dev_path, O_RDONLY);
338
	if (0 > fd) {
339
		if (0 == (hwtype->flags & ATTR_KEYBOARD)) {
340
			LogMessage(X_INFO, "config/devd: device %s already opened\n",
341
			    dev_path);
342
			/*
343
			 * Fail if cannot open device, it breaks AllowMouseOpenFail,
344
			 * but it should not matter when config/devd enabled
345
			 */
346
			return;
347
		}
348
		if (0 == allow_no_device) {
349
			/*
350
			 * There can be only one keyboard attached to console and
351
			 * it is already added.
352
			 */
353
			LogMessage(X_WARNING, "config/devd: console keyboard is "
354
			    "already added, ignoring %s\n",
355
			    dev_path);
356
			return;
357
		}
358
		/*
359
		 * Don't pass "device" option if the keyboard is already
360
		 * attached to the console (ie. open() fails).
361
		 * This would activate a special logic in xf86-input-keyboard.
362
		 * Prevent any other attached to console keyboards being
363
		 * processed. There can be only one such device.
364
		 */
365
		goto skip_ioctl;
366
	}
367
368
	/* Try to get device info via ioctl(). */
369
	if (-1 != ioctl(fd, KDGKBINFO, &kbdi)) { /* Is this keyboard? */
370
		memcpy(product, kbdi.kb_name, sizeof(kbdi.kb_name));
371
		attrs.product = product;
372
		attrs.flags = ATTR_KEYBOARD;
373
		LogMessage(X_INFO, "config/devd: detected keyboard: %s, kb_index=%i, kb_unit=%i, kb_type=%i, kb_config=%i\n",
374
		    kbdi.kb_name, kbdi.kb_index, kbdi.kb_unit, kbdi.kb_type, kbdi.kb_config);
375
	} else if (-1 != ioctl(fd, MOUSE_GETHWINFO, &mshw)) { /* Is this mouse? */
376
#ifdef notyet /* FreeBSD mouse drivers does not return real vid+pid. */
377
		/* construct USB ID in lowercase hex - "0000:ffff" */
378
		snprintf(pnp_usb_id, sizeof(pnp_usb_id), "%04x:%04x",
379
		    mshw.hwid, mshw.model);
380
		attrs.usb_id = pnp_usb_id;
381
#endif
382
383
		switch (mshw.type) {
384
		case MOUSE_MOUSE:
385
		case MOUSE_TRACKBALL:
386
		case MOUSE_STICK:
387
			attrs.flags = ATTR_POINTER;
388
			break;
389
		case MOUSE_PAD:
390
			attrs.flags = ATTR_TOUCHPAD;		
391
			break;
392
		}
393
		LogMessage(X_INFO, "config/devd: detected mouse: hwid=%04x, model=%04x, type=%04x, iftype=%04x, buttons=%04x\n",
394
		    mshw.hwid, mshw.model, mshw.type, mshw.iftype, mshw.buttons);
395
	} else if (-1 != ioctl(fd, JSIOCGNAME((sizeof(product) - 1)), product)) { /* Is this joystick? */
396
		attrs.product = product;
397
		attrs.flags = ATTR_JOYSTICK;
398
		LogMessage(X_INFO, "config/devd: detected joystick: %s\n",
399
		    product);
400
	} else if (-1 != ioctl(fd, EVIOCGID, &iid) &&
401
	    -1 != ioctl(fd, EVIOCGNAME((sizeof(product) - 1)), product)) { /* Is this event? */
402
		/* construct USB ID in lowercase hex - "0000:ffff" */
403
		snprintf(pnp_usb_id, sizeof(pnp_usb_id), "%04x:%04x",
404
		    iid.vendor, iid.product);
405
		attrs.usb_id = pnp_usb_id;
406
		attrs.product = product;
407
		attrs.flags = (ATTR_POINTER | ATTR_TABLET | ATTR_TOUCHPAD);
408
		LogMessage(X_INFO, "config/devd: detected event: %s, bustype=%04x, vendor=%04x, product=%04x, version=%04x\n",
409
		    product, iid.bustype, iid.vendor, iid.product, iid.version);
410
	}
411
412
	if (NULL == attrs.usb_id) { /* Is this webcamd device? */
413
		if (-1 != ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vid) &&
414
		    -1 != ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &pid)) {
415
			snprintf(pnp_usb_id, sizeof(pnp_usb_id),
416
			    "%04x:%04x", vid, pid);
417
			attrs.usb_id = pnp_usb_id;
418
			LogMessage(X_INFO, "config/devd: WebCamD device: %s\n", pnp_usb_id);
419
		}
420
	}
421
422
	close(fd);
423
424
skip_ioctl:
425
	/* Try to get device info via sysctl(). */
426
	if (NULL == attrs.usb_id && NULL == attrs.pnp_id) {
427
		snprintf(sysctlname, sizeof(sysctlname), "dev.%.*s.%s.%%pnpinfo",
428
		    (int)hwtype->dev_name_size, (hwtype->dev_name + hwtype->path_offset),
429
		    (dev_name + hwtype->path_offset + hwtype->dev_name_size));
430
		sdata = sysctl_get_str(sysctlname, &sdata_size);
431
		if (NULL != sdata) {
432
			ptr_vid = devd_get_val_cstr("vendor",
433
			    sdata, sdata_size, &vid_size);
434
			ptr_pid = devd_get_val_cstr("product",
435
			    sdata, sdata_size, &pid_size);
436
			if (NULL != ptr_vid && NULL != ptr_pid) { /* usb_id */
437
				ptr_vid[vid_size] = 0;
438
				ptr_pid[pid_size] = 0;
439
				snprintf(pnp_usb_id, sizeof(pnp_usb_id),
440
				    "%s:%s", ptr_vid, ptr_pid);
441
				attrs.usb_id = pnp_usb_id;
442
				LogMessage(X_INFO, "config/devd: usb_id: %s\n", pnp_usb_id);
443
			} else { /* pnp_id */
444
				strlcpy(pnp_usb_id, sdata, sizeof(pnp_usb_id));
445
				attrs.pnp_id = pnp_usb_id;
446
			}
447
			free(sdata);
448
		}
449
	}
450
	if (NULL == attrs.vendor || NULL == attrs.product) {
451
		snprintf(sysctlname, sizeof(sysctlname), "dev.%.*s.%s.%%desc",
452
		    (int)hwtype->dev_name_size, (hwtype->dev_name + hwtype->path_offset),
453
		    (dev_name + hwtype->path_offset + hwtype->dev_name_size));
454
		sdata = sysctl_get_str(sysctlname, &sdata_size);
455
		if (NULL != sdata) {
456
			/* Vendor. */
457
			ptr_pid = memchr(sdata, ' ', sdata_size);
458
			if (NULL != ptr_pid)
459
				ptr_pid[0] = 0;
460
			strlcpy(vendor, sdata, sizeof(vendor));
461
			attrs.vendor = vendor;
462
			/* Product. */
463
			if (NULL == attrs.product && NULL != ptr_pid) {
464
				ptr_pid ++;
465
				ptr_vid = memchr(ptr_pid, ',',
466
				    (sdata_size - (ptr_pid - sdata)));
467
				if (NULL != ptr_vid)
468
					ptr_vid[0] = 0;
469
				strlcpy(product, ptr_pid, sizeof(product));
470
				attrs.product = product;
471
			} else {
472
				product[0] = 0;
473
			}
474
			free(sdata);
475
			LogMessage(X_INFO, "config/devd: vendor: %s, product: %s\n", vendor, product);
476
		}
477
	}
478
479
	/* Init options. */
480
	options = input_option_new(NULL, "_source", "server/devd");
481
	if (NULL == options)
482
		goto err_out;
483
484
	options = input_option_new(options, "config_info", config_info);
485
	if (NULL == options)
486
		goto err_out;
487
488
	options = input_option_new(options, "name",
489
	    ((NULL != attrs.product) ? attrs.product : dev_name));
490
	if (NULL == options)
491
		goto err_out;
492
493
	if (fd >= 0) {
494
		options = input_option_new(options, "device", dev_path);
495
		if (NULL == options)
496
			goto err_out;
497
	}
498
499
	if (NULL != hwtype->xdriver) {
500
		options = input_option_new(options, "driver", hwtype->xdriver);
501
		if (NULL == options)
502
			goto err_out;
503
	}
504
505
	LogMessage(X_INFO, "config/devd: adding input device %s\n", dev_path);
506
	NewInputDeviceRequest(options, &attrs, &dev_iptr);
507
	goto done;
508
509
err_out:
510
	LogMessage(X_INFO, "config/devd: error adding device '%s'\n",
511
	    dev_path);
512
done:
513
	input_option_free_list(&options);
514
}
515
516
static void
517
device_removed(const char *dev_name, size_t dev_name_size)
518
{
519
	char config_info[(PATH_MAX + 32)];
520
	hw_type_t hwtype_cust;
521
522
	if (NULL == dev_name || 0 == dev_name_size || PATH_MAX < dev_name_size)
523
		return;
524
	if (NULL == get_dev_type_by_name(dev_name, dev_name_size) &&
525
	    NULL == get_dev_type_by_path(dev_name, dev_name_size, &hwtype_cust))
526
		return; /* Device not in list - unknown. */
527
	memcpy(config_info, DEVD_PATH_DEV, DEVD_PATH_DEV_LEN);
528
	memcpy((config_info + DEVD_PATH_DEV_LEN), dev_name, dev_name_size);
529
	config_info[(DEVD_PATH_DEV_LEN + dev_name_size)] = 0;
530
531
	/* Skip non added devices. */
532
	if (device_is_duplicate(config_info)) {
533
		LogMessage(X_INFO, "config/devd: removing input device '%s'\n",
534
		    (config_info + DEVD_PATH_DEV_LEN));
535
	}
536
	remove_devices("devd", config_info);
537
}
538
539
540
static void
541
disconnect_devd(int sock)
542
{
543
	if (0 > sock)
544
		return;
545
	RemoveGeneralSocket(sock);
546
	close(sock);
547
}
548
549
static int
550
connect_devd(void)
551
{
552
	int sock;
553
	struct sockaddr_un devd;
554
555
	sock = socket(AF_UNIX, SOCK_STREAM, 0);
556
	if (0 > sock) {
557
		LogMessage(X_ERROR, "config/devd: fail opening stream socket\n");
558
		return (-1);
559
	}
560
561
	devd.sun_family = AF_UNIX;
562
	memcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(DEVD_SOCK_PATH));
563
	if (0 > connect(sock, (struct sockaddr*)&devd, sizeof(devd))) {
564
		close(sock);
565
		LogMessage(X_ERROR, "config/devd: fail to connect to devd\n");
566
		return (-1);
567
	}
568
569
	AddGeneralSocket(sock);
570
571
	return (sock);
572
}
573
574
static CARD32
575
reconnect_handler(OsTimerPtr timer, CARD32 time, void *arg)
576
{
577
	devd_buf_used = 0;
578
	devd_skt = connect_devd();
579
	if (-1 == devd_skt) /* Try again after RECONNECT_DELAY */
580
		return (RECONNECT_DELAY);
581
	TimerFree(rtimer);
582
	rtimer = NULL;
583
	LogMessage(X_INFO, "config/devd: reopening devd socket\n");
584
585
	return (0);
586
}
587
588
static void
589
wakeup_handler(void *data, int err, void *read_mask)
590
{
591
	int error;
592
	char *ptr, *line, *val, *cdev;
593
	size_t line_size, val_size, cdev_size;
594
	ssize_t ios;
595
596
	if (0 > err)
597
		return;
598
	if (!FD_ISSET(devd_skt, (fd_set*)read_mask))
599
		return;
600
	/* Read new data. */
601
	for (;;) {
602
		ios = recv(devd_skt, (devd_buf + devd_buf_used), 
603
		    (sizeof(devd_buf) - devd_buf_used), MSG_DONTWAIT);
604
		if (0 < ios) { /* Read OK. */
605
			devd_buf_used += ios;
606
			continue; /* Try to read more. */
607
		}
608
		/* Something wrong. */
609
		error = errno;
610
		if (error == EAGAIN)
611
			break; /* All avaible data readed. */
612
		if (error == EINTR)
613
			continue;
614
		if (sizeof(devd_buf) == devd_buf_used) { /* Message to long, reset buf. */
615
			devd_buf_used = 0;
616
			continue;
617
		}
618
		/* devd socket is lost */
619
		disconnect_devd(devd_skt);
620
		rtimer = TimerSet(NULL, 0, 1, reconnect_handler, NULL);
621
		LogMessage(X_WARNING, "config/devd: devd socket read error: %i - %s\n", error, strerror(error));
622
		return;
623
	}
624
	ptr = memchr(devd_buf, '\n', devd_buf_used);
625
	if (NULL == ptr)
626
		return;
627
628
	/* Process data. */
629
	line = (devd_buf + 1);
630
	for (;;) {
631
		line_size = (ptr - line);
632
		if (DEVD_EVENT_NOTIFY != (*(line - 1)))
633
			goto move_next_event; /* Handle only notify. */
634
		/* Check: is system=DEVFS. */
635
		val = devd_get_val_cstr("system", line, line_size, &val_size);
636
		if (!is_meuqual_cstr("DEVFS", val, val_size))
637
			goto move_next_event;
638
		/* Check: is subsystem=CDEV. */
639
		val = devd_get_val_cstr("subsystem", line, line_size, &val_size);
640
		if (!is_meuqual_cstr("CDEV", val, val_size))
641
			goto move_next_event;
642
		/* Get device name. */
643
		cdev = devd_get_val_cstr("cdev", line, line_size, &cdev_size);
644
		if (NULL == cdev)
645
			goto move_next_event;
646
		/* Get event type. */
647
		val = devd_get_val_cstr("type", line, line_size, &val_size);
648
		if (is_meuqual_cstr("CREATE", val, val_size)) {
649
			device_added(cdev, cdev_size, 0);
650
		} else if (is_meuqual_cstr("DESTROY", val, val_size)) {
651
			device_removed(cdev, cdev_size);
652
		}
653
654
move_next_event:
655
		line += (line_size + 2); /* Skip '\n' and event type byte. */
656
		line_size = (line - devd_buf);
657
		if (devd_buf_used <= line_size) {
658
			devd_buf_used = 0;
659
			return;
660
		}
661
		ptr = memchr(line, '\n', (devd_buf_used - line_size));
662
		if (NULL == ptr)
663
			break;
664
	}
665
	/* Save line without end marker. */
666
	devd_buf_used -= (line_size - 1);
667
	memmove(devd_buf, (line - 1), devd_buf_used);
668
}
669
670
static void
671
block_handler(void *data, struct timeval **tv, void *read_mask)
672
{
673
}
674
675
int
676
config_devd_init(void)
677
{
678
	size_t i, j, dir_cnt, sdir_cnt, tm;
679
	char devicename[PATH_MAX];
680
	struct dirent *de, **namelist, *sde, **snamelist;
681
682
	LogMessage(X_INFO, "config/devd: probing input devices...\n");
683
684
	/*
685
	 * Add fake keyboard and give up on keyboards management
686
	 * if kbdmux is enabled
687
	 */
688
	is_kbdmux = is_kbdmux_enabled();
689
	if (is_kbdmux)
690
		device_added("kbdmux0", 7, 1);
691
692
	/* Scan /dev/ for devices. */
693
	dir_cnt = scandir(_PATH_DEV, &namelist, 0, alphasort);
694
	for (i = 0; i < dir_cnt; i ++) {
695
		de = namelist[i];
696
		if (is_de_euqual_cstr(de, ".") ||
697
		    is_de_euqual_cstr(de, "..")) {
698
			free(de);
699
			continue;
700
		}
701
		if (DT_DIR != de->d_type) {
702
			device_added(de->d_name, de->d_namlen, 0);
703
		} else { /* Sub folder. */
704
			snprintf(devicename, sizeof(devicename),
705
			    _PATH_DEV "%s", de->d_name);
706
			sdir_cnt = scandir(devicename, &snamelist, 0, alphasort);
707
			for (j = 0; j < sdir_cnt; j ++) {
708
				sde = snamelist[j];
709
				if (!is_de_euqual_cstr(sde, ".") &&
710
				    !is_de_euqual_cstr(sde, "..") &&
711
				    DT_DIR != sde->d_type) {
712
					tm = snprintf(devicename, sizeof(devicename),
713
					    "%s/%s", de->d_name, sde->d_name);
714
					device_added(devicename, tm, 0);
715
				}
716
				free(sde);
717
			}
718
			free(snamelist);
719
		}
720
		free(de);
721
	}
722
	free(namelist);
723
724
	devd_buf_used = 0;
725
	devd_skt = connect_devd();
726
	if (-1 == devd_skt)
727
		return (0);
728
729
	/* Register wakeup handler */
730
	RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL);
731
732
	return (1);
733
}
734
735
void
736
config_devd_fini(void)
737
{
738
	LogMessage(X_INFO, "config/devd: terminating backend...\n");
739
740
	if (rtimer) {
741
		TimerFree(rtimer);
742
		rtimer = NULL;
743
	}
744
745
	disconnect_devd(devd_skt);
746
747
	RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL);
748
}

Return to bug 196678