xref: /freebsd/sbin/devfs/rule.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
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/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
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
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
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
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
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
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
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
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
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
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
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
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
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