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
49static void openline();
50static void invoke_service();
51static char	*do_autobaud();
52static	struct	Gdef	*next_speed();
53static int check_hup();
54
55extern	struct	Gdef	*get_speed();
56extern 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 */
63void
64tmchild(pmtab)
65struct	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
231static void
232openline(pmtab, speedef)
233struct	pmtab 	*pmtab;
234struct 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 */
338void
339write_prompt(fd, pmtab, flush, clear)
340int	fd;
341struct	pmtab	*pmtab;
342int	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 */
367void
368timedout()
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 */
378void
379sys_name(fd)
380int	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 */
412static	char	*
413do_autobaud(pmtab, speedef)
414struct	pmtab	*pmtab;
415struct	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
456static	struct	Gdef *
457next_speed(speedef)
458struct	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 */
481static	void
482inform_parent(fd)
483int	fd;
484{
485	pid_t	pid;
486
487	pid = getpid();
488	(void) write(fd, &pid, sizeof (pid));
489}
490
491static	char	 pbuf[BUFSIZ];	/* static buf for TTYPROMPT 	*/
492static	char	 hbuf[BUFSIZ];	/* static buf for HOME 		*/
493static	char	 tbuf[BUFSIZ];	/* static buf for TERM		*/
494
495/*
496 * void invoke_service	- invoke the service
497 */
498
499static	void
500invoke_service(pmtab)
501struct	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
646static	int
647check_hup(fd)
648int	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 */
678void
679sigpoll()
680{
681#ifdef	DEBUG
682	debug("tmchild got SIGPOLL, exiting");
683#endif
684	exit(1);
685}
686