/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ /* * Streams Command strchg: change the configuration of the * stream associated with stdin. * * USAGE: strchg -h module1[,module2,module3 ...] * or: strchg -p * or: strchg -p -a * or: strchg -p -u module * or: strchg -f file * * -h pusHes the named module(s) onto the stdin stream * -p poPs the topmost module from the stdin stream * -p -a poPs All modules * -p -u module poPs all modules Up to, but not including, the named module * -f file reads a list of modules from the named File, pops all modules, * then pushes the list of modules * * RETURNS: * 0 SUCCESS it worked * 1 ERR_USAGE bad invocation * 2 ERR_MODULE bad module name(s) * 3 ERR_STDIN an ioctl or stat on the stdin stream failed * 4 ERR_MEM couldn't allocate memory * 5 ERR_OPEN couldn't open file in -f opt * 6 ERR_PERM not owner or superuser * */ #include #include #include #include #include #define FALSE 0 #define TRUE 1 #define SUCCESS 0 #define FAILURE 1 #define NMODULES 16 /* "reasonable" # of modules to push */ /* (can push more if you like) */ #define MAXMODULES 2048 /* max # of modules to push */ #define OPTLIST "af:h:pu:" #define USAGE "Usage:\t%s -h module1[,module2 ... ]\n\t%s -f file\n\t%s -p [-a | -u module ]\n" #define ERR_USAGE 1 /* bad invocation */ #define ERR_MODULE 2 /* bad module name(s) or too many modules */ #define ERR_STDIN 3 /* an ioctl or stat on stdin failed */ #define ERR_MEM 4 /* couldn't allocate memory */ #define ERR_OPEN 5 /* couldn't open file in -f opt */ #define ERR_PERM 6 /* not owner or superuser */ #define STDIN 0 #define CNULL (char *)NULL #define SAME 0 /* return from str[n]cmp if match */ static char *Cmd_namep; /* how was it invoked? */ static struct str_mlist Oldmods[NMODULES]; /* modlist for Oldlist */ static struct str_list Oldlist; /* original modules */ extern char *strncpy(); extern char *strtok(); extern int getopt(); extern unsigned short geteuid(); extern int ioctl(); extern int strncmp(); extern void perror(); static int pop_modules(); /* pop 'n' modules */ static int push_module(); /* push a module */ static int more_modules(); /* increase size of mod lists */ static void restore(); /* restore state of stdin */ main( argc, argv) int argc; char *argv[]; { char buf[BUFSIZ]; /* input buffer */ char *file_namep; /* file from -f opt */ char *modnamep; /* mods from -h or -u opt */ char *modp; /* for walking thru modnamep */ FILE *fp; /* file pointer for -f file */ register int i; /* loop index and junk var */ register int j; /* loop index and junk var */ int euid; /* effective uid */ short error; /* TRUE if usage error */ short fromfile; /* TRUE if -f file */ short is_a_tty; /* TRUE if TCGETA succeeds */ short pop; /* TRUE if -p */ short popall; /* TRUE if -p -a */ short popupto; /* TRUE if -p -u module */ short push; /* TRUE if -h mod1[,mod2 ...] */ struct str_mlist newmods[NMODULES];/* mod list for new list */ struct stat stats; /* stream stats */ struct str_list newlist; /* modules to be pushed */ struct termio termio; /* save state of tty */ extern char *optarg; /* for getopt() */ extern int optind; /* for getopt() */ /* * init */ Cmd_namep = argv[0]; error = fromfile = is_a_tty = pop = popall = popupto = push = FALSE; Oldlist.sl_modlist = Oldmods; Oldlist.sl_nmods = NMODULES; newlist.sl_modlist = newmods; newlist.sl_nmods = NMODULES; /* * only owner and root can change stream configuration */ if ( (euid = geteuid()) != 0 ) { if ( fstat(0, &stats) < 0 ) { perror("fstat"); (void) fprintf(stderr, "%s: fstat of stdin failed\n", Cmd_namep); return(ERR_STDIN); } if ( euid != stats.st_uid ) { (void) fprintf(stderr, "%s: not owner of stdin\n", Cmd_namep); return(ERR_PERM); } } /* * parse args */ if ( argc == 1 ) { (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return(ERR_USAGE); } while ( !error && (i = getopt( argc, argv, OPTLIST)) != -1 ) { switch (i) { case 'a': /* pop All */ if ( fromfile || popupto || push ) error = TRUE; else popall = TRUE; break; case 'f': /* read from File*/ if ( pop || push ) error = TRUE; else { fromfile = TRUE; file_namep = optarg; } break; case 'h': /* pusH */ if ( fromfile || pop ) error = TRUE; else { push = TRUE; modnamep = optarg; } break; case 'p': /* poP */ if ( fromfile || push ) error = TRUE; else pop = TRUE; break; case 'u': /* pop Upto */ if ( fromfile || popall || push ) error = TRUE; else { popupto = TRUE; modnamep = optarg; } break; default: (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return(ERR_USAGE); /*NOTREACHED*/ } } if ( error || optind < argc ) { (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return(ERR_USAGE); } if ( !pop && ( popall || popupto ) ) { (void) fprintf(stderr, "%s: -p option must be used with -a or -u to pop modules\n", Cmd_namep); (void) fprintf(stderr, USAGE, Cmd_namep, Cmd_namep, Cmd_namep); return(ERR_USAGE); } /* * Save state so can restore if something goes wrong * (If are only going to push modules, don't need to * save original module list for restore.) */ if ( fromfile || pop ) { /* * get number of modules on stream * allocate more room if needed */ if ( (i = ioctl(STDIN, I_LIST, (struct str_list *)NULL)) < 0 ) { perror("I_LIST"); (void) fprintf(stderr, "%s: I_LIST ioctl failed\n", Cmd_namep); return(ERR_STDIN); } if ( i > Oldlist.sl_nmods ) if ( more_modules(&Oldlist, i) != SUCCESS ) return(ERR_MEM); /* * get list of modules on stream */ Oldlist.sl_nmods = i; if ( ioctl(STDIN, I_LIST, &Oldlist) < 0 ) { perror("I_LIST"); (void) fprintf(stderr, "%s: I_LIST ioctl failed\n", Cmd_namep); return(ERR_STDIN); } /* * The following attempts to avoid leaving a * terminal line that does not respond to anything * if the strchg -h or -f options failed due to * specifying invalid module names for pushing */ if (ioctl(STDIN, TCGETA, &termio) >= 0 ) is_a_tty = TRUE; } /* * push modules on stream */ if ( push ) { /* * pull mod names out of comma-separated list */ for ( i = 0, modp = strtok(modnamep, ","); modp != CNULL; ++i, modp = strtok(CNULL, ",") ) { if ( push_module(modp) == FAILURE) { /* pop the 'i' modules we just added */ restore(i, 0); return(ERR_STDIN); } } return(SUCCESS); } /* * read configuration from a file */ if ( fromfile ) { if ( (fp = fopen(file_namep, "r")) == (FILE *)NULL ) { perror("fopen"); (void) fprintf(stderr, "%s: could not open file '%s'\n", Cmd_namep, file_namep); return(ERR_OPEN); } /* * read file and construct a new strlist */ i = 0; while ( fgets(buf, BUFSIZ, fp) != CNULL ) { if ( buf[0] == '#' ) continue; /* skip comments */ /* * skip trailing newline, trailing and leading * whitespace */ if ( (modp = strtok(buf, " \t\n")) == CNULL ) continue; /* blank line */ (void)strncpy(newlist.sl_modlist[i].l_name, modp, FMNAMESZ); ++i; if ( (modp = strtok(CNULL, " \t\n")) != CNULL ) { /* * bad format * should only be one name per line */ (void) fprintf(stderr, "%s: error on line %d in file %s: multiple module names??\n", Cmd_namep, i, file_namep); return(ERR_MODULE); } if ( i > newlist.sl_nmods ) if ( more_modules(&newlist, i) != SUCCESS ) return(ERR_MEM); } newlist.sl_nmods = i; /* * If an empty file, exit silently */ if ( i == 0 ) return(SUCCESS); /* * Pop all modules currently on the stream. */ if ( (i = pop_modules(Oldlist.sl_nmods - 1)) != (Oldlist.sl_nmods - 1) ) { /* put back whatever we've popped */ restore(0, i); return(ERR_STDIN); } /* * Push new modules */ for ( i = newlist.sl_nmods - 1; i >= 0; --i ) { if ( push_module(newlist.sl_modlist[i].l_name) == FAILURE ) { /* * pop whatever new modules we've pushed * then push old module list back on */ restore((newlist.sl_nmods - 1 - i), (Oldlist.sl_nmods - 1)); /* * If the stream is a tty line, at least try * to set the state to what it was before. */ if ( is_a_tty ) { if ( ioctl(STDIN, TCSETA, &termio) < 0 ) { perror("TCSETA"); (void) fprintf(stderr, "%s: WARNING: Could not restore the states of the terminal line discipline\n", Cmd_namep); } } return(ERR_STDIN); } } return(SUCCESS); } /* end if-fromfile */ /* * pop all modules (except driver) */ if ( popall ) { if ( Oldlist.sl_nmods > 1 ) { if ( (i = pop_modules(Oldlist.sl_nmods - 1)) != (Oldlist.sl_nmods - 1) ) { restore(0, i); return(ERR_STDIN); } } return(SUCCESS); } /* * pop up to (but not including) a module */ if ( popupto ) { /* * check that the module is in fact on the stream */ for ( i = 0; i < Oldlist.sl_nmods; ++i ) if ( strncmp(Oldlist.sl_modlist[i].l_name, modnamep, FMNAMESZ) == SAME ) break; if ( i == Oldlist.sl_nmods ) { /* no match found */ (void) fprintf(stderr, "%s: %s not found on stream\n", Cmd_namep, modnamep); return(ERR_MODULE); } if ( (j = pop_modules(i)) != i ) { /* put back whatever we've popped */ restore(0, j); return(ERR_STDIN); } return(SUCCESS); } /* * pop the topmost module */ if ( pop ) { if ( Oldlist.sl_nmods > 1 ) if ( pop_modules(1) != 1 ) /* no need to restore */ return(ERR_STDIN); return(SUCCESS); } /*NOTREACHED*/ } /* * pop_module(n) pop 'n' modules from stream * * returns # of modules popped */ static int pop_modules(num_modules) int num_modules; { register short i; /* the ubiquitous loop variable */ for ( i = 0; i < num_modules; i++ ) { if ( ioctl(STDIN, I_POP, 0) < 0 ) { perror("I_POP"); (void) fprintf(stderr, "%s: I_POP ioctl failed\n", Cmd_namep); return(i); } } return(i); } /* * push_module(modnamep) pushes 'modnamep' module on stream * * returns SUCCESS or FAILURE */ static int push_module(modnamep) char *modnamep; { if ( ioctl(STDIN, I_PUSH, modnamep) < 0 ) { perror("I_PUSH"); (void) fprintf(stderr, "%s: I_PUSH ioctl of %s failed\n", Cmd_namep, modnamep); return(FAILURE); } return (SUCCESS); } /* * restore(npop, npush) restore original state of stream * * pops 'npop' modules, then pushes the topmost 'npush' modules from * Oldlist * */ static void restore(npop, npush) int npop; int npush; { register int i; if ( (i = pop_modules(npop)) != npop ) { (void) fprintf(stderr, "%s: WARNING: could not restore state of stream\n", Cmd_namep); return; } if ( npush >= Oldlist.sl_nmods ) { /* "cannot" happen */ (void) fprintf(stderr, "%s: internal logic error in restore\n", Cmd_namep); (void) fprintf(stderr, "%s: WARNING: could not restore state of stream\n", Cmd_namep); return; } for ( i = npush - 1; i >= 0; --i ) { if ( push_module(Oldlist.sl_modlist[i].l_name) == FAILURE ) { (void) fprintf(stderr, "%s: WARNING: could not restore state of stream\n", Cmd_namep); return; } } return; } /* * more_modules(listp, n) allocate space for 'n' modules in 'listp' * * returns: SUCCESS or FAILURE */ static int more_modules(listp, n) struct str_list *listp; /* streams module list */ int n; /* # of modules */ { register int i; register struct str_mlist *modp; extern char *calloc(); if ( n > MAXMODULES ) { (void) fprintf(stderr, "%s: too many modules (%d) -- max is %d\n", Cmd_namep, n, MAXMODULES); return(FAILURE); } if ( (modp = (struct str_mlist *)calloc((unsigned)n, (unsigned)sizeof(struct str_mlist))) == (struct str_mlist *)NULL ) { perror("calloc"); (void) fprintf(stderr, "%s: failed to allocate space for module list\n", Cmd_namep); return(FAILURE); } for ( i = 0; i < listp->sl_nmods; ++i ) (void) strncpy(modp[i].l_name, listp->sl_modlist[i].l_name, FMNAMESZ); listp->sl_nmods = n; listp->sl_modlist = modp; return(SUCCESS); }