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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ 27 28 /* 29 * Streams Command strchg: change the configuration of the 30 * stream associated with stdin. 31 * 32 * USAGE: strchg -h module1[,module2,module3 ...] 33 * or: strchg -p 34 * or: strchg -p -a 35 * or: strchg -p -u module 36 * or: strchg -f file 37 * 38 * -h pusHes the named module(s) onto the stdin stream 39 * -p poPs the topmost module from the stdin stream 40 * -p -a poPs All modules 41 * -p -u module poPs all modules Up to, but not including, the named module 42 * -f file reads a list of modules from the named File, pops all modules, 43 * then pushes the list of modules 44 * 45 * RETURNS: 46 * 0 SUCCESS it worked 47 * 1 ERR_USAGE bad invocation 48 * 2 ERR_MODULE bad module name(s) 49 * 3 ERR_STDIN an ioctl or stat on the stdin stream failed 50 * 4 ERR_MEM couldn't allocate memory 51 * 5 ERR_OPEN couldn't open file in -f opt 52 * 6 ERR_PERM not owner or superuser 53 * 54 */ 55 56 57 #include <stdio.h> 58 #include <sys/stropts.h> 59 #include <sys/termio.h> 60 #include <sys/types.h> 61 #include <sys/stat.h> 62 63 #define FALSE 0 64 #define TRUE 1 65 66 #define SUCCESS 0 67 #define FAILURE 1 68 69 #define NMODULES 16 /* "reasonable" # of modules to push */ 70 /* (can push more if you like) */ 71 #define MAXMODULES 2048 /* max # of modules to push */ 72 73 #define OPTLIST "af:h:pu:" 74 #define USAGE "Usage:\t%s -h module1[,module2 ... ]\n\t%s -f file\n\t%s -p [-a | -u module ]\n" 75 76 #define ERR_USAGE 1 /* bad invocation */ 77 #define ERR_MODULE 2 /* bad module name(s) or too many modules */ 78 #define ERR_STDIN 3 /* an ioctl or stat on stdin failed */ 79 #define ERR_MEM 4 /* couldn't allocate memory */ 80 #define ERR_OPEN 5 /* couldn't open file in -f opt */ 81 #define ERR_PERM 6 /* not owner or superuser */ 82 83 #define STDIN 0 84 #define CNULL (char *)NULL 85 #define SAME 0 /* return from str[n]cmp if match */ 86 87 static char *Cmd_namep; /* how was it invoked? */ 88 static struct str_mlist Oldmods[NMODULES]; /* modlist for Oldlist */ 89 static struct str_list Oldlist; /* original modules */ 90 91 extern char *strncpy(); 92 extern char *strtok(); 93 extern int getopt(); 94 extern unsigned short geteuid(); 95 extern int ioctl(); 96 extern int strncmp(); 97 extern void perror(); 98 99 static int pop_modules(); /* pop 'n' modules */ 100 static int push_module(); /* push a module */ 101 static int more_modules(); /* increase size of mod lists */ 102 static void restore(); /* restore state of stdin */ 103 104 main( argc, argv) 105 int argc; 106 char *argv[]; 107 { 108 char buf[BUFSIZ]; /* input buffer */ 109 char *file_namep; /* file from -f opt */ 110 char *modnamep; /* mods from -h or -u opt */ 111 char *modp; /* for walking thru modnamep */ 112 113 FILE *fp; /* file pointer for -f file */ 114 115 register int i; /* loop index and junk var */ 116 register int j; /* loop index and junk var */ 117 int euid; /* effective uid */ 118 119 short error; /* TRUE if usage error */ 120 short fromfile; /* TRUE if -f file */ 121 short is_a_tty; /* TRUE if TCGETA succeeds */ 122 short pop; /* TRUE if -p */ 123 short popall; /* TRUE if -p -a */ 124 short popupto; /* TRUE if -p -u module */ 125 short push; /* TRUE if -h mod1[,mod2 ...] */ 126 127 struct str_mlist 128 newmods[NMODULES];/* mod list for new list */ 129 struct stat stats; /* stream stats */ 130 struct str_list newlist; /* modules to be pushed */ 131 struct termio termio; /* save state of tty */ 132 133 extern char *optarg; /* for getopt() */ 134 extern int optind; /* for getopt() */ 135 136 /* 137 * init 138 */ 139 140 Cmd_namep = argv[0]; 141 error = fromfile = is_a_tty = pop = popall = popupto = push = FALSE; 142 Oldlist.sl_modlist = Oldmods; 143 Oldlist.sl_nmods = NMODULES; 144 newlist.sl_modlist = newmods; 145 newlist.sl_nmods = NMODULES; 146 147 /* 148 * only owner and root can change stream configuration 149 */ 150 if ( (euid = geteuid()) != 0 ) { 151 if ( fstat(0, &stats) < 0 ) { 152 perror("fstat"); 153 (void) fprintf(stderr, "%s: fstat of stdin failed\n", 154 Cmd_namep); 155 return(ERR_STDIN); 156 } 157 if ( euid != stats.st_uid ) { 158 (void) fprintf(stderr, 159 "%s: not owner of stdin\n", Cmd_namep); 160 return(ERR_PERM); 161 } 162 } 163 164 165 /* 166 * parse args 167 */ 168 169 if ( argc == 1 ) { 170 (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); 171 return(ERR_USAGE); 172 } 173 174 while ( !error && (i = getopt( argc, argv, OPTLIST)) != -1 ) { 175 176 switch (i) { 177 178 case 'a': /* pop All */ 179 if ( fromfile || popupto || push ) 180 error = TRUE; 181 else 182 popall = TRUE; 183 break; 184 185 case 'f': /* read from File*/ 186 if ( pop || push ) 187 error = TRUE; 188 else { 189 fromfile = TRUE; 190 file_namep = optarg; 191 } 192 break; 193 194 case 'h': /* pusH */ 195 if ( fromfile || pop ) 196 error = TRUE; 197 else { 198 push = TRUE; 199 modnamep = optarg; 200 } 201 break; 202 203 case 'p': /* poP */ 204 if ( fromfile || push ) 205 error = TRUE; 206 else 207 pop = TRUE; 208 break; 209 210 case 'u': /* pop Upto */ 211 if ( fromfile || popall || push ) 212 error = TRUE; 213 else { 214 popupto = TRUE; 215 modnamep = optarg; 216 } 217 break; 218 219 default: 220 (void) fprintf(stderr, 221 USAGE, Cmd_namep, Cmd_namep, Cmd_namep); 222 return(ERR_USAGE); 223 /*NOTREACHED*/ 224 } 225 } 226 227 if ( error || optind < argc ) { 228 (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); 229 return(ERR_USAGE); 230 } 231 232 if ( !pop && ( popall || popupto ) ) { 233 (void) fprintf(stderr, 234 "%s: -p option must be used with -a or -u to pop modules\n", 235 Cmd_namep); 236 (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); 237 return(ERR_USAGE); 238 } 239 240 241 /* 242 * Save state so can restore if something goes wrong 243 * (If are only going to push modules, don't need to 244 * save original module list for restore.) 245 */ 246 if ( fromfile || pop ) { 247 248 /* 249 * get number of modules on stream 250 * allocate more room if needed 251 */ 252 if ( (i = ioctl(STDIN, I_LIST, (struct str_list *)NULL)) 253 < 0 ) { 254 perror("I_LIST"); 255 (void) fprintf(stderr, 256 "%s: I_LIST ioctl failed\n", Cmd_namep); 257 return(ERR_STDIN); 258 } 259 if ( i > Oldlist.sl_nmods ) 260 if ( more_modules(&Oldlist, i) != SUCCESS ) 261 return(ERR_MEM); 262 263 /* 264 * get list of modules on stream 265 */ 266 Oldlist.sl_nmods = i; 267 if ( ioctl(STDIN, I_LIST, &Oldlist) < 0 ) { 268 perror("I_LIST"); 269 (void) fprintf(stderr, 270 "%s: I_LIST ioctl failed\n", Cmd_namep); 271 return(ERR_STDIN); 272 } 273 274 /* 275 * The following attempts to avoid leaving a 276 * terminal line that does not respond to anything 277 * if the strchg -h or -f options failed due to 278 * specifying invalid module names for pushing 279 */ 280 if (ioctl(STDIN, TCGETA, &termio) >= 0 ) 281 is_a_tty = TRUE; 282 } 283 284 285 /* 286 * push modules on stream 287 */ 288 if ( push ) { 289 /* 290 * pull mod names out of comma-separated list 291 */ 292 for ( i = 0, modp = strtok(modnamep, ","); 293 modp != CNULL; ++i, modp = strtok(CNULL, ",") ) { 294 if ( push_module(modp) == FAILURE) { 295 /* pop the 'i' modules we just added */ 296 restore(i, 0); 297 return(ERR_STDIN); 298 } 299 } 300 return(SUCCESS); 301 } 302 303 /* 304 * read configuration from a file 305 */ 306 if ( fromfile ) { 307 308 if ( (fp = fopen(file_namep, "r")) == (FILE *)NULL ) { 309 perror("fopen"); 310 (void) fprintf(stderr, 311 "%s: could not open file '%s'\n", 312 Cmd_namep, file_namep); 313 return(ERR_OPEN); 314 } 315 316 /* 317 * read file and construct a new strlist 318 */ 319 i = 0; 320 while ( fgets(buf, BUFSIZ, fp) != CNULL ) { 321 322 if ( buf[0] == '#' ) 323 continue; /* skip comments */ 324 325 /* 326 * skip trailing newline, trailing and leading 327 * whitespace 328 */ 329 if ( (modp = strtok(buf, " \t\n")) == CNULL ) 330 continue; /* blank line */ 331 332 (void)strncpy(newlist.sl_modlist[i].l_name, 333 modp, FMNAMESZ); 334 ++i; 335 if ( (modp = strtok(CNULL, " \t\n")) != CNULL ) { 336 /* 337 * bad format 338 * should only be one name per line 339 */ 340 (void) fprintf(stderr, 341 "%s: error on line %d in file %s: multiple module names??\n", 342 Cmd_namep, i, file_namep); 343 return(ERR_MODULE); 344 } 345 if ( i > newlist.sl_nmods ) 346 if ( more_modules(&newlist, i) != SUCCESS ) 347 return(ERR_MEM); 348 } 349 newlist.sl_nmods = i; 350 351 /* 352 * If an empty file, exit silently 353 */ 354 if ( i == 0 ) 355 return(SUCCESS); 356 357 /* 358 * Pop all modules currently on the stream. 359 */ 360 361 if ( (i = pop_modules(Oldlist.sl_nmods - 1)) 362 != (Oldlist.sl_nmods - 1) ) { 363 /* put back whatever we've popped */ 364 restore(0, i); 365 return(ERR_STDIN); 366 } 367 368 /* 369 * Push new modules 370 */ 371 for ( i = newlist.sl_nmods - 1; i >= 0; --i ) { 372 373 if ( push_module(newlist.sl_modlist[i].l_name) 374 == FAILURE ) { 375 376 /* 377 * pop whatever new modules we've pushed 378 * then push old module list back on 379 */ 380 restore((newlist.sl_nmods - 1 - i), 381 (Oldlist.sl_nmods - 1)); 382 383 /* 384 * If the stream is a tty line, at least try 385 * to set the state to what it was before. 386 */ 387 if ( is_a_tty ) { 388 if ( ioctl(STDIN, TCSETA, &termio) < 0 ) { 389 perror("TCSETA"); 390 (void) fprintf(stderr, 391 "%s: WARNING: Could not restore the states of the terminal line discipline\n", 392 Cmd_namep); 393 } 394 } 395 return(ERR_STDIN); 396 } 397 } 398 return(SUCCESS); 399 } /* end if-fromfile */ 400 401 402 /* 403 * pop all modules (except driver) 404 */ 405 if ( popall ) { 406 if ( Oldlist.sl_nmods > 1 ) { 407 if ( (i = pop_modules(Oldlist.sl_nmods - 1)) 408 != (Oldlist.sl_nmods - 1) ) { 409 restore(0, i); 410 return(ERR_STDIN); 411 } 412 } 413 return(SUCCESS); 414 } 415 416 /* 417 * pop up to (but not including) a module 418 */ 419 if ( popupto ) { 420 /* 421 * check that the module is in fact on the stream 422 */ 423 for ( i = 0; i < Oldlist.sl_nmods; ++i ) 424 if ( strncmp(Oldlist.sl_modlist[i].l_name, modnamep, 425 FMNAMESZ) == SAME ) 426 break; 427 if ( i == Oldlist.sl_nmods ) { 428 /* no match found */ 429 (void) fprintf(stderr, "%s: %s not found on stream\n", 430 Cmd_namep, modnamep); 431 return(ERR_MODULE); 432 } 433 434 if ( (j = pop_modules(i)) != i ) { 435 /* put back whatever we've popped */ 436 restore(0, j); 437 return(ERR_STDIN); 438 } 439 return(SUCCESS); 440 } 441 442 /* 443 * pop the topmost module 444 */ 445 if ( pop ) { 446 if ( Oldlist.sl_nmods > 1 ) 447 if ( pop_modules(1) != 1 ) 448 /* no need to restore */ 449 return(ERR_STDIN); 450 return(SUCCESS); 451 } 452 453 /*NOTREACHED*/ 454 } 455 456 /* 457 * pop_module(n) pop 'n' modules from stream 458 * 459 * returns # of modules popped 460 */ 461 static int 462 pop_modules(num_modules) 463 int num_modules; 464 { 465 466 register short i; /* the ubiquitous loop variable */ 467 468 for ( i = 0; i < num_modules; i++ ) { 469 if ( ioctl(STDIN, I_POP, 0) < 0 ) { 470 perror("I_POP"); 471 (void) fprintf(stderr, 472 "%s: I_POP ioctl failed\n", Cmd_namep); 473 return(i); 474 } 475 } 476 return(i); 477 } 478 479 /* 480 * push_module(modnamep) pushes 'modnamep' module on stream 481 * 482 * returns SUCCESS or FAILURE 483 */ 484 static int 485 push_module(modnamep) 486 char *modnamep; 487 { 488 if ( ioctl(STDIN, I_PUSH, modnamep) < 0 ) { 489 perror("I_PUSH"); 490 (void) fprintf(stderr, 491 "%s: I_PUSH ioctl of %s failed\n", Cmd_namep, modnamep); 492 return(FAILURE); 493 } 494 return (SUCCESS); 495 } 496 497 498 /* 499 * restore(npop, npush) restore original state of stream 500 * 501 * pops 'npop' modules, then pushes the topmost 'npush' modules from 502 * Oldlist 503 * 504 */ 505 static void 506 restore(npop, npush) 507 int npop; 508 int npush; 509 { 510 register int i; 511 512 if ( (i = pop_modules(npop)) != npop ) { 513 (void) fprintf(stderr, 514 "%s: WARNING: could not restore state of stream\n", Cmd_namep); 515 return; 516 } 517 if ( npush >= Oldlist.sl_nmods ) { /* "cannot" happen */ 518 (void) fprintf(stderr, 519 "%s: internal logic error in restore\n", Cmd_namep); 520 (void) fprintf(stderr, 521 "%s: WARNING: could not restore state of stream\n", Cmd_namep); 522 return; 523 } 524 for ( i = npush - 1; i >= 0; --i ) { 525 if ( push_module(Oldlist.sl_modlist[i].l_name) == FAILURE ) { 526 (void) fprintf(stderr, 527 "%s: WARNING: could not restore state of stream\n", 528 Cmd_namep); 529 return; 530 } 531 } 532 return; 533 } 534 535 /* 536 * more_modules(listp, n) allocate space for 'n' modules in 'listp' 537 * 538 * returns: SUCCESS or FAILURE 539 */ 540 541 static int 542 more_modules(listp, n) 543 struct str_list *listp; /* streams module list */ 544 int n; /* # of modules */ 545 { 546 register int i; 547 register struct str_mlist *modp; 548 549 extern char *calloc(); 550 551 if ( n > MAXMODULES ) { 552 (void) fprintf(stderr, 553 "%s: too many modules (%d) -- max is %d\n", 554 Cmd_namep, n, MAXMODULES); 555 return(FAILURE); 556 } 557 558 if ( (modp = (struct str_mlist *)calloc((unsigned)n, 559 (unsigned)sizeof(struct str_mlist))) == (struct str_mlist *)NULL ) { 560 perror("calloc"); 561 (void) fprintf(stderr, 562 "%s: failed to allocate space for module list\n", 563 Cmd_namep); 564 return(FAILURE); 565 } 566 567 for ( i = 0; i < listp->sl_nmods; ++i ) 568 (void) strncpy(modp[i].l_name, listp->sl_modlist[i].l_name, 569 FMNAMESZ); 570 listp->sl_nmods = n; 571 listp->sl_modlist = modp; 572 return(SUCCESS); 573 } 574