xref: /dragonfly/sbin/devfsctl/devfsctl.c (revision d50f9ae3)
114f0742eSAlex Hornung /*
214f0742eSAlex Hornung  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
314f0742eSAlex Hornung  *
414f0742eSAlex Hornung  * This code is derived from software contributed to The DragonFly Project
514f0742eSAlex Hornung  * by Alex Hornung <ahornung@gmail.com>
614f0742eSAlex Hornung  *
714f0742eSAlex Hornung  * Redistribution and use in source and binary forms, with or without
814f0742eSAlex Hornung  * modification, are permitted provided that the following conditions
914f0742eSAlex Hornung  * are met:
1014f0742eSAlex Hornung  *
1114f0742eSAlex Hornung  * 1. Redistributions of source code must retain the above copyright
1214f0742eSAlex Hornung  *    notice, this list of conditions and the following disclaimer.
1314f0742eSAlex Hornung  * 2. Redistributions in binary form must reproduce the above copyright
1414f0742eSAlex Hornung  *    notice, this list of conditions and the following disclaimer in
1514f0742eSAlex Hornung  *    the documentation and/or other materials provided with the
1614f0742eSAlex Hornung  *    distribution.
1714f0742eSAlex Hornung  * 3. Neither the name of The DragonFly Project nor the names of its
1814f0742eSAlex Hornung  *    contributors may be used to endorse or promote products derived
1914f0742eSAlex Hornung  *    from this software without specific, prior written permission.
2014f0742eSAlex Hornung  *
2114f0742eSAlex Hornung  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2214f0742eSAlex Hornung  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2314f0742eSAlex Hornung  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2414f0742eSAlex Hornung  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2514f0742eSAlex Hornung  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2614f0742eSAlex Hornung  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2714f0742eSAlex Hornung  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2814f0742eSAlex Hornung  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2914f0742eSAlex Hornung  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3014f0742eSAlex Hornung  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3114f0742eSAlex Hornung  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3214f0742eSAlex Hornung  * SUCH DAMAGE.
3314f0742eSAlex Hornung  */
3414f0742eSAlex Hornung 
35804580cbSSascha Wildner #include <sys/param.h>
3614f0742eSAlex Hornung #include <sys/ioctl.h>
3714f0742eSAlex Hornung #include <sys/device.h>
3814f0742eSAlex Hornung #include <sys/queue.h>
3914f0742eSAlex Hornung #include <sys/stat.h>
4014f0742eSAlex Hornung #include <sys/devfs_rules.h>
4114f0742eSAlex Hornung 
4214f0742eSAlex Hornung #include <ctype.h>
4314f0742eSAlex Hornung #include <err.h>
4414f0742eSAlex Hornung #include <errno.h>
4514f0742eSAlex Hornung #include <fcntl.h>
4614f0742eSAlex Hornung #include <grp.h>
4714f0742eSAlex Hornung #include <libgen.h>
4814f0742eSAlex Hornung #include <pwd.h>
4914f0742eSAlex Hornung #include <stdarg.h>
5014f0742eSAlex Hornung #include <stdio.h>
5114f0742eSAlex Hornung #include <stdlib.h>
5214f0742eSAlex Hornung #include <string.h>
5314f0742eSAlex Hornung #include <unistd.h>
5414f0742eSAlex Hornung 
5514f0742eSAlex Hornung 
5614f0742eSAlex Hornung #include "devfsctl.h"
5714f0742eSAlex Hornung 
5814f0742eSAlex Hornung struct verb {
5914f0742eSAlex Hornung 	const char *verb;
6014f0742eSAlex Hornung 	rule_parser_t *parser;
6114f0742eSAlex Hornung 	int	min_args;
6214f0742eSAlex Hornung };
6314f0742eSAlex Hornung 
6414f0742eSAlex Hornung struct devtype {
6514f0742eSAlex Hornung 	const char *name;
6614f0742eSAlex Hornung 	int	value;
6714f0742eSAlex Hornung };
6814f0742eSAlex Hornung 
6914f0742eSAlex Hornung 
7014f0742eSAlex Hornung 
7114f0742eSAlex Hornung static int parser_include(char **);
7214f0742eSAlex Hornung static int parser_jail(char **);
7314f0742eSAlex Hornung static int parser_hide(char **);
7414f0742eSAlex Hornung static int parser_show(char **);
7514f0742eSAlex Hornung static int parser_link(char **);
7614f0742eSAlex Hornung static int parser_group(char **);
7714f0742eSAlex Hornung static int parser_perm(char **);
7814f0742eSAlex Hornung static int dump_config_entry(struct rule *, struct groupdevid *);
7914f0742eSAlex Hornung static int rule_id_iterate(struct groupdevid *, struct rule *,
8014f0742eSAlex Hornung 		rule_iterate_callback_t *);
8114f0742eSAlex Hornung static int rule_ioctl(unsigned long, struct devfs_rule_ioctl *);
8214f0742eSAlex Hornung static void rule_fill(struct devfs_rule_ioctl *, struct rule *,
8314f0742eSAlex Hornung 		struct groupdevid *);
8414f0742eSAlex Hornung static int rule_send(struct rule *, struct groupdevid *);
8514f0742eSAlex Hornung static int rule_check_num_args(char **, int);
8614f0742eSAlex Hornung static int process_line(FILE*, int);
8714f0742eSAlex Hornung static int rule_parser(char **tokens);
8814f0742eSAlex Hornung #if 0
8914f0742eSAlex Hornung static int ruletab_parser(char **tokens);
9014f0742eSAlex Hornung #endif
9114f0742eSAlex Hornung static void usage(void);
9214f0742eSAlex Hornung 
9314f0742eSAlex Hornung static int dev_fd;
9414f0742eSAlex Hornung 
95*d50f9ae3SSascha Wildner static const char *config_name = NULL, *mountp = NULL;
9614f0742eSAlex Hornung static int dflag = 0;
9714f0742eSAlex Hornung static int aflag = 0, cflag = 0, rflag = 0, tflag = 0;
9814f0742eSAlex Hornung static int line_stack[RULE_MAX_STACK];
9914f0742eSAlex Hornung static char *file_stack[RULE_MAX_STACK];
10014f0742eSAlex Hornung static char *cwd_stack[RULE_MAX_STACK];
10114f0742eSAlex Hornung static int line_stack_depth = 0;
10214f0742eSAlex Hornung static int jail = 0;
10314f0742eSAlex Hornung 
10414f0742eSAlex Hornung static TAILQ_HEAD(, rule) rule_list =
10514f0742eSAlex Hornung 		TAILQ_HEAD_INITIALIZER(rule_list);
10614f0742eSAlex Hornung static TAILQ_HEAD(, rule_tab) rule_tab_list =
10714f0742eSAlex Hornung 		TAILQ_HEAD_INITIALIZER(rule_tab_list);
10814f0742eSAlex Hornung static TAILQ_HEAD(, groupdevid) group_list =
10914f0742eSAlex Hornung 		TAILQ_HEAD_INITIALIZER(group_list);
11014f0742eSAlex Hornung 
11114f0742eSAlex Hornung 
11214f0742eSAlex Hornung static const struct verb parsers[] = {
11314f0742eSAlex Hornung 	{ "include", parser_include, 1 },
11414f0742eSAlex Hornung 	{ "jail", parser_jail, 1 },
11514f0742eSAlex Hornung 	{ "group", parser_group, 2 },
11614f0742eSAlex Hornung 	{ "perm", parser_perm, 2 },
11714f0742eSAlex Hornung 	{ "link", parser_link, 2 },
11814f0742eSAlex Hornung 	{ "hide", parser_hide, 2 },
11914f0742eSAlex Hornung 	{ "show", parser_show, 2 },
12014f0742eSAlex Hornung 	{ NULL, NULL, 0 }
12114f0742eSAlex Hornung };
12214f0742eSAlex Hornung 
12314f0742eSAlex Hornung static const struct devtype devtypes[] = {
12414f0742eSAlex Hornung 	{ "D_TAPE", D_TAPE },
12514f0742eSAlex Hornung 	{ "D_DISK", D_DISK },
12614f0742eSAlex Hornung 	{ "D_TTY", D_TTY },
12714f0742eSAlex Hornung 	{ "D_MEM", D_MEM },
12814f0742eSAlex Hornung 	{ NULL, 0 }
12914f0742eSAlex Hornung };
13014f0742eSAlex Hornung 
131488b1be5SSascha Wildner void
syntax_error(const char * fmt,...)13214f0742eSAlex Hornung syntax_error(const char *fmt, ...)
13314f0742eSAlex Hornung {
13414f0742eSAlex Hornung 	char buf[1024];
13514f0742eSAlex Hornung 	va_list ap;
13614f0742eSAlex Hornung 
13714f0742eSAlex Hornung 	va_start(ap, fmt);
13814f0742eSAlex Hornung 	vsnprintf(buf, sizeof(buf), fmt, ap);
13914f0742eSAlex Hornung 	va_end(ap);
140488b1be5SSascha Wildner 	errx(1, "%s: syntax error on line %d: %s\n",
141488b1be5SSascha Wildner 	    file_stack[line_stack_depth], line_stack[line_stack_depth], buf);
14214f0742eSAlex Hornung }
14314f0742eSAlex Hornung 
14414f0742eSAlex Hornung static int
parser_include(char ** tokens)14514f0742eSAlex Hornung parser_include(char **tokens)
14614f0742eSAlex Hornung {
14714f0742eSAlex Hornung 	struct stat	sb;
14814f0742eSAlex Hornung 	int error;
14914f0742eSAlex Hornung 
15014f0742eSAlex Hornung 	error = stat(tokens[1], &sb);
15114f0742eSAlex Hornung 
15214f0742eSAlex Hornung 	if (error)
15314f0742eSAlex Hornung 		syntax_error("could not stat %s on include, error: %s",
15414f0742eSAlex Hornung 		    tokens[1], strerror(errno));
15514f0742eSAlex Hornung 
15614f0742eSAlex Hornung 	chdir(dirname(tokens[1]));
15714f0742eSAlex Hornung 	read_config(basename(tokens[1]), RULES_FILE);
15814f0742eSAlex Hornung 
15914f0742eSAlex Hornung 	return 0;
16014f0742eSAlex Hornung }
16114f0742eSAlex Hornung 
16214f0742eSAlex Hornung static int
parser_jail(char ** tokens)16314f0742eSAlex Hornung parser_jail(char **tokens)
16414f0742eSAlex Hornung {
16514f0742eSAlex Hornung 	if (tokens[1][0] == 'y') {
16614f0742eSAlex Hornung 		jail = 1;
16714f0742eSAlex Hornung 	} else if (tokens[1][0] == 'n') {
16814f0742eSAlex Hornung 		jail = 0;
16914f0742eSAlex Hornung 	} else {
17014f0742eSAlex Hornung 		syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]");
17114f0742eSAlex Hornung 	}
17214f0742eSAlex Hornung 
17314f0742eSAlex Hornung 	return 0;
17414f0742eSAlex Hornung }
17514f0742eSAlex Hornung 
17614f0742eSAlex Hornung static int
parser_hide(char ** tokens)17714f0742eSAlex Hornung parser_hide(char **tokens)
17814f0742eSAlex Hornung {
17914f0742eSAlex Hornung 	struct groupdevid *id;
18014f0742eSAlex Hornung 	struct rule *rule;
18114f0742eSAlex Hornung 
18214f0742eSAlex Hornung 	id = get_id(tokens[1]);
18314f0742eSAlex Hornung 	rule = new_rule(rHIDE, id);
18414f0742eSAlex Hornung 	add_rule(rule);
18514f0742eSAlex Hornung 
18614f0742eSAlex Hornung 	return 0;
18714f0742eSAlex Hornung }
18814f0742eSAlex Hornung 
18914f0742eSAlex Hornung static int
parser_show(char ** tokens)19014f0742eSAlex Hornung parser_show(char **tokens)
19114f0742eSAlex Hornung {
19214f0742eSAlex Hornung 	struct groupdevid *id;
19314f0742eSAlex Hornung 	struct rule *rule;
19414f0742eSAlex Hornung 
19514f0742eSAlex Hornung 	id = get_id(tokens[1]);
19614f0742eSAlex Hornung 	rule = new_rule(rSHOW, id);
19714f0742eSAlex Hornung 	add_rule(rule);
19814f0742eSAlex Hornung 
19914f0742eSAlex Hornung 	return 0;
20014f0742eSAlex Hornung }
20114f0742eSAlex Hornung 
20214f0742eSAlex Hornung static int
parser_link(char ** tokens)20314f0742eSAlex Hornung parser_link(char **tokens)
20414f0742eSAlex Hornung {
20514f0742eSAlex Hornung 	struct groupdevid *id;
20614f0742eSAlex Hornung 	struct rule *rule;
20714f0742eSAlex Hornung 
20814f0742eSAlex Hornung 	id = get_id(tokens[1]);
20914f0742eSAlex Hornung 	rule = new_rule(rLINK, id);
21014f0742eSAlex Hornung 	rule->dest = strdup(tokens[2]);
21114f0742eSAlex Hornung 	add_rule(rule);
21214f0742eSAlex Hornung 
21314f0742eSAlex Hornung 	return 0;
21414f0742eSAlex Hornung }
21514f0742eSAlex Hornung 
21614f0742eSAlex Hornung static int
parser_group(char ** tokens)21714f0742eSAlex Hornung parser_group(char **tokens)
21814f0742eSAlex Hornung {
21914f0742eSAlex Hornung 	struct groupdevid *gid, *id;
22014f0742eSAlex Hornung 	int i;
22114f0742eSAlex Hornung 	size_t k;
22214f0742eSAlex Hornung 
22314f0742eSAlex Hornung 	gid = get_group(tokens[1], 1);
22414f0742eSAlex Hornung 	for (k = 0; gid->list[k] != NULL; k++)
22514f0742eSAlex Hornung 		/* Do nothing */;
22614f0742eSAlex Hornung 	for (i = 2; tokens[i] != NULL; i++) {
22714f0742eSAlex Hornung 		id = get_id(tokens[i]);
22814f0742eSAlex Hornung 		if (id == gid) {
22914f0742eSAlex Hornung 			syntax_error("recursive group definition for group %s", gid->name);
23014f0742eSAlex Hornung 		} else {
23114f0742eSAlex Hornung 			if (k >= gid->listsize-1 ) {
23214f0742eSAlex Hornung 				gid->list = realloc(gid->list,
23314f0742eSAlex Hornung 						2*gid->listsize*sizeof(struct groupdevid *));
23414f0742eSAlex Hornung 				gid->listsize *= 2;
23514f0742eSAlex Hornung 			}
23614f0742eSAlex Hornung 
23714f0742eSAlex Hornung 			gid->list[k++] = id;
23814f0742eSAlex Hornung 		}
23914f0742eSAlex Hornung 	}
24014f0742eSAlex Hornung 	gid->list[k] = NULL;
24114f0742eSAlex Hornung 
24214f0742eSAlex Hornung 	return 0;
24314f0742eSAlex Hornung }
24414f0742eSAlex Hornung 
24514f0742eSAlex Hornung static int
parser_perm(char ** tokens)24614f0742eSAlex Hornung parser_perm(char **tokens)
24714f0742eSAlex Hornung {
24814f0742eSAlex Hornung 	struct passwd *pwd;
24914f0742eSAlex Hornung 	struct group *grp;
25014f0742eSAlex Hornung 	struct groupdevid *id;
25114f0742eSAlex Hornung 	struct rule *rule;
25214f0742eSAlex Hornung 	char *uname;
25314f0742eSAlex Hornung 	char *grname;
25414f0742eSAlex Hornung 
25514f0742eSAlex Hornung 	id = get_id(tokens[1]);
25614f0742eSAlex Hornung 	rule = new_rule(rPERM, id);
25714f0742eSAlex Hornung 
25814f0742eSAlex Hornung 	rule->mode = strtol(tokens[3], NULL, 8);
25914f0742eSAlex Hornung 	uname = tokens[2];
26014f0742eSAlex Hornung 	grname = strchr(tokens[2], ':');
26114f0742eSAlex Hornung 	if (grname == NULL)
26214f0742eSAlex Hornung 		syntax_error("invalid format for user/group (%s)", tokens[2]);
26314f0742eSAlex Hornung 
26414f0742eSAlex Hornung 	*grname = '\0';
26514f0742eSAlex Hornung 	++grname;
26614f0742eSAlex Hornung 	if ((pwd = getpwnam(uname)))
26714f0742eSAlex Hornung 		rule->uid = pwd->pw_uid;
26814f0742eSAlex Hornung 	else
26914f0742eSAlex Hornung 		syntax_error("invalid user name %s", uname);
27014f0742eSAlex Hornung 
27114f0742eSAlex Hornung 	if ((grp = getgrnam(grname)))
27214f0742eSAlex Hornung 		rule->gid = grp->gr_gid;
27314f0742eSAlex Hornung 	else
27414f0742eSAlex Hornung 		syntax_error("invalid group name %s", grname);
27514f0742eSAlex Hornung 
27614f0742eSAlex Hornung 	add_rule(rule);
27714f0742eSAlex Hornung 	return 0;
27814f0742eSAlex Hornung }
27914f0742eSAlex Hornung 
28014f0742eSAlex Hornung struct groupdevid *
new_id(const char * name,int type_in)28114f0742eSAlex Hornung new_id(const char *name, int type_in)
28214f0742eSAlex Hornung {
28314f0742eSAlex Hornung 	struct groupdevid *id;
28414f0742eSAlex Hornung 	int type = (type_in != 0)?(type_in):(isNAME), i;
28514f0742eSAlex Hornung 
28614f0742eSAlex Hornung 	id = calloc(1, sizeof(*id));
28714f0742eSAlex Hornung 	if (id == NULL)
28814f0742eSAlex Hornung 		err(1, NULL);
28914f0742eSAlex Hornung 
29014f0742eSAlex Hornung 	if (type_in == 0) {
29114f0742eSAlex Hornung 		for (i = 0; devtypes[i].name != NULL; i++) {
29214f0742eSAlex Hornung 			if (!strcmp(devtypes[i].name, name)) {
29314f0742eSAlex Hornung 				type = isTYPE;
29414f0742eSAlex Hornung 				id->devtype = devtypes[i].value;
29514f0742eSAlex Hornung 				break;
29614f0742eSAlex Hornung 			}
29714f0742eSAlex Hornung 		}
29814f0742eSAlex Hornung 	}
29914f0742eSAlex Hornung 	id->type = type;
30014f0742eSAlex Hornung 
30114f0742eSAlex Hornung 	if ((type == isNAME) || (type == isGROUP)) {
30214f0742eSAlex Hornung 		id->name = strdup(name);
30314f0742eSAlex Hornung 	}
30414f0742eSAlex Hornung 
30514f0742eSAlex Hornung 	if (type == isGROUP) {
30614f0742eSAlex Hornung 		id->list = calloc(4, sizeof(struct groupdevid *));
30714f0742eSAlex Hornung 		memset(id->list, 0, 4 * sizeof(struct groupdevid *));
30814f0742eSAlex Hornung 		id->listsize = 4;
30914f0742eSAlex Hornung 	}
31014f0742eSAlex Hornung 
31114f0742eSAlex Hornung 	return (id);
31214f0742eSAlex Hornung }
31314f0742eSAlex Hornung 
31414f0742eSAlex Hornung struct groupdevid *
get_id(const char * name)31514f0742eSAlex Hornung get_id(const char *name)
31614f0742eSAlex Hornung {
31714f0742eSAlex Hornung 	struct groupdevid *id;
31814f0742eSAlex Hornung 
31914f0742eSAlex Hornung 	if ((name[0] == '@') && (name[1] != '\0')) {
32014f0742eSAlex Hornung 		id = get_group(name+1, 0);
32114f0742eSAlex Hornung 		if (id == NULL)
32214f0742eSAlex Hornung 			syntax_error("unknown group name '%s', you "
32314f0742eSAlex Hornung 					"have to use the 'group' verb first.", name+1);
32414f0742eSAlex Hornung 	}
32514f0742eSAlex Hornung 	else
32614f0742eSAlex Hornung 		id = new_id(name, 0);
32714f0742eSAlex Hornung 
32814f0742eSAlex Hornung 	return id;
32914f0742eSAlex Hornung }
33014f0742eSAlex Hornung 
33114f0742eSAlex Hornung struct groupdevid *
get_group(const char * name,int expect)33214f0742eSAlex Hornung get_group(const char *name, int expect)
33314f0742eSAlex Hornung {
33414f0742eSAlex Hornung 	struct groupdevid *g;
33514f0742eSAlex Hornung 
33614f0742eSAlex Hornung 	TAILQ_FOREACH(g, &group_list, link) {
33714f0742eSAlex Hornung 		if (strcmp(g->name, name) == 0)
33814f0742eSAlex Hornung 			return (g);
33914f0742eSAlex Hornung 	}
34014f0742eSAlex Hornung 
34114f0742eSAlex Hornung 	/* Caller doesn't expect to get a group no matter what */
34214f0742eSAlex Hornung 	if (!expect)
34314f0742eSAlex Hornung 		return NULL;
34414f0742eSAlex Hornung 
34514f0742eSAlex Hornung 	g = new_id(name, isGROUP);
34614f0742eSAlex Hornung 	TAILQ_INSERT_TAIL(&group_list, g, link);
34714f0742eSAlex Hornung 	return (g);
34814f0742eSAlex Hornung }
34914f0742eSAlex Hornung 
35014f0742eSAlex Hornung struct rule *
new_rule(int type,struct groupdevid * id)35114f0742eSAlex Hornung new_rule(int type, struct groupdevid *id)
35214f0742eSAlex Hornung {
35314f0742eSAlex Hornung 	struct rule *rule;
35414f0742eSAlex Hornung 
35514f0742eSAlex Hornung 	rule = calloc(1, sizeof(*rule));
35614f0742eSAlex Hornung 	if (rule == NULL)
35714f0742eSAlex Hornung 		err(1, NULL);
35814f0742eSAlex Hornung 
35914f0742eSAlex Hornung 	rule->type = type;
36014f0742eSAlex Hornung 	rule->id = id;
36114f0742eSAlex Hornung 	rule->jail = jail;
36214f0742eSAlex Hornung 	return (rule);
36314f0742eSAlex Hornung }
36414f0742eSAlex Hornung 
36514f0742eSAlex Hornung void
add_rule(struct rule * rule)36614f0742eSAlex Hornung add_rule(struct rule *rule)
36714f0742eSAlex Hornung {
36814f0742eSAlex Hornung 	TAILQ_INSERT_TAIL(&rule_list, rule, link);
36914f0742eSAlex Hornung }
37014f0742eSAlex Hornung 
37114f0742eSAlex Hornung static int
dump_config_entry(struct rule * rule,struct groupdevid * id)37214f0742eSAlex Hornung dump_config_entry(struct rule *rule, struct groupdevid *id)
37314f0742eSAlex Hornung {
37414f0742eSAlex Hornung 	struct passwd *pwd;
37514f0742eSAlex Hornung 	struct group *grp;
37614f0742eSAlex Hornung 	int i;
37714f0742eSAlex Hornung 
37814f0742eSAlex Hornung 	switch (rule->type) {
37914f0742eSAlex Hornung 	case rPERM: printf("perm "); break;
38014f0742eSAlex Hornung 	case rLINK: printf("link "); break;
38114f0742eSAlex Hornung 	case rHIDE: printf("hide "); break;
38214f0742eSAlex Hornung 	case rSHOW: printf("show "); break;
38314f0742eSAlex Hornung 	default: errx(1, "invalid rule type");
38414f0742eSAlex Hornung 	}
38514f0742eSAlex Hornung 
38614f0742eSAlex Hornung 	switch (id->type) {
38714f0742eSAlex Hornung 	case isGROUP: printf("@"); /* FALLTHROUGH */
38814f0742eSAlex Hornung 	case isNAME: printf("%s", id->name); break;
38914f0742eSAlex Hornung 	case isTYPE:
39014f0742eSAlex Hornung 		for (i = 0; devtypes[i].name != NULL; i++) {
39114f0742eSAlex Hornung 			if (devtypes[i].value == id->devtype) {
39214f0742eSAlex Hornung 				printf("%s", devtypes[i].name);
39314f0742eSAlex Hornung 				break;
39414f0742eSAlex Hornung 			}
39514f0742eSAlex Hornung 		}
39614f0742eSAlex Hornung 		break;
39714f0742eSAlex Hornung 	default: errx(1, "invalid id type %d", id->type);
39814f0742eSAlex Hornung 	}
39914f0742eSAlex Hornung 
40014f0742eSAlex Hornung 	switch (rule->type) {
40114f0742eSAlex Hornung 	case rPERM:
40214f0742eSAlex Hornung 		pwd = getpwuid(rule->uid);
40314f0742eSAlex Hornung 		grp = getgrgid(rule->gid);
40414f0742eSAlex Hornung 		if (pwd && grp) {
40514f0742eSAlex Hornung 			printf(" %s:%s 0%.03o",
40614f0742eSAlex Hornung 				   pwd->pw_name,
40714f0742eSAlex Hornung 				   grp->gr_name,
40814f0742eSAlex Hornung 				   rule->mode);
40914f0742eSAlex Hornung 		} else {
41014f0742eSAlex Hornung 			printf(" %d:%d 0%.03o",
41114f0742eSAlex Hornung 				   rule->uid,
41214f0742eSAlex Hornung 				   rule->gid,
41314f0742eSAlex Hornung 				   rule->mode);
41414f0742eSAlex Hornung 		}
41514f0742eSAlex Hornung 		break;
41614f0742eSAlex Hornung 	case rLINK:
41714f0742eSAlex Hornung 		printf(" %s", rule->dest);
41814f0742eSAlex Hornung 		break;
41914f0742eSAlex Hornung 	default: /* NOTHING */;
42014f0742eSAlex Hornung 	}
42114f0742eSAlex Hornung 
42214f0742eSAlex Hornung 	if (rule->jail)
42314f0742eSAlex Hornung 		printf("\t(only affects jails)");
42414f0742eSAlex Hornung 
42514f0742eSAlex Hornung 	printf("\n");
42614f0742eSAlex Hornung 
42714f0742eSAlex Hornung 	return 0;
42814f0742eSAlex Hornung }
42914f0742eSAlex Hornung 
43014f0742eSAlex Hornung static int
rule_id_iterate(struct groupdevid * id,struct rule * rule,rule_iterate_callback_t * callback)43114f0742eSAlex Hornung rule_id_iterate(struct groupdevid *id, struct rule *rule,
43214f0742eSAlex Hornung 		rule_iterate_callback_t *callback)
43314f0742eSAlex Hornung {
43414f0742eSAlex Hornung 	int error = 0;
43514f0742eSAlex Hornung 	int i;
43614f0742eSAlex Hornung 
43714f0742eSAlex Hornung 	if (id->type == isGROUP) {
43814f0742eSAlex Hornung 		for (i = 0; id->list[i] != NULL; i++) {
43914f0742eSAlex Hornung 			if ((error = rule_id_iterate(id->list[i], rule, callback)))
44014f0742eSAlex Hornung 				return error;
44114f0742eSAlex Hornung 		}
44214f0742eSAlex Hornung 	} else {
44314f0742eSAlex Hornung 		error = callback(rule, id);
44414f0742eSAlex Hornung 	}
44514f0742eSAlex Hornung 
44614f0742eSAlex Hornung 	return error;
44714f0742eSAlex Hornung }
44814f0742eSAlex Hornung 
44914f0742eSAlex Hornung void
dump_config(void)45014f0742eSAlex Hornung dump_config(void)
45114f0742eSAlex Hornung {
45214f0742eSAlex Hornung 	struct rule *rule;
45314f0742eSAlex Hornung 
45414f0742eSAlex Hornung 	TAILQ_FOREACH(rule, &rule_list, link) {
45514f0742eSAlex Hornung 		rule_id_iterate(rule->id, rule, dump_config_entry);
45614f0742eSAlex Hornung 	}
45714f0742eSAlex Hornung }
45814f0742eSAlex Hornung 
45914f0742eSAlex Hornung static int
rule_ioctl(unsigned long cmd,struct devfs_rule_ioctl * rule)46014f0742eSAlex Hornung rule_ioctl(unsigned long cmd, struct devfs_rule_ioctl *rule)
46114f0742eSAlex Hornung {
46214f0742eSAlex Hornung 	if (ioctl(dev_fd, cmd, rule) == -1)
46314f0742eSAlex Hornung 		err(1, "ioctl");
46414f0742eSAlex Hornung 
46514f0742eSAlex Hornung 	return 0;
46614f0742eSAlex Hornung }
46714f0742eSAlex Hornung 
46814f0742eSAlex Hornung static void
rule_fill(struct devfs_rule_ioctl * dr,struct rule * r,struct groupdevid * id)46914f0742eSAlex Hornung rule_fill(struct devfs_rule_ioctl *dr, struct rule *r, struct groupdevid *id)
47014f0742eSAlex Hornung {
47114f0742eSAlex Hornung 	dr->rule_type = 0;
47214f0742eSAlex Hornung 	dr->rule_cmd = 0;
47314f0742eSAlex Hornung 
47414f0742eSAlex Hornung 	switch (id->type) {
47514f0742eSAlex Hornung 	default:
47614f0742eSAlex Hornung 		errx(1, "invalid id type");
47714f0742eSAlex Hornung 	case isGROUP:
47814f0742eSAlex Hornung 		errx(1, "internal error: can not fill group rule");
47914f0742eSAlex Hornung 		/* NOTREACHED */
48014f0742eSAlex Hornung 	case isNAME:
48114f0742eSAlex Hornung 		dr->rule_type |= DEVFS_RULE_NAME;
48214f0742eSAlex Hornung 		strncpy(dr->name, id->name, PATH_MAX-1);
48314f0742eSAlex Hornung 		break;
48414f0742eSAlex Hornung 	case isTYPE:
48514f0742eSAlex Hornung 		dr->rule_type |= DEVFS_RULE_TYPE;
48614f0742eSAlex Hornung 		dr->dev_type = id->devtype;
48714f0742eSAlex Hornung 		break;
48814f0742eSAlex Hornung 	}
48914f0742eSAlex Hornung 
49014f0742eSAlex Hornung 	switch (r->type) {
49114f0742eSAlex Hornung 	case rPERM:
49214f0742eSAlex Hornung 		dr->rule_cmd |= DEVFS_RULE_PERM;
49314f0742eSAlex Hornung 		dr->uid = r->uid;
49414f0742eSAlex Hornung 		dr->gid = r->gid;
49514f0742eSAlex Hornung 		dr->mode = r->mode;
49614f0742eSAlex Hornung 		break;
49714f0742eSAlex Hornung 	case rLINK:
49814f0742eSAlex Hornung 		dr->rule_cmd |= DEVFS_RULE_LINK;
49914f0742eSAlex Hornung 		strncpy(dr->linkname, r->dest, PATH_MAX-1);
50014f0742eSAlex Hornung 		break;
50114f0742eSAlex Hornung 	case rHIDE:
50214f0742eSAlex Hornung 		dr->rule_cmd |= DEVFS_RULE_HIDE;
50314f0742eSAlex Hornung 		break;
50414f0742eSAlex Hornung 	case rSHOW:
50514f0742eSAlex Hornung 		dr->rule_cmd |= DEVFS_RULE_SHOW;
50614f0742eSAlex Hornung 		break;
50714f0742eSAlex Hornung 	}
50814f0742eSAlex Hornung 
50914f0742eSAlex Hornung 	if (r->jail)
51014f0742eSAlex Hornung 		dr->rule_type |= DEVFS_RULE_JAIL;
51114f0742eSAlex Hornung }
51214f0742eSAlex Hornung 
51314f0742eSAlex Hornung static int
rule_send(struct rule * rule,struct groupdevid * id)51414f0742eSAlex Hornung rule_send(struct rule *rule, struct groupdevid *id)
51514f0742eSAlex Hornung {
51614f0742eSAlex Hornung 	struct devfs_rule_ioctl dr;
51714f0742eSAlex Hornung 	int r = 0;
51814f0742eSAlex Hornung 
51914f0742eSAlex Hornung 	strncpy(dr.mntpoint, mountp, PATH_MAX-1);
52014f0742eSAlex Hornung 
52114f0742eSAlex Hornung 	rule_fill(&dr, rule, id);
52214f0742eSAlex Hornung 	r = rule_ioctl(DEVFS_RULE_ADD, &dr);
52314f0742eSAlex Hornung 
52414f0742eSAlex Hornung 	return r;
52514f0742eSAlex Hornung }
52614f0742eSAlex Hornung 
52714f0742eSAlex Hornung int
rule_apply(void)52814f0742eSAlex Hornung rule_apply(void)
52914f0742eSAlex Hornung {
53014f0742eSAlex Hornung 	struct devfs_rule_ioctl dr;
53114f0742eSAlex Hornung 	struct rule *rule;
53214f0742eSAlex Hornung 	int r = 0;
53314f0742eSAlex Hornung 
53414f0742eSAlex Hornung 	strncpy(dr.mntpoint, mountp, PATH_MAX-1);
53514f0742eSAlex Hornung 
53614f0742eSAlex Hornung 	TAILQ_FOREACH(rule, &rule_list, link) {
53714f0742eSAlex Hornung 		r = rule_id_iterate(rule->id, rule, rule_send);
53814f0742eSAlex Hornung 		if (r != 0)
53914f0742eSAlex Hornung 			return (-1);
54014f0742eSAlex Hornung 	}
54114f0742eSAlex Hornung 
54214f0742eSAlex Hornung 	return (rule_ioctl(DEVFS_RULE_APPLY, &dr));
54314f0742eSAlex Hornung }
54414f0742eSAlex Hornung 
54514f0742eSAlex Hornung static int
rule_check_num_args(char ** tokens,int num)54614f0742eSAlex Hornung rule_check_num_args(char **tokens, int num)
54714f0742eSAlex Hornung {
54862f13ae6SSascha Wildner 	int i;
54962f13ae6SSascha Wildner 
55062f13ae6SSascha Wildner 	for (i = 0; tokens[i] != NULL; i++)
55162f13ae6SSascha Wildner 		;
55214f0742eSAlex Hornung 
55314f0742eSAlex Hornung 	if (i < num) {
55414f0742eSAlex Hornung 		syntax_error("at least %d tokens were expected but only %d were found", num, i);
55514f0742eSAlex Hornung 		return 1;
55614f0742eSAlex Hornung 	}
55714f0742eSAlex Hornung 	return 0;
55814f0742eSAlex Hornung }
55914f0742eSAlex Hornung 
56014f0742eSAlex Hornung int
read_config(const char * name,int ftype)56114f0742eSAlex Hornung read_config(const char *name, int ftype)
56214f0742eSAlex Hornung {
56314f0742eSAlex Hornung 	FILE *fd;
56414f0742eSAlex Hornung 	struct stat sb;
56514f0742eSAlex Hornung 
56614f0742eSAlex Hornung 	if ((fd = fopen(name, "r")) == NULL) {
56714f0742eSAlex Hornung 		printf("Error opening config file %s\n", name);
56814f0742eSAlex Hornung 		perror("fopen");
56914f0742eSAlex Hornung 		return 1;
57014f0742eSAlex Hornung 	}
57114f0742eSAlex Hornung 
57214f0742eSAlex Hornung 	if (fstat(fileno(fd), &sb) != 0) {
57314f0742eSAlex Hornung 		errx(1, "file %s could not be fstat'ed, aborting", name);
57414f0742eSAlex Hornung 	}
57514f0742eSAlex Hornung 
57614f0742eSAlex Hornung 	if (sb.st_uid != 0)
57714f0742eSAlex Hornung 		errx(1, "file %s does not belong to root, aborting!", name);
57814f0742eSAlex Hornung 
57914f0742eSAlex Hornung 	if (++line_stack_depth >= RULE_MAX_STACK) {
58014f0742eSAlex Hornung 		--line_stack_depth;
58114f0742eSAlex Hornung 		syntax_error("Maximum include depth (%d) exceeded, "
58214f0742eSAlex Hornung 				"check for recursion.", RULE_MAX_STACK);
58314f0742eSAlex Hornung 	}
58414f0742eSAlex Hornung 
58514f0742eSAlex Hornung 	line_stack[line_stack_depth] = 1;
58614f0742eSAlex Hornung 	file_stack[line_stack_depth] = strdup(name);
58714f0742eSAlex Hornung 	cwd_stack[line_stack_depth] = getwd(NULL);
58814f0742eSAlex Hornung 
58914f0742eSAlex Hornung 	while (process_line(fd, ftype) == 0)
59014f0742eSAlex Hornung 		line_stack[line_stack_depth]++;
59114f0742eSAlex Hornung 
59214f0742eSAlex Hornung 	fclose(fd);
59314f0742eSAlex Hornung 
59414f0742eSAlex Hornung 	free(file_stack[line_stack_depth]);
59514f0742eSAlex Hornung 	free(cwd_stack[line_stack_depth]);
59614f0742eSAlex Hornung 	--line_stack_depth;
59714f0742eSAlex Hornung 	chdir(cwd_stack[line_stack_depth]);
59814f0742eSAlex Hornung 
59914f0742eSAlex Hornung 	return 0;
60014f0742eSAlex Hornung }
60114f0742eSAlex Hornung 
60214f0742eSAlex Hornung static int
process_line(FILE * fd,int ftype)60314f0742eSAlex Hornung process_line(FILE* fd, int ftype)
60414f0742eSAlex Hornung {
60514f0742eSAlex Hornung 	char buffer[4096];
60614f0742eSAlex Hornung 	char *tokens[256];
60714f0742eSAlex Hornung 	int c, n, i = 0;
60814f0742eSAlex Hornung 	int quote = 0;
60914f0742eSAlex Hornung 	int ret = 0;
61014f0742eSAlex Hornung 
61114f0742eSAlex Hornung 	while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
61214f0742eSAlex Hornung 		buffer[i++] = (char)c;
61314f0742eSAlex Hornung 		if (i == (sizeof(buffer) -1))
61414f0742eSAlex Hornung 			break;
61514f0742eSAlex Hornung 	}
61614f0742eSAlex Hornung 	buffer[i] = '\0';
61714f0742eSAlex Hornung 
61814f0742eSAlex Hornung 	if (feof(fd) || ferror(fd))
61914f0742eSAlex Hornung 		ret = 1;
62014f0742eSAlex Hornung 	c = 0;
62114f0742eSAlex Hornung 	while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++;
62214f0742eSAlex Hornung 	/*
62314f0742eSAlex Hornung 	 * If this line effectively (after indentation) begins with the comment
62414f0742eSAlex Hornung 	 * character #, we ignore the rest of the line.
62514f0742eSAlex Hornung 	 */
62614f0742eSAlex Hornung 	if (buffer[c] == '#')
62714f0742eSAlex Hornung 		return 0;
62814f0742eSAlex Hornung 
62914f0742eSAlex Hornung 	tokens[0] = &buffer[c];
63014f0742eSAlex Hornung 	for (n = 1; c < i; c++) {
63114f0742eSAlex Hornung 		if (buffer[c] == '"') {
63214f0742eSAlex Hornung 			quote = !quote;
63314f0742eSAlex Hornung 			if (quote) {
63414f0742eSAlex Hornung 				if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
63514f0742eSAlex Hornung 					syntax_error("stray opening quote not at beginning of token");
63614f0742eSAlex Hornung 					/* NOTREACHED */
63714f0742eSAlex Hornung 				}
63814f0742eSAlex Hornung 				tokens[n-1] = &buffer[c+1];
63914f0742eSAlex Hornung 			} else {
64014f0742eSAlex Hornung 				if ((c < i-1) && (!iswhitespace(buffer[c+1]))) {
64114f0742eSAlex Hornung 					syntax_error("stray closing quote not at end of token");
64214f0742eSAlex Hornung 					/* NOTREACHED */
64314f0742eSAlex Hornung 				}
64414f0742eSAlex Hornung 				buffer[c] = '\0';
64514f0742eSAlex Hornung 			}
64614f0742eSAlex Hornung 		}
64714f0742eSAlex Hornung 
64814f0742eSAlex Hornung 		if (quote) {
64914f0742eSAlex Hornung 			continue;
65014f0742eSAlex Hornung 		}
65114f0742eSAlex Hornung 
65214f0742eSAlex Hornung 		if ((buffer[c] == ' ') || (buffer[c] == '\t')) {
65314f0742eSAlex Hornung 			buffer[c++] = '\0';
65414f0742eSAlex Hornung 			while ((iswhitespace(buffer[c])) && (c < i)) c++;
65514f0742eSAlex Hornung 			tokens[n++] = &buffer[c--];
65614f0742eSAlex Hornung 		}
65714f0742eSAlex Hornung 	}
65814f0742eSAlex Hornung 	tokens[n] = NULL;
65914f0742eSAlex Hornung 
66014f0742eSAlex Hornung 	/*
66114f0742eSAlex Hornung 	 * If there are not enough arguments for any function or it is
66214f0742eSAlex Hornung 	 * a line full of whitespaces, we just return here. Or if a
66314f0742eSAlex Hornung 	 * quote wasn't closed.
66414f0742eSAlex Hornung 	 */
66514f0742eSAlex Hornung 	if ((quote) || (n < 2) || (tokens[0][0] == '\0'))
66614f0742eSAlex Hornung 		return ret;
66714f0742eSAlex Hornung 
66814f0742eSAlex Hornung 	switch (ftype) {
66914f0742eSAlex Hornung 	case RULES_FILE:
67014f0742eSAlex Hornung 		ret = rule_parser(tokens);
67114f0742eSAlex Hornung 		break;
67214f0742eSAlex Hornung #if 0
67314f0742eSAlex Hornung 	case RULETAB_FILE:
67414f0742eSAlex Hornung 		ret = ruletab_parser(tokens);
67514f0742eSAlex Hornung 		break;
67614f0742eSAlex Hornung #endif
67714f0742eSAlex Hornung 	default:
67814f0742eSAlex Hornung 		ret = 1;
67914f0742eSAlex Hornung 	}
68014f0742eSAlex Hornung 
68114f0742eSAlex Hornung 	return ret;
68214f0742eSAlex Hornung }
68314f0742eSAlex Hornung 
68414f0742eSAlex Hornung static int
rule_parser(char ** tokens)68514f0742eSAlex Hornung rule_parser(char **tokens)
68614f0742eSAlex Hornung {
68762f13ae6SSascha Wildner 	int i;
68814f0742eSAlex Hornung 	int parsed = 0;
68914f0742eSAlex Hornung 
69014f0742eSAlex Hornung 	/* Convert the command/verb to lowercase */
69114f0742eSAlex Hornung 	for (i = 0; tokens[0][i] != '\0'; i++)
69214f0742eSAlex Hornung 		tokens[0][i] = tolower(tokens[0][i]);
69314f0742eSAlex Hornung 
69414f0742eSAlex Hornung 	for (i = 0; parsers[i].verb; i++) {
69562f13ae6SSascha Wildner 		if (rule_check_num_args(tokens, parsers[i].min_args) != 0)
69614f0742eSAlex Hornung 			continue;
69714f0742eSAlex Hornung 
69814f0742eSAlex Hornung 		if (!strcmp(tokens[0], parsers[i].verb)) {
69914f0742eSAlex Hornung 			parsers[i].parser(tokens);
70014f0742eSAlex Hornung 			parsed = 1;
70114f0742eSAlex Hornung 			break;
70214f0742eSAlex Hornung 		}
70314f0742eSAlex Hornung 	}
70414f0742eSAlex Hornung 	if (parsed == 0) {
70514f0742eSAlex Hornung 		syntax_error("unknown verb/command %s", tokens[0]);
70614f0742eSAlex Hornung 	}
70714f0742eSAlex Hornung 
70814f0742eSAlex Hornung 	return 0;
70914f0742eSAlex Hornung }
71014f0742eSAlex Hornung 
71114f0742eSAlex Hornung #if 0
71214f0742eSAlex Hornung static int
71314f0742eSAlex Hornung ruletab_parser(char **tokens)
71414f0742eSAlex Hornung {
71514f0742eSAlex Hornung 	struct rule_tab *rt;
71614f0742eSAlex Hornung 	struct stat	sb;
71762f13ae6SSascha Wildner 	int i;
71862f13ae6SSascha Wildner 	int error;
71914f0742eSAlex Hornung 
72062f13ae6SSascha Wildner 	if (rule_check_num_args(tokens, 2) != 0)
72114f0742eSAlex Hornung 		return 0;
72214f0742eSAlex Hornung 
72314f0742eSAlex Hornung 	error = stat(tokens[0], &sb);
72414f0742eSAlex Hornung 	if (error) {
72514f0742eSAlex Hornung 		printf("ruletab warning: could not stat %s: %s\n",
72614f0742eSAlex Hornung 		    tokens[0], strerror(errno));
72714f0742eSAlex Hornung 	}
72814f0742eSAlex Hornung 
72914f0742eSAlex Hornung 	if (tokens[0][0] != '/') {
73014f0742eSAlex Hornung 		errx(1, "ruletab error: entry %s does not seem to be an absolute path",
73114f0742eSAlex Hornung 		    tokens[0]);
73214f0742eSAlex Hornung 	}
73314f0742eSAlex Hornung 
73414f0742eSAlex Hornung 	for (i = 1; tokens[i] != NULL; i++) {
73514f0742eSAlex Hornung 		rt = calloc(1, sizeof(struct rule_tab));
73614f0742eSAlex Hornung 		rt->mntpoint = strdup(tokens[0]);
73714f0742eSAlex Hornung 		rt->rule_file = strdup(tokens[i]);
73814f0742eSAlex Hornung 		TAILQ_INSERT_TAIL(&rule_tab_list, rt, link);
73914f0742eSAlex Hornung 	}
74014f0742eSAlex Hornung 
74114f0742eSAlex Hornung 	return 0;
74214f0742eSAlex Hornung }
74314f0742eSAlex Hornung 
74414f0742eSAlex Hornung void
74562f13ae6SSascha Wildner rule_tab(void)
74662f13ae6SSascha Wildner {
74714f0742eSAlex Hornung 	struct rule_tab *rt;
74814f0742eSAlex Hornung 	int error;
74914f0742eSAlex Hornung 	int mode;
75014f0742eSAlex Hornung 
75114f0742eSAlex Hornung 	chdir("/etc/devfs");
75214f0742eSAlex Hornung 	error = read_config("ruletab", RULETAB_FILE);
75314f0742eSAlex Hornung 
75414f0742eSAlex Hornung 	if (error)
75514f0742eSAlex Hornung 		errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)");
75614f0742eSAlex Hornung 
75714f0742eSAlex Hornung 	if (!strcmp(mountp, "*")) {
75814f0742eSAlex Hornung 		mode = RULETAB_ALL;
75914f0742eSAlex Hornung 	} else if (!strcmp(mountp, "boot")) {
76014f0742eSAlex Hornung 		mode = RULETAB_ONLY_BOOT;
76114f0742eSAlex Hornung 	} else if (mountp) {
76214f0742eSAlex Hornung 		mode = RULETAB_SPECIFIC;
76314f0742eSAlex Hornung 	} else {
76414f0742eSAlex Hornung 		errx(1, "-t needs -m");
76514f0742eSAlex Hornung 	}
76614f0742eSAlex Hornung 
76714f0742eSAlex Hornung 	dev_fd = open("/dev/devfs", O_RDWR);
76814f0742eSAlex Hornung 	if (dev_fd == -1)
76914f0742eSAlex Hornung 		err(1, "open(/dev/devfs)");
77014f0742eSAlex Hornung 
77114f0742eSAlex Hornung 	TAILQ_FOREACH(rt, &rule_tab_list, link) {
77214f0742eSAlex Hornung 		switch(mode) {
77314f0742eSAlex Hornung 		case RULETAB_ONLY_BOOT:
77414f0742eSAlex Hornung 			if ((strcmp(rt->mntpoint, "*") != 0) &&
77514f0742eSAlex Hornung 			    (strcmp(rt->mntpoint, "/dev") != 0)) {
77614f0742eSAlex Hornung 				continue;
77714f0742eSAlex Hornung 			}
77814f0742eSAlex Hornung 			break;
77914f0742eSAlex Hornung 		case RULETAB_SPECIFIC:
78014f0742eSAlex Hornung 			if (strcmp(rt->mntpoint, mountp) != 0)
78114f0742eSAlex Hornung 				continue;
78214f0742eSAlex Hornung 			break;
78314f0742eSAlex Hornung 		}
78414f0742eSAlex Hornung 		delete_rules();
78514f0742eSAlex Hornung 		read_config(rt->rule_file, RULES_FILE);
78614f0742eSAlex Hornung 		mountp = rt->mntpoint;
78714f0742eSAlex Hornung 		rule_apply();
78814f0742eSAlex Hornung 	}
78914f0742eSAlex Hornung 
79014f0742eSAlex Hornung 	close(dev_fd);
79114f0742eSAlex Hornung 
79214f0742eSAlex Hornung 	return;
79314f0742eSAlex Hornung }
79414f0742eSAlex Hornung 
79514f0742eSAlex Hornung void
79614f0742eSAlex Hornung delete_rules(void)
79714f0742eSAlex Hornung {
79814f0742eSAlex Hornung 	struct rule *rp;
79914f0742eSAlex Hornung 	struct groupdevid *gdp;
80014f0742eSAlex Hornung 
80114f0742eSAlex Hornung 	TAILQ_FOREACH(rp, &rule_list, link) {
80214f0742eSAlex Hornung 		TAILQ_REMOVE(&rule_list, rp, link);
80314f0742eSAlex Hornung 	}
80414f0742eSAlex Hornung 
80514f0742eSAlex Hornung 	TAILQ_FOREACH(gdp, &group_list, link) {
80614f0742eSAlex Hornung 		TAILQ_REMOVE(&group_list, gdp, link);
80714f0742eSAlex Hornung 	}
80814f0742eSAlex Hornung }
80914f0742eSAlex Hornung #endif
81014f0742eSAlex Hornung 
81114f0742eSAlex Hornung static void
usage(void)81214f0742eSAlex Hornung usage(void)
81314f0742eSAlex Hornung {
81414f0742eSAlex Hornung 	fprintf(stderr,
81514f0742eSAlex Hornung 		"Usage: devfsctl <commands> [options]\n"
81614f0742eSAlex Hornung 		"Valid commands are:\n"
81714f0742eSAlex Hornung 		" -a\n"
81814f0742eSAlex Hornung 		"\t Loads all read rules into the kernel and applies them\n"
81914f0742eSAlex Hornung 		" -c\n"
82014f0742eSAlex Hornung 		"\t Clears all rules stored in the kernel but does not reset the nodes\n"
82114f0742eSAlex Hornung 		" -d\n"
82214f0742eSAlex Hornung 		"\t Dumps the rules that have been loaded to the screen to verify syntax\n"
82314f0742eSAlex Hornung 		" -r\n"
82414f0742eSAlex Hornung 		"\t Resets all devfs_nodes but does not clear the rules stored\n"
82514f0742eSAlex Hornung 		"\n"
82614f0742eSAlex Hornung 		"Valid options and its arguments are:\n"
82714f0742eSAlex Hornung 		" -f <config_file>\n"
82814f0742eSAlex Hornung 		"\t Specifies the configuration file to be used\n"
82914f0742eSAlex Hornung 		" -m <mount_point>\n"
83014f0742eSAlex Hornung 		"\t Specifies a mount point to which the command will apply. Defaults to *\n"
83114f0742eSAlex Hornung 		);
83214f0742eSAlex Hornung 
83314f0742eSAlex Hornung 	exit(1);
83414f0742eSAlex Hornung }
83514f0742eSAlex Hornung 
main(int argc,char * argv[])83614f0742eSAlex Hornung int main(int argc, char *argv[])
83714f0742eSAlex Hornung {
83814f0742eSAlex Hornung 	struct devfs_rule_ioctl dummy_rule;
83914f0742eSAlex Hornung 	struct stat sb;
84014f0742eSAlex Hornung 	int ch, error;
84114f0742eSAlex Hornung 
84214f0742eSAlex Hornung 	while ((ch = getopt(argc, argv, "acdf:hm:r")) != -1) {
84314f0742eSAlex Hornung 		switch (ch) {
84414f0742eSAlex Hornung 		case 'f':
84514f0742eSAlex Hornung 			config_name = optarg;
84614f0742eSAlex Hornung 			break;
84714f0742eSAlex Hornung 		case 'm':
84814f0742eSAlex Hornung 			mountp = optarg;
84914f0742eSAlex Hornung 			break;
85014f0742eSAlex Hornung 		case 'a':
85114f0742eSAlex Hornung 			aflag = 1;
85214f0742eSAlex Hornung 			break;
85314f0742eSAlex Hornung 		case 'c':
85414f0742eSAlex Hornung 			cflag = 1;
85514f0742eSAlex Hornung 			break;
85614f0742eSAlex Hornung 		case 'r':
85714f0742eSAlex Hornung 			rflag = 1;
85814f0742eSAlex Hornung 			break;
85914f0742eSAlex Hornung 		case 'd':
86014f0742eSAlex Hornung 			dflag = 1;
86114f0742eSAlex Hornung 			break;
86214f0742eSAlex Hornung 
86314f0742eSAlex Hornung 		case 'h':
86414f0742eSAlex Hornung 		case '?':
86514f0742eSAlex Hornung 		default:
86614f0742eSAlex Hornung 			usage();
86714f0742eSAlex Hornung 			/* NOT REACHED */
86814f0742eSAlex Hornung 		}
86914f0742eSAlex Hornung 	}
87014f0742eSAlex Hornung 
87114f0742eSAlex Hornung 	argc -= optind;
87214f0742eSAlex Hornung 	argv += optind;
87314f0742eSAlex Hornung 
87414f0742eSAlex Hornung 	/*
87514f0742eSAlex Hornung 	 * Check arguments:
87614f0742eSAlex Hornung 	 * - need to use at least one mode
87714f0742eSAlex Hornung 	 * - can not use -d with any other mode
87814f0742eSAlex Hornung 	 * - can not use -t with any other mode or -f
87914f0742eSAlex Hornung 	 */
88014f0742eSAlex Hornung 	if (!(aflag || rflag || cflag || dflag) ||
88114f0742eSAlex Hornung 	    (dflag && (aflag || rflag || cflag || tflag))) {
88214f0742eSAlex Hornung 		usage();
88314f0742eSAlex Hornung 		/* NOT REACHED */
88414f0742eSAlex Hornung 	}
88514f0742eSAlex Hornung 
88614f0742eSAlex Hornung 	if (mountp == NULL)
88714f0742eSAlex Hornung 		mountp = "*";
88814f0742eSAlex Hornung 	else if (mountp[0] != '/') {
88914f0742eSAlex Hornung 		errx(1, "-m needs to be given an absolute path");
89014f0742eSAlex Hornung 	}
89114f0742eSAlex Hornung 
89214f0742eSAlex Hornung 	strncpy(dummy_rule.mntpoint, mountp, PATH_MAX-1);
89314f0742eSAlex Hornung 
89414f0742eSAlex Hornung 	if (config_name != NULL) {
89514f0742eSAlex Hornung 		error = stat(config_name, &sb);
89614f0742eSAlex Hornung 
89714f0742eSAlex Hornung 		if (error) {
89814f0742eSAlex Hornung 			chdir("/etc/devfs");
89914f0742eSAlex Hornung 			error = stat(config_name, &sb);
90014f0742eSAlex Hornung 		}
90114f0742eSAlex Hornung 
90214f0742eSAlex Hornung 		if (error)
90314f0742eSAlex Hornung 			err(1, "could not stat specified configuration file %s", config_name);
90414f0742eSAlex Hornung 
90514f0742eSAlex Hornung 		if (config_name[0] == '/')
906207ba670SMatthew Dillon 			chdir(dirname(strdup(config_name)));
90714f0742eSAlex Hornung 
90814f0742eSAlex Hornung 		read_config(config_name, RULES_FILE);
90914f0742eSAlex Hornung 	}
91014f0742eSAlex Hornung 
91114f0742eSAlex Hornung 	if (dflag) {
91214f0742eSAlex Hornung 		dump_config();
91314f0742eSAlex Hornung 		exit(0);
91414f0742eSAlex Hornung 	}
91514f0742eSAlex Hornung 
91614f0742eSAlex Hornung 	dev_fd = open("/dev/devfs", O_RDWR);
91714f0742eSAlex Hornung 	if (dev_fd == -1)
91814f0742eSAlex Hornung 		err(1, "open(/dev/devfs)");
91914f0742eSAlex Hornung 
92014f0742eSAlex Hornung 	if (cflag)
92114f0742eSAlex Hornung 		rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule);
92214f0742eSAlex Hornung 
92314f0742eSAlex Hornung 	if (rflag)
92414f0742eSAlex Hornung 		rule_ioctl(DEVFS_RULE_RESET, &dummy_rule);
92514f0742eSAlex Hornung 
92614f0742eSAlex Hornung 	if (aflag)
92714f0742eSAlex Hornung 		rule_apply();
92814f0742eSAlex Hornung 
92914f0742eSAlex Hornung 	close(dev_fd);
93014f0742eSAlex Hornung 
93114f0742eSAlex Hornung 	return 0;
93214f0742eSAlex Hornung }
933