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