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$"); 33 34 #include <sys/param.h> 35 #include <sys/conf.h> 36 #include <sys/ioctl.h> 37 38 #include <assert.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <grp.h> 42 #include <pwd.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "extern.h" 49 50 static void rulespec_infp(FILE *fp, unsigned long reqest, devfs_rsnum rsnum); 51 static void rulespec_instr(struct devfs_rule *dr, const char *str, 52 devfs_rsnum rsnum); 53 static void rulespec_intok(struct devfs_rule *dr, int ac, char **av, 54 devfs_rsnum rsnum); 55 static void rulespec_outfp(FILE *fp, struct devfs_rule *dr); 56 57 static command_t rule_add, rule_apply, rule_applyset; 58 static command_t rule_del, rule_delset, rule_show, rule_showsets; 59 60 static ctbl_t ctbl_rule = { 61 { "add", rule_add }, 62 { "apply", rule_apply }, 63 { "applyset", rule_applyset }, 64 { "del", rule_del }, 65 { "delset", rule_delset }, 66 { "show", rule_show }, 67 { "showsets", rule_showsets }, 68 { NULL, NULL } 69 }; 70 71 static struct intstr ist_type[] = { 72 { "disk", D_DISK }, 73 { "mem", D_MEM }, 74 { "tape", D_TAPE }, 75 { "tty", D_TTY }, 76 { NULL, -1 } 77 }; 78 79 devfs_rsnum in_rsnum; 80 81 int 82 rule_main(int ac, char **av) 83 { 84 struct cmd *c; 85 int ch; 86 87 setprogname("devfs rule"); 88 optreset = optind = 1; 89 while ((ch = getopt(ac, av, "s:")) != -1) 90 switch (ch) { 91 case 's': 92 in_rsnum = eatonum(optarg); 93 break; 94 default: 95 usage(); 96 } 97 ac -= optind; 98 av += optind; 99 if (ac < 1) 100 usage(); 101 102 for (c = ctbl_rule; c->name != NULL; ++c) 103 if (strcmp(c->name, av[0]) == 0) 104 exit((*c->handler)(ac, av)); 105 errx(1, "unknown command: %s", av[0]); 106 } 107 108 static int 109 rule_add(int ac, char **av) 110 { 111 struct devfs_rule dr; 112 int rv; 113 114 if (ac < 2) 115 usage(); 116 if (strcmp(av[1], "-") == 0) 117 rulespec_infp(stdin, DEVFSIO_RADD, in_rsnum); 118 else { 119 rulespec_intok(&dr, ac - 1, av + 1, in_rsnum); 120 rv = ioctl(mpfd, DEVFSIO_RADD, &dr); 121 if (rv == -1) 122 err(1, "ioctl DEVFSIO_RADD"); 123 } 124 return (0); 125 } 126 127 static int 128 rule_apply(int ac __unused, char **av __unused) 129 { 130 struct devfs_rule dr; 131 devfs_rnum rnum; 132 devfs_rid rid; 133 int rv; 134 135 if (ac < 2) 136 usage(); 137 if (!atonum(av[1], &rnum)) { 138 if (strcmp(av[1], "-") == 0) 139 rulespec_infp(stdin, DEVFSIO_RAPPLY, in_rsnum); 140 else { 141 rulespec_intok(&dr, ac - 1, av + 1, in_rsnum); 142 rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr); 143 if (rv == -1) 144 err(1, "ioctl DEVFSIO_RAPPLY"); 145 } 146 } else { 147 rid = mkrid(in_rsnum, rnum); 148 rv = ioctl(mpfd, DEVFSIO_RAPPLYID, &rid); 149 if (rv == -1) 150 err(1, "ioctl DEVFSIO_RAPPLYID"); 151 } 152 return (0); 153 } 154 155 static int 156 rule_applyset(int ac, char **av __unused) 157 { 158 int rv; 159 160 if (ac != 1) 161 usage(); 162 rv = ioctl(mpfd, DEVFSIO_SAPPLY, &in_rsnum); 163 if (rv == -1) 164 err(1, "ioctl DEVFSIO_SAPPLY"); 165 return (0); 166 } 167 168 static int 169 rule_del(int ac __unused, char **av) 170 { 171 devfs_rid rid; 172 int rv; 173 174 if (av[1] == NULL) 175 usage(); 176 rid = mkrid(in_rsnum, eatoi(av[1])); 177 rv = ioctl(mpfd, DEVFSIO_RDEL, &rid); 178 if (rv == -1) 179 err(1, "ioctl DEVFSIO_RDEL"); 180 return (0); 181 } 182 183 static int 184 rule_delset(int ac, char **av __unused) 185 { 186 struct devfs_rule dr; 187 int rv; 188 189 if (ac != 1) 190 usage(); 191 memset(&dr, '\0', sizeof(dr)); 192 dr.dr_magic = DEVFS_MAGIC; 193 dr.dr_id = mkrid(in_rsnum, 0); 194 while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1) { 195 rv = ioctl(mpfd, DEVFSIO_RDEL, &dr.dr_id); 196 if (rv == -1) 197 err(1, "ioctl DEVFSIO_RDEL"); 198 } 199 if (errno != ENOENT) 200 err(1, "ioctl DEVFSIO_RGETNEXT"); 201 return (0); 202 } 203 204 static int 205 rule_show(int ac __unused, char **av) 206 { 207 struct devfs_rule dr; 208 devfs_rnum rnum; 209 int rv; 210 211 memset(&dr, '\0', sizeof(dr)); 212 dr.dr_magic = DEVFS_MAGIC; 213 if (av[1] != NULL) { 214 rnum = eatoi(av[1]); 215 dr.dr_id = mkrid(in_rsnum, rnum - 1); 216 rv = ioctl(mpfd, DEVFSIO_RGETNEXT, &dr); 217 if (rv == -1) 218 err(1, "ioctl DEVFSIO_RGETNEXT"); 219 if (rid2rn(dr.dr_id) == rnum) 220 rulespec_outfp(stdout, &dr); 221 } else { 222 dr.dr_id = mkrid(in_rsnum, 0); 223 while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1) 224 rulespec_outfp(stdout, &dr); 225 if (errno != ENOENT) 226 err(1, "ioctl DEVFSIO_RGETNEXT"); 227 } 228 return (0); 229 } 230 231 static int 232 rule_showsets(int ac, char **av __unused) 233 { 234 devfs_rsnum rsnum; 235 236 if (ac != 1) 237 usage(); 238 rsnum = 0; 239 while (ioctl(mpfd, DEVFSIO_SGETNEXT, &rsnum) != -1) 240 printf("%d\n", rsnum); 241 if (errno != ENOENT) 242 err(1, "ioctl DEVFSIO_SGETNEXT"); 243 return (0); 244 } 245 246 int 247 ruleset_main(int ac, char **av) 248 { 249 devfs_rsnum rsnum; 250 int rv; 251 252 setprogname("devfs ruleset"); 253 if (ac < 2) 254 usage(); 255 rsnum = eatonum(av[1]); 256 rv = ioctl(mpfd, DEVFSIO_SUSE, &rsnum); 257 if (rv == -1) 258 err(1, "ioctl DEVFSIO_SUSE"); 259 return (0); 260 } 261 262 263 /* 264 * Input rules from a file (probably the standard input). This 265 * differs from the other rulespec_in*() routines in that it also 266 * calls ioctl() for the rules, since it is impractical (and not very 267 * useful) to return a list (or array) of rules, just so the caller 268 * can call call ioctl() for each of them. 269 */ 270 static void 271 rulespec_infp(FILE *fp, unsigned long request, devfs_rsnum rsnum) 272 { 273 struct devfs_rule dr; 274 char *line; 275 int rv; 276 277 assert(fp == stdin); /* XXX: De-hardcode "stdin" from error msg. */ 278 while (efgetln(fp, &line)) { 279 rulespec_instr(&dr, line, rsnum); 280 rv = ioctl(mpfd, request, &dr); 281 if (rv == -1) 282 err(1, "ioctl"); 283 free(line); /* efgetln() always malloc()s. */ 284 } 285 if (ferror(stdin)) 286 err(1, "stdin"); 287 } 288 289 /* 290 * Construct a /struct devfs_rule/ from a string. 291 */ 292 static void 293 rulespec_instr(struct devfs_rule *dr, const char *str, devfs_rsnum rsnum) 294 { 295 char **av; 296 int ac; 297 298 tokenize(str, &ac, &av); 299 if (ac == 0) 300 errx(1, "unexpected end of rulespec"); 301 rulespec_intok(dr, ac, av, rsnum); 302 free(av[0]); 303 free(av); 304 } 305 306 /* 307 * Construct a /struct devfs_rule/ from ac and av. 308 */ 309 static void 310 rulespec_intok(struct devfs_rule *dr, int ac __unused, char **av, 311 devfs_rsnum rsnum) 312 { 313 struct intstr *is; 314 struct passwd *pw; 315 struct group *gr; 316 devfs_rnum rnum; 317 char *cp; 318 long l; 319 320 memset(dr, '\0', sizeof(*dr)); 321 322 /* 323 * We don't maintain ac hereinafter. 324 */ 325 if (av[0] == NULL) 326 errx(1, "unexpected end of rulespec"); 327 328 /* If the first argument is an integer, treat it as a rule number. */ 329 if (!atonum(av[0], &rnum)) 330 rnum = 0; /* auto-number */ 331 else 332 ++av; 333 334 /* 335 * These aren't table-driven since that would result in more 336 * tiny functions than I care to deal with. 337 */ 338 for (;;) { 339 if (av[0] == NULL) 340 break; 341 else if (strcmp(av[0], "type") == 0) { 342 if (av[1] == NULL) 343 errx(1, "expecting argument for type"); 344 for (is = ist_type; is->s != NULL; ++is) 345 if (strcmp(av[1], is->s) == 0) { 346 dr->dr_dswflags |= is->i; 347 break; 348 } 349 if (is->s == NULL) 350 errx(1, "unknown type: %s", av[1]); 351 dr->dr_icond |= DRC_DSWFLAGS; 352 av += 2; 353 } else if (strcmp(av[0], "path") == 0) { 354 if (av[1] == NULL) 355 errx(1, "expecting argument for path"); 356 if (strlcpy(dr->dr_pathptrn, av[1], DEVFS_MAXPTRNLEN) 357 >= DEVFS_MAXPTRNLEN) 358 warnx("pattern specified too long; truncated"); 359 dr->dr_icond |= DRC_PATHPTRN; 360 av += 2; 361 } else if (strcmp(av[0], "major") == 0) { 362 if (av[1] == NULL) 363 errx(1, "expecting argument for major"); 364 dr->dr_major = eatoi(av[1]); 365 dr->dr_icond |= DRC_MAJOR; 366 av += 2; 367 } else 368 break; 369 } 370 for (;;) { 371 if (av[0] == NULL) 372 break; 373 else if (strcmp(av[0], "hide") == 0) { 374 dr->dr_iacts |= DRA_BACTS; 375 dr->dr_bacts |= DRB_HIDE; 376 ++av; 377 } else if (strcmp(av[0], "unhide") == 0) { 378 dr->dr_iacts |= DRA_BACTS; 379 dr->dr_bacts |= DRB_UNHIDE; 380 ++av; 381 } else if (strcmp(av[0], "user") == 0) { 382 if (av[1] == NULL) 383 errx(1, "expecting argument for user"); 384 dr->dr_iacts |= DRA_UID; 385 pw = getpwnam(av[1]); 386 if (pw != NULL) 387 dr->dr_uid = pw->pw_uid; 388 else 389 dr->dr_uid = eatoi(av[1]); /* XXX overflow */ 390 av += 2; 391 } else if (strcmp(av[0], "group") == 0) { 392 if (av[1] == NULL) 393 errx(1, "expecting argument for group"); 394 dr->dr_iacts |= DRA_GID; 395 gr = getgrnam(av[1]); 396 if (gr != NULL) 397 dr->dr_gid = gr->gr_gid; 398 else 399 dr->dr_gid = eatoi(av[1]); /* XXX overflow */ 400 av += 2; 401 } else if (strcmp(av[0], "mode") == 0) { 402 if (av[1] == NULL) 403 errx(1, "expecting argument for mode"); 404 dr->dr_iacts |= DRA_MODE; 405 l = strtol(av[1], &cp, 8); 406 if (l > (1 << (sizeof(dr->dr_mode) * 8)) - 1 || 407 *cp != '\0') 408 errx(1, "invalid mode: %s", av[1]); 409 dr->dr_mode = l; 410 av += 2; 411 } else if (strcmp(av[0], "include") == 0) { 412 if (av[1] == NULL) 413 errx(1, "expecting argument for include"); 414 dr->dr_iacts |= DRA_INCSET; 415 dr->dr_incset = eatonum(av[1]); 416 av += 2; 417 } else 418 errx(1, "unknown argument: %s", av[0]); 419 } 420 421 dr->dr_id = mkrid(rsnum, rnum); 422 dr->dr_magic = DEVFS_MAGIC; 423 } 424 425 /* 426 * Write a human-readable (and machine-parsable, by rulespec_in*()) 427 * representation of dr to bufp. *bufp should be free(3)'d when the 428 * caller is finished with it. 429 */ 430 static void 431 rulespec_outfp(FILE *fp, struct devfs_rule *dr) 432 { 433 struct intstr *is; 434 struct passwd *pw; 435 struct group *gr; 436 437 fprintf(fp, "%d", rid2rn(dr->dr_id)); 438 439 if (dr->dr_icond & DRC_DSWFLAGS) 440 for (is = ist_type; is->s != NULL; ++is) 441 if (dr->dr_dswflags & is->i) 442 fprintf(fp, " type %s", is->s); 443 if (dr->dr_icond & DRC_PATHPTRN) 444 fprintf(fp, " path %s", dr->dr_pathptrn); 445 if (dr->dr_icond & DRC_MAJOR) 446 fprintf(fp, " major %d", dr->dr_major); 447 448 if (dr->dr_iacts & DRA_BACTS) { 449 if (dr->dr_bacts & DRB_HIDE) 450 fprintf(fp, " hide"); 451 if (dr->dr_bacts & DRB_UNHIDE) 452 fprintf(fp, " unhide"); 453 } 454 if (dr->dr_iacts & DRA_UID) { 455 pw = getpwuid(dr->dr_uid); 456 if (pw == NULL) 457 fprintf(fp, " user %d", dr->dr_uid); 458 else 459 fprintf(fp, " user %s", pw->pw_name); 460 } 461 if (dr->dr_iacts & DRA_GID) { 462 gr = getgrgid(dr->dr_gid); 463 if (gr == NULL) 464 fprintf(fp, " group %d", dr->dr_gid); 465 else 466 fprintf(fp, " group %s", gr->gr_name); 467 } 468 if (dr->dr_iacts & DRA_MODE) 469 fprintf(fp, " mode %o", dr->dr_mode); 470 if (dr->dr_iacts & DRA_INCSET) 471 fprintf(fp, " include %d", dr->dr_incset); 472 473 fprintf(fp, "\n"); 474 } 475