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