coverity appeasement - redundant check
[minix.git] / commands / devmand / main.c
blob0e831adc252a947f163dcb8918dab348dd6495fb
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <getopt.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <lib.h>
7 #include <sys/stat.h>
8 #include <dirent.h>
9 #include <assert.h>
10 #include <signal.h>
11 #include "usb_driver.h"
12 #include "proto.h"
14 #define SERVICE_BINARY "/bin/service"
17 #define DEVMAN_TYPE_NAME "dev_type"
18 #define PATH_LEN 256
19 #define INVAL_MAJOR -1
20 #define MAX_CONFIG_DIRS 4
22 static void main_loop();
23 static void handle_event();
24 static void cleanup();
25 static void parse_config();
26 static void display_usage();
27 static enum dev_type determine_type(char *path);
28 static int get_major();
29 static void create_pid_file();
30 static void put_major(int major);
31 static struct devmand_usb_driver* match_usb_driver(struct usb_device_id *id);
32 static struct devmand_driver_instance *find_instance(int dev_id);
34 #define dbg(fmt, ... ) \
35 if (args.verbose) \
36 printf("%8s:%4d: %13s()| "fmt"\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__ )
38 static LIST_HEAD(usb_driver_head, devmand_usb_driver) drivers =
39 LIST_HEAD_INITIALIZER(drivers);
40 static LIST_HEAD(usb_driver_inst_head, devmand_driver_instance) instances =
41 LIST_HEAD_INITIALIZER(instances);
44 static int _run = 1;
45 struct global_args {
46 char *path;
47 char *config_dirs[MAX_CONFIG_DIRS];
48 int config_dir_count ;
49 int major_offset;
50 int verbose;
51 int check_config;
54 enum dev_type {
55 DEV_TYPE_USB_DEVICE,
56 DEV_TYPE_USB_INTF,
57 DEV_TYPE_UNKOWN
60 extern FILE *yyin;
62 static struct global_args args = {
63 .path = NULL,
64 .config_dirs = {NULL,NULL,NULL,NULL},
65 .config_dir_count = 0,
66 .major_offset = 25,
67 .verbose = 0,
68 .check_config = 0};
70 static struct option options[] =
72 {"dir" , required_argument, NULL, 'd'},
73 {"path", required_argument, NULL, 'p'},
74 {"verbose", required_argument, NULL, 'v'},
75 {"check-config", no_argument, NULL, 'x'},
76 {0,0,0,0} /* terminating entry */
79 static char major_bitmap[16]; /* can store up to 128 major number states */
82 /*===========================================================================*
83 * run_upscript *
84 *===========================================================================*/
85 int run_upscript(struct devmand_driver_instance *inst)
87 char cmdl[1024];
88 cmdl[0] = 0;
89 int ret;
91 snprintf(cmdl, 1024, "%s up %s %d %d",
92 inst->drv->upscript, inst->label, inst->major, inst->dev_id);
93 dbg("Running Upscript: \"%s\"", cmdl);
94 ret = system(cmdl);
95 if (ret != 0) {
96 return EINVAL;
98 return 0;
101 /*===========================================================================*
102 * run_cleanscript *
103 *===========================================================================*/
104 int run_cleanscript(struct devmand_usb_driver *drv)
106 char cmdl[1024];
107 cmdl[0] = 0;
108 int ret;
110 snprintf(cmdl, 1024, "%s clean %s ",
111 drv->upscript, drv->devprefix);
112 dbg("Running Upscript: \"%s\"", cmdl);
113 ret = system(cmdl);
115 if (ret != 0) {
116 return EINVAL;
119 return 0;
123 /*===========================================================================*
124 * run_downscript *
125 *===========================================================================*/
126 int run_downscript(struct devmand_driver_instance *inst)
128 char cmdl[1024];
129 cmdl[0] = 0;
130 int ret;
132 snprintf(cmdl, 1024, "%s down %s %d",
133 inst->drv->downscript, inst->label, inst->major);
135 dbg("Running Upscript: \"%s\"", cmdl);
137 ret = system(cmdl);
139 if (ret != 0) {
140 return EINVAL;
143 return 0;
147 /*===========================================================================*
148 * stop_driver *
149 *===========================================================================*/
150 int stop_driver(struct devmand_driver_instance *inst)
152 char cmdl[1024];
153 cmdl[0] = 0;
154 int ret;
156 snprintf(cmdl, 1024, "%s down %s %d",
157 SERVICE_BINARY, inst->label, inst->dev_id);
158 dbg("executing service: \"%s\"", cmdl);
159 ret = system(cmdl);
160 if (ret != 0)
162 return EINVAL;
164 printf("Stopped driver %s with label %s for device %d.\n",
165 inst->drv->binary, inst->label, inst->dev_id);
167 return 0;
171 /*===========================================================================*
172 * start_driver *
173 *===========================================================================*/
174 int start_driver(struct devmand_driver_instance *inst)
176 char cmdl[1024];
177 cmdl[0] = 0;
178 int ret;
180 /* generate label */
181 ret = snprintf(inst->label, 32, "%s%d", inst->drv->devprefix,
182 inst->dev_id);
183 if (ret < 0 || ret > DEVMAND_DRIVER_LABEL_LEN) {
184 dbg("label too long");
185 return ENOMEM;
188 snprintf(cmdl, 1024, "%s up %s -major %d -devid %d -label %s",
189 SERVICE_BINARY, inst->drv->binary, inst->major, inst->dev_id,
190 inst->label);
191 dbg("executing service: \"%s\"", cmdl);
193 ret = system(cmdl);
195 if (ret != 0) {
196 return EINVAL;
199 printf("Started driver %s with label %s for device %d.\n",
200 inst->drv->binary, inst->label, inst->dev_id);
202 return 0;
205 /*===========================================================================*
206 * find_instance *
207 *===========================================================================*/
208 static struct devmand_driver_instance *
209 find_instance(int dev_id)
211 struct devmand_driver_instance *inst;
213 LIST_FOREACH(inst, &instances, list) {
214 if (inst->dev_id == dev_id) {
215 return inst;
218 return NULL;
221 /*===========================================================================*
222 * match_usb_driver *
223 *===========================================================================*/
224 static int
225 match_usb_id(struct devmand_usb_match_id *mid, struct usb_device_id *id)
227 int res = 1;
228 unsigned long match = mid->match_flags;
229 struct usb_device_id *_id = &mid->match_id;
231 if (match & USB_MATCH_ID_VENDOR)
232 if (id->idVendor != _id->idVendor) res = 0;
233 if (match & USB_MATCH_ID_PRODUCT)
234 if (id->idProduct != _id->idProduct) res = 0;
235 if (match & USB_MATCH_BCD_DEVICE)
236 if (id->bcdDevice != _id->bcdDevice) res = 0;
237 if (match & USB_MATCH_DEVICE_PROTOCOL)
238 if (id->bDeviceProtocol != _id->bDeviceProtocol) res = 0;
239 if (match & USB_MATCH_DEVICE_SUBCLASS)
240 if (id->bDeviceSubClass != _id->bDeviceSubClass) res = 0;
241 if (match & USB_MATCH_DEVICE_PROTOCOL)
242 if (id->bDeviceProtocol != _id->bDeviceProtocol) res = 0;
243 if (match & USB_MATCH_INTERFACE_CLASS)
244 if (id->bInterfaceClass != _id->bInterfaceClass) res = 0;
245 if (match & USB_MATCH_INTERFACE_SUBCLASS)
246 if (id->bInterfaceSubClass != _id->bInterfaceSubClass) res = 0;
247 if (match & USB_MATCH_INTERFACE_PROTOCOL)
248 if (id->bInterfaceProtocol != _id->bInterfaceProtocol) res = 0;
250 if (match == 0UL) {
251 res = 0;
254 return res;
257 /*===========================================================================*
258 * match_usb_driver *
259 *===========================================================================*/
260 static struct devmand_usb_driver*
261 match_usb_driver(struct usb_device_id *id)
263 struct devmand_usb_driver *driver;
264 struct devmand_usb_match_id *mid;
266 LIST_FOREACH(driver, &drivers, list) {
267 LIST_FOREACH(mid, &driver->ids, list) {
268 if (match_usb_id(mid, id)) {
269 return driver;
273 return NULL;
276 /*===========================================================================*
277 * add_usb_match_id *
278 *===========================================================================*/
279 struct devmand_usb_driver * add_usb_driver(char *name)
281 struct devmand_usb_driver *udrv = (struct devmand_usb_driver*)
282 malloc(sizeof(struct devmand_usb_driver));
284 LIST_INSERT_HEAD(&drivers, udrv, list);
285 LIST_INIT(&udrv->ids);
287 udrv->name = name;
288 return udrv;
291 /*===========================================================================*
292 * add_usb_match_id *
293 *===========================================================================*/
294 struct devmand_usb_match_id *
295 add_usb_match_id
296 (struct devmand_usb_driver *drv)
298 struct devmand_usb_match_id *id = (struct devmand_usb_match_id*)
299 malloc(sizeof(struct devmand_usb_match_id));
301 memset(id, 0, sizeof(struct devmand_usb_match_id));
303 LIST_INSERT_HEAD(&drv->ids, id, list);
305 return id;
309 /*===========================================================================*
310 * parse_config *
311 *===========================================================================*/
312 static void parse_config()
314 int i, status, error;
315 struct stat stats;
316 char * dirname;
318 DIR * dir;
319 struct dirent entry;
320 struct dirent *result;
321 char config_file[PATH_MAX];
323 dbg("Parsing configuration directories... ");
324 /* Next parse the configuration directories */
325 for(i=0; i < args.config_dir_count; i++){
326 dirname = args.config_dirs[i];
327 dbg("Parsing config dir %s ", dirname);
328 status = stat(dirname,&stats);
329 if (status == -1){
330 error = errno;
331 dbg("Failed to read directory '%s':%s (skipping) \n",
332 dirname,strerror(error));
333 continue;
335 if (!S_ISDIR(stats.st_mode)){
336 dbg("Parse configuration skipping %s "
337 "(not a directory) \n",dirname);
338 continue;
340 dir = opendir(dirname);
341 if (dir == NULL){
342 error = errno;
343 dbg("Parse configuration failed to read dir '%s'"
344 "(skipping) :%s\n",dirname, strerror(error));
345 continue;
347 while( (status = readdir_r(dir,&entry,&result)) == 0 ){
348 if (result == NULL){ /* last entry */
349 closedir(dir);
350 break;
353 /* concatenate dir and file name to open it */
354 snprintf(config_file,PATH_MAX, "%s/%s",
355 dirname,entry.d_name);
356 status = stat(config_file, &stats);
357 if (status == -1){
358 error = errno;
359 dbg("Parse configuration Failed to stat file "
360 "'%s': %s (skipping)\n", config_file,
361 strerror(error));
363 if (S_ISREG(stats.st_mode)){
364 dbg("Parsing file %s",config_file);
365 yyin = fopen(config_file, "r");
367 if (yyin < 0) {
368 dbg("Can not open config file:"
369 " %d.\n", errno);
371 yyparse();
372 dbg("Done.");
373 fclose(yyin);
377 dbg("Parsing configuration directories done... ");
381 /*===========================================================================*
382 * cleanup *
383 *===========================================================================*/
384 static void cleanup() {
385 struct devmand_driver_instance *inst;
386 /* destroy fifo */
387 dbg("cleaning up... ");
388 /* quit all running drivers */
389 LIST_FOREACH(inst, &instances, list) {
390 dbg("stopping driver %s", inst->label);
391 run_downscript (inst);
392 stop_driver(inst);
394 unlink("/var/run/devmand.pid");
397 static void sig_int(int sig) {
398 dbg("devman: Received SIGINT... cleaning up.");
399 _run = 0;
402 /*===========================================================================*
403 * create_pid_file *
404 *===========================================================================*/
405 void create_pid_file()
407 FILE *fd;
409 fd = fopen("/var/run/devmand.pid", "r");
410 if(fd) {
411 fprintf(stderr, "devmand: /var/run/devmand.pid exists... "
412 "another devmand running?\n");
413 fclose(fd);
414 exit(1);
415 } else {
416 fd = fopen("/var/run/devmand.pid","w");
417 fprintf(fd, "%d", getpid());
418 fclose(fd);
422 /*===========================================================================*
423 * main *
424 *===========================================================================*/
425 int main(int argc, char *argv[])
427 int opt, optindex;
428 struct devmand_usb_driver *driver;
431 /* get command line arguments */
432 while ((opt = getopt_long(argc, argv, "d:p:vxh?", options, &optindex))
433 != -1) {
434 switch (opt) {
435 case 'd':/* config directory */
436 if (args.config_dir_count >= MAX_CONFIG_DIRS){
437 fprintf(stderr,"Parse arguments: Maximum"
438 " of %i configuration directories"
439 " reached skipping directory '%s'\n"
440 , MAX_CONFIG_DIRS, optarg);
441 break;
443 args.config_dirs[args.config_dir_count] = optarg;
444 args.config_dir_count++;
445 break;
446 case 'p': /* sysfs path */
447 args.path = optarg;
448 break;
449 case 'v': /* verbose */
450 args.verbose = 1;
451 break;
452 case 'x': /* check config */
453 args.check_config = 1;
454 break;
455 case 'h': /* help */
456 case '?': /* help */
457 default:
458 display_usage(argv[0]);
459 return 0;
464 /* is path set? */
465 if (args.path == NULL) {
466 args.path = "/sys/";
469 /* is the configuration directory set? */
470 if (args.config_dir_count == 0) {
471 dbg("Using default configuration directory");
472 args.config_dirs[0] = "/etc/devmand";
473 args.config_dir_count = 1;
476 /* If we only check the configuration run and exit imediately */
477 if (args.check_config == 1){
478 fprintf(stdout, "Only parsing configuration\n");
479 parse_config();
480 exit(0);
483 create_pid_file();
485 parse_config();
486 LIST_FOREACH(driver, &drivers, list) {
487 run_cleanscript(driver);
490 signal(SIGINT, sig_int);
492 main_loop();
494 cleanup();
496 return 0;
499 /*===========================================================================*
500 * determine_type *
501 *===========================================================================*/
502 static enum dev_type determine_type (char *path)
504 FILE * fd;
505 char *mypath;
506 char buf[256];
507 int res;
509 mypath = (char *) calloc(1, strlen(path)+strlen(DEVMAN_TYPE_NAME)+1);
511 if (mypath == NULL) {
512 fprintf(stderr, "ERROR: out of mem\n");
513 cleanup();
514 exit(1);
517 strcat(mypath, path);
518 strcat(mypath, DEVMAN_TYPE_NAME);
520 fd = fopen(mypath, "r");
521 free(mypath);
523 if (fd == NULL) {
524 fprintf(stderr, "WARN: could not open %s\n", mypath);
525 return DEV_TYPE_UNKOWN;
528 res = fscanf(fd , "%s\n", buf);
529 fclose(fd);
531 if (res != 1) {
532 fprintf(stderr, "WARN: could not parse %s\n", mypath);
533 return DEV_TYPE_UNKOWN;
536 if (strcmp(buf, "USB_DEV") == 0) {
537 return DEV_TYPE_USB_DEVICE;
538 } else if (strcmp(buf, "USB_INTF") == 0) {
539 return DEV_TYPE_USB_INTF;
542 return DEV_TYPE_UNKOWN;
545 /*===========================================================================*
546 * read_hex_uint *
547 *===========================================================================*/
548 static int read_hex_uint(char *base_path, char *name, unsigned int* val )
550 char my_path[PATH_LEN];
551 FILE *fd;
552 memset(my_path,0,PATH_LEN);
553 int ret = 0;
555 strcat(my_path, base_path);
556 strcat(my_path, name);
558 fd = fopen(my_path, "r");
560 if (fd == NULL) {
561 fprintf(stderr, "WARN: could not open %s\n", my_path);
562 return EEXIST;
563 } else if (fscanf(fd, "0x%x\n", val ) != 1) {
564 fprintf(stderr, "WARN: could not parse %s\n", my_path);
565 ret = EINVAL;
567 fclose(fd);
569 return ret;
572 /*===========================================================================*
573 * get_major *
574 *===========================================================================*/
575 static int get_major() {
576 int i, ret = args.major_offset;
578 for (i=0; i < 16; i++) {
579 int j;
580 for (j = 0; j < 8; j++ ) {
581 if ((major_bitmap[i] & (1 << j))) {
582 major_bitmap[i] &= !(1 << j);
583 return ret;
585 ret++;
588 return INVAL_MAJOR;
591 /*===========================================================================*
592 * put_major *
593 *===========================================================================*/
594 static void put_major(int major) {
595 int i;
596 major -= args.major_offset;
597 assert(major >= 0);
599 for (i=0; i < 16; i++) {
600 int j;
601 for (j = 0; j < 8; j++ ) {
602 if (major==0) {
603 assert(!(major_bitmap[i] & (1 <<j)));
604 major_bitmap[i] |= (1 << j);
605 return;
607 major--;
612 /*===========================================================================*
613 * generate_usb_device_id *
614 *===========================================================================*/
615 static struct usb_device_id *
616 generate_usb_device_id(char * path, int is_interface)
618 struct usb_device_id *ret;
619 int res;
620 unsigned int val;
622 ret = (struct usb_device_id *)
623 calloc(1,sizeof (struct usb_device_id));
625 if (is_interface) {
627 res = read_hex_uint(path, "../idVendor", &val);
628 if (res) goto err;
629 ret->idVendor = val;
631 res = read_hex_uint(path, "../idProduct", &val);
632 if (res) goto err;
633 ret->idProduct = val;
634 #if 0
635 res = read_hex_uint(path, "../bcdDevice", &val);
636 if (res) goto err;
637 ret->bcdDevice = val;
638 #endif
639 res = read_hex_uint(path, "../bDeviceClass", &val);
640 if (res) goto err;
641 ret->bDeviceClass = val;
643 res = read_hex_uint(path, "../bDeviceSubClass", &val);
644 if (res) goto err;
645 ret->bDeviceSubClass = val;
647 res = read_hex_uint(path, "../bDeviceProtocol", &val);
648 if (res) goto err;
649 ret->bDeviceProtocol = val;
651 res = read_hex_uint(path, "/bInterfaceClass", &val);
652 if (res) goto err;
653 ret->bInterfaceClass = val;
655 res = read_hex_uint(path, "/bInterfaceSubClass", &val);
656 if (res) goto err;
657 ret->bInterfaceSubClass = val;
659 res = read_hex_uint(path, "/bInterfaceProtocol", &val);
660 if (res) goto err;
661 ret->bInterfaceProtocol = val;
664 return ret;
666 err:
667 free(ret);
668 return NULL;
671 /*===========================================================================*
672 * usb_intf_add_even *
673 *===========================================================================*/
674 static void usb_intf_add_event(char *path, int dev_id)
676 struct usb_device_id *id;
677 struct devmand_usb_driver *drv;
678 struct devmand_driver_instance *drv_inst;
679 int major, ret;
681 /* generate usb_match_id */
682 id = generate_usb_device_id(path,TRUE);
683 if (id == NULL) {
684 fprintf(stderr, "WARN: could not create usb_device id...\n"
685 " ommiting event\n");
686 free(id);
687 return;
690 /* find suitable driver */
691 drv = match_usb_driver(id);
692 free (id);
694 if (drv == NULL) {
695 dbg("INFO: could not find a suitable driver for %s", path);
696 return;
699 /* create instance */
700 drv_inst = (struct devmand_driver_instance *)
701 calloc(1,sizeof(struct devmand_driver_instance));
703 if (drv_inst == NULL) {
704 fprintf(stderr, "ERROR: out of memory");
705 return; /* maybe better quit here. */
709 /* allocate inode number, if device files needed */
710 major = get_major();
711 if (major == INVAL_MAJOR) {
712 fprintf(stderr, "WARN: ran out of major numbers\n"
713 " cannot start driver %s for %s\n",
714 drv->name, path);
715 return;
718 drv_inst->major = major;
719 drv_inst->drv = drv;
720 drv_inst->dev_id = dev_id;
723 /* start driver (invoke service) */
724 start_driver(drv_inst);
727 * run the up action
729 * An up action can be any executable. Before running it devmand
730 * will set certain environment variables so the script can configure
731 * the device (or generate device files, etc). See up_action() for that.
733 if (drv->upscript) {
734 ret = run_upscript(drv_inst);
735 if (ret) {
736 stop_driver(drv_inst);
737 fprintf(stderr, "devmand: warning, could not run up_action\n");
738 free(drv_inst);
739 return;
743 LIST_INSERT_HEAD(&instances,drv_inst,list);
746 /*===========================================================================*
747 * usb_intf_remove_event *
748 *===========================================================================*/
749 static void usb_intf_remove_event(char *path, int dev_id)
751 struct devmand_driver_instance *inst;
752 struct devmand_usb_driver *drv;
753 int ret;
755 /* find the driver instance */
756 inst = find_instance(dev_id);
758 if (inst == NULL) {
759 dbg("No driver running for id: %d", dev_id);
760 return;
762 drv = inst->drv;
764 /* run the down script */
765 if (drv->downscript) {
766 ret = run_downscript(inst);
767 if (ret) {
768 fprintf(stderr, "WARN: error running up_action");
772 /* stop the driver */
773 stop_driver(inst);
775 /* free major */
776 put_major(inst->major);
778 /* free instance */
779 LIST_REMOVE(inst,list);
780 free(inst);
783 /*===========================================================================*
784 * handle_event *
785 *===========================================================================*/
786 static void handle_event(char *event)
788 enum dev_type type;
789 char path[PATH_LEN];
790 char tmp_path[PATH_LEN];
791 int dev_id, res;
793 path[0]=0;
795 if (strncmp("ADD ", event, 4) == 0) {
797 /* read data from event */
798 res = sscanf(event, "ADD %s 0x%x", tmp_path, &dev_id);
800 if (res != 2) {
801 fprintf(stderr, "WARN: could not parse event: %s", event);
802 fprintf(stderr, "WARN: omitting event: %s", event);
805 strcpy(path, args.path);
806 strcat(path, tmp_path);
808 /* what kind of device is added? */
809 type = determine_type(path);
811 switch (type) {
812 case DEV_TYPE_USB_DEVICE:
813 dbg("USB device added: ommited....");
814 /* ommit usb devices for now */
815 break;
816 case DEV_TYPE_USB_INTF:
817 dbg("USB interface added: (%s, devid: = %d)",path, dev_id);
818 usb_intf_add_event(path, dev_id);
819 return;
820 default:
821 dbg("default");
822 fprintf(stderr, "WARN: ommiting event\n");
824 } else if (strncmp("REMOVE ", event, 7) == 0) {
826 /* read data from event */
827 res = sscanf(event,"REMOVE %s 0x%x", tmp_path, &dev_id);
829 if (res != 2) {
830 fprintf(stderr, "WARN: could not parse event: %s", event);
831 fprintf(stderr, "WARN: omitting event: %s", event);
834 usb_intf_remove_event(path, dev_id);
836 #if 0
837 strcpy(path, args.path);
838 strcat(path, tmp_path);
840 /* what kind of device is added? */
841 type = determine_type(path);
843 switch (type) {
844 case DEV_TYPE_USB_DEVICE:
845 /* ommit usb devices for now */
846 break;
847 case DEV_TYPE_USB_INTF:
848 usb_intf_remove_event(path, dev_id);
849 return;
850 default:
851 fprintf(stderr, "WARN: ommiting event\n");
853 #endif
858 /*===========================================================================*
859 * main_loop *
860 *===========================================================================*/
861 static void main_loop()
863 char ev_path[128];
864 char buf[256];
865 int len;
866 FILE* fd;
867 len = strlen(args.path);
869 /* init major numbers */
871 memset(&major_bitmap, 0xff, 16);
873 if (len > 128 - 7 /*len of "events" */) {
874 fprintf(stderr, "pathname to long\n");
875 cleanup();
876 exit(1);
879 strcpy(ev_path, args.path);
880 strcat(ev_path, "events");
883 while (_run) {
885 char *res;
887 fd = fopen(ev_path, "r");
888 if (fd == NULL) {
889 fprintf(stderr,"devmand error: could not open event "
890 "file %s bailing out\n", ev_path);
891 cleanup();
892 exit(1);
895 res = fgets(buf, 256, fd);
896 fclose(fd);
898 if (res == NULL) {
899 usleep(50000);
900 continue;
902 dbg("handle_event: %s", buf);
903 handle_event(buf);
907 /*===========================================================================*
908 * display_usage *
909 *===========================================================================*/
910 static void display_usage(const char *name)
912 printf("Usage: %s [{-p|--pathname} PATH_TO_SYS}"
913 " [{-d|--config-dir} CONFIG_DIR] [-v|--verbose]"
914 " [[x||--check-config]\n", name);