Remove unneeded inclusions of <sys/cdefs.h> throughout the tree.
[games.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/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 }