devfsctl(8): Some style cleanup.
[dragonfly.git] / sbin / devfsctl / devfsctl.c
CommitLineData
cf0e588c
MD
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
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
16 * distribution.
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.
20 *
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
32 * SUCH DAMAGE.
33 */
4cf905ba 34
cf0e588c 35#include <sys/types.h>
5cb6a05c
AH
36#include <sys/cdefs.h>
37#include <sys/syslimits.h>
4cf905ba
AH
38#include <sys/ioctl.h>
39#include <sys/device.h>
40#include <sys/queue.h>
b68ba70c 41#include <sys/stat.h>
2c1e28dd 42#include <sys/devfs_rules.h>
4cf905ba 43
e87b69b2 44#include <ctype.h>
4cf905ba 45#include <err.h>
b68ba70c 46#include <errno.h>
4cf905ba
AH
47#include <fcntl.h>
48#include <grp.h>
8212e700 49#include <libgen.h>
4cf905ba
AH
50#include <pwd.h>
51#include <stdarg.h>
cf0e588c
MD
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
4cf905ba 55#include <unistd.h>
cf0e588c 56
5cb6a05c 57
4cf905ba
AH
58#include "devfsctl.h"
59
60struct verb {
61 const char *verb;
62 rule_parser_t *parser;
63 int min_args;
64};
65
66struct devtype {
67 const char *name;
68 int value;
69};
70
71
72
73static int parser_include(char **);
74static int parser_jail(char **);
75static int parser_hide(char **);
76static int parser_show(char **);
77static int parser_link(char **);
78static int parser_group(char **);
79static int parser_perm(char **);
80static int dump_config_entry(struct rule *, struct groupdevid *);
81static int rule_id_iterate(struct groupdevid *, struct rule *,
82 rule_iterate_callback_t *);
5cb6a05c
AH
83static int rule_ioctl(unsigned long, struct devfs_rule_ioctl *);
84static void rule_fill(struct devfs_rule_ioctl *, struct rule *,
4cf905ba
AH
85 struct groupdevid *);
86static int rule_send(struct rule *, struct groupdevid *);
87static int rule_check_num_args(char **, int);
b68ba70c
AH
88static int process_line(FILE*, int);
89static int rule_parser(char **tokens);
8212e700 90#if 0
b68ba70c 91static int ruletab_parser(char **tokens);
8212e700 92#endif
4cf905ba 93static void usage(void);
cf0e588c
MD
94
95static int dev_fd;
4cf905ba
AH
96
97const char *config_name = NULL, *mountp = NULL;
98static int dflag = 0;
b68ba70c 99static int aflag = 0, cflag = 0, rflag = 0, tflag = 0;
4cf905ba
AH
100static int line_stack[RULE_MAX_STACK];
101static char *file_stack[RULE_MAX_STACK];
8212e700 102static char *cwd_stack[RULE_MAX_STACK];
4cf905ba 103static int line_stack_depth = 0;
cf0e588c 104static int jail = 0;
cf0e588c 105
4cf905ba
AH
106static TAILQ_HEAD(, rule) rule_list =
107 TAILQ_HEAD_INITIALIZER(rule_list);
b68ba70c
AH
108static TAILQ_HEAD(, rule_tab) rule_tab_list =
109 TAILQ_HEAD_INITIALIZER(rule_tab_list);
4cf905ba
AH
110static TAILQ_HEAD(, groupdevid) group_list =
111 TAILQ_HEAD_INITIALIZER(group_list);
cf0e588c 112
cf0e588c 113
4cf905ba
AH
114static 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 },
122 { NULL, NULL, 0 }
123};
cf0e588c 124
4cf905ba
AH
125static const struct devtype devtypes[] = {
126 { "D_TAPE", D_TAPE },
127 { "D_DISK", D_DISK },
128 { "D_TTY", D_TTY },
129 { "D_MEM", D_MEM },
130 { NULL, 0 }
131};
cf0e588c 132
4cf905ba
AH
133int
134syntax_error(const char *fmt, ...)
135{
136 char buf[1024];
137 va_list ap;
138
139 va_start(ap, fmt);
140 vsnprintf(buf, sizeof(buf), fmt, ap);
141 va_end(ap);
142 errx(1, "%s: syntax error on line %d: %s\n",file_stack[line_stack_depth],
143 line_stack[line_stack_depth], buf);
144}
cf0e588c 145
4cf905ba
AH
146static int
147parser_include(char **tokens)
148{
b68ba70c
AH
149 struct stat sb;
150 int error;
151
152 error = stat(tokens[1], &sb);
153
154 if (error)
155 syntax_error("could not stat %s on include, error: %s",
156 tokens[1], strerror(errno));
157
8212e700
AH
158 chdir(dirname(tokens[1]));
159 read_config(basename(tokens[1]), RULES_FILE);
cf0e588c 160
4cf905ba 161 return 0;
cf0e588c
MD
162}
163
4cf905ba
AH
164static int
165parser_jail(char **tokens)
cf0e588c 166{
4cf905ba
AH
167 if (tokens[1][0] == 'y') {
168 jail = 1;
169 } else if (tokens[1][0] == 'n') {
170 jail = 0;
cf0e588c 171 } else {
4cf905ba 172 syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]");
cf0e588c
MD
173 }
174
4cf905ba 175 return 0;
cf0e588c
MD
176}
177
4cf905ba
AH
178static int
179parser_hide(char **tokens)
cf0e588c 180{
4cf905ba
AH
181 struct groupdevid *id;
182 struct rule *rule;
cf0e588c 183
4cf905ba
AH
184 id = get_id(tokens[1]);
185 rule = new_rule(rHIDE, id);
186 add_rule(rule);
cf0e588c 187
cf0e588c
MD
188 return 0;
189}
190
4cf905ba
AH
191static int
192parser_show(char **tokens)
cf0e588c 193{
4cf905ba
AH
194 struct groupdevid *id;
195 struct rule *rule;
cf0e588c 196
4cf905ba
AH
197 id = get_id(tokens[1]);
198 rule = new_rule(rSHOW, id);
199 add_rule(rule);
908cd01a 200
4cf905ba
AH
201 return 0;
202}
cf0e588c 203
4cf905ba
AH
204static int
205parser_link(char **tokens)
206{
207 struct groupdevid *id;
208 struct rule *rule;
209
210 id = get_id(tokens[1]);
211 rule = new_rule(rLINK, id);
212 rule->dest = strdup(tokens[2]);
213 add_rule(rule);
cf0e588c 214
4cf905ba 215 return 0;
cf0e588c
MD
216}
217
4cf905ba
AH
218static int
219parser_group(char **tokens)
cf0e588c 220{
4cf905ba
AH
221 struct groupdevid *gid, *id;
222 int i;
223 size_t k;
224
225 gid = get_group(tokens[1], 1);
226 for (k = 0; gid->list[k] != NULL; k++)
227 /* Do nothing */;
228 for (i = 2; tokens[i] != NULL; i++) {
229 id = get_id(tokens[i]);
230 if (id == gid) {
231 syntax_error("recursive group definition for group %s", gid->name);
232 } else {
233 if (k >= gid->listsize-1 ) {
234 gid->list = realloc(gid->list,
235 2*gid->listsize*sizeof(struct groupdevid *));
236 gid->listsize *= 2;
38d3371e 237 }
cf0e588c 238
4cf905ba
AH
239 gid->list[k++] = id;
240 }
cf0e588c 241 }
4cf905ba 242 gid->list[k] = NULL;
cf0e588c 243
4cf905ba 244 return 0;
cf0e588c
MD
245}
246
4cf905ba
AH
247static int
248parser_perm(char **tokens)
cf0e588c 249{
cf0e588c
MD
250 struct passwd *pwd;
251 struct group *grp;
4cf905ba
AH
252 struct groupdevid *id;
253 struct rule *rule;
254 char *uname;
255 char *grname;
256
257 id = get_id(tokens[1]);
258 rule = new_rule(rPERM, id);
259
260 rule->mode = strtol(tokens[3], NULL, 8);
261 uname = tokens[2];
262 grname = strchr(tokens[2], ':');
263 if (grname == NULL)
264 syntax_error("invalid format for user/group (%s)", tokens[2]);
265
266 *grname = '\0';
267 ++grname;
268 if ((pwd = getpwnam(uname)))
cf0e588c 269 rule->uid = pwd->pw_uid;
4cf905ba
AH
270 else
271 syntax_error("invalid user name %s", uname);
272
273 if ((grp = getgrnam(grname)))
cf0e588c 274 rule->gid = grp->gr_gid;
4cf905ba
AH
275 else
276 syntax_error("invalid group name %s", grname);
277
278 add_rule(rule);
279 return 0;
cf0e588c
MD
280}
281
4cf905ba
AH
282struct groupdevid *
283new_id(const char *name, int type_in)
cf0e588c 284{
4cf905ba
AH
285 struct groupdevid *id;
286 int type = (type_in != 0)?(type_in):(isNAME), i;
287
288 id = calloc(1, sizeof(*id));
289 if (id == NULL)
290 err(1, NULL);
291
292 if (type_in == 0) {
293 for (i = 0; devtypes[i].name != NULL; i++) {
294 if (!strcmp(devtypes[i].name, name)) {
295 type = isTYPE;
296 id->devtype = devtypes[i].value;
297 break;
298 }
299 }
300 }
301 id->type = type;
cf0e588c 302
4cf905ba
AH
303 if ((type == isNAME) || (type == isGROUP)) {
304 id->name = strdup(name);
305 }
cf0e588c 306
4cf905ba
AH
307 if (type == isGROUP) {
308 id->list = calloc(4, sizeof(struct groupdevid *));
309 memset(id->list, 0, 4 * sizeof(struct groupdevid *));
310 id->listsize = 4;
311 }
312
313 return (id);
cf0e588c
MD
314}
315
4cf905ba
AH
316struct groupdevid *
317get_id(const char *name)
cf0e588c 318{
4cf905ba 319 struct groupdevid *id;
cf0e588c 320
4cf905ba
AH
321 if ((name[0] == '@') && (name[1] != '\0')) {
322 id = get_group(name+1, 0);
323 if (id == NULL)
324 syntax_error("unknown group name '%s', you "
325 "have to use the 'group' verb first.", name+1);
326 }
327 else
328 id = new_id(name, 0);
cf0e588c 329
4cf905ba 330 return id;
cf0e588c
MD
331}
332
4cf905ba
AH
333struct groupdevid *
334get_group(const char *name, int expect)
cf0e588c 335{
4cf905ba
AH
336 struct groupdevid *g;
337
338 TAILQ_FOREACH(g, &group_list, link) {
339 if (strcmp(g->name, name) == 0)
340 return (g);
341 }
cf0e588c 342
4cf905ba
AH
343 /* Caller doesn't expect to get a group no matter what */
344 if (!expect)
345 return NULL;
cf0e588c 346
4cf905ba
AH
347 g = new_id(name, isGROUP);
348 TAILQ_INSERT_TAIL(&group_list, g, link);
349 return (g);
cf0e588c
MD
350}
351
4cf905ba
AH
352struct rule *
353new_rule(int type, struct groupdevid *id)
cf0e588c 354{
4cf905ba 355 struct rule *rule;
cf0e588c 356
4cf905ba
AH
357 rule = calloc(1, sizeof(*rule));
358 if (rule == NULL)
359 err(1, NULL);
cf0e588c 360
4cf905ba
AH
361 rule->type = type;
362 rule->id = id;
363 rule->jail = jail;
364 return (rule);
cf0e588c
MD
365}
366
4cf905ba
AH
367void
368add_rule(struct rule *rule)
cf0e588c 369{
4cf905ba
AH
370 TAILQ_INSERT_TAIL(&rule_list, rule, link);
371}
cf0e588c 372
4cf905ba
AH
373static int
374dump_config_entry(struct rule *rule, struct groupdevid *id)
375{
376 struct passwd *pwd;
377 struct group *grp;
378 int i;
379
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");
386 }
387
388 switch (id->type) {
389 case isGROUP: printf("@"); /* FALLTHROUGH */
390 case isNAME: printf("%s", id->name); break;
391 case isTYPE:
392 for (i = 0; devtypes[i].name != NULL; i++) {
393 if (devtypes[i].value == id->devtype) {
394 printf("%s", devtypes[i].name);
395 break;
396 }
397 }
398 break;
399 default: errx(1, "invalid id type %d", id->type);
908cd01a
AH
400 }
401
4cf905ba
AH
402 switch (rule->type) {
403 case rPERM:
404 pwd = getpwuid(rule->uid);
405 grp = getgrgid(rule->gid);
406 if (pwd && grp) {
407 printf(" %s:%s 0%.03o",
408 pwd->pw_name,
409 grp->gr_name,
410 rule->mode);
908cd01a 411 } else {
4cf905ba
AH
412 printf(" %d:%d 0%.03o",
413 rule->uid,
414 rule->gid,
415 rule->mode);
908cd01a 416 }
4cf905ba
AH
417 break;
418 case rLINK:
419 printf(" %s", rule->dest);
420 break;
421 default: /* NOTHING */;
cf0e588c
MD
422 }
423
4cf905ba
AH
424 if (rule->jail)
425 printf("\t(only affects jails)");
426
427 printf("\n");
428
429 return 0;
cf0e588c
MD
430}
431
4cf905ba
AH
432static int
433rule_id_iterate(struct groupdevid *id, struct rule *rule,
434 rule_iterate_callback_t *callback)
cf0e588c 435{
4cf905ba
AH
436 int error = 0;
437 int i;
38d3371e 438
4cf905ba
AH
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)))
442 return error;
443 }
444 } else {
445 error = callback(rule, id);
446 }
38d3371e 447
4cf905ba
AH
448 return error;
449}
450
451void
452dump_config(void)
453{
454 struct rule *rule;
38d3371e 455
4cf905ba
AH
456 TAILQ_FOREACH(rule, &rule_list, link) {
457 rule_id_iterate(rule->id, rule, dump_config_entry);
458 }
cf0e588c
MD
459}
460
4cf905ba 461static int
5cb6a05c 462rule_ioctl(unsigned long cmd, struct devfs_rule_ioctl *rule)
cf0e588c 463{
4cf905ba
AH
464 if (ioctl(dev_fd, cmd, rule) == -1)
465 err(1, "ioctl");
cf0e588c
MD
466
467 return 0;
468}
469
4cf905ba 470static void
5cb6a05c 471rule_fill(struct devfs_rule_ioctl *dr, struct rule *r, struct groupdevid *id)
cf0e588c 472{
4cf905ba 473 dr->rule_type = 0;
8212e700 474 dr->rule_cmd = 0;
4cf905ba
AH
475
476 switch (id->type) {
477 default:
478 errx(1, "invalid id type");
479 case isGROUP:
480 errx(1, "internal error: can not fill group rule");
481 /* NOTREACHED */
482 case isNAME:
483 dr->rule_type |= DEVFS_RULE_NAME;
5cb6a05c 484 strncpy(dr->name, id->name, PATH_MAX-1);
4cf905ba
AH
485 break;
486 case isTYPE:
487 dr->rule_type |= DEVFS_RULE_TYPE;
488 dr->dev_type = id->devtype;
489 break;
490 }
491
492 switch (r->type) {
493 case rPERM:
5cb6a05c 494 dr->rule_cmd |= DEVFS_RULE_PERM;
4cf905ba
AH
495 dr->uid = r->uid;
496 dr->gid = r->gid;
497 dr->mode = r->mode;
498 break;
499 case rLINK:
5cb6a05c
AH
500 dr->rule_cmd |= DEVFS_RULE_LINK;
501 strncpy(dr->linkname, r->dest, PATH_MAX-1);
4cf905ba
AH
502 break;
503 case rHIDE:
5cb6a05c 504 dr->rule_cmd |= DEVFS_RULE_HIDE;
4cf905ba
AH
505 break;
506 case rSHOW:
5cb6a05c 507 dr->rule_cmd |= DEVFS_RULE_SHOW;
4cf905ba
AH
508 break;
509 }
510
511 if (r->jail)
512 dr->rule_type |= DEVFS_RULE_JAIL;
513}
514
515static int
516rule_send(struct rule *rule, struct groupdevid *id)
517{
5cb6a05c 518 struct devfs_rule_ioctl dr;
4cf905ba
AH
519 int r = 0;
520
5cb6a05c 521 strncpy(dr.mntpoint, mountp, PATH_MAX-1);
4cf905ba
AH
522
523 rule_fill(&dr, rule, id);
524 r = rule_ioctl(DEVFS_RULE_ADD, &dr);
525
526 return r;
cf0e588c
MD
527}
528
529int
4cf905ba 530rule_apply(void)
cf0e588c 531{
5cb6a05c 532 struct devfs_rule_ioctl dr;
4cf905ba
AH
533 struct rule *rule;
534 int r = 0;
535
5cb6a05c 536 strncpy(dr.mntpoint, mountp, PATH_MAX-1);
4cf905ba
AH
537
538 TAILQ_FOREACH(rule, &rule_list, link) {
539 r = rule_id_iterate(rule->id, rule, rule_send);
540 if (r != 0)
541 return (-1);
cf0e588c
MD
542 }
543
4cf905ba
AH
544 return (rule_ioctl(DEVFS_RULE_APPLY, &dr));
545}
546
547static int
548rule_check_num_args(char **tokens, int num)
549{
6d2f0a97
SW
550 int i;
551
552 for (i = 0; tokens[i] != NULL; i++)
553 ;
4cf905ba
AH
554
555 if (i < num) {
556 syntax_error("at least %d tokens were expected but only %d were found", num, i);
557 return 1;
558 }
cf0e588c
MD
559 return 0;
560}
561
562int
b68ba70c 563read_config(const char *name, int ftype)
cf0e588c
MD
564{
565 FILE *fd;
8212e700 566 struct stat sb;
4cf905ba 567
cf0e588c
MD
568 if ((fd = fopen(name, "r")) == NULL) {
569 printf("Error opening config file %s\n", name);
570 perror("fopen");
571 return 1;
572 }
573
8212e700
AH
574 if (fstat(fileno(fd), &sb) != 0) {
575 errx(1, "file %s could not be fstat'ed, aborting", name);
576 }
577
578 if (sb.st_uid != 0)
579 errx(1, "file %s does not belong to root, aborting!", name);
580
4cf905ba
AH
581 if (++line_stack_depth >= RULE_MAX_STACK) {
582 --line_stack_depth;
583 syntax_error("Maximum include depth (%d) exceeded, "
584 "check for recursion.", RULE_MAX_STACK);
585 }
586
587 line_stack[line_stack_depth] = 1;
588 file_stack[line_stack_depth] = strdup(name);
8212e700 589 cwd_stack[line_stack_depth] = getwd(NULL);
4cf905ba 590
b68ba70c 591 while (process_line(fd, ftype) == 0)
4cf905ba 592 line_stack[line_stack_depth]++;
cf0e588c
MD
593
594 fclose(fd);
595
4cf905ba 596 free(file_stack[line_stack_depth]);
8212e700 597 free(cwd_stack[line_stack_depth]);
4cf905ba 598 --line_stack_depth;
8212e700 599 chdir(cwd_stack[line_stack_depth]);
4cf905ba 600
cf0e588c
MD
601 return 0;
602}
603
4cf905ba 604static int
b68ba70c 605process_line(FILE* fd, int ftype)
cf0e588c
MD
606{
607 char buffer[4096];
608 char *tokens[256];
609 int c, n, i = 0;
4cf905ba 610 int quote = 0;
cf0e588c 611 int ret = 0;
cf0e588c
MD
612
613 while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
614 buffer[i++] = (char)c;
615 if (i == (sizeof(buffer) -1))
616 break;
617 }
618 buffer[i] = '\0';
4cf905ba 619
cf0e588c
MD
620 if (feof(fd) || ferror(fd))
621 ret = 1;
622 c = 0;
623 while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++;
4cf905ba
AH
624 /*
625 * If this line effectively (after indentation) begins with the comment
626 * character #, we ignore the rest of the line.
627 */
628 if (buffer[c] == '#')
629 return 0;
630
cf0e588c 631 tokens[0] = &buffer[c];
908cd01a 632 for (n = 1; c < i; c++) {
4cf905ba
AH
633 if (buffer[c] == '"') {
634 quote = !quote;
635 if (quote) {
636 if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
637 syntax_error("stray opening quote not at beginning of token");
638 /* NOTREACHED */
639 }
640 tokens[n-1] = &buffer[c+1];
641 } else {
642 if ((c < i-1) && (!iswhitespace(buffer[c+1]))) {
643 syntax_error("stray closing quote not at end of token");
644 /* NOTREACHED */
645 }
646 buffer[c] = '\0';
647 }
648 }
649
650 if (quote) {
651 continue;
652 }
653
cf0e588c
MD
654 if ((buffer[c] == ' ') || (buffer[c] == '\t')) {
655 buffer[c++] = '\0';
4cf905ba
AH
656 while ((iswhitespace(buffer[c])) && (c < i)) c++;
657 tokens[n++] = &buffer[c--];
cf0e588c
MD
658 }
659 }
660 tokens[n] = NULL;
661
908cd01a
AH
662 /*
663 * If there are not enough arguments for any function or it is
4cf905ba
AH
664 * a line full of whitespaces, we just return here. Or if a
665 * quote wasn't closed.
908cd01a 666 */
4cf905ba 667 if ((quote) || (n < 2) || (tokens[0][0] == '\0'))
cf0e588c
MD
668 return ret;
669
b68ba70c
AH
670 switch (ftype) {
671 case RULES_FILE:
672 ret = rule_parser(tokens);
673 break;
8212e700 674#if 0
b68ba70c
AH
675 case RULETAB_FILE:
676 ret = ruletab_parser(tokens);
677 break;
8212e700 678#endif
b68ba70c
AH
679 default:
680 ret = 1;
681 }
682
683 return ret;
684}
685
686static int
687rule_parser(char **tokens)
688{
6d2f0a97 689 int i;
b68ba70c
AH
690 int parsed = 0;
691
4cf905ba
AH
692 /* Convert the command/verb to lowercase */
693 for (i = 0; tokens[0][i] != '\0'; i++)
694 tokens[0][i] = tolower(tokens[0][i]);
cf0e588c 695
4cf905ba 696 for (i = 0; parsers[i].verb; i++) {
6d2f0a97 697 if (rule_check_num_args(tokens, parsers[i].min_args) != 0)
4cf905ba
AH
698 continue;
699
700 if (!strcmp(tokens[0], parsers[i].verb)) {
701 parsers[i].parser(tokens);
702 parsed = 1;
703 break;
704 }
705 }
706 if (parsed == 0) {
707 syntax_error("unknown verb/command %s", tokens[0]);
cf0e588c 708 }
b68ba70c
AH
709
710 return 0;
711}
712
8212e700 713#if 0
b68ba70c
AH
714static int
715ruletab_parser(char **tokens)
716{
717 struct rule_tab *rt;
718 struct stat sb;
6d2f0a97
SW
719 int i;
720 int error;
b68ba70c 721
6d2f0a97 722 if (rule_check_num_args(tokens, 2) != 0)
b68ba70c
AH
723 return 0;
724
725 error = stat(tokens[0], &sb);
726 if (error) {
727 printf("ruletab warning: could not stat %s: %s\n",
728 tokens[0], strerror(errno));
729 }
730
731 if (tokens[0][0] != '/') {
732 errx(1, "ruletab error: entry %s does not seem to be an absolute path",
733 tokens[0]);
734 }
735
736 for (i = 1; tokens[i] != NULL; i++) {
737 rt = calloc(1, sizeof(struct rule_tab));
738 rt->mntpoint = strdup(tokens[0]);
739 rt->rule_file = strdup(tokens[i]);
740 TAILQ_INSERT_TAIL(&rule_tab_list, rt, link);
741 }
742
743 return 0;
744}
745
746void
6d2f0a97
SW
747rule_tab(void)
748{
b68ba70c
AH
749 struct rule_tab *rt;
750 int error;
751 int mode;
b68ba70c
AH
752
753 chdir("/etc/devfs");
754 error = read_config("ruletab", RULETAB_FILE);
755
756 if (error)
757 errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)");
758
759 if (!strcmp(mountp, "*")) {
760 mode = RULETAB_ALL;
761 } else if (!strcmp(mountp, "boot")) {
762 mode = RULETAB_ONLY_BOOT;
763 } else if (mountp) {
764 mode = RULETAB_SPECIFIC;
765 } else {
766 errx(1, "-t needs -m");
767 }
768
769 dev_fd = open("/dev/devfs", O_RDWR);
770 if (dev_fd == -1)
771 err(1, "open(/dev/devfs)");
772
773 TAILQ_FOREACH(rt, &rule_tab_list, link) {
774 switch(mode) {
775 case RULETAB_ONLY_BOOT:
776 if ((strcmp(rt->mntpoint, "*") != 0) &&
777 (strcmp(rt->mntpoint, "/dev") != 0)) {
778 continue;
779 }
780 break;
781 case RULETAB_SPECIFIC:
782 if (strcmp(rt->mntpoint, mountp) != 0)
783 continue;
784 break;
785 }
786 delete_rules();
787 read_config(rt->rule_file, RULES_FILE);
788 mountp = rt->mntpoint;
789 rule_apply();
790 }
791
792 close(dev_fd);
793
794 return;
795}
796
797void
798delete_rules(void)
799{
800 struct rule *rp;
801 struct groupdevid *gdp;
802
803 TAILQ_FOREACH(rp, &rule_list, link) {
804 TAILQ_REMOVE(&rule_list, rp, link);
805 }
806
807 TAILQ_FOREACH(gdp, &group_list, link) {
808 TAILQ_REMOVE(&group_list, gdp, link);
809 }
cf0e588c 810}
8212e700 811#endif
cf0e588c 812
4cf905ba 813static void
cf0e588c
MD
814usage(void)
815{
4cf905ba
AH
816 fprintf(stderr,
817 "Usage: devfsctl <commands> [options]\n"
818 "Valid commands are:\n"
819 " -a\n"
820 "\t Loads all read rules into the kernel and applies them\n"
821 " -c\n"
822 "\t Clears all rules stored in the kernel but does not reset the nodes\n"
823 " -d\n"
824 "\t Dumps the rules that have been loaded to the screen to verify syntax\n"
825 " -r\n"
826 "\t Resets all devfs_nodes but does not clear the rules stored\n"
827 "\n"
828 "Valid options and its arguments are:\n"
829 " -f <config_file>\n"
830 "\t Specifies the configuration file to be used\n"
831 " -m <mount_point>\n"
832 "\t Specifies a mount point to which the command will apply. Defaults to *\n"
833 );
834
835 exit(1);
cf0e588c
MD
836}
837
cf0e588c
MD
838int main(int argc, char *argv[])
839{
5cb6a05c 840 struct devfs_rule_ioctl dummy_rule;
8212e700
AH
841 struct stat sb;
842 int ch, error;
4cf905ba 843
8212e700 844 while ((ch = getopt(argc, argv, "acdf:hm:r")) != -1) {
4cf905ba 845 switch (ch) {
cf0e588c 846 case 'f':
4cf905ba 847 config_name = optarg;
cf0e588c
MD
848 break;
849 case 'm':
4cf905ba 850 mountp = optarg;
cf0e588c
MD
851 break;
852 case 'a':
853 aflag = 1;
854 break;
855 case 'c':
856 cflag = 1;
857 break;
858 case 'r':
859 rflag = 1;
860 break;
861 case 'd':
862 dflag = 1;
863 break;
8212e700 864
a0d68d0c 865 case 'h':
4cf905ba
AH
866 case '?':
867 default:
868 usage();
869 /* NOT REACHED */
870 }
cf0e588c
MD
871 }
872
4cf905ba
AH
873 argc -= optind;
874 argv += optind;
cf0e588c 875
4cf905ba
AH
876 /*
877 * Check arguments:
878 * - need to use at least one mode
879 * - can not use -d with any other mode
b68ba70c 880 * - can not use -t with any other mode or -f
4cf905ba 881 */
8212e700
AH
882 if (!(aflag || rflag || cflag || dflag) ||
883 (dflag && (aflag || rflag || cflag || tflag))) {
4cf905ba
AH
884 usage();
885 /* NOT REACHED */
cf0e588c
MD
886 }
887
4cf905ba
AH
888 if (mountp == NULL)
889 mountp = "*";
b68ba70c
AH
890 else if (mountp[0] != '/') {
891 errx(1, "-m needs to be given an absolute path");
892 }
4cf905ba 893
5cb6a05c 894 strncpy(dummy_rule.mntpoint, mountp, PATH_MAX-1);
4cf905ba 895
8212e700
AH
896 if (config_name != NULL) {
897 error = stat(config_name, &sb);
898
899 if (error) {
900 chdir("/etc/devfs");
901 error = stat(config_name, &sb);
902 }
903
904 if (error)
905 err(1, "could not stat specified configuration file %s", config_name);
906
907 if (config_name[0] == '/')
908 chdir(dirname(config_name));
909
b68ba70c 910 read_config(config_name, RULES_FILE);
8212e700 911 }
4cf905ba 912
cf0e588c
MD
913 if (dflag) {
914 dump_config();
4cf905ba 915 exit(0);
cf0e588c
MD
916 }
917
4cf905ba
AH
918 dev_fd = open("/dev/devfs", O_RDWR);
919 if (dev_fd == -1)
920 err(1, "open(/dev/devfs)");
cf0e588c 921
4cf905ba
AH
922 if (cflag)
923 rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule);
cf0e588c 924
4cf905ba
AH
925 if (rflag)
926 rule_ioctl(DEVFS_RULE_RESET, &dummy_rule);
cf0e588c 927
4cf905ba
AH
928 if (aflag)
929 rule_apply();
930
931 close(dev_fd);
cf0e588c
MD
932
933 return 0;
934}