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