xref: /dragonfly/sbin/devfsctl/devfsctl.c (revision 9ddb8543)
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 = 0;
551 	for (i = 0; tokens[i] != NULL; i++);
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 = 0;
688 	int error = 0;
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 ((error = rule_check_num_args(tokens, parsers[i].min_args)))
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 = 0;
719 	int error = 0;
720 
721 	if ((error = rule_check_num_args(tokens, 2)))
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 	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 }
933