1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 /* 31 * autopush(1) is the command interface to the STREAMS autopush 32 * mechanism. The autopush command can be used to configure autopush 33 * information about a STREAMS driver, remove autopush information, 34 * and report on current configuration information. Its use is as 35 * follows: 36 * 37 * autopush -f file 38 * autopush -r -M major -m minor 39 * autopush -g -M major -m minor 40 * 41 * The -f option allows autopush information to be set from a file. The 42 * format of the file is as follows: 43 * 44 * # Comment lines begin with a # in column one. 45 * # The fields are separated by white space and are: 46 * # major minor lastminor module1 module2 ... module8 47 * 48 * "lastminor" is used to configure ranges of minor devices, from "minor" 49 * to "lastminor" inclusive. It should be set to zero when not in use. 50 * The -r option allows autopush information to be removed for the given 51 * major/minor pair. The -g option allows the configuration information 52 * to be printed. The format of printing is the same as for the file. 53 */ 54 55 /* 56 * Use autopush version 1; keep before #include <sys/sad.h>. 57 * See <sys/sad.h> for details. 58 */ 59 #define AP_VERSION 1 60 61 #include <sys/types.h> 62 #include <sys/conf.h> 63 #include <sys/modctl.h> 64 #include <sys/sad.h> 65 #include <stdio.h> 66 #include <fcntl.h> 67 #include <errno.h> 68 #include <ctype.h> 69 #include <stdlib.h> 70 #include <unistd.h> 71 #include <string.h> 72 #include <locale.h> 73 #include <sys/stat.h> 74 #include <zone.h> 75 76 #define OPTIONS "M:f:gm:r" /* command line options for getopt(3C) */ 77 #define COMMENT '#' 78 #define MINUS '-' 79 #define SLASH '/' 80 81 /* 82 * Output format. 83 */ 84 #define OHEADER " Major Minor Lastminor\tModules\n" 85 #define OFORMAT1_ONE "%10ld %10ld - \t" 86 #define OFORMAT1_RANGE "%10ld %10ld %10ld\t" 87 #define OFORMAT1_ALL "%10ld ALL - \t" 88 89 #define AP_ANCHOR "[anchor]" 90 91 #define Openerr gettext("%s: ERROR: Could not open %s: ") 92 #define Digiterr gettext("%s: ERROR: argument to %s option must be " \ 93 "numeric\n") 94 #define Badline gettext("%s: WARNING: File %s: bad input line %d " \ 95 "ignored\n") 96 97 static void usage(); 98 static int rem_info(), get_info(), set_info(); 99 static int is_white_space(), parse_line(); 100 101 static char *Cmdp; /* command name */ 102 103 /* 104 * main(): 105 * process command line arguments. 106 */ 107 int 108 main(int argc, char *argv[]) 109 { 110 int c; /* character read by getopt(3C) */ 111 char *filenamep; /* name of configuration file */ 112 major_t major; /* major device number */ 113 minor_t minor; /* minor device number */ 114 char *cp; 115 int exitcode; 116 ushort_t minflag = 0; /* -m option used */ 117 ushort_t majflag = 0; /* -M option used */ 118 ushort_t fflag = 0; /* -f option used */ 119 ushort_t rflag = 0; /* -r option used */ 120 ushort_t gflag = 0; /* -g option used */ 121 ushort_t errflag = 0; /* options usage error */ 122 123 (void) setlocale(LC_ALL, ""); 124 #if !defined(TEXT_DOMAIN) 125 #define TEXT_DOMAIN "SYS_TEST" 126 #endif 127 (void) textdomain(TEXT_DOMAIN); 128 129 /* 130 * Get command name. 131 */ 132 Cmdp = argv[0]; 133 for (filenamep = argv[0]; *filenamep; filenamep++) 134 if (*filenamep == SLASH) 135 Cmdp = filenamep + 1; 136 137 /* 138 * Get options. 139 */ 140 while (!errflag && ((c = getopt(argc, argv, OPTIONS)) != -1)) { 141 switch (c) { 142 case 'M': 143 if (fflag|majflag) 144 errflag++; 145 else { 146 majflag++; 147 for (cp = optarg; *cp; cp++) 148 if (!isdigit(*cp)) { 149 (void) fprintf(stderr, 150 Digiterr, Cmdp, "-M"); 151 exit(1); 152 } 153 major = (major_t)atol(optarg); 154 } 155 break; 156 157 case 'm': 158 if (fflag|minflag) 159 errflag++; 160 else { 161 minflag++; 162 for (cp = optarg; *cp; cp++) 163 if (!isdigit(*cp)) { 164 (void) fprintf(stderr, 165 Digiterr, Cmdp, "-m"); 166 exit(1); 167 } 168 minor = (minor_t)atol(optarg); 169 } 170 break; 171 172 case 'f': 173 if (fflag|gflag|rflag|majflag|minflag) 174 errflag++; 175 else { 176 fflag++; 177 filenamep = optarg; 178 } 179 break; 180 181 case 'r': 182 if (fflag|gflag|rflag) 183 errflag++; 184 else 185 rflag++; 186 break; 187 188 case 'g': 189 if (fflag|gflag|rflag) 190 errflag++; 191 else 192 gflag++; 193 break; 194 195 default: 196 errflag++; 197 break; 198 } /* switch */ 199 if (errflag) { 200 usage(); 201 exit(1); 202 } 203 } /* while */ 204 if (((gflag || rflag) && (!majflag || !minflag)) || (optind != argc)) { 205 usage(); 206 exit(1); 207 } 208 209 if (getzoneid() != GLOBAL_ZONEID) { 210 (void) fprintf(stderr, gettext("autopush " 211 "can only be run from the global zone.\n")); 212 exit(1); 213 } 214 215 if (fflag) 216 exitcode = set_info(filenamep); 217 else if (rflag) 218 exitcode = rem_info(major, minor); 219 else if (gflag) 220 exitcode = get_info(major, minor); 221 else { 222 usage(); 223 exit(1); 224 } 225 226 return (exitcode); 227 } 228 229 /* 230 * usage(): 231 * print out usage statement. 232 */ 233 static void 234 usage() 235 { 236 (void) fprintf(stderr, gettext("%s: USAGE:\n\t%s -f filename\n" 237 "\t%s -r -M major -m minor\n" 238 "\t%s -g -M major -m minor\n"), Cmdp, Cmdp, Cmdp, Cmdp); 239 } 240 241 /* 242 * set_info(): 243 * set autopush configuration information. 244 * namep: autopush configuration filename 245 */ 246 static int 247 set_info(char *namep) 248 { 249 int line; /* line number of file */ 250 FILE *fp; /* file pointer of config file */ 251 char buf[256]; /* input buffer */ 252 struct strapush push; /* configuration information */ 253 int sadfd; /* file descriptor to SAD driver */ 254 int retcode = 0; /* return code */ 255 int parsecode; /* return value from parse function */ 256 257 if ((sadfd = open(ADMINDEV, O_RDWR)) < 0) { 258 (void) fprintf(stderr, Openerr, Cmdp, ADMINDEV); 259 perror(""); 260 return (1); 261 } 262 if ((fp = fopen(namep, "r")) == NULL) { 263 (void) fprintf(stderr, Openerr, Cmdp, namep); 264 perror(""); 265 return (1); 266 } 267 line = 0; 268 while (fgets(buf, sizeof (buf), fp) != NULL) { 269 line++; 270 if ((buf[0] == COMMENT) || is_white_space(buf)) 271 continue; 272 (void) memset(&push, 0, sizeof (struct strapush)); 273 274 parsecode = parse_line(buf, line, namep, &push); 275 if (parsecode != 0) { 276 retcode = parsecode; 277 continue; 278 } 279 280 if (push.sap_minor == (minor_t)-1) 281 push.sap_cmd = SAP_ALL; 282 else if (push.sap_lastminor == 0) 283 push.sap_cmd = SAP_ONE; 284 else 285 push.sap_cmd = SAP_RANGE; 286 287 if (ioctl(sadfd, SAD_SAP, &push) < 0) { 288 int error = errno; 289 290 retcode = 1; 291 (void) fprintf(stderr, 292 gettext("%s: ERROR: File %s: could not configure " 293 "autopush for line %d\n"), Cmdp, namep, line); 294 switch (error) { 295 case EPERM: 296 (void) fprintf(stderr, gettext("%s: ERROR: " 297 "You don't have permission to set autopush " 298 "information\n"), Cmdp); 299 break; 300 301 case EINVAL: 302 (void) fprintf(stderr, gettext("%s: ERROR: " 303 "Invalid major device number or invalid " 304 "module name or too many modules\n"), Cmdp); 305 break; 306 307 case ENOSTR: 308 (void) fprintf(stderr, gettext("%s: ERROR: " 309 "Major device is not a STREAMS " 310 "driver\n"), Cmdp); 311 break; 312 313 case EEXIST: 314 (void) fprintf(stderr, gettext("%s: ERROR: " 315 "Major/minor already configured\n"), Cmdp); 316 break; 317 318 case ENOSR: 319 (void) fprintf(stderr, gettext("%s: ERROR: Ran " 320 "out of autopush structures\n"), Cmdp); 321 break; 322 323 case ERANGE: 324 (void) fprintf(stderr, gettext("%s: ERROR: " 325 "lastminor must be greater than minor\n"), 326 Cmdp); 327 break; 328 329 default: 330 (void) fprintf(stderr, gettext("%s: ERROR: "), 331 Cmdp); 332 (void) fprintf(stderr, "%s\n", strerror(error)); 333 break; 334 } /* switch */ 335 } /* if */ 336 } /* while */ 337 return (retcode); 338 } 339 340 /* 341 * rem_info(): 342 * remove autopush configuration information. 343 */ 344 static int 345 rem_info(major_t maj, minor_t min) 346 { 347 struct strapush push; /* configuration information */ 348 int sadfd; /* file descriptor to SAD driver */ 349 int retcode = 0; /* return code */ 350 351 if ((sadfd = open(ADMINDEV, O_RDWR)) < 0) { 352 (void) fprintf(stderr, Openerr, Cmdp, ADMINDEV); 353 perror(""); 354 return (1); 355 } 356 push.sap_cmd = SAP_CLEAR; 357 push.sap_minor = min; 358 push.sap_major = maj; 359 360 if (ioctl(sadfd, SAD_SAP, &push) < 0) { 361 int error = errno; 362 363 retcode = 1; 364 (void) fprintf(stderr, gettext("%s: ERROR: Could not remove " 365 "autopush information\n"), Cmdp); 366 switch (error) { 367 case EPERM: 368 (void) fprintf(stderr, gettext("%s: ERROR: You don't " 369 "have permission to remove autopush " 370 "information\n"), Cmdp); 371 break; 372 373 case EINVAL: 374 if ((min != 0) && (ioctl(sadfd, SAD_GAP, &push) == 0) && 375 (push.sap_cmd == SAP_ALL)) 376 (void) fprintf(stderr, gettext("%s: ERROR: " 377 "When removing an entry for ALL minors, " 378 "minor must be set to 0\n"), Cmdp); 379 else 380 (void) fprintf(stderr, gettext("%s: ERROR: " 381 "Invalid major device number\n"), Cmdp); 382 break; 383 384 case ENODEV: 385 (void) fprintf(stderr, gettext("%s: ERROR: Major/minor " 386 "not configured for autopush\n"), Cmdp); 387 break; 388 389 case ERANGE: 390 (void) fprintf(stderr, gettext("%s: ERROR: minor must " 391 "be set to begining of range when clearing\n"), 392 Cmdp); 393 break; 394 395 default: 396 (void) fprintf(stderr, gettext("%s: ERROR: "), Cmdp); 397 (void) fprintf(stderr, "%s\n", strerror(error)); 398 break; 399 } /* switch */ 400 } 401 return (retcode); 402 } 403 404 /* 405 * get_info(): 406 * get autopush configuration information. 407 */ 408 static int 409 get_info(major_t maj, minor_t min) 410 { 411 struct strapush push; /* configuration information */ 412 int i; /* counter */ 413 int sadfd; /* file descriptor to SAD driver */ 414 415 if ((sadfd = open(USERDEV, O_RDWR)) < 0) { 416 (void) fprintf(stderr, Openerr, Cmdp, USERDEV); 417 perror(""); 418 return (1); 419 } 420 push.sap_major = maj; 421 push.sap_minor = min; 422 423 if (ioctl(sadfd, SAD_GAP, &push) < 0) { 424 int error = errno; 425 426 (void) fprintf(stderr, gettext("%s: ERROR: Could not get " 427 "autopush information\n"), Cmdp); 428 switch (error) { 429 case EINVAL: 430 (void) fprintf(stderr, gettext("%s: ERROR: Invalid " 431 "major device number\n"), Cmdp); 432 break; 433 434 case ENOSTR: 435 (void) fprintf(stderr, gettext("%s: ERROR: Major " 436 "device is not a STREAMS driver\n"), Cmdp); 437 break; 438 439 case ENODEV: 440 (void) fprintf(stderr, gettext("%s: ERROR: Major/minor " 441 "not configured for autopush\n"), Cmdp); 442 break; 443 444 default: 445 (void) fprintf(stderr, gettext("%s: ERROR: "), Cmdp); 446 (void) fprintf(stderr, "%s\n", strerror(error)); 447 break; 448 } /* switch */ 449 return (1); 450 } 451 (void) printf(OHEADER); 452 switch (push.sap_cmd) { 453 case SAP_ONE: 454 (void) printf(OFORMAT1_ONE, push.sap_major, push.sap_minor); 455 break; 456 457 case SAP_RANGE: 458 (void) printf(OFORMAT1_RANGE, push.sap_major, push.sap_minor, 459 push.sap_lastminor); 460 break; 461 462 case SAP_ALL: 463 (void) printf(OFORMAT1_ALL, push.sap_major); 464 break; 465 466 default: 467 (void) fprintf(stderr, 468 gettext("%s: ERROR: Unknown configuration type\n"), Cmdp); 469 return (1); 470 } 471 472 for (i = 0; i < push.sap_npush; i++) { 473 474 (void) printf("%s", push.sap_list[i]); 475 476 if (push.sap_anchor == (i + 1)) 477 (void) printf(" %s", AP_ANCHOR); 478 479 if (i < push.sap_npush - 1) 480 (void) printf(" "); 481 482 } 483 484 (void) printf("\n"); 485 return (0); 486 } 487 488 /* 489 * is_white_space(): 490 * Return 1 if buffer is all white space. 491 * Return 0 otherwise. 492 */ 493 static int 494 is_white_space(char *bufp) 495 { 496 while (*bufp) { 497 if (!isspace(*bufp)) 498 return (0); 499 bufp++; 500 } 501 return (1); 502 } 503 504 /* 505 * parse_line(): 506 * Parse input line from file and report any errors found. Fill 507 * strapush structure along the way. Returns 1 if the line has 508 * errors and 0 if the line is well-formed. Another hidden 509 * dependency on MAXAPUSH. `linep' is the input buffer, `lineno' 510 * is the current line number, and `namep' is the filename. 511 */ 512 static int 513 parse_line(char *linep, int lineno, char *namep, struct strapush *pushp) 514 { 515 char *wp; /* word pointer */ 516 char *cp; /* character pointer */ 517 int midx; /* module index */ 518 int npush; /* number of modules to push */ 519 char c; 520 major_t major_num; 521 522 pushp->sap_anchor = 0; /* by default, no anchor */ 523 524 /* 525 * Find the major device number. 526 */ 527 for (wp = linep; isspace(*wp); wp++) 528 ; 529 for (cp = wp; !isspace(*cp); cp++) 530 ; 531 if (!isspace(*cp)) { 532 (void) fprintf(stderr, Badline, Cmdp, namep, lineno); 533 return (1); 534 } 535 c = *cp; 536 *cp = '\0'; 537 if (modctl(MODGETMAJBIND, wp, strlen(wp) + 1, &major_num) != 0) { 538 (void) fprintf(stderr, Badline, Cmdp, namep, lineno); 539 return (1); 540 } 541 *cp = c; 542 pushp->sap_major = major_num; 543 544 /* 545 * Find the minor device number. Must handle negative values here. 546 */ 547 for (wp = cp; isspace(*wp); wp++) 548 ; 549 for (cp = wp; (isdigit(*cp) || (*cp == MINUS)); cp++) 550 ; 551 if (!isspace(*cp)) { 552 (void) fprintf(stderr, Badline, Cmdp, namep, lineno); 553 return (1); 554 } 555 pushp->sap_minor = (minor_t)atol(wp); 556 557 /* 558 * Find the lastminor. 559 */ 560 for (wp = cp; isspace(*wp); wp++) 561 ; 562 for (cp = wp; isdigit(*cp); cp++) 563 ; 564 if (!isspace(*cp)) { 565 (void) fprintf(stderr, Badline, Cmdp, namep, lineno); 566 return (1); 567 } 568 pushp->sap_lastminor = (minor_t)atol(wp); 569 570 /* 571 * Read the list of module names. 572 */ 573 npush = 0; 574 while ((npush < MAXAPUSH) && (*cp)) { 575 576 while (isspace(*cp)) 577 cp++; 578 579 if (strncasecmp(cp, AP_ANCHOR, sizeof (AP_ANCHOR) - 1) == 0) { 580 if (pushp->sap_anchor != 0) { 581 (void) fprintf(stderr, 582 gettext("%s: ERROR: File %s: more than " 583 "one anchor in line, line %d ignored\n"), 584 Cmdp, namep, lineno); 585 return (1); 586 } 587 if (npush == 0) 588 (void) fprintf(stderr, 589 gettext("%s: WARNING: File %s: anchor at " 590 "beginning of stream on line %d ignored\n"), 591 Cmdp, namep, lineno); 592 pushp->sap_anchor = npush; 593 cp += sizeof (AP_ANCHOR) - 1; 594 continue; 595 } 596 597 for (midx = 0; !isspace(*cp) && *cp; midx++) { 598 if (midx == FMNAMESZ) { 599 (void) fprintf(stderr, gettext("%s: ERROR: " 600 "File %s: module name too long, line %d " 601 "ignored\n"), Cmdp, namep, lineno); 602 return (1); 603 } 604 pushp->sap_list[npush][midx] = *cp++; 605 } 606 607 if (midx > 0) { 608 pushp->sap_list[npush][midx] = '\0'; 609 npush++; 610 } 611 } 612 pushp->sap_npush = npush; 613 614 /* 615 * We have everything we want from the line. 616 * Now make sure there is no extra garbage on the line. 617 */ 618 while (isspace(*cp)) 619 cp++; 620 if (*cp) { 621 (void) fprintf(stderr, 622 gettext("%s: ERROR: File %s: too many modules, line %d " 623 "ignored\n"), Cmdp, namep, lineno); 624 return (1); 625 } 626 return (0); 627 } 628