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