hotplug2: patches from OpenWRT/svn
[tomato.git] / release / src / router / hotplug2 / hotplug2-dnode.c
blobd1ac7d3e922ba040e95734f8bdfee7edff2e61dd
1 /*****************************************************************************\
2 * _ _ _ _ ___ *
3 * | || | ___ | |_ _ __ | | _ _ __ _ |_ ) *
4 * | __ |/ _ \| _|| '_ \| || || |/ _` | / / *
5 * |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___| *
6 * |_| |___/ *
7 \*****************************************************************************/
9 #define _GNU_SOURCE
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <ctype.h>
18 #include <sys/socket.h>
19 #include <sys/types.h>
20 #include <sys/un.h>
21 #include <sys/wait.h>
22 #include <sys/stat.h>
23 #include <sys/mman.h>
24 #include <linux/types.h>
25 #include <linux/netlink.h>
26 #include <linux/input.h>
28 #include "mem_utils.h"
29 #include "hotplug2.h"
30 #include "hotplug2_utils.h"
31 #include "parser_utils.h"
33 #define MODALIAS_MAX_LEN 1024
35 #ifndef KEY_MIN_INTERESTING
36 #define KEY_MIN_INTERESTING KEY_MUTE
37 #endif
39 /* Some kernel headers appear to define it even without __KERNEL__ */
40 #ifndef BITS_PER_LONG
41 #define BITS_PER_LONG (sizeof(long) * 8)
42 #endif
43 #ifndef NBITS
44 #define NBITS(x) ((x / BITS_PER_LONG) + 1)
45 #endif
47 #define TEST_INPUT_BIT(i,bm) (bm[i / BITS_PER_LONG] & (((unsigned long)1) << (i%BITS_PER_LONG)))
49 /**
50 * Parses a bitmap; output is a list of offsets of bits of a bitmap
51 * of arbitrary size that are set to 1.
53 * @1 Name of the bitmap parsed
54 * @2 The actual bitmap pointer
55 * @3 Lower boundary of the bitmap
56 * @4 Upper boundary of the bitmap
58 * Returns: Newly allocated string containing the offsets
60 char *bitmap_to_bitstring(char name, unsigned long *bm, unsigned int min_bit, unsigned int max_bit)
62 char *rv;
63 unsigned int i, len = 0, size = 16, srv;
65 rv = xmalloc(size);
67 len += snprintf(rv + len, size - len, "%c", name);
69 for (i = min_bit; i < max_bit; i++) {
70 if (TEST_INPUT_BIT(i, bm)) {
71 while ((srv = snprintf(rv + len, size - len, "%X,", i)) >= (size - len)) {
72 size = size * 2;
73 rv = xrealloc(rv, size);
75 len += srv;
79 return rv;
82 /**
83 * Reverses the bitmap_to_bitstring function.
85 * @1 Bitstring to be converted
86 * @2 Output bitmap
87 * @3 Size of the whole bitmap
89 * Returns: void
91 void string_to_bitmap(char *input, unsigned long *bitmap, int bm_len) {
92 char *token, *ptr;
93 int i = 0;
95 ptr = input + strlen(input);
97 while ((token = dup_token_r(ptr, input, &ptr, isspace)) != NULL) {
98 bitmap[i] = strtoul(token, NULL, 16);
99 free(token);
100 i++;
103 while (i < bm_len)
104 bitmap[i++] = 0;
107 #define GET_BITMAP(mapkey, bitmap, name, min) \
108 if (TEST_INPUT_BIT(EV_ ## mapkey, ev_bits)) { \
109 token = getenv(#mapkey); \
110 if (token == NULL) \
111 return -1; \
113 string_to_bitmap(token, bitmap ## _bits, NBITS(mapkey ## _MAX)); \
115 bitmap = bitmap_to_bitstring(name, bitmap ## _bits, min, mapkey ## _MAX);
118 * Creates an input modalias out of preset environmental variables.
120 * @1 Pointer to where modalias will be created
121 * @2 Maximum size of the modalias
123 * Returns: 0 if success, -1 otherwise
125 int get_input_modalias(char *modalias, int modalias_len) {
126 char *product_env;
127 char *ptr;
128 char *token;
129 unsigned int bustype, vendor, product, version;
131 char *ev, *key, *rel, *abs, *sw, *msc, *led, *snd, *ff;
133 unsigned long ev_bits[NBITS(EV_MAX)];
134 unsigned long key_bits[NBITS(KEY_MAX)];
135 unsigned long rel_bits[NBITS(REL_MAX)];
136 unsigned long abs_bits[NBITS(ABS_MAX)];
137 unsigned long msc_bits[NBITS(MSC_MAX)];
138 unsigned long led_bits[NBITS(LED_MAX)];
139 unsigned long snd_bits[NBITS(SND_MAX)];
140 unsigned long ff_bits[NBITS(FF_MAX)];
142 #if defined(SW_MAX) && defined(EV_SW)
143 unsigned long sw_bits[NBITS(SW_MAX)];
144 #endif
146 memset(ev_bits, 0, NBITS(EV_MAX) * sizeof(long));
147 memset(key_bits, 0, NBITS(KEY_MAX) * sizeof(long));
148 memset(rel_bits, 0, NBITS(REL_MAX) * sizeof(long));
149 memset(abs_bits, 0, NBITS(ABS_MAX) * sizeof(long));
150 memset(msc_bits, 0, NBITS(MSC_MAX) * sizeof(long));
151 memset(led_bits, 0, NBITS(LED_MAX) * sizeof(long));
152 memset(snd_bits, 0, NBITS(SND_MAX) * sizeof(long));
153 memset(ff_bits, 0, NBITS(FF_MAX) * sizeof(long));
154 #if defined(SW_MAX) && defined(EV_SW)
155 memset(sw_bits, 0, NBITS(SW_MAX) * sizeof(long));
156 #endif
158 product_env = getenv("PRODUCT");
160 if (product_env == NULL)
161 return -1;
163 /* PRODUCT */
164 ptr = strchr(product_env, '/');
165 if (ptr == NULL || ptr[1] == '\0')
166 return -1;
168 bustype = strtoul(product_env, NULL, 16);
169 vendor = strtoul(ptr+1, NULL, 16);
170 ptr = strchr(ptr+1, '/');
171 if (ptr == NULL || ptr[1] == '\0')
172 return -1;
174 product = strtoul(ptr+1, NULL, 16);
175 ptr = strchr(ptr+1, '/');
176 if (ptr == NULL || ptr[1] == '\0')
177 return -1;
179 version = strtoul(ptr+1, NULL, 16);
181 /* EV */
182 token = getenv("EV");
183 if (token == NULL)
184 return -1;
186 string_to_bitmap(token, ev_bits, NBITS(EV_MAX));
188 ev = bitmap_to_bitstring('e', ev_bits, 0, EV_MAX);
189 GET_BITMAP(KEY, key, 'k', KEY_MIN_INTERESTING);
190 GET_BITMAP(REL, rel, 'r', 0);
191 GET_BITMAP(ABS, abs, 'a', 0);
192 GET_BITMAP(MSC, msc, 'm', 0);
193 GET_BITMAP(LED, led, 'l', 0);
194 GET_BITMAP(SND, snd, 's', 0);
195 GET_BITMAP(FF, ff, 'f', 0);
196 #if defined(SW_MAX) && defined(EV_SW)
197 GET_BITMAP(SW, sw, 'w', 0);
198 #else
199 sw=strdup("");
200 #endif
202 snprintf(modalias, modalias_len,
203 "MODALIAS=input:b%04Xv%04Xp%04Xe%04X-"
204 "%s%s%s%s%s%s%s%s%s",
205 bustype, vendor, product, version,
206 ev, key, rel, abs, msc, led, snd, ff, sw);
208 /* Ugly but straightforward*/
209 free(ev);
210 free(key);
211 free(rel);
212 free(abs);
213 free(msc);
214 free(led);
215 free(snd);
216 free(ff);
217 free(sw);
219 return 0;
221 #undef NBITS
222 #undef TEST_INPUT_BIT
225 * Creates a PCI modalias out of preset environmental variables.
227 * @1 Pointer to where modalias will be created
228 * @2 Maximum size of the modalias
230 * Returns: 0 if success, -1 otherwise
232 int get_pci_modalias(char *modalias, int modalias_len) {
233 char *class_env, *id_env, *subsys_env;
234 char *ptr;
235 unsigned long vendor, device, sub_vendor, sub_device, class_type;
236 unsigned char baseclass, subclass, interface;
238 id_env = getenv("PCI_ID");
239 subsys_env = getenv("PCI_SUBSYS_ID");
240 class_env = getenv("PCI_CLASS");
241 if (id_env == NULL || subsys_env == NULL || class_env == NULL)
242 return -1;
244 if (strlen(id_env) < 9 || strlen(subsys_env) < 9)
245 return -1;
247 /* PCI_ID */
248 ptr = strchr(id_env, ':');
249 if (ptr == NULL || ptr[1] == '\0')
250 return -1;
252 vendor = strtoul(id_env, NULL, 16);
253 device = strtoul(ptr+1, NULL, 16);
255 /* PCI_SUBSYS_ID */
256 ptr = strchr(subsys_env, ':');
257 if (ptr == NULL || ptr[1] == '\0')
258 return -1;
260 sub_vendor = strtoul(id_env, NULL, 16);
261 sub_device = strtoul(ptr+1, NULL, 16);
263 /* PCI_CLASS */
264 class_type = strtoul(class_env, NULL, 16);
265 baseclass = (unsigned char)(class_type >> 16);
266 subclass = (unsigned char)(class_type >> 8);
267 interface = (unsigned char)class_type;
269 snprintf(modalias, modalias_len,
270 "MODALIAS=pci:v%08lXd%08lXsv%08lXsd%08lXbc%02Xsc%02Xi%02X",
271 vendor, device, sub_vendor, sub_device,
272 baseclass, subclass, interface);
274 return 0;
278 * Creates an IEEE1394 (FireWire) modalias out of preset environmental
279 * variables.
281 * @1 Pointer to where modalias will be created
282 * @2 Maximum size of the modalias
284 * Returns: 0 if success, -1 otherwise
286 int get_ieee1394_modalias(char *modalias, int modalias_len) {
287 char *vendor_env, *model_env;
288 char *specifier_env, *version_env;
289 unsigned long vendor, model;
290 unsigned long specifier, version;
292 vendor_env = getenv("VENDOR_ID");
293 model_env = getenv("MODEL_ID");
294 specifier_env = getenv("SPECIFIER_ID");
295 version_env = getenv("VERSION");
297 if (vendor_env == NULL || model_env == NULL ||
298 specifier_env == NULL || version_env == NULL)
299 return -1;
301 vendor = strtoul(vendor_env, NULL, 16);
302 model = strtoul(model_env, NULL, 16);
303 specifier = strtoul(specifier_env, NULL, 16);
304 version = strtoul(version_env, NULL, 16);
306 snprintf(modalias, modalias_len,
307 "MODALIAS=ieee1394:ven%08lXmo%08lXsp%08lXver%08lX",
308 vendor, model, specifier, version);
310 return 0;
314 * Creates a serio modalias out of preset environmental variables.
316 * @1 Pointer to where modalias will be created
317 * @2 Maximum size of the modalias
319 * Returns: 0 if success, -1 otherwise
321 int get_serio_modalias(char *modalias, int modalias_len) {
322 char *serio_type_env, *serio_proto_env;
323 char *serio_id_env, *serio_extra_env;
324 unsigned int serio_type, serio_proto;
325 unsigned int serio_specifier, serio_version;
327 serio_type_env = getenv("SERIO_TYPE");
328 serio_proto_env = getenv("SERIO_PROTO");
329 serio_id_env = getenv("SERIO_ID");
330 serio_extra_env = getenv("SERIO_EXTRA");
332 if (serio_type_env == NULL || serio_proto_env == NULL ||
333 serio_id_env == NULL || serio_extra_env == NULL)
334 return -1;
336 serio_type = strtoul(serio_type_env, NULL, 16);
337 serio_proto = strtoul(serio_proto_env, NULL, 16);
338 serio_specifier = strtoul(serio_id_env, NULL, 16);
339 serio_version = strtoul(serio_extra_env, NULL, 16);
341 snprintf(modalias, modalias_len,
342 "MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
343 serio_type, serio_proto, serio_specifier, serio_version);
345 return 0;
349 * Creates an USB modalias out of preset environmental variables.
351 * @1 Pointer to where modalias will be created
352 * @2 Maximum size of the modalias
354 * Returns: 0 if success, -1 otherwise
356 int get_usb_modalias(char *modalias, int modalias_len) {
357 char *product_env, *type_env, *interface_env;
358 char *ptr;
359 unsigned int idVendor, idProduct, bcdDevice;
360 unsigned int device_class, device_subclass, device_protocol;
361 unsigned int interface_class, interface_subclass, interface_protocol;
363 product_env = getenv("PRODUCT");
364 type_env = getenv("TYPE");
365 interface_env = getenv("INTERFACE");
367 if (product_env == NULL || type_env == NULL)
368 return -1;
370 /* PRODUCT */
371 ptr = strchr(product_env, '/');
372 if (ptr == NULL || ptr[1] == '\0')
373 return -1;
374 idVendor = strtoul(product_env, NULL, 16);
375 idProduct = strtoul(ptr+1, NULL, 16);
376 ptr = strchr(ptr+1, '/');
377 if (ptr == NULL || ptr[1] == '\0')
378 return -1;
379 bcdDevice = strtoul(ptr+1, NULL, 16);
381 /* TYPE */
382 ptr = strchr(type_env, '/');
383 if (ptr == NULL || ptr[1] == '\0')
384 return -1;
385 device_class = strtoul(type_env, NULL, 10);
386 device_subclass = strtoul(ptr+1, NULL, 10);
387 ptr = strchr(ptr+1, '/');
388 if (ptr == NULL || ptr[1] == '\0')
389 return -1;
390 device_protocol = strtoul(ptr+1, NULL, 10);
392 /* INTERFACE */
393 if (interface_env != NULL) {
394 ptr = strchr(interface_env, '/');
395 if (ptr == NULL || ptr[1] == '\0')
396 return -1;
397 interface_class = strtoul(interface_env, NULL, 10);
398 interface_subclass = strtoul(ptr+1, NULL, 10);
399 ptr = strchr(ptr+1, '/');
400 if (ptr == NULL || ptr[1] == '\0')
401 return -1;
402 interface_protocol = strtoul(ptr+1, NULL, 10);
404 snprintf(modalias, modalias_len,
405 "MODALIAS=usb:v%04Xp%04Xd%04X"
406 "dc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
407 idVendor, idProduct, bcdDevice,
408 device_class, device_subclass, device_protocol,
409 interface_class, interface_subclass, interface_protocol);
410 } else {
411 snprintf(modalias, modalias_len,
412 "MODALIAS=usb:v%04Xp%04Xd%04X"
413 "dc%02Xdsc%02Xdp%02Xic*isc*ip*",
414 idVendor, idProduct, bcdDevice,
415 device_class, device_subclass, device_protocol);
418 return 0;
422 * Distributes modalias generating according to the bus name.
424 * @1 Bus name
425 * @2 Pointer to where modalias will be created
426 * @3 Maximum size of the modalias
428 * Returns: The return value of the subsystem modalias function, or -1 if
429 * no match.
431 int get_modalias(char *bus, char *modalias, int modalias_len) {
432 memset(modalias, 0, modalias_len);
434 if (!strcmp(bus, "pci"))
435 return get_pci_modalias(modalias, modalias_len);
437 if (!strcmp(bus, "usb"))
438 return get_usb_modalias(modalias, modalias_len);
440 if (!strcmp(bus, "ieee1394"))
441 return get_ieee1394_modalias(modalias, modalias_len);
443 if (!strcmp(bus, "serio"))
444 return get_serio_modalias(modalias, modalias_len);
446 if (!strcmp(bus, "input"))
447 return get_input_modalias(modalias, modalias_len);
449 /* 'ccw' devices do not generate events, we do not need to handle them */
450 /* 'of' devices do not generate events either */
451 /* 'pnp' devices do generate events, but they lack any device */
452 /* description whatsoever. */
454 return -1;
458 * Turns all environmental variables as set when invoked by /proc/sys/hotplug
459 * into an uevent formatted (thus not null-terminated) string.
461 * @1 All environmental variables
462 * @2 Bus of the event (as read from argv)
463 * @3 Pointer to size of the returned uevent string
465 * Returns: Not null terminated uevent string.
467 inline char *get_uevent_string(char **environ, char *bus, unsigned long *uevent_string_len) {
468 char *uevent_string;
469 char *tmp;
470 char modalias[MODALIAS_MAX_LEN];
471 unsigned long offset;
473 tmp = getenv("ACTION");
474 if (tmp == NULL)
475 return NULL;
477 *uevent_string_len = strlen(tmp) + 1;
478 offset = *uevent_string_len - 1;
479 uevent_string = xmalloc(*uevent_string_len);
480 strcpy(uevent_string, tmp);
481 uevent_string[offset] = '@';
482 offset++;
484 for (; *environ != NULL; environ++) {
485 *uevent_string_len += strlen(*environ) + 1;
486 uevent_string = xrealloc(uevent_string, *uevent_string_len);
487 strcpy(&uevent_string[offset], *environ);
488 offset = *uevent_string_len;
491 if (getenv("SEQNUM") == NULL) {
492 /* 64 + 7 ('SEQNUM=') + 1 ('\0') */
493 tmp = xmalloc(72);
494 memset(tmp, 0, 72);
495 snprintf(tmp, 72, "SEQNUM=%llu", get_kernel_seqnum());
497 *uevent_string_len += strlen(tmp) + 1;
498 uevent_string = xrealloc(uevent_string, *uevent_string_len);
499 strcpy(&uevent_string[offset], tmp);
500 offset = *uevent_string_len;
502 free(tmp);
505 if (getenv("SUBSYSTEM") == NULL) {
506 /* 10 ('SUBSYSTEM=') + 1 ('\0') */
507 tmp = xmalloc(11 + strlen(bus));
508 strcpy(tmp, "SUBSYSTEM=");
509 strcat(tmp+10, bus);
511 *uevent_string_len += strlen(tmp) + 1;
512 uevent_string = xrealloc(uevent_string, *uevent_string_len);
513 strcpy(&uevent_string[offset], tmp);
514 offset = *uevent_string_len;
516 free(tmp);
519 /* Only create our own MODALIAS if we do not have one set... */
520 if (getenv("MODALIAS") == NULL) {
521 if (!get_modalias(bus, modalias, MODALIAS_MAX_LEN)) {
522 *uevent_string_len += strlen(modalias) + 1;
523 uevent_string = xrealloc(uevent_string, *uevent_string_len);
524 strcpy(&uevent_string[offset], modalias);
525 offset = *uevent_string_len;
529 return uevent_string;
532 int main(int argc, char *argv[], char **environ) {
533 char *uevent_string;
534 unsigned long uevent_string_len;
535 int netlink_socket;
537 if (argv[1] == NULL) {
538 ERROR("parsing arguments", "Malformed event arguments (bus missing).");
539 return 1;
542 uevent_string = get_uevent_string(environ, argv[1], &uevent_string_len);
543 if (uevent_string == NULL) {
544 ERROR("parsing env vars", "Malformed event environmental variables.");
545 return 1;
548 netlink_socket = init_netlink_socket(NETLINK_CONNECT);
549 if (netlink_socket == -1) {
550 ERROR("netlink init","Unable to open netlink socket.");
551 goto exit;
554 if (send(netlink_socket, uevent_string, uevent_string_len, 0) == -1) {
555 ERROR("sending data","send failed: %s.", strerror(errno));
557 close(netlink_socket);
559 exit:
560 free(uevent_string);
562 return 0;