Retire the binutils-2.24.
[dragonfly.git] / sbin / devfsctl / devfsctl.c
blobf8738e335ddcf35691ad8640042310bc2eb462cc
1 /*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
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>
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>
57 #include "devfsctl.h"
59 struct verb {
60 const char *verb;
61 rule_parser_t *parser;
62 int min_args;
65 struct devtype {
66 const char *name;
67 int value;
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);
94 static int dev_fd;
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;
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);
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 }
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 }
133 syntax_error(const char *fmt, ...)
135 char buf[1024];
136 va_list ap;
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);
145 static int
146 parser_include(char **tokens)
148 struct stat sb;
149 int error;
151 error = stat(tokens[1], &sb);
153 if (error)
154 syntax_error("could not stat %s on include, error: %s",
155 tokens[1], strerror(errno));
157 chdir(dirname(tokens[1]));
158 read_config(basename(tokens[1]), RULES_FILE);
160 return 0;
163 static int
164 parser_jail(char **tokens)
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]");
174 return 0;
177 static int
178 parser_hide(char **tokens)
180 struct groupdevid *id;
181 struct rule *rule;
183 id = get_id(tokens[1]);
184 rule = new_rule(rHIDE, id);
185 add_rule(rule);
187 return 0;
190 static int
191 parser_show(char **tokens)
193 struct groupdevid *id;
194 struct rule *rule;
196 id = get_id(tokens[1]);
197 rule = new_rule(rSHOW, id);
198 add_rule(rule);
200 return 0;
203 static int
204 parser_link(char **tokens)
206 struct groupdevid *id;
207 struct rule *rule;
209 id = get_id(tokens[1]);
210 rule = new_rule(rLINK, id);
211 rule->dest = strdup(tokens[2]);
212 add_rule(rule);
214 return 0;
217 static int
218 parser_group(char **tokens)
220 struct groupdevid *gid, *id;
221 int i;
222 size_t k;
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;
238 gid->list[k++] = id;
241 gid->list[k] = NULL;
243 return 0;
246 static int
247 parser_perm(char **tokens)
249 struct passwd *pwd;
250 struct group *grp;
251 struct groupdevid *id;
252 struct rule *rule;
253 char *uname;
254 char *grname;
256 id = get_id(tokens[1]);
257 rule = new_rule(rPERM, id);
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]);
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);
272 if ((grp = getgrnam(grname)))
273 rule->gid = grp->gr_gid;
274 else
275 syntax_error("invalid group name %s", grname);
277 add_rule(rule);
278 return 0;
281 struct groupdevid *
282 new_id(const char *name, int type_in)
284 struct groupdevid *id;
285 int type = (type_in != 0)?(type_in):(isNAME), i;
287 id = calloc(1, sizeof(*id));
288 if (id == NULL)
289 err(1, NULL);
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;
300 id->type = type;
302 if ((type == isNAME) || (type == isGROUP)) {
303 id->name = strdup(name);
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;
312 return (id);
315 struct groupdevid *
316 get_id(const char *name)
318 struct groupdevid *id;
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);
326 else
327 id = new_id(name, 0);
329 return id;
332 struct groupdevid *
333 get_group(const char *name, int expect)
335 struct groupdevid *g;
337 TAILQ_FOREACH(g, &group_list, link) {
338 if (strcmp(g->name, name) == 0)
339 return (g);
342 /* Caller doesn't expect to get a group no matter what */
343 if (!expect)
344 return NULL;
346 g = new_id(name, isGROUP);
347 TAILQ_INSERT_TAIL(&group_list, g, link);
348 return (g);
351 struct rule *
352 new_rule(int type, struct groupdevid *id)
354 struct rule *rule;
356 rule = calloc(1, sizeof(*rule));
357 if (rule == NULL)
358 err(1, NULL);
360 rule->type = type;
361 rule->id = id;
362 rule->jail = jail;
363 return (rule);
366 void
367 add_rule(struct rule *rule)
369 TAILQ_INSERT_TAIL(&rule_list, rule, link);
372 static int
373 dump_config_entry(struct rule *rule, struct groupdevid *id)
375 struct passwd *pwd;
376 struct group *grp;
377 int i;
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");
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;
397 break;
398 default: errx(1, "invalid id type %d", id->type);
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);
416 break;
417 case rLINK:
418 printf(" %s", rule->dest);
419 break;
420 default: /* NOTHING */;
423 if (rule->jail)
424 printf("\t(only affects jails)");
426 printf("\n");
428 return 0;
431 static int
432 rule_id_iterate(struct groupdevid *id, struct rule *rule,
433 rule_iterate_callback_t *callback)
435 int error = 0;
436 int i;
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;
443 } else {
444 error = callback(rule, id);
447 return error;
450 void
451 dump_config(void)
453 struct rule *rule;
455 TAILQ_FOREACH(rule, &rule_list, link) {
456 rule_id_iterate(rule->id, rule, dump_config_entry);
460 static int
461 rule_ioctl(unsigned long cmd, struct devfs_rule_ioctl *rule)
463 if (ioctl(dev_fd, cmd, rule) == -1)
464 err(1, "ioctl");
466 return 0;
469 static void
470 rule_fill(struct devfs_rule_ioctl *dr, struct rule *r, struct groupdevid *id)
472 dr->rule_type = 0;
473 dr->rule_cmd = 0;
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;
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;
510 if (r->jail)
511 dr->rule_type |= DEVFS_RULE_JAIL;
514 static int
515 rule_send(struct rule *rule, struct groupdevid *id)
517 struct devfs_rule_ioctl dr;
518 int r = 0;
520 strncpy(dr.mntpoint, mountp, PATH_MAX-1);
522 rule_fill(&dr, rule, id);
523 r = rule_ioctl(DEVFS_RULE_ADD, &dr);
525 return r;
529 rule_apply(void)
531 struct devfs_rule_ioctl dr;
532 struct rule *rule;
533 int r = 0;
535 strncpy(dr.mntpoint, mountp, PATH_MAX-1);
537 TAILQ_FOREACH(rule, &rule_list, link) {
538 r = rule_id_iterate(rule->id, rule, rule_send);
539 if (r != 0)
540 return (-1);
543 return (rule_ioctl(DEVFS_RULE_APPLY, &dr));
546 static int
547 rule_check_num_args(char **tokens, int num)
549 int i;
551 for (i = 0; tokens[i] != NULL; i++)
554 if (i < num) {
555 syntax_error("at least %d tokens were expected but only %d were found", num, i);
556 return 1;
558 return 0;
562 read_config(const char *name, int ftype)
564 FILE *fd;
565 struct stat sb;
567 if ((fd = fopen(name, "r")) == NULL) {
568 printf("Error opening config file %s\n", name);
569 perror("fopen");
570 return 1;
573 if (fstat(fileno(fd), &sb) != 0) {
574 errx(1, "file %s could not be fstat'ed, aborting", name);
577 if (sb.st_uid != 0)
578 errx(1, "file %s does not belong to root, aborting!", name);
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);
586 line_stack[line_stack_depth] = 1;
587 file_stack[line_stack_depth] = strdup(name);
588 cwd_stack[line_stack_depth] = getwd(NULL);
590 while (process_line(fd, ftype) == 0)
591 line_stack[line_stack_depth]++;
593 fclose(fd);
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]);
600 return 0;
603 static int
604 process_line(FILE* fd, int ftype)
606 char buffer[4096];
607 char *tokens[256];
608 int c, n, i = 0;
609 int quote = 0;
610 int ret = 0;
612 while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
613 buffer[i++] = (char)c;
614 if (i == (sizeof(buffer) -1))
615 break;
617 buffer[i] = '\0';
619 if (feof(fd) || ferror(fd))
620 ret = 1;
621 c = 0;
622 while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++;
624 * If this line effectively (after indentation) begins with the comment
625 * character #, we ignore the rest of the line.
627 if (buffer[c] == '#')
628 return 0;
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 */
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 */
645 buffer[c] = '\0';
649 if (quote) {
650 continue;
653 if ((buffer[c] == ' ') || (buffer[c] == '\t')) {
654 buffer[c++] = '\0';
655 while ((iswhitespace(buffer[c])) && (c < i)) c++;
656 tokens[n++] = &buffer[c--];
659 tokens[n] = NULL;
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.
666 if ((quote) || (n < 2) || (tokens[0][0] == '\0'))
667 return ret;
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;
682 return ret;
685 static int
686 rule_parser(char **tokens)
688 int i;
689 int parsed = 0;
691 /* Convert the command/verb to lowercase */
692 for (i = 0; tokens[0][i] != '\0'; i++)
693 tokens[0][i] = tolower(tokens[0][i]);
695 for (i = 0; parsers[i].verb; i++) {
696 if (rule_check_num_args(tokens, parsers[i].min_args) != 0)
697 continue;
699 if (!strcmp(tokens[0], parsers[i].verb)) {
700 parsers[i].parser(tokens);
701 parsed = 1;
702 break;
705 if (parsed == 0) {
706 syntax_error("unknown verb/command %s", tokens[0]);
709 return 0;
712 #if 0
713 static int
714 ruletab_parser(char **tokens)
716 struct rule_tab *rt;
717 struct stat sb;
718 int i;
719 int error;
721 if (rule_check_num_args(tokens, 2) != 0)
722 return 0;
724 error = stat(tokens[0], &sb);
725 if (error) {
726 printf("ruletab warning: could not stat %s: %s\n",
727 tokens[0], strerror(errno));
730 if (tokens[0][0] != '/') {
731 errx(1, "ruletab error: entry %s does not seem to be an absolute path",
732 tokens[0]);
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);
742 return 0;
745 void
746 rule_tab(void)
748 struct rule_tab *rt;
749 int error;
750 int mode;
752 chdir("/etc/devfs");
753 error = read_config("ruletab", RULETAB_FILE);
755 if (error)
756 errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)");
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");
768 dev_fd = open("/dev/devfs", O_RDWR);
769 if (dev_fd == -1)
770 err(1, "open(/dev/devfs)");
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;
779 break;
780 case RULETAB_SPECIFIC:
781 if (strcmp(rt->mntpoint, mountp) != 0)
782 continue;
783 break;
785 delete_rules();
786 read_config(rt->rule_file, RULES_FILE);
787 mountp = rt->mntpoint;
788 rule_apply();
791 close(dev_fd);
793 return;
796 void
797 delete_rules(void)
799 struct rule *rp;
800 struct groupdevid *gdp;
802 TAILQ_FOREACH(rp, &rule_list, link) {
803 TAILQ_REMOVE(&rule_list, rp, link);
806 TAILQ_FOREACH(gdp, &group_list, link) {
807 TAILQ_REMOVE(&group_list, gdp, link);
810 #endif
812 static void
813 usage(void)
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"
834 exit(1);
837 int main(int argc, char *argv[])
839 struct devfs_rule_ioctl dummy_rule;
840 struct stat sb;
841 int ch, error;
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;
864 case 'h':
865 case '?':
866 default:
867 usage();
868 /* NOT REACHED */
872 argc -= optind;
873 argv += optind;
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
881 if (!(aflag || rflag || cflag || dflag) ||
882 (dflag && (aflag || rflag || cflag || tflag))) {
883 usage();
884 /* NOT REACHED */
887 if (mountp == NULL)
888 mountp = "*";
889 else if (mountp[0] != '/') {
890 errx(1, "-m needs to be given an absolute path");
893 strncpy(dummy_rule.mntpoint, mountp, PATH_MAX-1);
895 if (config_name != NULL) {
896 error = stat(config_name, &sb);
898 if (error) {
899 chdir("/etc/devfs");
900 error = stat(config_name, &sb);
903 if (error)
904 err(1, "could not stat specified configuration file %s", config_name);
906 if (config_name[0] == '/')
907 chdir(dirname(config_name));
909 read_config(config_name, RULES_FILE);
912 if (dflag) {
913 dump_config();
914 exit(0);
917 dev_fd = open("/dev/devfs", O_RDWR);
918 if (dev_fd == -1)
919 err(1, "open(/dev/devfs)");
921 if (cflag)
922 rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule);
924 if (rflag)
925 rule_ioctl(DEVFS_RULE_RESET, &dummy_rule);
927 if (aflag)
928 rule_apply();
930 close(dev_fd);
932 return 0;