sbin/hammer2: Fix hammer2(8) and usage()
[dragonfly.git] / sbin / devfsctl / devfsctl.c
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 }