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
main(int argc,char ** argv)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
pop_modules(int num_modules)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
push_module(const char * modnamep)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
restore(int npop,int npush)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
more_modules(struct str_list * listp,int n)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