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