1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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
52static void rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum);
53static void rulespec_instr(struct devfs_rule *dr, const char *str,
54    devfs_rsnum rsnum);
55static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
56    devfs_rsnum rsnum);
57static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
58
59static command_t rule_add, rule_apply, rule_applyset;
60static command_t rule_del, rule_delset, rule_show, rule_showsets;
61
62static 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
73static 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
81static devfs_rsnum in_rsnum;
82
83int
84rule_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
110static int
111rule_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
129static int
130rule_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
157static int
158rule_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
170static int
171rule_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
185static int
186rule_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
206static int
207rule_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
233static int
234rule_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
248int
249ruleset_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 */
272static void
273rulespec_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 */
294static void
295rulespec_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 */
311static void
312rulespec_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 */
422static void
423rulespec_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