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