newsyslog(8): Remove a NULL-check-after-use and instead assert != NULL.
[dragonfly.git] / usr.sbin / usbd / usbd.c
1 /*
2  * $NetBSD: usbd.c,v 1.4 1998/12/09 00:57:19 augustss Exp $
3  * $FreeBSD: src/usr.sbin/usbd/usbd.c,v 1.29 2003/10/25 22:03:10 jmg Exp $
4  */
5
6 /*
7  * Copyright (c) 1998 The NetBSD Foundation, Inc.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by Lennart Augustsson (augustss@netbsd.org).
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *        This product includes software developed by the NetBSD
24  *        Foundation, Inc. and its contributors.
25  * 4. Neither the name of The NetBSD Foundation nor the names of its
26  *    contributors may be used to endorse or promote products derived
27  *    from this software without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39  * POSSIBILITY OF SUCH DAMAGE.
40  */
41
42 /* USBD creates 'threads' in the kernel, used for doing discovery when a
43  * device has attached or detached. This functionality should be removed
44  * once kernel threads have been added to the kernel.
45  * It also handles the event queue, and executing commands based on those
46  * events.
47  *
48  * See usbd(8).
49  */
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <fcntl.h>
55 #include <unistd.h>
56 #include <ctype.h>
57 #include <signal.h>
58 #include <paths.h>
59 #include <sys/param.h>
60 #include <sys/types.h>
61 #include <sys/errno.h>
62 #include <sys/ioctl.h>
63 #include <sys/linker.h>
64 #include <sys/module.h>
65 #include <sys/queue.h>
66 #include <sys/time.h>
67 #include <sys/wait.h>
68 #include <regex.h>
69
70 #include <bus/usb/usb.h>
71
72 /* default name of configuration file
73  */
74
75 #define CONFIGFILE      "/etc/usbd.conf"
76
77 /* the name of the device spitting out usb attach/detach events as well as
78  * the prefix for the individual busses (used as a semi kernel thread).
79  */
80 #define USBDEV          "/dev/usb"
81
82 /* Maximum number of USB busses expected to be in a system
83  * XXX should be replaced by dynamic allocation.
84  */
85 #define MAXUSBDEV       4
86
87 /* Sometimes a device does not respond in time for interrupt
88  * driven explore to find it.  Therefore we run an exploration
89  * at regular intervals to catch those.
90  */
91 #define TIMEOUT         30
92
93 /* The wildcard used in actions for strings and integers
94  */
95 #define WILDCARD_STRING NULL
96 #define WILDCARD_INT    -1
97
98
99 extern char *__progname;        /* name of program */
100
101 const char *configfile = CONFIGFILE; /* name of configuration file */
102
103 char *devs[MAXUSBDEV];          /* device names */
104 int fds[MAXUSBDEV];             /* file descriptors for USBDEV\d+ */
105 int ndevs = 0;                  /* number of entries in fds / devs */
106 int fd = -1;                    /* file descriptor for USBDEV */
107
108 int lineno;
109 int verbose = 0;                /* print message on what it is doing */
110
111 typedef struct event_name_s {
112         int     type;           /* event number (from usb.h) */
113         const char *name;
114 } event_name_t;
115
116 event_name_t event_names[] = {
117         {USB_EVENT_CTRLR_ATTACH, "ctrlr-attach"},
118         {USB_EVENT_CTRLR_DETACH, "ctrlr-detach"},
119         {USB_EVENT_DRIVER_ATTACH, "driver-attach"},
120         {USB_EVENT_DRIVER_DETACH, "driver-detach"},
121         {USB_EVENT_DEVICE_ATTACH, "device-attach"},
122         {USB_EVENT_DEVICE_DETACH, "device-detach"},
123         {0, NULL}                       /* NULL indicates end of list, not 0 */
124 };
125
126 #define DEVICE_FIELD            0       /* descriptive field */
127
128 #define VENDOR_FIELD            1       /* selective fields */
129 #define PRODUCT_FIELD           2
130 #define RELEASE_FIELD           3
131 #define CLASS_FIELD             4
132 #define SUBCLASS_FIELD          5
133 #define PROTOCOL_FIELD          6
134 #define DEVNAME_FIELD           7
135
136 #define ATTACH_FIELD            8       /* command fields */
137 #define DETACH_FIELD            9
138
139
140 typedef struct action_s {
141         char    *name;          /* descriptive string */
142
143         int     vendor; /* selection criteria */
144         int     product;
145         int     release;
146         int     class;
147         int     subclass;
148         int     protocol;
149         char    *devname;
150         regex_t devname_regex;
151
152         char    *attach;        /* commands to execute */
153         char    *detach;
154
155         STAILQ_ENTRY(action_s) next;
156 } action_t;
157
158 STAILQ_HEAD(action_list, action_s) actions = STAILQ_HEAD_INITIALIZER(actions);
159
160 typedef struct action_match_s {
161         action_t *action;
162         char    *devname;
163 } action_match_t;
164
165
166 /* the function returns 0 for failure, 1 for all arguments found and 2 for
167  * arguments left over in trail.
168  */
169 typedef int (*config_field_fn)(action_t *action, char *args, char **trail);
170
171 static void execute_command(char *);
172 static int  find_action(struct usb_device_info *, action_match_t *);
173 static int get_integer(char *, int *, char **);
174 static int get_string(char *, char **, char **);
175 static int match_devname(action_t *, struct usb_device_info *);
176 static void print_action(action_t *, int);
177 static void print_actions(void);
178 static void print_event(struct usb_event *);
179 static void process_event_queue(int);
180 static void read_configuration(void);
181 static int set_attach_field(action_t *, char *, char **);
182 static int set_class_field(action_t *, char *, char **);
183 static int set_detach_field(action_t *, char *, char **);
184 static int set_device_field(action_t *, char *, char **);
185 static int set_devname_field(action_t *, char *, char **);
186 static int set_product_field(action_t *, char *, char **);
187 static int set_protocol_field(action_t *, char *, char **);
188 static int set_release_field(action_t *, char *, char **);
189 static int set_subclass_field(action_t *, char *, char **);
190 static int set_vendor_field(action_t *, char *, char **);
191 static void usage(void);
192
193
194 /* the list of fields supported in an entry */
195 typedef struct config_field_s {
196         int     event;
197         const char *name;
198         config_field_fn function;
199 } config_field_t;
200
201 config_field_t config_fields[] = {
202         {DEVICE_FIELD,          "device",       set_device_field},
203
204         {VENDOR_FIELD,          "vendor",       set_vendor_field},
205         {PRODUCT_FIELD,         "product",      set_product_field},
206         {RELEASE_FIELD,         "release",      set_release_field},
207         {CLASS_FIELD,           "class",        set_class_field},
208         {SUBCLASS_FIELD,        "subclass",     set_subclass_field},
209         {PROTOCOL_FIELD,        "protocol",     set_protocol_field},
210         {DEVNAME_FIELD,         "devname",      set_devname_field},
211
212         {ATTACH_FIELD,          "attach",       set_attach_field},
213         {DETACH_FIELD,          "detach",       set_detach_field},
214
215         {0, NULL, NULL}         /* NULL is EOL marker, not the 0 */
216 };
217
218 static void
219 usage(void)
220 {
221         fprintf(stderr, "usage: %s [-d] [-v] [-t timeout] [-e] [-f dev]\n"
222                         "           [-n] [-c config]\n",
223                 __progname);
224         exit(1);
225 }
226
227
228 /* generic helper functions for the functions to set the fields of actions */
229 static int
230 get_string(char *src, char **rdst, char **rsrc)
231 {
232         /* Takes the first string from src, taking quoting into account.
233          * rsrc (if not NULL) is set to the first byte not included in the
234          * string returned in rdst.
235          *
236          * Input is:
237          *   src = 'fir"st \'par"t       second part';
238          * Returned is:
239          *   *dst = 'hello \'world';
240          *   if (rsrc != NULL)
241          *     *rsrc = 'second part';
242          *
243          * Notice the fact that the single quote enclosed in double quotes is
244          * returned. Also notice that before second part there is more than
245          * one space, which is removed in rsrc.
246          *
247          * The string in src is not modified.
248          */
249
250         char *dst;              /* destination string */
251         size_t i;               /* index into src */
252         int j;                  /* index into dst */
253         int quoted = 0;         /* 1 for single, 2 for double quoted */
254
255         dst = malloc(strlen(src)+1);    /* XXX allocation is too big, realloc?*/
256         if (dst == NULL) {              /* should not happen, really */
257                 fprintf(stderr, "%s:%d: Out of memory\n", configfile, lineno);
258                 exit(2);
259         }
260
261         /* find the end of the current string. If quotes are found the search
262          * continues until the corresponding quote is found.
263          * So,
264          *   hel'lo" "wor'ld
265          * represents the string
266          *   hello" "world
267          * and not (hello world).
268          */
269         for (i = 0, j = 0; i < strlen(src); i++) {
270                 if (src[i] == '\'' && (quoted == 0 || quoted == 1)) {
271                         quoted = (quoted? 0:1);
272                 } else if (src[i] == '"' && (quoted == 0 || quoted == 2)) {
273                         quoted = (quoted? 0:2);
274                 } else if (isspace(src[i]) && !quoted) {
275                         /* found a space outside quotes -> terminates src */
276                         break;
277                 } else {
278                         dst[j++] = src[i];      /* copy character */
279                 }
280         }
281
282         /* quotes being left open? */
283         if (quoted) {
284                 fprintf(stderr, "%s:%d: Missing %s quote at end of '%s'\n",
285                         configfile, lineno,
286                         (quoted == 1? "single":"double"), src);
287                 exit(2);
288         }
289
290         /* skip whitespace for second part */
291         for (/*i is set*/; i < strlen(src) && isspace(src[i]); i++)
292                 ;       /* nop */
293
294         dst[j] = '\0';                  /* make sure it's NULL terminated */
295
296         *rdst = dst;                    /* and return the pointers */
297         if (rsrc != NULL)               /* if info wanted */
298                 *rsrc = &src[i];
299
300         if (*dst == '\0') {             /* empty string */
301                 return 0;
302         } else if (src[i] == '\0') {    /* completely used (1 argument) */
303                 return 1;
304         } else {                        /* 2 or more args, *rsrc is rest */
305                 return 2;
306         }
307 }
308
309 static int
310 get_integer(char *src, int *dst, char **rsrc)
311 {
312         char *endptr;
313
314         /* Converts str to a number. If one argument was found in
315          * str, 1 is returned and *dst is set to the value of the integer.
316          * If 2 or more arguments were presented, 2 is returned,
317          * *dst is set to the converted value and rsrc, if not null, points
318          * at the start of the next argument (whitespace skipped).
319          * Else 0 is returned and nothing else is valid.
320          */
321
322         if (src == NULL || *src == '\0')        /* empty src */
323                 return(0);
324
325         *dst = (int) strtol(src, &endptr, 0);
326
327         /* skip over whitespace of second argument */
328         while (isspace(*endptr))
329                 endptr++;
330
331         if (rsrc)
332                 *rsrc = endptr;
333
334         if (isspace(*endptr)) {         /* partial match, 2 or more arguments */
335                 return(2);
336         } else if (*endptr == '\0') {   /* full match, 1 argument */
337                 return(1);
338         } else {                        /* invalid src, no match */
339                 return(0);
340         }
341 }
342
343 /* functions to set the fields of the actions appropriately */
344 static int
345 set_device_field(action_t *action, char *args, char **trail)
346 {
347         return(get_string(args, &action->name, trail));
348 }
349
350 static int
351 set_vendor_field(action_t *action, char *args, char **trail)
352 {
353         return(get_integer(args, &action->vendor, trail));
354 }
355
356 static int
357 set_product_field(action_t *action, char *args, char **trail)
358 {
359         return(get_integer(args, &action->product, trail));
360 }
361
362 static int
363 set_release_field(action_t *action, char *args, char **trail)
364 {
365         return(get_integer(args, &action->release, trail));
366 }
367
368 static int
369 set_class_field(action_t *action, char *args, char **trail)
370 {
371         return(get_integer(args, &action->class, trail));
372 }
373
374 static int
375 set_subclass_field(action_t *action, char *args, char **trail)
376 {
377         return(get_integer(args, &action->subclass, trail));
378 }
379
380 static int
381 set_protocol_field(action_t *action, char *args, char **trail)
382 {
383         return(get_integer(args, &action->protocol, trail));
384 }
385
386 static int
387 set_devname_field(action_t *action, char *args, char **trail)
388 {
389         int match = get_string(args, &action->devname, trail);
390         int len;
391         int error;
392         char *string;
393 #       define ERRSTR_SIZE      100
394         char errstr[ERRSTR_SIZE];
395
396         if (match == 0)
397                 return(0);
398
399         len = strlen(action->devname);
400         string = malloc(len + 15);
401         if (string == NULL)
402                 return(0);
403
404         bcopy(action->devname, string+7, len);  /* make some space for */
405         bcopy("[[:<:]]", string, 7);            /*   beginning of word */
406         bcopy("[[:>:]]", string+7+len, 7);      /*   and end of word   */
407         string[len + 14] = '\0';
408
409         error = regcomp(&action->devname_regex, string, REG_NOSUB|REG_EXTENDED);
410         if (error) {
411                 errstr[0] = '\0';
412                 regerror(error, &action->devname_regex, errstr, ERRSTR_SIZE);
413                 fprintf(stderr, "%s:%d: %s\n", configfile, lineno, errstr);
414                 return(0);
415         }
416
417         return(match);
418 }
419
420 static int
421 set_attach_field(action_t *action, char *args, char **trail)
422 {
423         return(get_string(args, &action->attach, trail));
424 }
425
426 static int
427 set_detach_field(action_t *action, char *args, char **trail)
428 {
429         return(get_string(args, &action->detach, trail));
430 }
431
432 static void
433 read_configuration(void)
434 {
435         FILE *file;             /* file descriptor */
436         char *line;             /* current line */
437         char *linez;            /* current line, NULL terminated */
438         char *field;            /* first part, the field name */
439         char *args;             /* second part, arguments */
440         char *trail;            /* remaining part after parsing, should be '' */
441         size_t len;             /* length of current line */
442         int i,j;                /* loop counters */
443         action_t *action = NULL;        /* current action */
444
445         file = fopen(configfile, "r");
446         if (file == NULL) {
447                 fprintf(stderr, "%s: Could not open for reading, %s\n",
448                         configfile, strerror(errno));
449                 exit(2);
450         }
451
452         for (lineno = 1; /* nop */;lineno++) {
453         
454                 line = fgetln(file, &len);
455                 if (line == NULL) {
456                         if (feof(file))                 /* EOF */
457                                 break;
458                         if (ferror(file)) {
459                                 fprintf(stderr, "%s:%d: Could not read, %s\n",
460                                         configfile, lineno, strerror(errno));
461                                 exit(2);
462                         }
463                 }
464
465                 /* skip initial spaces */
466                 while (len > 0 && isspace(*line)) {
467                         line++;
468                         len--;
469                 }
470
471                 if (len == 0)           /* empty line */
472                         continue;
473                 if (line[0] == '#')     /* comment line */
474                         continue;
475
476                 /* make a NULL terminated copy of the string */
477                 linez = malloc(len+1);
478                 if (linez == NULL) {
479                         fprintf(stderr, "%s:%d: Out of memory\n",
480                                 configfile, lineno);
481                         exit(2);
482                 }
483                 strncpy(linez, line, len);
484                 linez[len] = '\0';
485
486                 /* find the end of the current word (is field), that's the
487                  * start of the arguments
488                  */
489                 field = linez;
490                 args = linez;
491                 while (*args != '\0' && !isspace(*args))
492                         args++;
493
494                 /* If arguments is not the empty string, NULL terminate the
495                  * field and move the argument pointer to the first character
496                  * of the arguments.
497                  * If arguments is the empty string field and arguments both
498                  * are terminated (strlen(field) >= 0, strlen(arguments) == 0).
499                  */
500                 if (*args != '\0') {
501                         *args = '\0';
502                         args++;
503                 }
504
505                 /* Skip initial spaces */
506                 while (*args != '\0' && isspace(*args))
507                         args++;
508
509                 /* Cut off trailing whitespace */
510                 for (i = 0, j = 0; args[i] != '\0'; i++)
511                         if (!isspace(args[i]))
512                                 j = i+1;
513                 args[j] = '\0';
514
515                 /* We now have the field and the argument separated into
516                  * two strings that are NULL terminated
517                  */
518
519                 /* If the field is 'device' we have to start a new action. */
520                 if (strcmp(field, "device") == 0) {
521                         /* Allocate a new action and set defaults */
522                         action = malloc(sizeof(*action));
523                         if (action == NULL) {
524                                 fprintf(stderr, "%s:%d: Out of memory\n",
525                                         configfile, lineno);
526                                 exit(2);
527                         }
528                         memset(action, 0, sizeof(*action));
529                         action->product = WILDCARD_INT;
530                         action->vendor = WILDCARD_INT;
531                         action->release = WILDCARD_INT;
532                         action->class = WILDCARD_INT;
533                         action->subclass = WILDCARD_INT;
534                         action->protocol = WILDCARD_INT;
535                         action->devname = WILDCARD_STRING;
536
537                         /* Add it to the end of the list to preserve order */
538                         STAILQ_INSERT_TAIL(&actions, action, next);
539                 }
540
541                 if (action == NULL) {
542                         line[len] = '\0';       /* XXX zero terminate */
543                         fprintf(stderr, "%s:%d: Doesn't start with 'device' "
544                                 "but '%s'\n", configfile, lineno, field);
545                         exit(2);
546                 }
547                 
548                 for (i = 0; config_fields[i].name  ; i++) {
549                         /* does the field name match? */
550                         if (strcmp(config_fields[i].name, field) == 0) {
551                                 /* execute corresponding set-field function */
552                                 if ((config_fields[i].function)(action, args,
553                                                                 &trail)
554                                     != 1) {
555                                         fprintf(stderr,"%s:%d: "
556                                                 "Syntax error in '%s'\n",
557                                                 configfile, lineno, linez);
558                                         exit(2);
559                                 }
560                                 break;
561                         }
562                 }
563                 if (config_fields[i].name == NULL) {    /* Reached end of list*/
564                         fprintf(stderr, "%s:%d: Unknown field '%s'\n",
565                                 configfile, lineno, field);
566                         exit(2);
567                 }
568         }
569
570         fclose(file);
571
572         if (verbose >= 2)
573                 print_actions();
574 }
575
576
577 static void
578 print_event(struct usb_event *event)
579 {
580         int i;
581         struct timespec *timespec = &event->ue_time;
582         struct usb_device_info *devinfo = &event->u.ue_device;
583
584         printf("%s: ", __progname);
585         for (i = 0; event_names[i].name != NULL; i++) {
586                 if (event->ue_type == event_names[i].type) {
587                         printf("%s event", event_names[i].name);
588                         break;
589                 }
590         }
591         if (event_names[i].name == NULL)
592                 printf("unknown event %d", event->ue_type);
593
594         if (event->ue_type == USB_EVENT_DEVICE_ATTACH ||
595             event->ue_type == USB_EVENT_DEVICE_DETACH) {
596                 devinfo = &event->u.ue_device;
597
598                 printf(" at %ld.%09ld, %s, %s:\n",
599                         timespec->tv_sec, timespec->tv_nsec,
600                         devinfo->udi_product, devinfo->udi_vendor);
601
602                 printf("  vndr=0x%04x prdct=0x%04x rlse=0x%04x "
603                        "clss=0x%04x subclss=0x%04x prtcl=0x%04x\n",
604                        devinfo->udi_vendorNo, devinfo->udi_productNo,
605                        devinfo->udi_releaseNo,
606                        devinfo->udi_class, devinfo->udi_subclass, devinfo->udi_protocol);
607
608                 if (devinfo->udi_devnames[0][0] != '\0') {
609                         char c = ' ';
610
611                         printf("  device names:");
612                         for (i = 0; i < USB_MAX_DEVNAMES; i++) {
613                                 if (devinfo->udi_devnames[i][0] == '\0')
614                                         break;
615
616                                 printf("%c%s", c, devinfo->udi_devnames[i]);
617                                 c = ',';
618                         }
619                 }
620         } else if (event->ue_type == USB_EVENT_CTRLR_ATTACH ||
621             event->ue_type == USB_EVENT_CTRLR_DETACH) {
622                 printf(" bus=%d", event->u.ue_ctrlr.ue_bus);
623         } else if (event->ue_type == USB_EVENT_DRIVER_ATTACH ||
624             event->ue_type == USB_EVENT_DRIVER_DETACH) {
625                 printf(" cookie=%u devname=%s",
626                     event->u.ue_driver.ue_cookie.cookie,
627                     event->u.ue_driver.ue_devname);
628         }
629         printf("\n");
630 }
631
632 static void
633 print_action(action_t *action, int i)
634 {
635         if (action == NULL)
636                 return;
637
638         printf("%s: action %d: %s\n",
639                 __progname, i,
640                 (action->name? action->name:""));
641         if (action->product != WILDCARD_INT ||
642             action->vendor != WILDCARD_INT ||
643             action->release != WILDCARD_INT ||
644             action->class != WILDCARD_INT ||
645             action->subclass != WILDCARD_INT ||
646             action->protocol != WILDCARD_INT)
647                 printf(" ");
648         if (action->vendor != WILDCARD_INT)
649                 printf(" vndr=0x%04x", action->vendor);
650         if (action->product != WILDCARD_INT)
651                 printf(" prdct=0x%04x", action->product);
652         if (action->release != WILDCARD_INT)
653                 printf(" rlse=0x%04x", action->release);
654         if (action->class != WILDCARD_INT)
655                 printf(" clss=0x%04x", action->class);
656         if (action->subclass != WILDCARD_INT)
657                 printf(" subclss=0x%04x", action->subclass);
658         if (action->protocol != WILDCARD_INT)
659                 printf(" prtcl=0x%04x", action->protocol);
660         if (action->vendor != WILDCARD_INT ||
661             action->product != WILDCARD_INT ||
662             action->release != WILDCARD_INT ||
663             action->class != WILDCARD_INT ||
664             action->subclass != WILDCARD_INT ||
665             action->protocol != WILDCARD_INT)
666                 printf("\n");
667         if (action->devname != WILDCARD_STRING)
668                 printf("  devname: %s\n", action->devname);
669
670         if (action->attach != NULL)
671                 printf("  attach='%s'\n",
672                         action->attach);
673         if (action->detach != NULL)
674                 printf("  detach='%s'\n",
675                         action->detach);
676 }
677
678 static void
679 print_actions(void)
680 {
681         int i = 0;
682         action_t *action;
683
684         STAILQ_FOREACH(action, &actions, next)
685                 print_action(action, ++i);
686
687         printf("%s: %d action%s\n", __progname, i, (i == 1? "":"s"));
688 }
689
690
691 static int
692 match_devname(action_t *action, struct usb_device_info *devinfo)
693 {
694         int i;
695         regmatch_t match;
696         int error;
697
698         for (i = 0; i < USB_MAX_DEVNAMES; i++) {
699                 if (devinfo->udi_devnames[i][0] == '\0')
700                         break;
701
702                 error = regexec(&action->devname_regex, devinfo->udi_devnames[i],
703                                 1, &match, 0);
704                 if (error == 0) {
705                         if (verbose >= 2)
706                                 printf("%s: %s matches %s\n", __progname,
707                                         devinfo->udi_devnames[i], action->devname);
708                         return(i);
709                 }
710         }
711         
712         return(-1);
713 }
714
715
716 static int
717 find_action(struct usb_device_info *devinfo, action_match_t *action_match)
718 {
719         action_t *action;
720         char *_devname = NULL;
721         int match = -1;
722
723         STAILQ_FOREACH(action, &actions, next) {
724                 if ((action->vendor == WILDCARD_INT ||
725                      action->vendor == devinfo->udi_vendorNo) &&
726                     (action->product == WILDCARD_INT ||
727                      action->product == devinfo->udi_productNo) &&
728                     (action->release == WILDCARD_INT ||
729                      action->release == devinfo->udi_releaseNo) &&
730                     (action->class == WILDCARD_INT ||
731                      action->class == devinfo->udi_class) &&
732                     (action->subclass == WILDCARD_INT ||
733                      action->subclass == devinfo->udi_subclass) &&
734                     (action->protocol == WILDCARD_INT ||
735                      action->protocol == devinfo->udi_protocol) &&
736                     (action->devname == WILDCARD_STRING ||
737                      (match = match_devname(action, devinfo)) != -1)) {
738                         /* found match !*/
739
740                         /* Find a devname for pretty printing. Either
741                          * the matched one or otherwise, if there is only
742                          * one devname for that device, use that.
743                          */
744                         if (match >= 0)
745                                 _devname = devinfo->udi_devnames[match];
746                         else if (devinfo->udi_devnames[0][0] != '\0' &&
747                                  devinfo->udi_devnames[1][0] == '\0')
748                                 /* if we have exactly 1 device name */
749                                 _devname = devinfo->udi_devnames[0];
750
751                         if (verbose) {
752                                 printf("%s: Found action '%s' for %s, %s",
753                                         __progname, action->name,
754                                         devinfo->udi_product, devinfo->udi_vendor);
755                                 if (_devname)
756                                         printf(" at %s", _devname);
757                                 printf("\n");
758                         }
759
760                         action_match->action = action;
761                         action_match->devname = _devname;
762
763                         return(1);
764                 }
765         }
766
767         return(0);
768 }
769
770 static void
771 execute_command(char *cmd)
772 {
773         pid_t pid;
774         struct sigaction ign, intact, quitact;
775         sigset_t newsigblock, oldsigblock;
776         int status;
777         int i;
778
779         if (verbose)
780                 printf("%s: Executing '%s'\n", __progname, cmd);
781         if (cmd == NULL)
782                 return;
783
784         /* The code below is directly taken from the system(3) call.
785          * Added to it is the closing of open file descriptors.
786          */
787         /*
788          * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
789          * existing signal dispositions.
790          */
791         ign.sa_handler = SIG_IGN;
792         sigemptyset(&ign.sa_mask);
793         ign.sa_flags = 0;
794         sigaction(SIGINT, &ign, &intact);
795         sigaction(SIGQUIT, &ign, &quitact);
796         sigemptyset(&newsigblock);
797         sigaddset(&newsigblock, SIGCHLD);
798         sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
799         pid = fork();
800         if (pid == -1) {
801                 fprintf(stderr, "%s: fork failed, %s\n",
802                         __progname, strerror(errno));
803         } else if (pid == 0) {
804                 /* child here */
805
806                 /* close all open file handles for USBDEV\d* devices */
807                 for (i = 0; i < ndevs; i++)
808                         close(fds[i]);          /* USBDEV\d+ */
809                 close(fd);                      /* USBDEV */
810
811                 /* Restore original signal dispositions and exec the command. */
812                 sigaction(SIGINT, &intact, NULL);
813                 sigaction(SIGQUIT,  &quitact, NULL);
814                 sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
815
816                 execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
817
818                 /* should only be reached in case of error */
819                 exit(127);
820         } else {
821                 /* parent here */
822                 do {
823                         pid = waitpid(pid, &status, 0);
824                 } while (pid == -1 && errno == EINTR);
825         }
826         sigaction(SIGINT, &intact, NULL);
827         sigaction(SIGQUIT,  &quitact, NULL);
828         sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
829
830         if (pid == -1) {
831                 fprintf(stderr, "%s: waitpid returned: %s\n",
832                         __progname, strerror(errno));
833         } else if (pid == 0) {
834                 fprintf(stderr, "%s: waitpid returned 0 ?!\n",
835                         __progname);
836         } else {
837                 if (status == -1) {
838                         fprintf(stderr, "%s: Could not start '%s'\n",
839                                 __progname, cmd);
840                 } else if (status == 127) {
841                         fprintf(stderr, "%s: Shell failed for '%s'\n",
842                                 __progname, cmd);
843                 } else if (WIFEXITED(status) && WEXITSTATUS(status)) {
844                         fprintf(stderr, "%s: '%s' returned %d\n",
845                                 __progname, cmd, WEXITSTATUS(status));
846                 } else if (WIFSIGNALED(status)) {
847                         fprintf(stderr, "%s: '%s' caught signal %d\n",
848                                 __progname, cmd, WTERMSIG(status));
849                 } else if (verbose >= 2) {
850                         printf("%s: '%s' is ok\n", __progname, cmd);
851                 }
852         }
853 }
854
855 static void
856 process_event_queue(int fdesc)
857 {
858         struct usb_event event;
859         int error;
860         int len;
861         action_match_t action_match;
862
863         for (;;) {
864                 len = read(fdesc, &event, sizeof(event));
865                 if (len == -1) {
866                         if (errno == EWOULDBLOCK) {
867                                 /* no more events */
868                                 break;
869                         } else {
870                                 fprintf(stderr,"%s: Could not read event, %s\n",
871                                         __progname, strerror(errno));
872                                 exit(1);
873                         }
874                 }
875                 if (len == 0)
876                         break;
877                 if (len != sizeof(event)) {
878                         fprintf(stderr, "partial read on %s\n", USBDEV);
879                         exit(1);
880                 }
881
882                 /* we seem to have gotten a valid event */
883
884                 if (verbose)
885                         print_event(&event);
886
887                 /* handle the event appropriately */
888                 switch (event.ue_type) {
889                 case USB_EVENT_CTRLR_ATTACH:
890                         if (verbose)
891                                 printf("USB_EVENT_CTRLR_ATTACH\n");
892                         break;
893                 case USB_EVENT_CTRLR_DETACH:
894                         if (verbose)
895                                 printf("USB_EVENT_CTRLR_DETACH\n");
896                         break;
897                 case USB_EVENT_DEVICE_ATTACH:
898                 case USB_EVENT_DEVICE_DETACH:
899                         if (find_action(&event.u.ue_device, &action_match) == 0)
900                                 /* nothing found */
901                                 break;
902
903                         if (verbose >= 2)
904                                 print_action(action_match.action, 0);
905
906                         if (action_match.devname) {
907                                 if (verbose >= 2)
908                                         printf("%s: Setting DEVNAME='%s'\n",
909                                                 __progname, action_match.devname);
910
911                                 error = setenv("DEVNAME", action_match.devname, 1);
912                                 if (error)
913                                         fprintf(stderr, "%s: setenv(\"DEVNAME\", \"%s\",1) failed, %s\n",
914                                                 __progname, action_match.devname, strerror(errno));
915                         }
916
917                         if (USB_EVENT_IS_ATTACH(event.ue_type) &&
918                             action_match.action->attach) 
919                                 execute_command(action_match.action->attach);
920                         if (USB_EVENT_IS_DETACH(event.ue_type) &&
921                             action_match.action->detach)
922                                 execute_command(action_match.action->detach);
923                         break;
924                 case USB_EVENT_DRIVER_ATTACH:
925                         if (verbose)
926                                 printf("USB_EVENT_DRIVER_ATTACH\n");
927                         break;
928                 case USB_EVENT_DRIVER_DETACH:
929                         if (verbose)
930                                 printf("USB_EVENT_DRIVER_DETACH\n");
931                         break;
932                 default:
933                         printf("Unknown USB event %d\n", event.ue_type);
934                 }
935         }       
936 }
937
938
939 int
940 main(int argc, char **argv)
941 {
942         int error, i;
943         int ch;                 /* getopt option */
944         int debug = 0;          /* print debugging output */
945         int explore_once = 0;   /* don't do only explore */
946         int handle_events = 1;  /* do handle the event queue */
947         int maxfd;              /* maximum fd in use */
948         char buf[50];           /* for creation of the filename */
949         fd_set r,w;
950         int itimeout = TIMEOUT; /* timeout for select */
951         struct timeval tv;
952
953         if (modfind(USB_UHUB) < 0) {
954                 if (kldload(USB_KLD) < 0 || modfind(USB_UHUB) < 0) {
955                         perror(USB_KLD ": Kernel module not available");
956                         return 1;
957                 }
958         }
959
960         while ((ch = getopt(argc, argv, "c:def:nt:v")) != -1) {
961                 switch(ch) {
962                 case 'c':
963                         configfile = strdup(optarg);
964                         if (configfile == NULL) {
965                                 fprintf(stderr, "strdup returned NULL\n");
966                                 return 1;
967                         }
968                         break;
969                 case 'd':
970                         debug++;
971                         break;
972                 case 'e':
973                         explore_once = 1;
974                         break;
975                 case 'f':
976                         if (ndevs < MAXUSBDEV)
977                                 devs[ndevs++] = optarg;
978                         break;
979                 case 'n':
980                         handle_events = 0;
981                         break;
982                 case 't':
983                         itimeout = atoi(optarg);
984                         break;
985                 case 'v':
986                         verbose++;
987                         break;
988                 case '?':
989                 default:
990                         usage();
991                 }
992         }
993         argc -= optind;
994         argv += optind;
995
996         maxfd = 0;
997         if (ndevs == 0) {
998                 /* open all the USBDEVS\d+ devices */
999                 for (i = 0; i < MAXUSBDEV; i++) {
1000                         sprintf(buf, "%s%d", USBDEV, i);
1001                         fds[ndevs] = open(buf, O_RDWR);
1002                         if (fds[ndevs] >= 0) {
1003                                 devs[ndevs] = strdup(buf);
1004                                 if (devs[ndevs] == NULL) {
1005                                         fprintf(stderr, "strdup returned NULL\n");
1006                                         return 1;
1007                                 }
1008                                 if (verbose)
1009                                         printf("%s: opened %s\n", 
1010                                                __progname, devs[ndevs]);
1011                                 if (fds[ndevs] > maxfd)
1012                                         maxfd = fds[ndevs];
1013                                 ndevs++;
1014                         } else if (errno != ENXIO && errno != ENOENT) {
1015                                 /* there was an error, on a device that does
1016                                  * exist (device is configured)
1017                                  */
1018                                 fprintf(stderr, "%s: Could not open %s, %s\n",
1019                                         __progname, buf, strerror(errno));
1020                                 exit(1);
1021                         }
1022                 }
1023         } else {
1024                 /* open all the files specified with -f */
1025                 for (i = 0; i < ndevs; i++) {
1026                         fds[i] = open(devs[i], O_RDWR);
1027                         if (fds[i] < 0) {
1028                                 fprintf(stderr, "%s: Could not open %s, %s\n",
1029                                         __progname, devs[i], strerror(errno));
1030                                 exit(1);
1031                         } else {
1032                                 if (verbose)
1033                                         printf("%s: opened %s\n", 
1034                                                __progname, devs[i]);
1035                                 if (fds[i] > maxfd)
1036                                         maxfd = fds[i];
1037                         }
1038                 }
1039         }
1040
1041         if (ndevs == 0) {
1042                 fprintf(stderr, "No USB host controllers found\n");
1043                 exit(1);
1044         }
1045
1046
1047         /* Do the explore once and exit */
1048         if (explore_once) {
1049                 for (i = 0; i < ndevs; i++) {
1050                         error = ioctl(fds[i], USB_DISCOVER);
1051                         if (error < 0) {
1052                                 fprintf(stderr, "%s: ioctl(%s, USB_DISCOVER) "
1053                                         "failed, %s\n",
1054                                         __progname, devs[i], strerror(errno));
1055                                 exit(1);
1056                         }
1057                 }
1058                 exit(0);
1059         }
1060
1061         if (handle_events) {
1062                 if (verbose)
1063                         printf("%s: reading configuration file %s\n",
1064                                 __progname, configfile);
1065                 read_configuration();
1066
1067                 fd = open(USBDEV, O_RDONLY | O_NONBLOCK);
1068                 if (fd < 0) {
1069                         fprintf(stderr, "%s: Could not open %s, %s\n",
1070                                 __progname, USBDEV, strerror(errno));
1071                         exit(1);
1072                 }
1073                 if (verbose)
1074                         printf("%s: opened %s\n", __progname, USBDEV);
1075                 if (fd > maxfd)
1076                         maxfd = fd;
1077
1078                 process_event_queue(fd);        /* dequeue the initial events */
1079         }
1080
1081         /* move to the background */
1082         if (!debug)
1083                 daemon(0, 0);
1084
1085         /* start select on all the open file descriptors */
1086         for (;;) {
1087                 FD_ZERO(&r);
1088                 FD_ZERO(&w);
1089                 if (handle_events)
1090                         FD_SET(fd, &r);         /* device USBDEV */
1091                 for (i = 0; i < ndevs; i++)
1092                         FD_SET(fds[i], &w);     /* device USBDEV\d+ */
1093                 tv.tv_usec = 0;
1094                 tv.tv_sec = itimeout;
1095                 error = select(maxfd+1, &r, &w, 0, itimeout ? &tv : 0);
1096                 if (error < 0) {
1097                         fprintf(stderr, "%s: Select failed, %s\n",
1098                                 __progname, strerror(errno));
1099                         exit(1);
1100                 }
1101
1102                 /* USBDEV\d+ devices have signaled change, do a usb_discover */
1103                 for (i = 0; i < ndevs; i++) {
1104                         if (error == 0 || FD_ISSET(fds[i], &w)) {
1105                                 if (verbose >= 2)
1106                                         printf("%s: doing %sdiscovery on %s\n", 
1107                                                __progname,
1108                                                (error? "":"timeout "), devs[i]);
1109                                 if (ioctl(fds[i], USB_DISCOVER) < 0) {
1110                                         fprintf(stderr, "%s: ioctl(%s, "
1111                                                 "USB_DISCOVER) failed, %s\n",
1112                                                 __progname, devs[i],
1113                                                 strerror(errno));
1114                                         exit(1);
1115                                 }
1116                         }
1117                 }
1118
1119                 /* check the event queue */
1120                 if (handle_events && (FD_ISSET(fd, &r) || error == 0)) {
1121                         if (verbose >= 2)
1122                                 printf("%s: processing event queue %son %s\n",
1123                                         __progname,
1124                                        (error? "":"due to timeout "), USBDEV);
1125                         process_event_queue(fd);
1126                 }
1127         }
1128 }