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/param.h>
36 #include <sys/ioctl.h>
37 #include <sys/device.h>
38 #include <sys/queue.h>
40 #include <sys/devfs_rules.h>
60 rule_parser_t
*parser
;
71 static int parser_include(char **);
72 static int parser_jail(char **);
73 static int parser_hide(char **);
74 static int parser_show(char **);
75 static int parser_link(char **);
76 static int parser_group(char **);
77 static int parser_perm(char **);
78 static int dump_config_entry(struct rule
*, struct groupdevid
*);
79 static int rule_id_iterate(struct groupdevid
*, struct rule
*,
80 rule_iterate_callback_t
*);
81 static int rule_ioctl(unsigned long, struct devfs_rule_ioctl
*);
82 static void rule_fill(struct devfs_rule_ioctl
*, struct rule
*,
84 static int rule_send(struct rule
*, struct groupdevid
*);
85 static int rule_check_num_args(char **, int);
86 static int process_line(FILE*, int);
87 static int rule_parser(char **tokens
);
89 static int ruletab_parser(char **tokens
);
91 static void usage(void);
95 static const char *config_name
= NULL
, *mountp
= NULL
;
97 static int aflag
= 0, cflag
= 0, rflag
= 0, tflag
= 0;
98 static int line_stack
[RULE_MAX_STACK
];
99 static char *file_stack
[RULE_MAX_STACK
];
100 static char *cwd_stack
[RULE_MAX_STACK
];
101 static int line_stack_depth
= 0;
104 static TAILQ_HEAD(, rule
) rule_list
=
105 TAILQ_HEAD_INITIALIZER(rule_list
);
106 static TAILQ_HEAD(, rule_tab
) rule_tab_list
=
107 TAILQ_HEAD_INITIALIZER(rule_tab_list
);
108 static TAILQ_HEAD(, groupdevid
) group_list
=
109 TAILQ_HEAD_INITIALIZER(group_list
);
112 static const struct verb parsers
[] = {
113 { "include", parser_include
, 1 },
114 { "jail", parser_jail
, 1 },
115 { "group", parser_group
, 2 },
116 { "perm", parser_perm
, 2 },
117 { "link", parser_link
, 2 },
118 { "hide", parser_hide
, 2 },
119 { "show", parser_show
, 2 },
123 static const struct devtype devtypes
[] = {
124 { "D_TAPE", D_TAPE
},
125 { "D_DISK", D_DISK
},
132 syntax_error(const char *fmt
, ...)
138 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
140 errx(1, "%s: syntax error on line %d: %s\n",
141 file_stack
[line_stack_depth
], line_stack
[line_stack_depth
], buf
);
145 parser_include(char **tokens
)
150 error
= stat(tokens
[1], &sb
);
153 syntax_error("could not stat %s on include, error: %s",
154 tokens
[1], strerror(errno
));
156 chdir(dirname(tokens
[1]));
157 read_config(basename(tokens
[1]), RULES_FILE
);
163 parser_jail(char **tokens
)
165 if (tokens
[1][0] == 'y') {
167 } else if (tokens
[1][0] == 'n') {
170 syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]");
177 parser_hide(char **tokens
)
179 struct groupdevid
*id
;
182 id
= get_id(tokens
[1]);
183 rule
= new_rule(rHIDE
, id
);
190 parser_show(char **tokens
)
192 struct groupdevid
*id
;
195 id
= get_id(tokens
[1]);
196 rule
= new_rule(rSHOW
, id
);
203 parser_link(char **tokens
)
205 struct groupdevid
*id
;
208 id
= get_id(tokens
[1]);
209 rule
= new_rule(rLINK
, id
);
210 rule
->dest
= strdup(tokens
[2]);
217 parser_group(char **tokens
)
219 struct groupdevid
*gid
, *id
;
223 gid
= get_group(tokens
[1], 1);
224 for (k
= 0; gid
->list
[k
] != NULL
; k
++)
226 for (i
= 2; tokens
[i
] != NULL
; i
++) {
227 id
= get_id(tokens
[i
]);
229 syntax_error("recursive group definition for group %s", gid
->name
);
231 if (k
>= gid
->listsize
-1 ) {
232 gid
->list
= realloc(gid
->list
,
233 2*gid
->listsize
*sizeof(struct groupdevid
*));
246 parser_perm(char **tokens
)
250 struct groupdevid
*id
;
255 id
= get_id(tokens
[1]);
256 rule
= new_rule(rPERM
, id
);
258 rule
->mode
= strtol(tokens
[3], NULL
, 8);
260 grname
= strchr(tokens
[2], ':');
262 syntax_error("invalid format for user/group (%s)", tokens
[2]);
266 if ((pwd
= getpwnam(uname
)))
267 rule
->uid
= pwd
->pw_uid
;
269 syntax_error("invalid user name %s", uname
);
271 if ((grp
= getgrnam(grname
)))
272 rule
->gid
= grp
->gr_gid
;
274 syntax_error("invalid group name %s", grname
);
281 new_id(const char *name
, int type_in
)
283 struct groupdevid
*id
;
284 int type
= (type_in
!= 0)?(type_in
):(isNAME
), i
;
286 id
= calloc(1, sizeof(*id
));
291 for (i
= 0; devtypes
[i
].name
!= NULL
; i
++) {
292 if (!strcmp(devtypes
[i
].name
, name
)) {
294 id
->devtype
= devtypes
[i
].value
;
301 if ((type
== isNAME
) || (type
== isGROUP
)) {
302 id
->name
= strdup(name
);
305 if (type
== isGROUP
) {
306 id
->list
= calloc(4, sizeof(struct groupdevid
*));
307 memset(id
->list
, 0, 4 * sizeof(struct groupdevid
*));
315 get_id(const char *name
)
317 struct groupdevid
*id
;
319 if ((name
[0] == '@') && (name
[1] != '\0')) {
320 id
= get_group(name
+1, 0);
322 syntax_error("unknown group name '%s', you "
323 "have to use the 'group' verb first.", name
+1);
326 id
= new_id(name
, 0);
332 get_group(const char *name
, int expect
)
334 struct groupdevid
*g
;
336 TAILQ_FOREACH(g
, &group_list
, link
) {
337 if (strcmp(g
->name
, name
) == 0)
341 /* Caller doesn't expect to get a group no matter what */
345 g
= new_id(name
, isGROUP
);
346 TAILQ_INSERT_TAIL(&group_list
, g
, link
);
351 new_rule(int type
, struct groupdevid
*id
)
355 rule
= calloc(1, sizeof(*rule
));
366 add_rule(struct rule
*rule
)
368 TAILQ_INSERT_TAIL(&rule_list
, rule
, link
);
372 dump_config_entry(struct rule
*rule
, struct groupdevid
*id
)
378 switch (rule
->type
) {
379 case rPERM
: printf("perm "); break;
380 case rLINK
: printf("link "); break;
381 case rHIDE
: printf("hide "); break;
382 case rSHOW
: printf("show "); break;
383 default: errx(1, "invalid rule type");
387 case isGROUP
: printf("@"); /* FALLTHROUGH */
388 case isNAME
: printf("%s", id
->name
); break;
390 for (i
= 0; devtypes
[i
].name
!= NULL
; i
++) {
391 if (devtypes
[i
].value
== id
->devtype
) {
392 printf("%s", devtypes
[i
].name
);
397 default: errx(1, "invalid id type %d", id
->type
);
400 switch (rule
->type
) {
402 pwd
= getpwuid(rule
->uid
);
403 grp
= getgrgid(rule
->gid
);
405 printf(" %s:%s 0%.03o",
410 printf(" %d:%d 0%.03o",
417 printf(" %s", rule
->dest
);
419 default: /* NOTHING */;
423 printf("\t(only affects jails)");
431 rule_id_iterate(struct groupdevid
*id
, struct rule
*rule
,
432 rule_iterate_callback_t
*callback
)
437 if (id
->type
== isGROUP
) {
438 for (i
= 0; id
->list
[i
] != NULL
; i
++) {
439 if ((error
= rule_id_iterate(id
->list
[i
], rule
, callback
)))
443 error
= callback(rule
, id
);
454 TAILQ_FOREACH(rule
, &rule_list
, link
) {
455 rule_id_iterate(rule
->id
, rule
, dump_config_entry
);
460 rule_ioctl(unsigned long cmd
, struct devfs_rule_ioctl
*rule
)
462 if (ioctl(dev_fd
, cmd
, rule
) == -1)
469 rule_fill(struct devfs_rule_ioctl
*dr
, struct rule
*r
, struct groupdevid
*id
)
476 errx(1, "invalid id type");
478 errx(1, "internal error: can not fill group rule");
481 dr
->rule_type
|= DEVFS_RULE_NAME
;
482 strncpy(dr
->name
, id
->name
, PATH_MAX
-1);
485 dr
->rule_type
|= DEVFS_RULE_TYPE
;
486 dr
->dev_type
= id
->devtype
;
492 dr
->rule_cmd
|= DEVFS_RULE_PERM
;
498 dr
->rule_cmd
|= DEVFS_RULE_LINK
;
499 strncpy(dr
->linkname
, r
->dest
, PATH_MAX
-1);
502 dr
->rule_cmd
|= DEVFS_RULE_HIDE
;
505 dr
->rule_cmd
|= DEVFS_RULE_SHOW
;
510 dr
->rule_type
|= DEVFS_RULE_JAIL
;
514 rule_send(struct rule
*rule
, struct groupdevid
*id
)
516 struct devfs_rule_ioctl dr
;
519 strncpy(dr
.mntpoint
, mountp
, PATH_MAX
-1);
521 rule_fill(&dr
, rule
, id
);
522 r
= rule_ioctl(DEVFS_RULE_ADD
, &dr
);
530 struct devfs_rule_ioctl dr
;
534 strncpy(dr
.mntpoint
, mountp
, PATH_MAX
-1);
536 TAILQ_FOREACH(rule
, &rule_list
, link
) {
537 r
= rule_id_iterate(rule
->id
, rule
, rule_send
);
542 return (rule_ioctl(DEVFS_RULE_APPLY
, &dr
));
546 rule_check_num_args(char **tokens
, int num
)
550 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
)
690 /* Convert the command/verb to lowercase */
691 for (i
= 0; tokens
[0][i
] != '\0'; i
++)
692 tokens
[0][i
] = tolower(tokens
[0][i
]);
694 for (i
= 0; parsers
[i
].verb
; i
++) {
695 if (rule_check_num_args(tokens
, parsers
[i
].min_args
) != 0)
698 if (!strcmp(tokens
[0], parsers
[i
].verb
)) {
699 parsers
[i
].parser(tokens
);
705 syntax_error("unknown verb/command %s", tokens
[0]);
713 ruletab_parser(char **tokens
)
720 if (rule_check_num_args(tokens
, 2) != 0)
723 error
= stat(tokens
[0], &sb
);
725 printf("ruletab warning: could not stat %s: %s\n",
726 tokens
[0], strerror(errno
));
729 if (tokens
[0][0] != '/') {
730 errx(1, "ruletab error: entry %s does not seem to be an absolute path",
734 for (i
= 1; tokens
[i
] != NULL
; i
++) {
735 rt
= calloc(1, sizeof(struct rule_tab
));
736 rt
->mntpoint
= strdup(tokens
[0]);
737 rt
->rule_file
= strdup(tokens
[i
]);
738 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(strdup(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
);