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/ioctl.h>
37 #include <sys/device.h>
38 #include <sys/queue.h>
40 #include <vfs/devfs/devfs_rules.h>
56 rule_parser_t *parser;
67 static int parser_include(char **);
68 static int parser_jail(char **);
69 static int parser_hide(char **);
70 static int parser_show(char **);
71 static int parser_link(char **);
72 static int parser_group(char **);
73 static int parser_perm(char **);
74 static int dump_config_entry(struct rule *, struct groupdevid *);
75 static int rule_id_iterate(struct groupdevid *, struct rule *,
76 rule_iterate_callback_t *);
77 static int rule_ioctl(unsigned long, struct devfs_rule *);
78 static void rule_fill(struct devfs_rule *, struct rule *,
80 static int rule_send(struct rule *, struct groupdevid *);
81 static int rule_check_num_args(char **, int);
82 static int process_line(FILE*);
83 static void usage(void);
87 const char *config_name = NULL, *mountp = NULL;
89 static int aflag = 0, cflag = 0, rflag = 0;
90 static int line_stack[RULE_MAX_STACK];
91 static char *file_stack[RULE_MAX_STACK];
92 static int line_stack_depth = 0;
95 static TAILQ_HEAD(, rule) rule_list =
96 TAILQ_HEAD_INITIALIZER(rule_list);
98 static TAILQ_HEAD(, groupdevid) group_list =
99 TAILQ_HEAD_INITIALIZER(group_list);
102 static const struct verb parsers[] = {
103 { "include", parser_include, 1 },
104 { "jail", parser_jail, 1 },
105 { "group", parser_group, 2 },
106 { "perm", parser_perm, 2 },
107 { "link", parser_link, 2 },
108 { "hide", parser_hide, 2 },
109 { "show", parser_show, 2 },
113 static const struct devtype devtypes[] = {
114 { "D_TAPE", D_TAPE },
115 { "D_DISK", D_DISK },
122 syntax_error(const char *fmt, ...)
128 vsnprintf(buf, sizeof(buf), fmt, ap);
130 errx(1, "%s: syntax error on line %d: %s\n",file_stack[line_stack_depth],
131 line_stack[line_stack_depth], buf);
135 parser_include(char **tokens)
137 read_config(tokens[1]);
143 parser_jail(char **tokens)
145 if (tokens[1][0] == 'y') {
147 } else if (tokens[1][0] == 'n') {
150 syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]");
157 parser_hide(char **tokens)
159 struct groupdevid *id;
162 id = get_id(tokens[1]);
163 rule = new_rule(rHIDE, id);
170 parser_show(char **tokens)
172 struct groupdevid *id;
175 id = get_id(tokens[1]);
176 rule = new_rule(rSHOW, id);
183 parser_link(char **tokens)
185 struct groupdevid *id;
188 id = get_id(tokens[1]);
189 rule = new_rule(rLINK, id);
190 rule->dest = strdup(tokens[2]);
197 parser_group(char **tokens)
199 struct groupdevid *gid, *id;
203 gid = get_group(tokens[1], 1);
204 for (k = 0; gid->list[k] != NULL; k++)
206 for (i = 2; tokens[i] != NULL; i++) {
207 id = get_id(tokens[i]);
209 syntax_error("recursive group definition for group %s", gid->name);
211 if (k >= gid->listsize-1 ) {
212 gid->list = realloc(gid->list,
213 2*gid->listsize*sizeof(struct groupdevid *));
226 parser_perm(char **tokens)
230 struct groupdevid *id;
235 id = get_id(tokens[1]);
236 rule = new_rule(rPERM, id);
238 rule->mode = strtol(tokens[3], NULL, 8);
240 grname = strchr(tokens[2], ':');
242 syntax_error("invalid format for user/group (%s)", tokens[2]);
246 if ((pwd = getpwnam(uname)))
247 rule->uid = pwd->pw_uid;
249 syntax_error("invalid user name %s", uname);
251 if ((grp = getgrnam(grname)))
252 rule->gid = grp->gr_gid;
254 syntax_error("invalid group name %s", grname);
261 new_id(const char *name, int type_in)
263 struct groupdevid *id;
264 int type = (type_in != 0)?(type_in):(isNAME), i;
266 id = calloc(1, sizeof(*id));
271 for (i = 0; devtypes[i].name != NULL; i++) {
272 if (!strcmp(devtypes[i].name, name)) {
274 id->devtype = devtypes[i].value;
281 if ((type == isNAME) || (type == isGROUP)) {
282 id->name = strdup(name);
285 if (type == isGROUP) {
286 id->list = calloc(4, sizeof(struct groupdevid *));
287 memset(id->list, 0, 4 * sizeof(struct groupdevid *));
295 get_id(const char *name)
297 struct groupdevid *id;
299 if ((name[0] == '@') && (name[1] != '\0')) {
300 id = get_group(name+1, 0);
302 syntax_error("unknown group name '%s', you "
303 "have to use the 'group' verb first.", name+1);
306 id = new_id(name, 0);
312 get_group(const char *name, int expect)
314 struct groupdevid *g;
316 TAILQ_FOREACH(g, &group_list, link) {
317 if (strcmp(g->name, name) == 0)
321 /* Caller doesn't expect to get a group no matter what */
325 g = new_id(name, isGROUP);
326 TAILQ_INSERT_TAIL(&group_list, g, link);
331 new_rule(int type, struct groupdevid *id)
335 rule = calloc(1, sizeof(*rule));
346 add_rule(struct rule *rule)
348 TAILQ_INSERT_TAIL(&rule_list, rule, link);
352 dump_config_entry(struct rule *rule, struct groupdevid *id)
358 switch (rule->type) {
359 case rPERM: printf("perm "); break;
360 case rLINK: printf("link "); break;
361 case rHIDE: printf("hide "); break;
362 case rSHOW: printf("show "); break;
363 default: errx(1, "invalid rule type");
367 case isGROUP: printf("@"); /* FALLTHROUGH */
368 case isNAME: printf("%s", id->name); break;
370 for (i = 0; devtypes[i].name != NULL; i++) {
371 if (devtypes[i].value == id->devtype) {
372 printf("%s", devtypes[i].name);
377 default: errx(1, "invalid id type %d", id->type);
380 switch (rule->type) {
382 pwd = getpwuid(rule->uid);
383 grp = getgrgid(rule->gid);
385 printf(" %s:%s 0%.03o",
390 printf(" %d:%d 0%.03o",
397 printf(" %s", rule->dest);
399 default: /* NOTHING */;
403 printf("\t(only affects jails)");
411 rule_id_iterate(struct groupdevid *id, struct rule *rule,
412 rule_iterate_callback_t *callback)
417 if (id->type == isGROUP) {
418 for (i = 0; id->list[i] != NULL; i++) {
419 if ((error = rule_id_iterate(id->list[i], rule, callback)))
423 error = callback(rule, id);
434 TAILQ_FOREACH(rule, &rule_list, link) {
435 rule_id_iterate(rule->id, rule, dump_config_entry);
440 rule_ioctl(unsigned long cmd, struct devfs_rule *rule)
442 if (ioctl(dev_fd, cmd, rule) == -1)
449 rule_fill(struct devfs_rule *dr, struct rule *r, struct groupdevid *id)
455 errx(1, "invalid id type");
457 errx(1, "internal error: can not fill group rule");
460 dr->rule_type |= DEVFS_RULE_NAME;
462 dr->namlen = strlen(dr->name);
465 dr->rule_type |= DEVFS_RULE_TYPE;
466 dr->dev_type = id->devtype;
472 dr->rule_type |= DEVFS_RULE_PERM;
478 dr->rule_type |= DEVFS_RULE_LINK;
479 dr->linkname = r->dest;
480 dr->linknamlen = strlen(dr->linkname);
483 dr->rule_type |= DEVFS_RULE_HIDE;
486 dr->rule_type |= DEVFS_RULE_SHOW;
491 dr->rule_type |= DEVFS_RULE_JAIL;
495 rule_send(struct rule *rule, struct groupdevid *id)
497 struct devfs_rule dr;
500 dr.mntpoint = mountp;
501 dr.mntpointlen = strlen(mountp);
503 rule_fill(&dr, rule, id);
504 r = rule_ioctl(DEVFS_RULE_ADD, &dr);
512 struct devfs_rule dr;
516 dr.mntpoint = mountp;
517 dr.mntpointlen = strlen(mountp);
519 TAILQ_FOREACH(rule, &rule_list, link) {
520 r = rule_id_iterate(rule->id, rule, rule_send);
525 return (rule_ioctl(DEVFS_RULE_APPLY, &dr));
529 rule_check_num_args(char **tokens, int num)
532 for (i = 0; tokens[i] != NULL; i++);
535 syntax_error("at least %d tokens were expected but only %d were found", num, i);
542 read_config(char *name)
546 if ((fd = fopen(name, "r")) == NULL) {
547 printf("Error opening config file %s\n", name);
552 if (++line_stack_depth >= RULE_MAX_STACK) {
554 syntax_error("Maximum include depth (%d) exceeded, "
555 "check for recursion.", RULE_MAX_STACK);
558 line_stack[line_stack_depth] = 1;
559 file_stack[line_stack_depth] = strdup(name);
561 while (process_line(fd) == 0)
562 line_stack[line_stack_depth]++;
566 free(file_stack[line_stack_depth]);
573 process_line(FILE* fd)
584 while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
585 buffer[i++] = (char)c;
586 if (i == (sizeof(buffer) -1))
591 if (feof(fd) || ferror(fd))
594 while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++;
596 * If this line effectively (after indentation) begins with the comment
597 * character #, we ignore the rest of the line.
599 if (buffer[c] == '#')
602 tokens[0] = &buffer[c];
603 for (n = 1; c < i; c++) {
604 if (buffer[c] == '"') {
607 if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
608 syntax_error("stray opening quote not at beginning of token");
611 tokens[n-1] = &buffer[c+1];
613 if ((c < i-1) && (!iswhitespace(buffer[c+1]))) {
614 syntax_error("stray closing quote not at end of token");
625 if ((buffer[c] == ' ') || (buffer[c] == '\t')) {
627 while ((iswhitespace(buffer[c])) && (c < i)) c++;
628 tokens[n++] = &buffer[c--];
634 * If there are not enough arguments for any function or it is
635 * a line full of whitespaces, we just return here. Or if a
636 * quote wasn't closed.
638 if ((quote) || (n < 2) || (tokens[0][0] == '\0'))
641 /* Convert the command/verb to lowercase */
642 for (i = 0; tokens[0][i] != '\0'; i++)
643 tokens[0][i] = tolower(tokens[0][i]);
645 for (i = 0; parsers[i].verb; i++) {
646 if ((error = rule_check_num_args(tokens, parsers[i].min_args)))
649 if (!strcmp(tokens[0], parsers[i].verb)) {
650 parsers[i].parser(tokens);
656 syntax_error("unknown verb/command %s", tokens[0]);
665 "Usage: devfsctl <commands> [options]\n"
666 "Valid commands are:\n"
668 "\t Loads all read rules into the kernel and applies them\n"
670 "\t Clears all rules stored in the kernel but does not reset the nodes\n"
672 "\t Dumps the rules that have been loaded to the screen to verify syntax\n"
674 "\t Resets all devfs_nodes but does not clear the rules stored\n"
676 "Valid options and its arguments are:\n"
677 " -f <config_file>\n"
678 "\t Specifies the configuration file to be used\n"
679 " -m <mount_point>\n"
680 "\t Specifies a mount point to which the command will apply. Defaults to *\n"
686 int main(int argc, char *argv[])
688 struct devfs_rule dummy_rule;
691 while ((ch = getopt(argc, argv, "acdf:hm:r")) != -1) {
694 config_name = optarg;
724 * - need to use at least one mode
725 * - can not use -d with any other mode
727 if (!(aflag || rflag || cflag || dflag) ||
728 (dflag && (aflag || rflag || cflag))) {
736 dummy_rule.mntpoint = mountp;
737 dummy_rule.mntpointlen = strlen(dummy_rule.mntpoint);
739 if (config_name != NULL)
740 read_config(config_name);
747 dev_fd = open("/dev/devfs", O_RDWR);
749 err(1, "open(/dev/devfs)");
752 rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule);
755 rule_ioctl(DEVFS_RULE_RESET, &dummy_rule);