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