diff --git a/sbin/devfs/devfs.8 b/sbin/devfs/devfs.8 index 11cede356389..b22a9ad89e0a 100644 --- a/sbin/devfs/devfs.8 +++ b/sbin/devfs/devfs.8 @@ -143,6 +143,10 @@ and the actions determine what should be done when a rule matches a node. For example, a rule can be written that sets the GID to .Li games for all devices with major number 53. +If the first token of a rule specification is a single dash +.Pq Dq - , +rules are read from the standard input and the rest of the specification +is ignored. .Pp The following conditions are recognized. Conditions are ANDed together when matching a device; @@ -311,6 +315,28 @@ will be applied to all nodes. Since hiding all nodes isn't very useful, we can undo like so: .Pp .Dl devfs rule apply unhide +.Pp +which applies +.Cm unhide +to all the nodes, +causing them to reappear. +.Pp +.Dl cat my_rules | devfs rule -s 10 add - +.Pp +Add all the rules from the file +.Pa my_rules +to ruleset 10. +.Pp +.Dl devfs rule -s 20 show | devfs rule -s 10 add - +.Pp +Since +.Cm show +outputs valid rules, +this feature can be used to copy rulesets. +The above copies all the rules from ruleset 20 into ruleset 10. +The rule numbers are preserved, +but ruleset 10 may already have rules with non-conflicting numbers +(these will be preserved). .Sh SEE ALSO .Xr jail 2 , .Xr glob 3 , diff --git a/sbin/devfs/devfs.c b/sbin/devfs/devfs.c index 4622bb9de40c..8608af306f3a 100644 --- a/sbin/devfs/devfs.c +++ b/sbin/devfs/devfs.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002 Dima Dorfman. + * Copyright (c) 2001, 2002 Dima Dorfman. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,9 @@ __FBSDID("$FreeBSD$"); #include +#include +#include #include #include #include @@ -131,6 +133,93 @@ eatonum(const char *s) return (num); } +/* + * Read a line from a /FILE/. If the return value isn't 0, it is the + * length of the line, a pointer to which exists in /line/. It is the + * caller's responsibility to free(3) it. If the return value is 0, + * there was an error or we reached EOF, and /line/ is undefined (so, + * obviously, the caller shouldn't try to free(3) it). + */ +size_t +efgetln(FILE *fp, char **line) +{ + size_t rv; + char *cp; + + cp = fgetln(fp, &rv); + if (cp == NULL) { + *line = NULL; + return (rv); + } + if (cp[rv - 1] == '\n') { + cp[rv - 1] = '\0'; + *line = strdup(cp); + if (*line == NULL) + errx(1, "cannot allocate memory"); + --rv; + } else { + *line = malloc(rv + 1); + if (*line == NULL) + errx(1, "cannot allocate memory"); + memcpy(*line, cp, rv); + *line[rv] = '\0'; + } + assert(rv == strlen(*line)); + return (rv); +} + +struct ptrstq { + STAILQ_ENTRY(ptrstq) tq; + void *ptr; +}; + +/* + * Create an argument vector from /line/. The caller must free(3) + * /avp/, and /avp[0]/ when the argument vector is no longer + * needed unless /acp/ is 0, in which case /avp/ is undefined. + * /avp/ is NULL-terminated, so it is actually one longer than /acp/. + */ +void +tokenize(const char *line, int *acp, char ***avp) +{ + static const char *delims = " \t\n"; + struct ptrstq *pt; + STAILQ_HEAD(, ptrstq) plist; + char **ap, *cp, *wline, *xcp; + + line += strspn(line, delims); + wline = strdup(line); + if (wline == NULL) + errx(1, "cannot allocate memory"); + + STAILQ_INIT(&plist); + for (xcp = wline, *acp = 0; + (cp = strsep(&xcp, delims)) != NULL;) + if (*cp != '\0') { + pt = calloc(1, sizeof(*pt)); + if (pt == NULL) + errx(1, "cannot allocate memory"); + pt->ptr = cp; + STAILQ_INSERT_TAIL(&plist, pt, tq); + ++*acp; + } + if (*acp == 0) + return; + assert(STAILQ_FIRST(&plist)->ptr == wline); + *avp = malloc(sizeof(**avp) * (*acp + 1)); + if (*avp == NULL) + errx(1, "cannot allocate memory"); + for (ap = *avp; !STAILQ_EMPTY(&plist);) { + pt = STAILQ_FIRST(&plist); + *ap = pt->ptr; + ++ap; + assert(ap <= *avp + (*acp)); + STAILQ_REMOVE_HEAD(&plist, tq); + free(pt); + } + *ap = NULL; +} + void usage(void) { diff --git a/sbin/devfs/extern.h b/sbin/devfs/extern.h index 9814b1561ce8..d2c38ac90948 100644 --- a/sbin/devfs/extern.h +++ b/sbin/devfs/extern.h @@ -48,6 +48,8 @@ command_t rule_main, ruleset_main; int atonum(const char *, uint16_t *); int eatoi(const char *); uint16_t eatonum(const char *); +size_t efgetln(FILE *, char **); +void tokenize(const char *, int *, char ***); void usage(void) __dead2; extern int mpfd; /* Mount-point file descriptor. */ diff --git a/sbin/devfs/rule.c b/sbin/devfs/rule.c index 27ad2a600364..9d2bd3d4f6d6 100644 --- a/sbin/devfs/rule.c +++ b/sbin/devfs/rule.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -46,6 +47,9 @@ __FBSDID("$FreeBSD$"); #include "extern.h" +static void rulespec_infp(FILE *fp, int cmd, devfs_rsnum rsnum); +static void rulespec_instr(struct devfs_rule *dr, const char *str, + devfs_rsnum rsnum); static void rulespec_intok(struct devfs_rule *dr, int ac, char **av, devfs_rsnum rsnum); static void rulespec_outfp(FILE *fp, struct devfs_rule *dr); @@ -109,10 +113,14 @@ rule_add(int ac, char **av) if (ac < 2) usage(); - rulespec_intok(&dr, ac - 1, av + 1, in_rsnum); - rv = ioctl(mpfd, DEVFSIO_RADD, &dr); - if (rv == -1) - err(1, "ioctl DEVFSIO_RADD"); + if (strcmp(av[1], "-") == 0) + rulespec_infp(stdin, DEVFSIO_RADD, in_rsnum); + else { + rulespec_intok(&dr, ac - 1, av + 1, in_rsnum); + rv = ioctl(mpfd, DEVFSIO_RADD, &dr); + if (rv == -1) + err(1, "ioctl DEVFSIO_RADD"); + } return (0); } @@ -127,10 +135,14 @@ rule_apply(int ac __unused, char **av __unused) if (ac < 2) usage(); if (!atonum(av[1], &rnum)) { - rulespec_intok(&dr, ac - 1, av + 1, in_rsnum); - rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr); - if (rv == -1) - err(1, "ioctl DEVFSIO_RAPPLY"); + if (strcmp(av[1], "-") == 0) + rulespec_infp(stdin, DEVFSIO_RAPPLY, in_rsnum); + else { + rulespec_intok(&dr, ac - 1, av + 1, in_rsnum); + rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr); + if (rv == -1) + err(1, "ioctl DEVFSIO_RAPPLY"); + } } else { rid = mkrid(in_rsnum, rnum); rv = ioctl(mpfd, DEVFSIO_RAPPLYID, &rid); @@ -248,6 +260,49 @@ ruleset_main(int ac, char **av) } +/* + * Input rules from a file (probably the standard input). This + * differs from the other rulespec_in*() routines in that it also + * calls ioctl() for the rules, since it is impractical (and not very + * useful) to return a list (or array) of rules, just so the caller + * can call call ioctl() for each of them. + */ +static void +rulespec_infp(FILE *fp, int cmd, devfs_rsnum rsnum) +{ + struct devfs_rule dr; + char *line; + int rv; + + assert(fp == stdin); /* XXX: De-hardcode "stdin" from error msg. */ + while (efgetln(fp, &line)) { + rulespec_instr(&dr, line, rsnum); + rv = ioctl(mpfd, cmd, &dr); + if (rv == -1) + err(1, "ioctl"); + free(line); /* efgetln() always malloc()s. */ + } + if (ferror(stdin)) + err(1, "stdin"); +} + +/* + * Construct a /struct devfs_rule/ from a string. + */ +static void +rulespec_instr(struct devfs_rule *dr, const char *str, devfs_rsnum rsnum) +{ + char **av; + int ac; + + tokenize(str, &ac, &av); + if (ac == 0) + errx(1, "unexpected end of rulespec"); + rulespec_intok(dr, ac, av, rsnum); + free(av[0]); + free(av); +} + /* * Construct a /struct devfs_rule/ from ac and av. */