Merge llvm-project release/14.x llvmorg-14.0.0-2-g3f43d803382d
[freebsd.git] / sbin / devfs / rule.c
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
52 static void rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum);
53 static void rulespec_instr(struct devfs_rule *dr, const char *str,
54     devfs_rsnum rsnum);
55 static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
56     devfs_rsnum rsnum);
57 static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
58
59 static command_t rule_add, rule_apply, rule_applyset;
60 static command_t rule_del, rule_delset, rule_show, rule_showsets;
61
62 static 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
73 static 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
81 static devfs_rsnum in_rsnum;
82
83 int
84 rule_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
110 static int
111 rule_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
129 static int
130 rule_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
157 static int
158 rule_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
170 static int
171 rule_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
185 static int
186 rule_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
206 static int
207 rule_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
233 static int
234 rule_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
248 int
249 ruleset_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  */
272 static void
273 rulespec_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  */
294 static void
295 rulespec_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  */
311 static void
312 rulespec_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  */
422 static void
423 rulespec_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 }