1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2002 Dima Dorfman.
5 * All rights reserved.
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 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * Rule subsystem manipulation.
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/conf.h>
38 #include <sys/ioctl.h>
39
40 #include <assert.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <grp.h>
44 #include <pwd.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "extern.h"
51
52 static void rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum);
53 static void rulespec_instr(struct devfs_rule *dr, const char *str,
54 devfs_rsnum rsnum);
55 static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
56 devfs_rsnum rsnum);
57 static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
58
59 static command_t rule_add, rule_apply, rule_applyset;
60 static command_t rule_del, rule_delset, rule_show, rule_showsets;
61
62 static ctbl_t ctbl_rule = {
63 { "add", rule_add },
64 { "apply", rule_apply },
65 { "applyset", rule_applyset },
66 { "del", rule_del },
67 { "delset", rule_delset },
68 { "show", rule_show },
69 { "showsets", rule_showsets },
70 { NULL, NULL }
71 };
72
73 static struct intstr ist_type[] = {
74 { "disk", D_DISK },
75 { "mem", D_MEM },
76 { "tape", D_TAPE },
77 { "tty", D_TTY },
78 { NULL, -1 }
79 };
80
81 static devfs_rsnum in_rsnum;
82
83 int
rule_main(int ac,char ** av)84 rule_main(int ac, char **av)
85 {
86 struct cmd *c;
87 int ch;
88
89 setprogname("devfs rule");
90 optreset = optind = 1;
91 while ((ch = getopt(ac, av, "s:")) != -1)
92 switch (ch) {
93 case 's':
94 in_rsnum = eatonum(optarg);
95 break;
96 default:
97 usage();
98 }
99 ac -= optind;
100 av += optind;
101 if (ac < 1)
102 usage();
103
104 for (c = ctbl_rule; c->name != NULL; ++c)
105 if (strcmp(c->name, av[0]) == 0)
106 exit((*c->handler)(ac, av));
107 errx(1, "unknown command: %s", av[0]);
108 }
109
110 static int
rule_add(int ac,char ** av)111 rule_add(int ac, char **av)
112 {
113 struct devfs_rule dr;
114 int rv;
115
116 if (ac < 2)
117 usage();
118 if (strcmp(av[1], "-") == 0)
119 rulespec_infp(stdin, DEVFSIO_RADD, in_rsnum);
120 else {
121 rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
122 rv = ioctl(mpfd, DEVFSIO_RADD, &dr);
123 if (rv == -1)
124 err(1, "ioctl DEVFSIO_RADD");
125 }
126 return (0);
127 }
128
129 static int
rule_apply(int ac __unused,char ** av __unused)130 rule_apply(int ac __unused, char **av __unused)
131 {
132 struct devfs_rule dr;
133 devfs_rnum rnum;
134 devfs_rid rid;
135 int rv;
136
137 if (ac < 2)
138 usage();
139 if (!atonum(av[1], &rnum)) {
140 if (strcmp(av[1], "-") == 0)
141 rulespec_infp(stdin, DEVFSIO_RAPPLY, in_rsnum);
142 else {
143 rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
144 rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr);
145 if (rv == -1)
146 err(1, "ioctl DEVFSIO_RAPPLY");
147 }
148 } else {
149 rid = mkrid(in_rsnum, rnum);
150 rv = ioctl(mpfd, DEVFSIO_RAPPLYID, &rid);
151 if (rv == -1)
152 err(1, "ioctl DEVFSIO_RAPPLYID");
153 }
154 return (0);
155 }
156
157 static int
rule_applyset(int ac,char ** av __unused)158 rule_applyset(int ac, char **av __unused)
159 {
160 int rv;
161
162 if (ac != 1)
163 usage();
164 rv = ioctl(mpfd, DEVFSIO_SAPPLY, &in_rsnum);
165 if (rv == -1)
166 err(1, "ioctl DEVFSIO_SAPPLY");
167 return (0);
168 }
169
170 static int
rule_del(int ac __unused,char ** av)171 rule_del(int ac __unused, char **av)
172 {
173 devfs_rid rid;
174 int rv;
175
176 if (av[1] == NULL)
177 usage();
178 rid = mkrid(in_rsnum, eatoi(av[1]));
179 rv = ioctl(mpfd, DEVFSIO_RDEL, &rid);
180 if (rv == -1)
181 err(1, "ioctl DEVFSIO_RDEL");
182 return (0);
183 }
184
185 static int
rule_delset(int ac,char ** av __unused)186 rule_delset(int ac, char **av __unused)
187 {
188 struct devfs_rule dr;
189 int rv;
190
191 if (ac != 1)
192 usage();
193 memset(&dr, '\0', sizeof(dr));
194 dr.dr_magic = DEVFS_MAGIC;
195 dr.dr_id = mkrid(in_rsnum, 0);
196 while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1) {
197 rv = ioctl(mpfd, DEVFSIO_RDEL, &dr.dr_id);
198 if (rv == -1)
199 err(1, "ioctl DEVFSIO_RDEL");
200 }
201 if (errno != ENOENT)
202 err(1, "ioctl DEVFSIO_RGETNEXT");
203 return (0);
204 }
205
206 static int
rule_show(int ac __unused,char ** av)207 rule_show(int ac __unused, char **av)
208 {
209 struct devfs_rule dr;
210 devfs_rnum rnum;
211 int rv;
212
213 memset(&dr, '\0', sizeof(dr));
214 dr.dr_magic = DEVFS_MAGIC;
215 if (av[1] != NULL) {
216 rnum = eatoi(av[1]);
217 dr.dr_id = mkrid(in_rsnum, rnum - 1);
218 rv = ioctl(mpfd, DEVFSIO_RGETNEXT, &dr);
219 if (rv == -1)
220 err(1, "ioctl DEVFSIO_RGETNEXT");
221 if (rid2rn(dr.dr_id) == rnum)
222 rulespec_outfp(stdout, &dr);
223 } else {
224 dr.dr_id = mkrid(in_rsnum, 0);
225 while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1)
226 rulespec_outfp(stdout, &dr);
227 if (errno != ENOENT)
228 err(1, "ioctl DEVFSIO_RGETNEXT");
229 }
230 return (0);
231 }
232
233 static int
rule_showsets(int ac,char ** av __unused)234 rule_showsets(int ac, char **av __unused)
235 {
236 devfs_rsnum rsnum;
237
238 if (ac != 1)
239 usage();
240 rsnum = 0;
241 while (ioctl(mpfd, DEVFSIO_SGETNEXT, &rsnum) != -1)
242 printf("%d\n", rsnum);
243 if (errno != ENOENT)
244 err(1, "ioctl DEVFSIO_SGETNEXT");
245 return (0);
246 }
247
248 int
ruleset_main(int ac,char ** av)249 ruleset_main(int ac, char **av)
250 {
251 devfs_rsnum rsnum;
252 int rv;
253
254 setprogname("devfs ruleset");
255 if (ac < 2)
256 usage();
257 rsnum = eatonum(av[1]);
258 rv = ioctl(mpfd, DEVFSIO_SUSE, &rsnum);
259 if (rv == -1)
260 err(1, "ioctl DEVFSIO_SUSE");
261 return (0);
262 }
263
264
265 /*
266 * Input rules from a file (probably the standard input). This
267 * differs from the other rulespec_in*() routines in that it also
268 * calls ioctl() for the rules, since it is impractical (and not very
269 * useful) to return a list (or array) of rules, just so the caller
270 * can call ioctl() for each of them.
271 */
272 static void
rulespec_infp(FILE * fp,unsigned long request,devfs_rsnum rsnum)273 rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum)
274 {
275 struct devfs_rule dr;
276 char *line;
277 int rv;
278
279 assert(fp == stdin); /* XXX: De-hardcode "stdin" from error msg. */
280 while (efgetln(fp, &line)) {
281 rulespec_instr(&dr, line, rsnum);
282 rv = ioctl(mpfd, request, &dr);
283 if (rv == -1)
284 err(1, "ioctl");
285 free(line); /* efgetln() always malloc()s. */
286 }
287 if (ferror(stdin))
288 err(1, "stdin");
289 }
290
291 /*
292 * Construct a /struct devfs_rule/ from a string.
293 */
294 static void
rulespec_instr(struct devfs_rule * dr,const char * str,devfs_rsnum rsnum)295 rulespec_instr(struct devfs_rule *dr, const char *str, devfs_rsnum rsnum)
296 {
297 char **av;
298 int ac;
299
300 tokenize(str, &ac, &av);
301 if (ac == 0)
302 errx(1, "unexpected end of rulespec");
303 rulespec_intok(dr, ac, av, rsnum);
304 free(av[0]);
305 free(av);
306 }
307
308 /*
309 * Construct a /struct devfs_rule/ from ac and av.
310 */
311 static void
rulespec_intok(struct devfs_rule * dr,int ac __unused,char ** av,devfs_rsnum rsnum)312 rulespec_intok(struct devfs_rule *dr, int ac __unused, char **av,
313 devfs_rsnum rsnum)
314 {
315 struct intstr *is;
316 struct passwd *pw;
317 struct group *gr;
318 devfs_rnum rnum;
319 void *set;
320
321 memset(dr, '\0', sizeof(*dr));
322
323 /*
324 * We don't maintain ac hereinafter.
325 */
326 if (av[0] == NULL)
327 errx(1, "unexpected end of rulespec");
328
329 /* If the first argument is an integer, treat it as a rule number. */
330 if (!atonum(av[0], &rnum))
331 rnum = 0; /* auto-number */
332 else
333 ++av;
334
335 /*
336 * These aren't table-driven since that would result in more
337 * tiny functions than I care to deal with.
338 */
339 for (;;) {
340 if (av[0] == NULL)
341 break;
342 else if (strcmp(av[0], "type") == 0) {
343 if (av[1] == NULL)
344 errx(1, "expecting argument for type");
345 for (is = ist_type; is->s != NULL; ++is)
346 if (strcmp(av[1], is->s) == 0) {
347 dr->dr_dswflags |= is->i;
348 break;
349 }
350 if (is->s == NULL)
351 errx(1, "unknown type: %s", av[1]);
352 dr->dr_icond |= DRC_DSWFLAGS;
353 av += 2;
354 } else if (strcmp(av[0], "path") == 0) {
355 if (av[1] == NULL)
356 errx(1, "expecting argument for path");
357 if (strlcpy(dr->dr_pathptrn, av[1], DEVFS_MAXPTRNLEN)
358 >= DEVFS_MAXPTRNLEN)
359 warnx("pattern specified too long; truncated");
360 dr->dr_icond |= DRC_PATHPTRN;
361 av += 2;
362 } else
363 break;
364 }
365 while (av[0] != NULL) {
366 if (strcmp(av[0], "hide") == 0) {
367 dr->dr_iacts |= DRA_BACTS;
368 dr->dr_bacts |= DRB_HIDE;
369 ++av;
370 } else if (strcmp(av[0], "unhide") == 0) {
371 dr->dr_iacts |= DRA_BACTS;
372 dr->dr_bacts |= DRB_UNHIDE;
373 ++av;
374 } else if (strcmp(av[0], "user") == 0) {
375 if (av[1] == NULL)
376 errx(1, "expecting argument for user");
377 dr->dr_iacts |= DRA_UID;
378 pw = getpwnam(av[1]);
379 if (pw != NULL)
380 dr->dr_uid = pw->pw_uid;
381 else
382 dr->dr_uid = eatoi(av[1]); /* XXX overflow */
383 av += 2;
384 } else if (strcmp(av[0], "group") == 0) {
385 if (av[1] == NULL)
386 errx(1, "expecting argument for group");
387 dr->dr_iacts |= DRA_GID;
388 gr = getgrnam(av[1]);
389 if (gr != NULL)
390 dr->dr_gid = gr->gr_gid;
391 else
392 dr->dr_gid = eatoi(av[1]); /* XXX overflow */
393 av += 2;
394 } else if (strcmp(av[0], "mode") == 0) {
395 if (av[1] == NULL)
396 errx(1, "expecting argument for mode");
397 dr->dr_iacts |= DRA_MODE;
398 set = setmode(av[1]);
399 if (set == NULL)
400 errx(1, "invalid mode: %s", av[1]);
401 dr->dr_mode = getmode(set, 0);
402 av += 2;
403 } else if (strcmp(av[0], "include") == 0) {
404 if (av[1] == NULL)
405 errx(1, "expecting argument for include");
406 dr->dr_iacts |= DRA_INCSET;
407 dr->dr_incset = eatonum(av[1]);
408 av += 2;
409 } else
410 errx(1, "unknown argument: %s", av[0]);
411 }
412
413 dr->dr_id = mkrid(rsnum, rnum);
414 dr->dr_magic = DEVFS_MAGIC;
415 }
416
417 /*
418 * Write a human-readable (and machine-parsable, by rulespec_in*())
419 * representation of dr to bufp. *bufp should be free(3)'d when the
420 * caller is finished with it.
421 */
422 static void
rulespec_outfp(FILE * fp,struct devfs_rule * dr)423 rulespec_outfp(FILE *fp, struct devfs_rule *dr)
424 {
425 struct intstr *is;
426 struct passwd *pw;
427 struct group *gr;
428
429 fprintf(fp, "%d", rid2rn(dr->dr_id));
430
431 if (dr->dr_icond & DRC_DSWFLAGS)
432 for (is = ist_type; is->s != NULL; ++is)
433 if (dr->dr_dswflags & is->i)
434 fprintf(fp, " type %s", is->s);
435 if (dr->dr_icond & DRC_PATHPTRN)
436 fprintf(fp, " path %s", dr->dr_pathptrn);
437
438 if (dr->dr_iacts & DRA_BACTS) {
439 if (dr->dr_bacts & DRB_HIDE)
440 fprintf(fp, " hide");
441 if (dr->dr_bacts & DRB_UNHIDE)
442 fprintf(fp, " unhide");
443 }
444 if (dr->dr_iacts & DRA_UID) {
445 pw = getpwuid(dr->dr_uid);
446 if (pw == NULL)
447 fprintf(fp, " user %d", dr->dr_uid);
448 else
449 fprintf(fp, " user %s", pw->pw_name);
450 }
451 if (dr->dr_iacts & DRA_GID) {
452 gr = getgrgid(dr->dr_gid);
453 if (gr == NULL)
454 fprintf(fp, " group %d", dr->dr_gid);
455 else
456 fprintf(fp, " group %s", gr->gr_name);
457 }
458 if (dr->dr_iacts & DRA_MODE)
459 fprintf(fp, " mode %o", dr->dr_mode);
460 if (dr->dr_iacts & DRA_INCSET)
461 fprintf(fp, " include %d", dr->dr_incset);
462
463 fprintf(fp, "\n");
464 }
465