rule.c revision 100206
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: head/sbin/devfs/rule.c 100206 2002-07-17 01:46:48Z dd $");
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
49static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
50    devfs_rsnum rsnum);
51static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
52
53static command_t rule_add, rule_apply, rule_applyset;
54static command_t rule_del, rule_delset, rule_show, rule_showsets;
55
56static 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
67static 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
75devfs_rsnum in_rsnum;
76
77int
78rule_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
104static int
105rule_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
119static int
120rule_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
143static int
144rule_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
156static int
157rule_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
171static int
172rule_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
192static int
193rule_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
219static int
220rule_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
234int
235ruleset_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 */
254static void
255rulespec_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 */
375static void
376rulespec_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