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