xref: /dragonfly/sbin/devfsctl/devfsctl.c (revision 25a2db75)
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 }
934