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