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