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 = 0; 551 for (i = 0; tokens[i] != NULL; i++); 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 = 0; 688 int error = 0; 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 ((error = rule_check_num_args(tokens, parsers[i].min_args))) 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 = 0; 719 int error = 0; 720 721 if ((error = rule_check_num_args(tokens, 2))) 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 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