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/syslimits.h>
37 #include <sys/ioctl.h>
38 #include <sys/device.h>
39 #include <sys/queue.h>
41 #include <sys/devfs_rules.h>
61 rule_parser_t
*parser
;
72 static int parser_include(char **);
73 static int parser_jail(char **);
74 static int parser_hide(char **);
75 static int parser_show(char **);
76 static int parser_link(char **);
77 static int parser_group(char **);
78 static int parser_perm(char **);
79 static int dump_config_entry(struct rule
*, struct groupdevid
*);
80 static int rule_id_iterate(struct groupdevid
*, struct rule
*,
81 rule_iterate_callback_t
*);
82 static int rule_ioctl(unsigned long, struct devfs_rule_ioctl
*);
83 static void rule_fill(struct devfs_rule_ioctl
*, struct rule
*,
85 static int rule_send(struct rule
*, struct groupdevid
*);
86 static int rule_check_num_args(char **, int);
87 static int process_line(FILE*, int);
88 static int rule_parser(char **tokens
);
90 static int ruletab_parser(char **tokens
);
92 static void usage(void);
96 const char *config_name
= NULL
, *mountp
= NULL
;
98 static int aflag
= 0, cflag
= 0, rflag
= 0, tflag
= 0;
99 static int line_stack
[RULE_MAX_STACK
];
100 static char *file_stack
[RULE_MAX_STACK
];
101 static char *cwd_stack
[RULE_MAX_STACK
];
102 static int line_stack_depth
= 0;
105 static TAILQ_HEAD(, rule
) rule_list
=
106 TAILQ_HEAD_INITIALIZER(rule_list
);
107 static TAILQ_HEAD(, rule_tab
) rule_tab_list
=
108 TAILQ_HEAD_INITIALIZER(rule_tab_list
);
109 static TAILQ_HEAD(, groupdevid
) group_list
=
110 TAILQ_HEAD_INITIALIZER(group_list
);
113 static const struct verb parsers
[] = {
114 { "include", parser_include
, 1 },
115 { "jail", parser_jail
, 1 },
116 { "group", parser_group
, 2 },
117 { "perm", parser_perm
, 2 },
118 { "link", parser_link
, 2 },
119 { "hide", parser_hide
, 2 },
120 { "show", parser_show
, 2 },
124 static const struct devtype devtypes
[] = {
125 { "D_TAPE", D_TAPE
},
126 { "D_DISK", D_DISK
},
133 syntax_error(const char *fmt
, ...)
139 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
141 errx(1, "%s: syntax error on line %d: %s\n",file_stack
[line_stack_depth
],
142 line_stack
[line_stack_depth
], buf
);
146 parser_include(char **tokens
)
151 error
= stat(tokens
[1], &sb
);
154 syntax_error("could not stat %s on include, error: %s",
155 tokens
[1], strerror(errno
));
157 chdir(dirname(tokens
[1]));
158 read_config(basename(tokens
[1]), RULES_FILE
);
164 parser_jail(char **tokens
)
166 if (tokens
[1][0] == 'y') {
168 } else if (tokens
[1][0] == 'n') {
171 syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]");
178 parser_hide(char **tokens
)
180 struct groupdevid
*id
;
183 id
= get_id(tokens
[1]);
184 rule
= new_rule(rHIDE
, id
);
191 parser_show(char **tokens
)
193 struct groupdevid
*id
;
196 id
= get_id(tokens
[1]);
197 rule
= new_rule(rSHOW
, id
);
204 parser_link(char **tokens
)
206 struct groupdevid
*id
;
209 id
= get_id(tokens
[1]);
210 rule
= new_rule(rLINK
, id
);
211 rule
->dest
= strdup(tokens
[2]);
218 parser_group(char **tokens
)
220 struct groupdevid
*gid
, *id
;
224 gid
= get_group(tokens
[1], 1);
225 for (k
= 0; gid
->list
[k
] != NULL
; k
++)
227 for (i
= 2; tokens
[i
] != NULL
; i
++) {
228 id
= get_id(tokens
[i
]);
230 syntax_error("recursive group definition for group %s", gid
->name
);
232 if (k
>= gid
->listsize
-1 ) {
233 gid
->list
= realloc(gid
->list
,
234 2*gid
->listsize
*sizeof(struct groupdevid
*));
247 parser_perm(char **tokens
)
251 struct groupdevid
*id
;
256 id
= get_id(tokens
[1]);
257 rule
= new_rule(rPERM
, id
);
259 rule
->mode
= strtol(tokens
[3], NULL
, 8);
261 grname
= strchr(tokens
[2], ':');
263 syntax_error("invalid format for user/group (%s)", tokens
[2]);
267 if ((pwd
= getpwnam(uname
)))
268 rule
->uid
= pwd
->pw_uid
;
270 syntax_error("invalid user name %s", uname
);
272 if ((grp
= getgrnam(grname
)))
273 rule
->gid
= grp
->gr_gid
;
275 syntax_error("invalid group name %s", grname
);
282 new_id(const char *name
, int type_in
)
284 struct groupdevid
*id
;
285 int type
= (type_in
!= 0)?(type_in
):(isNAME
), i
;
287 id
= calloc(1, sizeof(*id
));
292 for (i
= 0; devtypes
[i
].name
!= NULL
; i
++) {
293 if (!strcmp(devtypes
[i
].name
, name
)) {
295 id
->devtype
= devtypes
[i
].value
;
302 if ((type
== isNAME
) || (type
== isGROUP
)) {
303 id
->name
= strdup(name
);
306 if (type
== isGROUP
) {
307 id
->list
= calloc(4, sizeof(struct groupdevid
*));
308 memset(id
->list
, 0, 4 * sizeof(struct groupdevid
*));
316 get_id(const char *name
)
318 struct groupdevid
*id
;
320 if ((name
[0] == '@') && (name
[1] != '\0')) {
321 id
= get_group(name
+1, 0);
323 syntax_error("unknown group name '%s', you "
324 "have to use the 'group' verb first.", name
+1);
327 id
= new_id(name
, 0);
333 get_group(const char *name
, int expect
)
335 struct groupdevid
*g
;
337 TAILQ_FOREACH(g
, &group_list
, link
) {
338 if (strcmp(g
->name
, name
) == 0)
342 /* Caller doesn't expect to get a group no matter what */
346 g
= new_id(name
, isGROUP
);
347 TAILQ_INSERT_TAIL(&group_list
, g
, link
);
352 new_rule(int type
, struct groupdevid
*id
)
356 rule
= calloc(1, sizeof(*rule
));
367 add_rule(struct rule
*rule
)
369 TAILQ_INSERT_TAIL(&rule_list
, rule
, link
);
373 dump_config_entry(struct rule
*rule
, struct groupdevid
*id
)
379 switch (rule
->type
) {
380 case rPERM
: printf("perm "); break;
381 case rLINK
: printf("link "); break;
382 case rHIDE
: printf("hide "); break;
383 case rSHOW
: printf("show "); break;
384 default: errx(1, "invalid rule type");
388 case isGROUP
: printf("@"); /* FALLTHROUGH */
389 case isNAME
: printf("%s", id
->name
); break;
391 for (i
= 0; devtypes
[i
].name
!= NULL
; i
++) {
392 if (devtypes
[i
].value
== id
->devtype
) {
393 printf("%s", devtypes
[i
].name
);
398 default: errx(1, "invalid id type %d", id
->type
);
401 switch (rule
->type
) {
403 pwd
= getpwuid(rule
->uid
);
404 grp
= getgrgid(rule
->gid
);
406 printf(" %s:%s 0%.03o",
411 printf(" %d:%d 0%.03o",
418 printf(" %s", rule
->dest
);
420 default: /* NOTHING */;
424 printf("\t(only affects jails)");
432 rule_id_iterate(struct groupdevid
*id
, struct rule
*rule
,
433 rule_iterate_callback_t
*callback
)
438 if (id
->type
== isGROUP
) {
439 for (i
= 0; id
->list
[i
] != NULL
; i
++) {
440 if ((error
= rule_id_iterate(id
->list
[i
], rule
, callback
)))
444 error
= callback(rule
, id
);
455 TAILQ_FOREACH(rule
, &rule_list
, link
) {
456 rule_id_iterate(rule
->id
, rule
, dump_config_entry
);
461 rule_ioctl(unsigned long cmd
, struct devfs_rule_ioctl
*rule
)
463 if (ioctl(dev_fd
, cmd
, rule
) == -1)
470 rule_fill(struct devfs_rule_ioctl
*dr
, struct rule
*r
, struct groupdevid
*id
)
477 errx(1, "invalid id type");
479 errx(1, "internal error: can not fill group rule");
482 dr
->rule_type
|= DEVFS_RULE_NAME
;
483 strncpy(dr
->name
, id
->name
, PATH_MAX
-1);
486 dr
->rule_type
|= DEVFS_RULE_TYPE
;
487 dr
->dev_type
= id
->devtype
;
493 dr
->rule_cmd
|= DEVFS_RULE_PERM
;
499 dr
->rule_cmd
|= DEVFS_RULE_LINK
;
500 strncpy(dr
->linkname
, r
->dest
, PATH_MAX
-1);
503 dr
->rule_cmd
|= DEVFS_RULE_HIDE
;
506 dr
->rule_cmd
|= DEVFS_RULE_SHOW
;
511 dr
->rule_type
|= DEVFS_RULE_JAIL
;
515 rule_send(struct rule
*rule
, struct groupdevid
*id
)
517 struct devfs_rule_ioctl dr
;
520 strncpy(dr
.mntpoint
, mountp
, PATH_MAX
-1);
522 rule_fill(&dr
, rule
, id
);
523 r
= rule_ioctl(DEVFS_RULE_ADD
, &dr
);
531 struct devfs_rule_ioctl dr
;
535 strncpy(dr
.mntpoint
, mountp
, PATH_MAX
-1);
537 TAILQ_FOREACH(rule
, &rule_list
, link
) {
538 r
= rule_id_iterate(rule
->id
, rule
, rule_send
);
543 return (rule_ioctl(DEVFS_RULE_APPLY
, &dr
));
547 rule_check_num_args(char **tokens
, int num
)
551 for (i
= 0; tokens
[i
] != NULL
; i
++)
555 syntax_error("at least %d tokens were expected but only %d were found", num
, i
);
562 read_config(const char *name
, int ftype
)
567 if ((fd
= fopen(name
, "r")) == NULL
) {
568 printf("Error opening config file %s\n", name
);
573 if (fstat(fileno(fd
), &sb
) != 0) {
574 errx(1, "file %s could not be fstat'ed, aborting", name
);
578 errx(1, "file %s does not belong to root, aborting!", name
);
580 if (++line_stack_depth
>= RULE_MAX_STACK
) {
582 syntax_error("Maximum include depth (%d) exceeded, "
583 "check for recursion.", RULE_MAX_STACK
);
586 line_stack
[line_stack_depth
] = 1;
587 file_stack
[line_stack_depth
] = strdup(name
);
588 cwd_stack
[line_stack_depth
] = getwd(NULL
);
590 while (process_line(fd
, ftype
) == 0)
591 line_stack
[line_stack_depth
]++;
595 free(file_stack
[line_stack_depth
]);
596 free(cwd_stack
[line_stack_depth
]);
598 chdir(cwd_stack
[line_stack_depth
]);
604 process_line(FILE* fd
, int ftype
)
612 while (((c
= fgetc(fd
)) != EOF
) && (c
!= '\n')) {
613 buffer
[i
++] = (char)c
;
614 if (i
== (sizeof(buffer
) -1))
619 if (feof(fd
) || ferror(fd
))
622 while (((buffer
[c
] == ' ') || (buffer
[c
] == '\t')) && (c
< i
)) c
++;
624 * If this line effectively (after indentation) begins with the comment
625 * character #, we ignore the rest of the line.
627 if (buffer
[c
] == '#')
630 tokens
[0] = &buffer
[c
];
631 for (n
= 1; c
< i
; c
++) {
632 if (buffer
[c
] == '"') {
635 if ((c
>= 1) && (&buffer
[c
] != tokens
[n
-1])) {
636 syntax_error("stray opening quote not at beginning of token");
639 tokens
[n
-1] = &buffer
[c
+1];
641 if ((c
< i
-1) && (!iswhitespace(buffer
[c
+1]))) {
642 syntax_error("stray closing quote not at end of token");
653 if ((buffer
[c
] == ' ') || (buffer
[c
] == '\t')) {
655 while ((iswhitespace(buffer
[c
])) && (c
< i
)) c
++;
656 tokens
[n
++] = &buffer
[c
--];
662 * If there are not enough arguments for any function or it is
663 * a line full of whitespaces, we just return here. Or if a
664 * quote wasn't closed.
666 if ((quote
) || (n
< 2) || (tokens
[0][0] == '\0'))
671 ret
= rule_parser(tokens
);
675 ret
= ruletab_parser(tokens
);
686 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 (rule_check_num_args(tokens
, parsers
[i
].min_args
) != 0)
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 (rule_check_num_args(tokens
, 2) != 0)
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
);
753 error
= read_config("ruletab", RULETAB_FILE
);
756 errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)");
758 if (!strcmp(mountp
, "*")) {
760 } else if (!strcmp(mountp
, "boot")) {
761 mode
= RULETAB_ONLY_BOOT
;
763 mode
= RULETAB_SPECIFIC
;
765 errx(1, "-t needs -m");
768 dev_fd
= open("/dev/devfs", O_RDWR
);
770 err(1, "open(/dev/devfs)");
772 TAILQ_FOREACH(rt
, &rule_tab_list
, link
) {
774 case RULETAB_ONLY_BOOT
:
775 if ((strcmp(rt
->mntpoint
, "*") != 0) &&
776 (strcmp(rt
->mntpoint
, "/dev") != 0)) {
780 case RULETAB_SPECIFIC
:
781 if (strcmp(rt
->mntpoint
, mountp
) != 0)
786 read_config(rt
->rule_file
, RULES_FILE
);
787 mountp
= rt
->mntpoint
;
800 struct groupdevid
*gdp
;
802 TAILQ_FOREACH(rp
, &rule_list
, link
) {
803 TAILQ_REMOVE(&rule_list
, rp
, link
);
806 TAILQ_FOREACH(gdp
, &group_list
, link
) {
807 TAILQ_REMOVE(&group_list
, gdp
, link
);
816 "Usage: devfsctl <commands> [options]\n"
817 "Valid commands are:\n"
819 "\t Loads all read rules into the kernel and applies them\n"
821 "\t Clears all rules stored in the kernel but does not reset the nodes\n"
823 "\t Dumps the rules that have been loaded to the screen to verify syntax\n"
825 "\t Resets all devfs_nodes but does not clear the rules stored\n"
827 "Valid options and its arguments are:\n"
828 " -f <config_file>\n"
829 "\t Specifies the configuration file to be used\n"
830 " -m <mount_point>\n"
831 "\t Specifies a mount point to which the command will apply. Defaults to *\n"
837 int main(int argc
, char *argv
[])
839 struct devfs_rule_ioctl dummy_rule
;
843 while ((ch
= getopt(argc
, argv
, "acdf:hm:r")) != -1) {
846 config_name
= optarg
;
877 * - need to use at least one mode
878 * - can not use -d with any other mode
879 * - can not use -t with any other mode or -f
881 if (!(aflag
|| rflag
|| cflag
|| dflag
) ||
882 (dflag
&& (aflag
|| rflag
|| cflag
|| tflag
))) {
889 else if (mountp
[0] != '/') {
890 errx(1, "-m needs to be given an absolute path");
893 strncpy(dummy_rule
.mntpoint
, mountp
, PATH_MAX
-1);
895 if (config_name
!= NULL
) {
896 error
= stat(config_name
, &sb
);
900 error
= stat(config_name
, &sb
);
904 err(1, "could not stat specified configuration file %s", config_name
);
906 if (config_name
[0] == '/')
907 chdir(dirname(config_name
));
909 read_config(config_name
, RULES_FILE
);
917 dev_fd
= open("/dev/devfs", O_RDWR
);
919 err(1, "open(/dev/devfs)");
922 rule_ioctl(DEVFS_RULE_CLEAR
, &dummy_rule
);
925 rule_ioctl(DEVFS_RULE_RESET
, &dummy_rule
);