xref: /illumos-gate/usr/src/cmd/streams/strcmd/strchg.c (revision 42b53e0fbc5c05289c3d334bb864b784fafe5ce4)
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