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