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