xref: /illumos-gate/usr/src/cmd/ttymon/tmchild.c (revision 3db86aab)
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  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include	<stdio.h>
34 #include	<stdlib.h>
35 #include	<fcntl.h>
36 #include	<errno.h>
37 #include	<sys/types.h>
38 #include	<termio.h>
39 #include	<string.h>
40 #include	<signal.h>
41 #include	<poll.h>
42 #include	<unistd.h>
43 #include 	"sys/stropts.h"
44 #include 	<sys/resource.h>
45 #include	"sac.h"
46 #include	"ttymon.h"
47 #include	"tmstruct.h"
48 #include	"tmextern.h"
49 #ifdef	SYS_NAME
50 #include	<sys/utsname.h>
51 #endif
52 
53 static void openline();
54 static void invoke_service();
55 static char	*do_autobaud();
56 static	struct	Gdef	*next_speed();
57 static int check_hup();
58 
59 extern	struct	Gdef	*get_speed();
60 extern struct strbuf *peek_ptr, *do_peek();
61 
62 /*
63  * tmchild	- process that handles peeking data, determine baud rate
64  *		  and invoke service on each individual port.
65  *
66  */
67 void
68 tmchild(pmtab)
69 struct	pmtab	*pmtab;
70 {
71 	register struct Gdef *speedef;
72 	char	*auto_speed = "";
73 	struct	sigaction sigact;
74 
75 #ifdef	DEBUG
76 	debug("in tmchild");
77 #endif
78 	peek_ptr = NULL;
79 	if (pmtab->p_status != GETTY) {
80 		child_sigcatch();
81 		(void)close(PCpipe[0]);	  /* close parent end of the pipe */
82 		if (ioctl(PCpipe[1], I_SETSIG, S_HANGUP) == -1) {
83 			log("I_SETSIG failed: %s", strerror(errno));
84 			exit(1);
85 		}
86 		/*
87 		 * the following check is to make sure no hangup
88 		 * happens before registering for SIGPOLL
89 		 */
90 		if (check_hup(PCpipe[1])) {
91 #ifdef	DEBUG
92 			debug("PCpipe hungup, tmchild exiting");
93 #endif
94 			exit(1);
95 		}
96 
97 		if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) {
98 			if (pmtab->p_fd > 0) {
99 				(void)close(pmtab->p_fd);
100 				pmtab->p_fd = 0;
101 			}
102 		}
103 
104 		/*
105 		 * become the session leader so that a controlling tty
106 		 * will be allocated.
107 		 */
108 		(void)setsid();
109 	}
110 	speedef = get_speed(pmtab->p_ttylabel);
111 	openline(pmtab, speedef);
112 	if (pmtab->p_ttyflags & (C_FLAG|B_FLAG)) {
113 	    if (pmtab->p_fd >= 0) {
114 		if ((pmtab->p_modules != NULL)&&(*(pmtab->p_modules) != '\0')) {
115 		    if (push_linedisc(pmtab->p_fd, pmtab->p_modules,pmtab->p_device) == -1) {
116 			(void)close(pmtab->p_fd);
117 			return;
118 		    }
119 		}
120 	    }
121 	}
122 	if ((pmtab->p_ttyflags & C_FLAG) &&
123 	    (State != PM_DISABLED) &&
124 	    (!(pmtab->p_flags & X_FLAG))) {
125 		/*
126 		 * if "c" flag is set, and the port is not disabled
127 		 * invoke service immediately
128 		 */
129 		if (set_termio(0,speedef->g_fflags,NULL,FALSE,CANON) == -1) {
130 			log("set final termio failed");
131 			exit(1);
132 		}
133 		invoke_service(pmtab);
134 		exit(1);	/*NOTREACHED*/
135 	}
136 	if (speedef->g_autobaud & A_FLAG) {
137 		auto_speed = do_autobaud(pmtab,speedef);
138 	}
139 	if (set_termio(0,speedef->g_fflags,NULL,FALSE,CANON) == -1) {
140 		log("set final termio failed");
141 		exit(1);
142 	}
143 	if ( (pmtab->p_ttyflags & (R_FLAG|A_FLAG)) ||
144 		(pmtab->p_status == GETTY) || (pmtab->p_timeout > 0) ) {
145 		write_prompt(1,pmtab,TRUE,TRUE);
146 		if(pmtab->p_timeout) {
147 			sigact.sa_flags = 0;
148 			sigact.sa_handler = timedout;
149 			(void)sigemptyset(&sigact.sa_mask);
150 			(void)sigaction(SIGALRM, &sigact, NULL);
151 			(void)alarm((unsigned)pmtab->p_timeout);
152 		}
153 	}
154 	else if ((pmtab->p_ttyflags & (B_FLAG)))
155 			write_prompt(pmtab->p_fd,pmtab,TRUE,TRUE);
156 
157 
158 	/* Loop until user is successful in invoking service. */
159 	for(;;) {
160 
161 		/* Peek the user's typed response and respond appropriately. */
162 		switch(poll_data()) {
163 		case GOODNAME:
164 #ifdef	DEBUG
165 			debug("got GOODNAME");
166 #endif
167 			if (pmtab->p_timeout) {
168 				(void)alarm((unsigned)0);
169 				sigact.sa_flags = 0;
170 				sigact.sa_handler = SIG_DFL;
171 				(void)sigemptyset(&sigact.sa_mask);
172 				(void)sigaction(SIGALRM, &sigact, NULL);
173 			}
174 			if ((State == PM_DISABLED)||(pmtab->p_flags & X_FLAG)){
175 				write_prompt(1,pmtab,TRUE,FALSE);
176 				break;
177 			}
178 			if (set_termio(0,speedef->g_fflags,auto_speed,
179 				FALSE,CANON)==-1) {
180 				log("set final termio failed");
181 				exit(1);
182 			}
183 			invoke_service(pmtab);
184 			exit(1);	/*NOTREACHED*/
185 
186 		case BADSPEED:
187 			/* wrong speed! try next speed in the list. */
188 			speedef = next_speed(speedef);
189 #ifdef	DEBUG
190 			debug("BADSPEED: setup next speed");
191 #endif
192 			if (speedef->g_autobaud & A_FLAG) {
193 				if (auto_termio(0) == -1) {
194 					exit(1);
195 				}
196 				auto_speed = do_autobaud(pmtab,speedef);
197 			}
198 			else {
199 				auto_speed = NULL;
200 				/*
201 				 * this reset may fail if the speed is not
202 				 * supported by the system
203 				 * we just cycle through it to the next one
204 				 */
205 				if (set_termio(0,speedef->g_iflags,NULL,
206 						FALSE,CANON) != 0) {
207 					log("Warning -- speed of <%s> may "
208 					    "be not supported by the system",
209 					    speedef->g_id);
210 				}
211 			}
212 			write_prompt(1,pmtab,TRUE,TRUE);
213 			break;
214 
215 		case NONAME:
216 #ifdef	DEBUG
217 			debug("got NONAME");
218 #endif
219 			write_prompt(1,pmtab,FALSE,FALSE);
220 			break;
221 
222 		}  /* end switch */
223 
224 		peek_ptr = NULL;
225 		if(pmtab->p_timeout) {
226 			sigact.sa_flags = 0;
227 			sigact.sa_handler = timedout;
228 			(void)sigemptyset(&sigact.sa_mask);
229 			(void)sigaction(SIGALRM, &sigact, NULL);
230 			(void)alarm((unsigned)pmtab->p_timeout);
231 		}
232 	} /* end for loop */
233 }
234 
235 static void
236 openline(pmtab, speedef)
237 struct	pmtab 	*pmtab;
238 struct Gdef *speedef;
239 {
240 	char	 buffer[5];
241 	int	 rtn = 0;
242 	int	 line_count;
243 
244 #ifdef	DEBUG
245 	debug("in openline");
246 #endif
247 	if (pmtab->p_status != GETTY) {
248 		(void)close(0);
249 		/* open should return fd 0, if not, then close it */
250 		if ((pmtab->p_fd = open(pmtab->p_device, O_RDWR)) != 0) {
251 			log("open \"%s\" failed: %s", pmtab->p_device,
252 			    strerror(errno));
253 			exit(1);
254 		}
255 	}
256 	(void)close(1);
257 	(void)close(2);
258 	(void)dup(0);
259 	(void)dup(0);
260 
261 	if (pmtab->p_ttyflags & R_FLAG) { /* wait_read is needed */
262 		if (pmtab->p_count) {
263 			if ( peek_ptr != NULL )
264 				if ((peek_ptr->buf[0]&0x7F)=='\n' ||
265 			     	    (peek_ptr->buf[0]&0x7F)=='\r')
266 					pmtab->p_count--;
267 
268 			/*
269 			 * - wait for "p_count" lines
270 			 * - datakit switch does not
271 			 *   know you are a host or a terminal
272 			 * - so it send you several lines of msg
273 			 * - we need to swallow that msg
274 			 * - we assume the baud rate is correct
275 			 * - if it is not, '\n' will not look like '\n'
276  			 *   and we will wait forever here
277 			 */
278 			if (set_termio(0,speedef->g_fflags,NULL,TRUE,CANON) == -1) {
279 				log("set final termio failed");
280 				exit(1);
281 			}
282 			for (line_count=0;line_count < pmtab->p_count;) {
283 				if ( read(0, buffer, 1) < 0
284 				     || *buffer == '\0'
285 				     || *buffer == '\004') {
286 					(void)close(0);
287 					exit(0);
288 				}
289 				if (*buffer == '\n')
290 					line_count++;
291 			}
292 		}
293 		else { /* wait for 1 char */
294 			if ( peek_ptr == NULL ) {
295 				if (set_termio(0,NULL,NULL,TRUE,RAW) == -1) {
296 					log("set termio RAW failed");
297 					exit(1);
298 				}
299 				rtn = read(0, buffer, 1);
300 			} else
301 				*buffer = (peek_ptr->buf[0]&0x7F);
302 
303 			/*
304 			 * NOTE: Cu on a direct line when ~. is encountered will
305 			 * send EOTs to the other side.  EOT=\004
306 			 */
307 			if ( rtn < 0 || *buffer == '\004') {
308 				(void)close(0);
309 				exit(0);
310 			}
311 		}
312 		peek_ptr = NULL;
313 		if (!(pmtab->p_ttyflags & A_FLAG)) { /* autobaud not enabled */
314 			if (set_termio(0,speedef->g_fflags,NULL,TRUE,CANON) == -1) {
315 				log("set final termio failed");
316 				exit(1);
317 			}
318 		}
319 	}
320 	if (pmtab->p_ttyflags & B_FLAG) { /* port is bi-directional */
321 		/* set advisory lock on the line */
322 		if (tm_lock(0) != 0) {
323 			/*
324 			 * device is locked
325 			 * child exits and let the parent wait for
326 			 * the lock to go away
327 			 */
328 			exit(0);
329 		}
330 		/* change ownership back to root */
331 		(void)fchown(0, ROOTUID, Tty_gid);
332 		(void)fchmod(0, 0620);
333 	}
334 	return;
335 }
336 
337 /*
338  *	write_prompt	- write the msg to fd
339  *			- if flush is set, flush input queue
340  *			- if clear is set, write a new line
341  */
342 void
343 write_prompt(fd,pmtab,flush,clear)
344 int	fd;
345 struct	pmtab	*pmtab;
346 int	flush, clear;
347 {
348 
349 #ifdef DEBUG
350 	debug("in write_prompt");
351 #endif
352 	if (flush)
353 		flush_input(fd);
354 	if (clear) {
355 		(void)write(fd,"\r\n",2);
356 	}
357 #ifdef SYS_NAME
358 	sys_name(fd);
359 #endif
360 	/* Print prompt/disable message. */
361 	if ((State == PM_DISABLED)||(pmtab->p_flags & X_FLAG))
362 		(void)write(fd, pmtab->p_dmsg, (unsigned)strlen(pmtab->p_dmsg));
363 	else
364 		(void)write(fd, pmtab->p_prompt,
365 			(unsigned)strlen(pmtab->p_prompt));
366 }
367 
368 /*
369  *	timedout	- input period timed out
370  */
371 void
372 timedout()
373 {
374 	exit(1);
375 }
376 
377 #ifdef SYS_NAME
378 /*
379  * void sys_name() - generate a msg with system id
380  *		   - print out /etc/issue file if it exists
381  */
382 void
383 sys_name(fd)
384 int	fd;
385 {
386 	char	*ptr, buffer[BUFSIZ];
387 	FILE	*fp;
388 
389 #if 0	/* 1111333 - don't print node name, we already do this elsewhere */
390 	struct	utsname utsname;
391 
392 	if (uname(&utsname) != FAILURE) {
393 		(void)sprintf(buffer,"%.9s\r\n", utsname.nodename);
394 		(void) write(fd,buffer,strlen(buffer));
395 	}
396 #endif
397 
398 	if ((fp = fopen(ISSUEFILE,"r")) != NULL) {
399 		while ((ptr = fgets(buffer,sizeof(buffer),fp)) != NULL) {
400 			(void)write(fd,ptr,strlen(ptr));
401 		}
402 		(void)fclose(fp);
403 	}
404 }
405 #endif
406 
407 
408 /*
409  *	do_autobaud	- do autobaud
410  *			- if it succeed, set the new speed and return
411  *			- if it failed, it will get the nextlabel
412  *			- if next entry is also autobaud,
413  *			  it will loop back to do autobaud again
414  *			- otherwise, it will set new termio and return
415  */
416 static	char	*
417 do_autobaud(pmtab,speedef)
418 struct	pmtab	*pmtab;
419 struct	Gdef	*speedef;
420 {
421 	int	done = FALSE;
422 	char	*auto_speed;
423 #ifdef	DEBUG
424 	debug("in do_autobaud");
425 #endif
426 	while (!done) {
427 		if ((auto_speed = autobaud(0,pmtab->p_timeout)) == NULL) {
428 			speedef = next_speed(speedef);
429 			if (speedef->g_autobaud & A_FLAG) {
430 				continue;
431 			}
432 			else {
433 				if (set_termio(0,speedef->g_iflags,NULL,
434 						TRUE,CANON) != 0) {
435 					exit(1);
436 				}
437 				done = TRUE;
438 			}
439 		}
440 		else {
441 			if (set_termio(0,speedef->g_fflags,auto_speed,
442 					TRUE,CANON) != 0) {
443 				exit(1);
444 			}
445 			done = TRUE;
446 		}
447 	}
448 #ifdef	DEBUG
449 	debug("autobaud done");
450 #endif
451 	return(auto_speed);
452 }
453 
454 /*
455  * 	next_speed(speedef)
456  *	- find the next entry according to nextlabel. If "nextlabel"
457  *	  is not valid, go back to the old ttylabel.
458  */
459 
460 static	struct	Gdef *
461 next_speed(speedef)
462 struct	Gdef *speedef;
463 {
464 	struct	Gdef *sp;
465 
466 	if (strcmp(speedef->g_nextid,speedef->g_id) == 0)
467 		return(speedef);
468 	if ((sp = find_def(speedef->g_nextid)) == NULL) {
469 		log("%s's next speed-label (%s) is bad.", speedef->g_id,
470 		    speedef->g_nextid);
471 
472 		/* go back to the original entry. */
473 		if((sp = find_def(speedef->g_id)) == NULL) {
474 			 /* if failed, complain and quit. */
475 			log("unable to find (%s) again", speedef->g_id);
476 			exit(1);
477 		}
478 	}
479 	return(sp);
480 }
481 
482 /*
483  * inform_parent()	- inform ttymon that tmchild is going to exec service
484  */
485 static	void
486 inform_parent(fd)
487 int	fd;
488 {
489 	pid_t	pid;
490 
491 	pid = getpid();
492 	(void)write(fd, &pid, sizeof(pid));
493 }
494 
495 static	char	 pbuf[BUFSIZ];	/* static buf for TTYPROMPT 	*/
496 static	char	 hbuf[BUFSIZ];	/* static buf for HOME 		*/
497 static	char	 tbuf[BUFSIZ];	/* static buf for TERM		*/
498 
499 /*
500  * void invoke_service	- invoke the service
501  */
502 
503 static	void
504 invoke_service(pmtab)
505 struct	pmtab	*pmtab;
506 {
507 	char	 *argvp[MAXARGS];		/* service cmd args */
508 	int	 cnt = 0;			/* arg counter */
509 	int	 i, fd;
510 	struct	 sigaction	sigact;
511 	extern	 struct	rlimit	Rlimit;
512 
513 #ifdef 	DEBUG
514 	debug("in invoke_service");
515 #endif
516 
517 	if (tcgetsid(0) != getsid(getpid())) {
518 		cons_printf("Warning -- ttymon cannot allocate controlling "
519 		    "tty on \"%s\",\n", pmtab->p_device);
520 		cons_printf("\tThere may be another session active on this "
521 		    "port.\n");
522 
523 		if (strcmp("/dev/console", pmtab->p_device) != 0) {
524 			/*
525 			 * if not on console, write to stderr to warn the user
526 			 * also.
527 			 */
528 			(void) fprintf(stderr, "Warning -- ttymon cannot "
529 			    "allocate controlling tty on \"%s\",\n",
530 			    pmtab->p_device);
531 			(void)fprintf(stderr, "\tthere may be another session "
532 			    "active on this port.\n");
533 		}
534 	}
535 
536 	if (pmtab->p_status != GETTY) {
537 		inform_parent(PCpipe[1]);
538 		sigact.sa_flags = 0;
539 		sigact.sa_handler = SIG_DFL;
540 		(void)sigemptyset(&sigact.sa_mask);
541 		(void)sigaction(SIGPOLL, &sigact, NULL);
542 	}
543 
544 	if (pmtab->p_flags & U_FLAG) {
545 		if (account(pmtab->p_device) != 0) {
546 			log("invoke_service: account failed");
547 			exit(1);
548 		}
549 	}
550 
551 	/* parse command line */
552 	mkargv(pmtab->p_server,&argvp[0],&cnt,MAXARGS-1);
553 
554 	if (!(pmtab->p_ttyflags & C_FLAG)) {
555 		(void) sprintf(pbuf, "TTYPROMPT=%s", pmtab->p_prompt);
556 		if (putenv(pbuf)) {
557 			log("cannot expand service <%s> environment", argvp[0]);
558 			exit(1);
559 		}
560 	}
561 	if (pmtab->p_status != GETTY) {
562 		(void) sprintf(hbuf, "HOME=%s", pmtab->p_dir);
563 		if (putenv(hbuf)) {
564 			log("cannot expand service <%s> environment",argvp[0]);
565 			exit(1);
566 		}
567 #ifdef	DEBUG
568 		debug("about to run config script");
569 #endif
570 		if ((i = doconfig(0, pmtab->p_tag, 0)) != 0) {
571 			if (i < 0) {
572 				log("doconfig failed, system error");
573 			}
574 			else {
575 				log("doconfig failed on line %d of script %s",
576 				    i, pmtab->p_tag);
577 			}
578 			exit(1);
579 		}
580 	}
581 
582 	if (setgid(pmtab->p_gid)) {
583 		log("cannot set group id to %ld: %s", pmtab->p_gid,
584 		    strerror(errno));
585 		exit(1);
586 	}
587 
588 	if (setuid(pmtab->p_uid)) {
589 		log("cannot set user id to %ld: %s", pmtab->p_uid,
590 		    strerror(errno));
591 		exit(1);
592 	}
593 
594 	if (chdir(pmtab->p_dir)) {
595 		log("cannot chdir to %s: %s", pmtab->p_dir, strerror(errno));
596 		exit(1);
597 	}
598 
599 	if (pmtab->p_uid != ROOTUID) {
600 		/* change ownership and mode of device */
601 		(void)fchown(0,pmtab->p_uid,Tty_gid);
602 		(void)fchmod(0,0620);
603 	}
604 
605 
606 	if (pmtab->p_status != GETTY) {
607 		sigact.sa_flags = 0;
608 		sigact.sa_handler = SIG_DFL;
609 		(void)sigemptyset(&sigact.sa_mask);
610 		(void)sigaction(SIGINT, &sigact, NULL);
611 		if (setrlimit(RLIMIT_NOFILE, &Rlimit) == -1) {
612 			log("setrlimit failed: %s", strerror(errno));
613 			exit(1);
614 		}
615 		/* invoke the service */
616 		log("Starting service (%s) on %s", argvp[0], pmtab->p_device);
617 	}
618 
619 	if (pmtab->p_termtype != (char *)NULL) {
620 		(void) sprintf(tbuf, "TERM=%s", pmtab->p_termtype);
621 		if (putenv(tbuf)) {
622 			log("cannot expand service <%s> environment", argvp[0]);
623 			exit(1);
624 		}
625 	}
626 	/* restore signal handlers and mask */
627 	(void)sigaction(SIGINT, &Sigint, NULL);
628 	(void)sigaction(SIGALRM, &Sigalrm, NULL);
629 	(void)sigaction(SIGPOLL, &Sigpoll, NULL);
630 	(void)sigaction(SIGCLD, &Sigcld, NULL);
631 	(void)sigaction(SIGTERM, &Sigterm, NULL);
632 #ifdef	DEBUG
633 	(void)sigaction(SIGUSR1, &Sigusr1, NULL);
634 	(void)sigaction(SIGUSR2, &Sigusr2, NULL);
635 #endif
636 	(void)sigprocmask(SIG_SETMASK, &Origmask, NULL);
637 	(void) execve(argvp[0], argvp, environ);
638 
639 	/* exec returns only on failure! */
640 	log("tmchild: exec service failed: %s", strerror(errno));
641 	exit(1);
642 }
643 
644 /*
645  *	check_hup(fd)	- do a poll on fd to check if it is in hangup state
646  *			- return 1 if hangup, otherwise return 0
647  */
648 
649 static	int
650 check_hup(fd)
651 int	fd;
652 {
653 	int	ret;
654 	struct	pollfd	pfd[1];
655 
656 	pfd[0].fd = fd;
657 	pfd[0].events = POLLHUP;
658 	for (;;) {
659 		ret = poll(pfd, 1, 0);
660 		if (ret < 0) {
661 			if (errno == EINTR)
662 				continue;
663 			log("check_hup: poll failed: %s", strerror(errno));
664 			exit(1);
665 		}
666 		else if (ret > 0) {
667 			if (pfd[0].revents & POLLHUP) {
668 				return(1);
669 			}
670 		}
671 		return(0);
672 	}
673 }
674 
675 /*
676  * sigpoll()	- SIGPOLL handle for tmchild
677  *		- when SIGPOLL is received by tmchild,
678  *		  the pipe between ttymon and tmchild is broken.
679  *		  Something must happen to ttymon.
680  */
681 void
682 sigpoll()
683 {
684 #ifdef	DEBUG
685 	debug("tmchild got SIGPOLL, exiting");
686 #endif
687 	exit(1);
688 }
689