1 /* 2 * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Alex Hornung <ahornung@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/ioctl.h> 37 #include <sys/device.h> 38 #include <sys/queue.h> 39 #include <sys/stat.h> 40 #include <sys/devfs_rules.h> 41 42 #include <ctype.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <grp.h> 47 #include <libgen.h> 48 #include <pwd.h> 49 #include <stdarg.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 56 #include "devfsctl.h" 57 58 struct verb { 59 const char *verb; 60 rule_parser_t *parser; 61 int min_args; 62 }; 63 64 struct devtype { 65 const char *name; 66 int value; 67 }; 68 69 70 71 static int parser_include(char **); 72 static int parser_jail(char **); 73 static int parser_hide(char **); 74 static int parser_show(char **); 75 static int parser_link(char **); 76 static int parser_group(char **); 77 static int parser_perm(char **); 78 static int dump_config_entry(struct rule *, struct groupdevid *); 79 static int rule_id_iterate(struct groupdevid *, struct rule *, 80 rule_iterate_callback_t *); 81 static int rule_ioctl(unsigned long, struct devfs_rule_ioctl *); 82 static void rule_fill(struct devfs_rule_ioctl *, struct rule *, 83 struct groupdevid *); 84 static int rule_send(struct rule *, struct groupdevid *); 85 static int rule_check_num_args(char **, int); 86 static int process_line(FILE*, int); 87 static int rule_parser(char **tokens); 88 #if 0 89 static int ruletab_parser(char **tokens); 90 #endif 91 static void usage(void); 92 93 static int dev_fd; 94 95 const char *config_name = NULL, *mountp = NULL; 96 static int dflag = 0; 97 static int aflag = 0, cflag = 0, rflag = 0, tflag = 0; 98 static int line_stack[RULE_MAX_STACK]; 99 static char *file_stack[RULE_MAX_STACK]; 100 static char *cwd_stack[RULE_MAX_STACK]; 101 static int line_stack_depth = 0; 102 static int jail = 0; 103 104 static TAILQ_HEAD(, rule) rule_list = 105 TAILQ_HEAD_INITIALIZER(rule_list); 106 static TAILQ_HEAD(, rule_tab) rule_tab_list = 107 TAILQ_HEAD_INITIALIZER(rule_tab_list); 108 static TAILQ_HEAD(, groupdevid) group_list = 109 TAILQ_HEAD_INITIALIZER(group_list); 110 111 112 static const struct verb parsers[] = { 113 { "include", parser_include, 1 }, 114 { "jail", parser_jail, 1 }, 115 { "group", parser_group, 2 }, 116 { "perm", parser_perm, 2 }, 117 { "link", parser_link, 2 }, 118 { "hide", parser_hide, 2 }, 119 { "show", parser_show, 2 }, 120 { NULL, NULL, 0 } 121 }; 122 123 static const struct devtype devtypes[] = { 124 { "D_TAPE", D_TAPE }, 125 { "D_DISK", D_DISK }, 126 { "D_TTY", D_TTY }, 127 { "D_MEM", D_MEM }, 128 { NULL, 0 } 129 }; 130 131 void 132 syntax_error(const char *fmt, ...) 133 { 134 char buf[1024]; 135 va_list ap; 136 137 va_start(ap, fmt); 138 vsnprintf(buf, sizeof(buf), fmt, ap); 139 va_end(ap); 140 errx(1, "%s: syntax error on line %d: %s\n", 141 file_stack[line_stack_depth], line_stack[line_stack_depth], buf); 142 } 143 144 static int 145 parser_include(char **tokens) 146 { 147 struct stat sb; 148 int error; 149 150 error = stat(tokens[1], &sb); 151 152 if (error) 153 syntax_error("could not stat %s on include, error: %s", 154 tokens[1], strerror(errno)); 155 156 chdir(dirname(tokens[1])); 157 read_config(basename(tokens[1]), RULES_FILE); 158 159 return 0; 160 } 161 162 static int 163 parser_jail(char **tokens) 164 { 165 if (tokens[1][0] == 'y') { 166 jail = 1; 167 } else if (tokens[1][0] == 'n') { 168 jail = 0; 169 } else { 170 syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]"); 171 } 172 173 return 0; 174 } 175 176 static int 177 parser_hide(char **tokens) 178 { 179 struct groupdevid *id; 180 struct rule *rule; 181 182 id = get_id(tokens[1]); 183 rule = new_rule(rHIDE, id); 184 add_rule(rule); 185 186 return 0; 187 } 188 189 static int 190 parser_show(char **tokens) 191 { 192 struct groupdevid *id; 193 struct rule *rule; 194 195 id = get_id(tokens[1]); 196 rule = new_rule(rSHOW, id); 197 add_rule(rule); 198 199 return 0; 200 } 201 202 static int 203 parser_link(char **tokens) 204 { 205 struct groupdevid *id; 206 struct rule *rule; 207 208 id = get_id(tokens[1]); 209 rule = new_rule(rLINK, id); 210 rule->dest = strdup(tokens[2]); 211 add_rule(rule); 212 213 return 0; 214 } 215 216 static int 217 parser_group(char **tokens) 218 { 219 struct groupdevid *gid, *id; 220 int i; 221 size_t k; 222 223 gid = get_group(tokens[1], 1); 224 for (k = 0; gid->list[k] != NULL; k++) 225 /* Do nothing */; 226 for (i = 2; tokens[i] != NULL; i++) { 227 id = get_id(tokens[i]); 228 if (id == gid) { 229 syntax_error("recursive group definition for group %s", gid->name); 230 } else { 231 if (k >= gid->listsize-1 ) { 232 gid->list = realloc(gid->list, 233 2*gid->listsize*sizeof(struct groupdevid *)); 234 gid->listsize *= 2; 235 } 236 237 gid->list[k++] = id; 238 } 239 } 240 gid->list[k] = NULL; 241 242 return 0; 243 } 244 245 static int 246 parser_perm(char **tokens) 247 { 248 struct passwd *pwd; 249 struct group *grp; 250 struct groupdevid *id; 251 struct rule *rule; 252 char *uname; 253 char *grname; 254 255 id = get_id(tokens[1]); 256 rule = new_rule(rPERM, id); 257 258 rule->mode = strtol(tokens[3], NULL, 8); 259 uname = tokens[2]; 260 grname = strchr(tokens[2], ':'); 261 if (grname == NULL) 262 syntax_error("invalid format for user/group (%s)", tokens[2]); 263 264 *grname = '\0'; 265 ++grname; 266 if ((pwd = getpwnam(uname))) 267 rule->uid = pwd->pw_uid; 268 else 269 syntax_error("invalid user name %s", uname); 270 271 if ((grp = getgrnam(grname))) 272 rule->gid = grp->gr_gid; 273 else 274 syntax_error("invalid group name %s", grname); 275 276 add_rule(rule); 277 return 0; 278 } 279 280 struct groupdevid * 281 new_id(const char *name, int type_in) 282 { 283 struct groupdevid *id; 284 int type = (type_in != 0)?(type_in):(isNAME), i; 285 286 id = calloc(1, sizeof(*id)); 287 if (id == NULL) 288 err(1, NULL); 289 290 if (type_in == 0) { 291 for (i = 0; devtypes[i].name != NULL; i++) { 292 if (!strcmp(devtypes[i].name, name)) { 293 type = isTYPE; 294 id->devtype = devtypes[i].value; 295 break; 296 } 297 } 298 } 299 id->type = type; 300 301 if ((type == isNAME) || (type == isGROUP)) { 302 id->name = strdup(name); 303 } 304 305 if (type == isGROUP) { 306 id->list = calloc(4, sizeof(struct groupdevid *)); 307 memset(id->list, 0, 4 * sizeof(struct groupdevid *)); 308 id->listsize = 4; 309 } 310 311 return (id); 312 } 313 314 struct groupdevid * 315 get_id(const char *name) 316 { 317 struct groupdevid *id; 318 319 if ((name[0] == '@') && (name[1] != '\0')) { 320 id = get_group(name+1, 0); 321 if (id == NULL) 322 syntax_error("unknown group name '%s', you " 323 "have to use the 'group' verb first.", name+1); 324 } 325 else 326 id = new_id(name, 0); 327 328 return id; 329 } 330 331 struct groupdevid * 332 get_group(const char *name, int expect) 333 { 334 struct groupdevid *g; 335 336 TAILQ_FOREACH(g, &group_list, link) { 337 if (strcmp(g->name, name) == 0) 338 return (g); 339 } 340 341 /* Caller doesn't expect to get a group no matter what */ 342 if (!expect) 343 return NULL; 344 345 g = new_id(name, isGROUP); 346 TAILQ_INSERT_TAIL(&group_list, g, link); 347 return (g); 348 } 349 350 struct rule * 351 new_rule(int type, struct groupdevid *id) 352 { 353 struct rule *rule; 354 355 rule = calloc(1, sizeof(*rule)); 356 if (rule == NULL) 357 err(1, NULL); 358 359 rule->type = type; 360 rule->id = id; 361 rule->jail = jail; 362 return (rule); 363 } 364 365 void 366 add_rule(struct rule *rule) 367 { 368 TAILQ_INSERT_TAIL(&rule_list, rule, link); 369 } 370 371 static int 372 dump_config_entry(struct rule *rule, struct groupdevid *id) 373 { 374 struct passwd *pwd; 375 struct group *grp; 376 int i; 377 378 switch (rule->type) { 379 case rPERM: printf("perm "); break; 380 case rLINK: printf("link "); break; 381 case rHIDE: printf("hide "); break; 382 case rSHOW: printf("show "); break; 383 default: errx(1, "invalid rule type"); 384 } 385 386 switch (id->type) { 387 case isGROUP: printf("@"); /* FALLTHROUGH */ 388 case isNAME: printf("%s", id->name); break; 389 case isTYPE: 390 for (i = 0; devtypes[i].name != NULL; i++) { 391 if (devtypes[i].value == id->devtype) { 392 printf("%s", devtypes[i].name); 393 break; 394 } 395 } 396 break; 397 default: errx(1, "invalid id type %d", id->type); 398 } 399 400 switch (rule->type) { 401 case rPERM: 402 pwd = getpwuid(rule->uid); 403 grp = getgrgid(rule->gid); 404 if (pwd && grp) { 405 printf(" %s:%s 0%.03o", 406 pwd->pw_name, 407 grp->gr_name, 408 rule->mode); 409 } else { 410 printf(" %d:%d 0%.03o", 411 rule->uid, 412 rule->gid, 413 rule->mode); 414 } 415 break; 416 case rLINK: 417 printf(" %s", rule->dest); 418 break; 419 default: /* NOTHING */; 420 } 421 422 if (rule->jail) 423 printf("\t(only affects jails)"); 424 425 printf("\n"); 426 427 return 0; 428 } 429 430 static int 431 rule_id_iterate(struct groupdevid *id, struct rule *rule, 432 rule_iterate_callback_t *callback) 433 { 434 int error = 0; 435 int i; 436 437 if (id->type == isGROUP) { 438 for (i = 0; id->list[i] != NULL; i++) { 439 if ((error = rule_id_iterate(id->list[i], rule, callback))) 440 return error; 441 } 442 } else { 443 error = callback(rule, id); 444 } 445 446 return error; 447 } 448 449 void 450 dump_config(void) 451 { 452 struct rule *rule; 453 454 TAILQ_FOREACH(rule, &rule_list, link) { 455 rule_id_iterate(rule->id, rule, dump_config_entry); 456 } 457 } 458 459 static int 460 rule_ioctl(unsigned long cmd, struct devfs_rule_ioctl *rule) 461 { 462 if (ioctl(dev_fd, cmd, rule) == -1) 463 err(1, "ioctl"); 464 465 return 0; 466 } 467 468 static void 469 rule_fill(struct devfs_rule_ioctl *dr, struct rule *r, struct groupdevid *id) 470 { 471 dr->rule_type = 0; 472 dr->rule_cmd = 0; 473 474 switch (id->type) { 475 default: 476 errx(1, "invalid id type"); 477 case isGROUP: 478 errx(1, "internal error: can not fill group rule"); 479 /* NOTREACHED */ 480 case isNAME: 481 dr->rule_type |= DEVFS_RULE_NAME; 482 strncpy(dr->name, id->name, PATH_MAX-1); 483 break; 484 case isTYPE: 485 dr->rule_type |= DEVFS_RULE_TYPE; 486 dr->dev_type = id->devtype; 487 break; 488 } 489 490 switch (r->type) { 491 case rPERM: 492 dr->rule_cmd |= DEVFS_RULE_PERM; 493 dr->uid = r->uid; 494 dr->gid = r->gid; 495 dr->mode = r->mode; 496 break; 497 case rLINK: 498 dr->rule_cmd |= DEVFS_RULE_LINK; 499 strncpy(dr->linkname, r->dest, PATH_MAX-1); 500 break; 501 case rHIDE: 502 dr->rule_cmd |= DEVFS_RULE_HIDE; 503 break; 504 case rSHOW: 505 dr->rule_cmd |= DEVFS_RULE_SHOW; 506 break; 507 } 508 509 if (r->jail) 510 dr->rule_type |= DEVFS_RULE_JAIL; 511 } 512 513 static int 514 rule_send(struct rule *rule, struct groupdevid *id) 515 { 516 struct devfs_rule_ioctl dr; 517 int r = 0; 518 519 strncpy(dr.mntpoint, mountp, PATH_MAX-1); 520 521 rule_fill(&dr, rule, id); 522 r = rule_ioctl(DEVFS_RULE_ADD, &dr); 523 524 return r; 525 } 526 527 int 528 rule_apply(void) 529 { 530 struct devfs_rule_ioctl dr; 531 struct rule *rule; 532 int r = 0; 533 534 strncpy(dr.mntpoint, mountp, PATH_MAX-1); 535 536 TAILQ_FOREACH(rule, &rule_list, link) { 537 r = rule_id_iterate(rule->id, rule, rule_send); 538 if (r != 0) 539 return (-1); 540 } 541 542 return (rule_ioctl(DEVFS_RULE_APPLY, &dr)); 543 } 544 545 static int 546 rule_check_num_args(char **tokens, int num) 547 { 548 int i; 549 550 for (i = 0; tokens[i] != NULL; i++) 551 ; 552 553 if (i < num) { 554 syntax_error("at least %d tokens were expected but only %d were found", num, i); 555 return 1; 556 } 557 return 0; 558 } 559 560 int 561 read_config(const char *name, int ftype) 562 { 563 FILE *fd; 564 struct stat sb; 565 566 if ((fd = fopen(name, "r")) == NULL) { 567 printf("Error opening config file %s\n", name); 568 perror("fopen"); 569 return 1; 570 } 571 572 if (fstat(fileno(fd), &sb) != 0) { 573 errx(1, "file %s could not be fstat'ed, aborting", name); 574 } 575 576 if (sb.st_uid != 0) 577 errx(1, "file %s does not belong to root, aborting!", name); 578 579 if (++line_stack_depth >= RULE_MAX_STACK) { 580 --line_stack_depth; 581 syntax_error("Maximum include depth (%d) exceeded, " 582 "check for recursion.", RULE_MAX_STACK); 583 } 584 585 line_stack[line_stack_depth] = 1; 586 file_stack[line_stack_depth] = strdup(name); 587 cwd_stack[line_stack_depth] = getwd(NULL); 588 589 while (process_line(fd, ftype) == 0) 590 line_stack[line_stack_depth]++; 591 592 fclose(fd); 593 594 free(file_stack[line_stack_depth]); 595 free(cwd_stack[line_stack_depth]); 596 --line_stack_depth; 597 chdir(cwd_stack[line_stack_depth]); 598 599 return 0; 600 } 601 602 static int 603 process_line(FILE* fd, int ftype) 604 { 605 char buffer[4096]; 606 char *tokens[256]; 607 int c, n, i = 0; 608 int quote = 0; 609 int ret = 0; 610 611 while (((c = fgetc(fd)) != EOF) && (c != '\n')) { 612 buffer[i++] = (char)c; 613 if (i == (sizeof(buffer) -1)) 614 break; 615 } 616 buffer[i] = '\0'; 617 618 if (feof(fd) || ferror(fd)) 619 ret = 1; 620 c = 0; 621 while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++; 622 /* 623 * If this line effectively (after indentation) begins with the comment 624 * character #, we ignore the rest of the line. 625 */ 626 if (buffer[c] == '#') 627 return 0; 628 629 tokens[0] = &buffer[c]; 630 for (n = 1; c < i; c++) { 631 if (buffer[c] == '"') { 632 quote = !quote; 633 if (quote) { 634 if ((c >= 1) && (&buffer[c] != tokens[n-1])) { 635 syntax_error("stray opening quote not at beginning of token"); 636 /* NOTREACHED */ 637 } 638 tokens[n-1] = &buffer[c+1]; 639 } else { 640 if ((c < i-1) && (!iswhitespace(buffer[c+1]))) { 641 syntax_error("stray closing quote not at end of token"); 642 /* NOTREACHED */ 643 } 644 buffer[c] = '\0'; 645 } 646 } 647 648 if (quote) { 649 continue; 650 } 651 652 if ((buffer[c] == ' ') || (buffer[c] == '\t')) { 653 buffer[c++] = '\0'; 654 while ((iswhitespace(buffer[c])) && (c < i)) c++; 655 tokens[n++] = &buffer[c--]; 656 } 657 } 658 tokens[n] = NULL; 659 660 /* 661 * If there are not enough arguments for any function or it is 662 * a line full of whitespaces, we just return here. Or if a 663 * quote wasn't closed. 664 */ 665 if ((quote) || (n < 2) || (tokens[0][0] == '\0')) 666 return ret; 667 668 switch (ftype) { 669 case RULES_FILE: 670 ret = rule_parser(tokens); 671 break; 672 #if 0 673 case RULETAB_FILE: 674 ret = ruletab_parser(tokens); 675 break; 676 #endif 677 default: 678 ret = 1; 679 } 680 681 return ret; 682 } 683 684 static int 685 rule_parser(char **tokens) 686 { 687 int i; 688 int parsed = 0; 689 690 /* Convert the command/verb to lowercase */ 691 for (i = 0; tokens[0][i] != '\0'; i++) 692 tokens[0][i] = tolower(tokens[0][i]); 693 694 for (i = 0; parsers[i].verb; i++) { 695 if (rule_check_num_args(tokens, parsers[i].min_args) != 0) 696 continue; 697 698 if (!strcmp(tokens[0], parsers[i].verb)) { 699 parsers[i].parser(tokens); 700 parsed = 1; 701 break; 702 } 703 } 704 if (parsed == 0) { 705 syntax_error("unknown verb/command %s", tokens[0]); 706 } 707 708 return 0; 709 } 710 711 #if 0 712 static int 713 ruletab_parser(char **tokens) 714 { 715 struct rule_tab *rt; 716 struct stat sb; 717 int i; 718 int error; 719 720 if (rule_check_num_args(tokens, 2) != 0) 721 return 0; 722 723 error = stat(tokens[0], &sb); 724 if (error) { 725 printf("ruletab warning: could not stat %s: %s\n", 726 tokens[0], strerror(errno)); 727 } 728 729 if (tokens[0][0] != '/') { 730 errx(1, "ruletab error: entry %s does not seem to be an absolute path", 731 tokens[0]); 732 } 733 734 for (i = 1; tokens[i] != NULL; i++) { 735 rt = calloc(1, sizeof(struct rule_tab)); 736 rt->mntpoint = strdup(tokens[0]); 737 rt->rule_file = strdup(tokens[i]); 738 TAILQ_INSERT_TAIL(&rule_tab_list, rt, link); 739 } 740 741 return 0; 742 } 743 744 void 745 rule_tab(void) 746 { 747 struct rule_tab *rt; 748 int error; 749 int mode; 750 751 chdir("/etc/devfs"); 752 error = read_config("ruletab", RULETAB_FILE); 753 754 if (error) 755 errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)"); 756 757 if (!strcmp(mountp, "*")) { 758 mode = RULETAB_ALL; 759 } else if (!strcmp(mountp, "boot")) { 760 mode = RULETAB_ONLY_BOOT; 761 } else if (mountp) { 762 mode = RULETAB_SPECIFIC; 763 } else { 764 errx(1, "-t needs -m"); 765 } 766 767 dev_fd = open("/dev/devfs", O_RDWR); 768 if (dev_fd == -1) 769 err(1, "open(/dev/devfs)"); 770 771 TAILQ_FOREACH(rt, &rule_tab_list, link) { 772 switch(mode) { 773 case RULETAB_ONLY_BOOT: 774 if ((strcmp(rt->mntpoint, "*") != 0) && 775 (strcmp(rt->mntpoint, "/dev") != 0)) { 776 continue; 777 } 778 break; 779 case RULETAB_SPECIFIC: 780 if (strcmp(rt->mntpoint, mountp) != 0) 781 continue; 782 break; 783 } 784 delete_rules(); 785 read_config(rt->rule_file, RULES_FILE); 786 mountp = rt->mntpoint; 787 rule_apply(); 788 } 789 790 close(dev_fd); 791 792 return; 793 } 794 795 void 796 delete_rules(void) 797 { 798 struct rule *rp; 799 struct groupdevid *gdp; 800 801 TAILQ_FOREACH(rp, &rule_list, link) { 802 TAILQ_REMOVE(&rule_list, rp, link); 803 } 804 805 TAILQ_FOREACH(gdp, &group_list, link) { 806 TAILQ_REMOVE(&group_list, gdp, link); 807 } 808 } 809 #endif 810 811 static void 812 usage(void) 813 { 814 fprintf(stderr, 815 "Usage: devfsctl <commands> [options]\n" 816 "Valid commands are:\n" 817 " -a\n" 818 "\t Loads all read rules into the kernel and applies them\n" 819 " -c\n" 820 "\t Clears all rules stored in the kernel but does not reset the nodes\n" 821 " -d\n" 822 "\t Dumps the rules that have been loaded to the screen to verify syntax\n" 823 " -r\n" 824 "\t Resets all devfs_nodes but does not clear the rules stored\n" 825 "\n" 826 "Valid options and its arguments are:\n" 827 " -f <config_file>\n" 828 "\t Specifies the configuration file to be used\n" 829 " -m <mount_point>\n" 830 "\t Specifies a mount point to which the command will apply. Defaults to *\n" 831 ); 832 833 exit(1); 834 } 835 836 int main(int argc, char *argv[]) 837 { 838 struct devfs_rule_ioctl dummy_rule; 839 struct stat sb; 840 int ch, error; 841 842 while ((ch = getopt(argc, argv, "acdf:hm:r")) != -1) { 843 switch (ch) { 844 case 'f': 845 config_name = optarg; 846 break; 847 case 'm': 848 mountp = optarg; 849 break; 850 case 'a': 851 aflag = 1; 852 break; 853 case 'c': 854 cflag = 1; 855 break; 856 case 'r': 857 rflag = 1; 858 break; 859 case 'd': 860 dflag = 1; 861 break; 862 863 case 'h': 864 case '?': 865 default: 866 usage(); 867 /* NOT REACHED */ 868 } 869 } 870 871 argc -= optind; 872 argv += optind; 873 874 /* 875 * Check arguments: 876 * - need to use at least one mode 877 * - can not use -d with any other mode 878 * - can not use -t with any other mode or -f 879 */ 880 if (!(aflag || rflag || cflag || dflag) || 881 (dflag && (aflag || rflag || cflag || tflag))) { 882 usage(); 883 /* NOT REACHED */ 884 } 885 886 if (mountp == NULL) 887 mountp = "*"; 888 else if (mountp[0] != '/') { 889 errx(1, "-m needs to be given an absolute path"); 890 } 891 892 strncpy(dummy_rule.mntpoint, mountp, PATH_MAX-1); 893 894 if (config_name != NULL) { 895 error = stat(config_name, &sb); 896 897 if (error) { 898 chdir("/etc/devfs"); 899 error = stat(config_name, &sb); 900 } 901 902 if (error) 903 err(1, "could not stat specified configuration file %s", config_name); 904 905 if (config_name[0] == '/') 906 chdir(dirname(config_name)); 907 908 read_config(config_name, RULES_FILE); 909 } 910 911 if (dflag) { 912 dump_config(); 913 exit(0); 914 } 915 916 dev_fd = open("/dev/devfs", O_RDWR); 917 if (dev_fd == -1) 918 err(1, "open(/dev/devfs)"); 919 920 if (cflag) 921 rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule); 922 923 if (rflag) 924 rule_ioctl(DEVFS_RULE_RESET, &dummy_rule); 925 926 if (aflag) 927 rule_apply(); 928 929 close(dev_fd); 930 931 return 0; 932 } 933