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