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
49static void rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum);
50static void rulespec_instr(struct devfs_rule *dr, const char *str,
51    devfs_rsnum rsnum);
52static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
53    devfs_rsnum rsnum);
54static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
55
56static command_t rule_add, rule_apply, rule_applyset;
57static command_t rule_del, rule_delset, rule_show, rule_showsets;
58
59static 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
70static 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
78static devfs_rsnum in_rsnum;
79
80int
81rule_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
107static int
108rule_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
126static int
127rule_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
154static int
155rule_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
167static int
168rule_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
182static int
183rule_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
203static int
204rule_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
230static int
231rule_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
245int
246ruleset_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 */
269static void
270rulespec_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 */
291static void
292rulespec_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 */
308static void
309rulespec_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 */
419static void
420rulespec_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