2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
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>
42 #include <sys/devfs_rules.h>
62 rule_parser_t *parser;
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 *,
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);
91 static int ruletab_parser(char **tokens);
93 static void usage(void);
97 const char *config_name = NULL, *mountp = NULL;
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;
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);
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 },
125 static const struct devtype devtypes[] = {
126 { "D_TAPE", D_TAPE },
127 { "D_DISK", D_DISK },
134 syntax_error(const char *fmt, ...)
140 vsnprintf(buf, sizeof(buf), fmt, ap);
142 errx(1, "%s: syntax error on line %d: %s\n",file_stack[line_stack_depth],
143 line_stack[line_stack_depth], buf);
147 parser_include(char **tokens)
152 error = stat(tokens[1], &sb);
155 syntax_error("could not stat %s on include, error: %s",
156 tokens[1], strerror(errno));
158 chdir(dirname(tokens[1]));
159 read_config(basename(tokens[1]), RULES_FILE);
165 parser_jail(char **tokens)
167 if (tokens[1][0] == 'y') {
169 } else if (tokens[1][0] == 'n') {
172 syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]");
179 parser_hide(char **tokens)
181 struct groupdevid *id;
184 id = get_id(tokens[1]);
185 rule = new_rule(rHIDE, id);
192 parser_show(char **tokens)
194 struct groupdevid *id;
197 id = get_id(tokens[1]);
198 rule = new_rule(rSHOW, id);
205 parser_link(char **tokens)
207 struct groupdevid *id;
210 id = get_id(tokens[1]);
211 rule = new_rule(rLINK, id);
212 rule->dest = strdup(tokens[2]);
219 parser_group(char **tokens)
221 struct groupdevid *gid, *id;
225 gid = get_group(tokens[1], 1);
226 for (k = 0; gid->list[k] != NULL; k++)
228 for (i = 2; tokens[i] != NULL; i++) {
229 id = get_id(tokens[i]);
231 syntax_error("recursive group definition for group %s", gid->name);
233 if (k >= gid->listsize-1 ) {
234 gid->list = realloc(gid->list,
235 2*gid->listsize*sizeof(struct groupdevid *));
248 parser_perm(char **tokens)
252 struct groupdevid *id;
257 id = get_id(tokens[1]);
258 rule = new_rule(rPERM, id);
260 rule->mode = strtol(tokens[3], NULL, 8);
262 grname = strchr(tokens[2], ':');
264 syntax_error("invalid format for user/group (%s)", tokens[2]);
268 if ((pwd = getpwnam(uname)))
269 rule->uid = pwd->pw_uid;
271 syntax_error("invalid user name %s", uname);
273 if ((grp = getgrnam(grname)))
274 rule->gid = grp->gr_gid;
276 syntax_error("invalid group name %s", grname);
283 new_id(const char *name, int type_in)
285 struct groupdevid *id;
286 int type = (type_in != 0)?(type_in):(isNAME), i;
288 id = calloc(1, sizeof(*id));
293 for (i = 0; devtypes[i].name != NULL; i++) {
294 if (!strcmp(devtypes[i].name, name)) {
296 id->devtype = devtypes[i].value;
303 if ((type == isNAME) || (type == isGROUP)) {
304 id->name = strdup(name);
307 if (type == isGROUP) {
308 id->list = calloc(4, sizeof(struct groupdevid *));
309 memset(id->list, 0, 4 * sizeof(struct groupdevid *));
317 get_id(const char *name)
319 struct groupdevid *id;
321 if ((name[0] == '@') && (name[1] != '\0')) {
322 id = get_group(name+1, 0);
324 syntax_error("unknown group name '%s', you "
325 "have to use the 'group' verb first.", name+1);
328 id = new_id(name, 0);
334 get_group(const char *name, int expect)
336 struct groupdevid *g;
338 TAILQ_FOREACH(g, &group_list, link) {
339 if (strcmp(g->name, name) == 0)
343 /* Caller doesn't expect to get a group no matter what */
347 g = new_id(name, isGROUP);
348 TAILQ_INSERT_TAIL(&group_list, g, link);
353 new_rule(int type, struct groupdevid *id)
357 rule = calloc(1, sizeof(*rule));
368 add_rule(struct rule *rule)
370 TAILQ_INSERT_TAIL(&rule_list, rule, link);
374 dump_config_entry(struct rule *rule, struct groupdevid *id)
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");
389 case isGROUP: printf("@"); /* FALLTHROUGH */
390 case isNAME: printf("%s", id->name); break;
392 for (i = 0; devtypes[i].name != NULL; i++) {
393 if (devtypes[i].value == id->devtype) {
394 printf("%s", devtypes[i].name);
399 default: errx(1, "invalid id type %d", id->type);
402 switch (rule->type) {
404 pwd = getpwuid(rule->uid);
405 grp = getgrgid(rule->gid);
407 printf(" %s:%s 0%.03o",
412 printf(" %d:%d 0%.03o",
419 printf(" %s", rule->dest);
421 default: /* NOTHING */;
425 printf("\t(only affects jails)");
433 rule_id_iterate(struct groupdevid *id, struct rule *rule,
434 rule_iterate_callback_t *callback)
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)))
445 error = callback(rule, id);
456 TAILQ_FOREACH(rule, &rule_list, link) {
457 rule_id_iterate(rule->id, rule, dump_config_entry);
462 rule_ioctl(unsigned long cmd, struct devfs_rule_ioctl *rule)
464 if (ioctl(dev_fd, cmd, rule) == -1)
471 rule_fill(struct devfs_rule_ioctl *dr, struct rule *r, struct groupdevid *id)
478 errx(1, "invalid id type");
480 errx(1, "internal error: can not fill group rule");
483 dr->rule_type |= DEVFS_RULE_NAME;
484 strncpy(dr->name, id->name, PATH_MAX-1);
487 dr->rule_type |= DEVFS_RULE_TYPE;
488 dr->dev_type = id->devtype;
494 dr->rule_cmd |= DEVFS_RULE_PERM;
500 dr->rule_cmd |= DEVFS_RULE_LINK;
501 strncpy(dr->linkname, r->dest, PATH_MAX-1);
504 dr->rule_cmd |= DEVFS_RULE_HIDE;
507 dr->rule_cmd |= DEVFS_RULE_SHOW;
512 dr->rule_type |= DEVFS_RULE_JAIL;
516 rule_send(struct rule *rule, struct groupdevid *id)
518 struct devfs_rule_ioctl dr;
521 strncpy(dr.mntpoint, mountp, PATH_MAX-1);
523 rule_fill(&dr, rule, id);
524 r = rule_ioctl(DEVFS_RULE_ADD, &dr);
532 struct devfs_rule_ioctl dr;
536 strncpy(dr.mntpoint, mountp, PATH_MAX-1);
538 TAILQ_FOREACH(rule, &rule_list, link) {
539 r = rule_id_iterate(rule->id, rule, rule_send);
544 return (rule_ioctl(DEVFS_RULE_APPLY, &dr));
548 rule_check_num_args(char **tokens, int num)
551 for (i = 0; tokens[i] != NULL; i++);
554 syntax_error("at least %d tokens were expected but only %d were found", num, i);
561 read_config(const char *name, int ftype)
566 if ((fd = fopen(name, "r")) == NULL) {
567 printf("Error opening config file %s\n", name);
572 if (fstat(fileno(fd), &sb) != 0) {
573 errx(1, "file %s could not be fstat'ed, aborting", name);
577 errx(1, "file %s does not belong to root, aborting!", name);
579 if (++line_stack_depth >= RULE_MAX_STACK) {
581 syntax_error("Maximum include depth (%d) exceeded, "
582 "check for recursion.", RULE_MAX_STACK);
585 line_stack[line_stack_depth] = 1;
586 file_stack[line_stack_depth] = strdup(name);
587 cwd_stack[line_stack_depth] = getwd(NULL);
589 while (process_line(fd, ftype) == 0)
590 line_stack[line_stack_depth]++;
594 free(file_stack[line_stack_depth]);
595 free(cwd_stack[line_stack_depth]);
597 chdir(cwd_stack[line_stack_depth]);
603 process_line(FILE* fd, int ftype)
611 while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
612 buffer[i++] = (char)c;
613 if (i == (sizeof(buffer) -1))
618 if (feof(fd) || ferror(fd))
621 while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++;
623 * If this line effectively (after indentation) begins with the comment
624 * character #, we ignore the rest of the line.
626 if (buffer[c] == '#')
629 tokens[0] = &buffer[c];
630 for (n = 1; c < i; c++) {
631 if (buffer[c] == '"') {
634 if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
635 syntax_error("stray opening quote not at beginning of token");
638 tokens[n-1] = &buffer[c+1];
640 if ((c < i-1) && (!iswhitespace(buffer[c+1]))) {
641 syntax_error("stray closing quote not at end of token");
652 if ((buffer[c] == ' ') || (buffer[c] == '\t')) {
654 while ((iswhitespace(buffer[c])) && (c < i)) c++;
655 tokens[n++] = &buffer[c--];
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.
665 if ((quote) || (n < 2) || (tokens[0][0] == '\0'))
670 ret = rule_parser(tokens);
674 ret = ruletab_parser(tokens);
685 rule_parser(char **tokens)
691 /* Convert the command/verb to lowercase */
692 for (i = 0; tokens[0][i] != '\0'; i++)
693 tokens[0][i] = tolower(tokens[0][i]);
695 for (i = 0; parsers[i].verb; i++) {
696 if ((error = rule_check_num_args(tokens, parsers[i].min_args)))
699 if (!strcmp(tokens[0], parsers[i].verb)) {
700 parsers[i].parser(tokens);
706 syntax_error("unknown verb/command %s", tokens[0]);
714 ruletab_parser(char **tokens)
721 if ((error = rule_check_num_args(tokens, 2)))
724 error = stat(tokens[0], &sb);
726 printf("ruletab warning: could not stat %s: %s\n",
727 tokens[0], strerror(errno));
730 if (tokens[0][0] != '/') {
731 errx(1, "ruletab error: entry %s does not seem to be an absolute path",
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);
752 error = read_config("ruletab", RULETAB_FILE);
755 errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)");
757 if (!strcmp(mountp, "*")) {
759 } else if (!strcmp(mountp, "boot")) {
760 mode = RULETAB_ONLY_BOOT;
762 mode = RULETAB_SPECIFIC;
764 errx(1, "-t needs -m");
767 dev_fd = open("/dev/devfs", O_RDWR);
769 err(1, "open(/dev/devfs)");
771 TAILQ_FOREACH(rt, &rule_tab_list, link) {
773 case RULETAB_ONLY_BOOT:
774 if ((strcmp(rt->mntpoint, "*") != 0) &&
775 (strcmp(rt->mntpoint, "/dev") != 0)) {
779 case RULETAB_SPECIFIC:
780 if (strcmp(rt->mntpoint, mountp) != 0)
785 read_config(rt->rule_file, RULES_FILE);
786 mountp = rt->mntpoint;
799 struct groupdevid *gdp;
801 TAILQ_FOREACH(rp, &rule_list, link) {
802 TAILQ_REMOVE(&rule_list, rp, link);
805 TAILQ_FOREACH(gdp, &group_list, link) {
806 TAILQ_REMOVE(&group_list, gdp, link);
815 "Usage: devfsctl <commands> [options]\n"
816 "Valid commands are:\n"
818 "\t Loads all read rules into the kernel and applies them\n"
820 "\t Clears all rules stored in the kernel but does not reset the nodes\n"
822 "\t Dumps the rules that have been loaded to the screen to verify syntax\n"
824 "\t Resets all devfs_nodes but does not clear the rules stored\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"
836 int main(int argc, char *argv[])
838 struct devfs_rule_ioctl dummy_rule;
842 while ((ch = getopt(argc, argv, "acdf:hm:r")) != -1) {
845 config_name = optarg;
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
880 if (!(aflag || rflag || cflag || dflag) ||
881 (dflag && (aflag || rflag || cflag || tflag))) {
888 else if (mountp[0] != '/') {
889 errx(1, "-m needs to be given an absolute path");
892 strncpy(dummy_rule.mntpoint, mountp, PATH_MAX-1);
894 if (config_name != NULL) {
895 error = stat(config_name, &sb);
899 error = stat(config_name, &sb);
903 err(1, "could not stat specified configuration file %s", config_name);
905 if (config_name[0] == '/')
906 chdir(dirname(config_name));
908 read_config(config_name, RULES_FILE);
916 dev_fd = open("/dev/devfs", O_RDWR);
918 err(1, "open(/dev/devfs)");
921 rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule);
924 rule_ioctl(DEVFS_RULE_RESET, &dummy_rule);