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