ttymon.c revision 4321:a8930ec16e52
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 2007 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
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32
33#include <stdio_ext.h>
34#include <stdlib.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <poll.h>
38#include <string.h>
39#include <signal.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <sys/stropts.h>
43#include <sys/resource.h>
44#include <sys/termios.h>
45#include <pwd.h>
46#include <grp.h>
47#include <unistd.h>
48#include <ulimit.h>
49
50#include "sac.h"
51#include "ttymon.h"
52#include "tmstruct.h"
53#include "tmextern.h"
54
55static	int	Initialized;
56
57extern	int	Retry;
58extern	struct	pollfd	*Pollp;
59static	void	initialize();
60static	void	open_all();
61static	int	set_poll();
62static	int	check_spawnlimit();
63static	int	mod_ttydefs();
64
65void	open_device();
66void	set_softcar();
67
68extern	int	check_session();
69extern	void	sigalarm();
70extern	void	revokedevaccess(char *, uid_t, gid_t, mode_t);
71/* can't include libdevinfo.h */
72extern int di_devperm_logout(const char *);
73
74/*
75 * 	ttymon	- a port monitor under SAC
76 *		- monitor ports, set terminal modes, baud rate
77 *		  and line discipline for the port
78 *		- invoke service on port if connection request received
79 *		- Usage: ttymon
80 *			 ttymon -g [options]
81 *			 Valid options are
82 *			 -h
83 *			 -d device
84 *			 -l ttylabel
85 *			 -t timeout
86 *			 -m modules
87 *			 -p prompt
88 *
89 *		- ttymon without args is invoked by SAC
90 *		- ttymon -g is invoked by process that needs to
91 *		  have login service on the fly
92 */
93
94int
95main(int argc, char *argv[])
96{
97	int	nfds;
98	extern	char	*lastname();
99
100	/*
101	 * Only the superuser should execute this command.
102	 */
103	if (getuid() != 0)
104		return (1);	/*NOTREACHED*/
105
106	if ((argc > 1) || (strcmp(lastname(argv[0]), "getty") == 0)) {
107		ttymon_express(argc, argv);
108		return (1);	/*NOTREACHED*/
109	}
110	/* remember original signal mask and dispositions */
111	(void) sigprocmask(SIG_SETMASK, NULL, &Origmask);
112	(void) sigaction(SIGINT, &Sigint, NULL);
113	(void) sigaction(SIGALRM, &Sigalrm, NULL);
114	(void) sigaction(SIGPOLL, &Sigpoll, NULL);
115	(void) sigaction(SIGCLD, &Sigcld, NULL);
116	(void) sigaction(SIGTERM, &Sigterm, NULL);
117#ifdef	DEBUG
118	(void) sigaction(SIGUSR1, &Sigusr1, NULL);
119	(void) sigaction(SIGUSR2, &Sigusr2, NULL);
120#endif
121	initialize();
122
123	for (;;) {
124		nfds = set_poll(Pollp);
125		if (!Reread_flag) {
126			if (nfds > 0)
127				do_poll(Pollp, nfds);
128			else
129				(void) pause();
130		}
131		/*
132		 * READDB messages may arrive during poll or pause.
133		 * So the flag needs to be checked again.
134		 */
135		if (Reread_flag) {
136			Reread_flag = FALSE;
137			re_read();
138		}
139		while (Retry) {
140			Retry = FALSE;
141			open_all();
142		}
143	}
144}
145
146static	void
147initialize()
148{
149	struct	pmtab	*tp;
150	register struct passwd *pwdp;
151	register struct	group	*gp;
152	struct	rlimit rlimit;
153	extern	struct	rlimit	Rlimit;
154	extern	 uid_t	Uucp_uid;
155	extern	 gid_t	Tty_gid;
156
157#ifdef 	DEBUG
158	extern	opendebug();
159#endif
160	Initialized = FALSE;
161	/*
162	 * get_environ() must be called first,
163	 * otherwise we don't know where the log file is
164	 */
165	get_environ();
166	openttymonlog();
167	openpid();
168	openpipes();
169	setup_PCpipe();
170
171	log("PMTAG: %s", Tag);
172	log("Starting state: %s",
173	    (State == PM_ENABLED) ? "enabled" : "disabled");
174
175#ifdef 	DEBUG
176	opendebug(FALSE);
177	debug("***** ttymon in initialize *****");
178	log("debug mode is \t on");
179#endif
180
181	catch_signals();
182
183	/* register to receive SIGPOLL when data comes to pmpipe */
184	if (ioctl(Pfd, I_SETSIG, S_INPUT) < 0)
185		fatal("I_SETSIG on pmpipe failed: %s", strerror(errno));
186
187	sacpoll(); /* this is needed because there may be data already */
188
189	Maxfiles = (int)ulimit(4, 0L);	/* get max number of open files */
190	if (Maxfiles < 0)
191		fatal("ulimit(4,0L) failed: %s", strerror(errno));
192
193	if (getrlimit(RLIMIT_NOFILE, &Rlimit) == -1)
194		fatal("getrlimit failed: %s", strerror(errno));
195
196	rlimit.rlim_cur = rlimit.rlim_max = Rlimit.rlim_max;
197	if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
198		fatal("setrlimit failed: %s", strerror(errno));
199
200	(void) enable_extended_FILE_stdio(-1, -1);
201
202	Maxfiles = rlimit.rlim_cur;
203	Maxfds = Maxfiles - FILE_RESERVED;
204
205	log("max open files = %d", Maxfiles);
206	log("max ports ttymon can monitor = %d", Maxfds);
207
208	read_pmtab();
209
210	/*
211	 * setup poll array
212	 * 	- we allocate 10 extra pollfd so that
213	 *	  we do not have to re-malloc when there is
214	 *	  minor fluctuation in Nentries
215	 */
216	Npollfd = Nentries + 10;
217	if (Npollfd > Maxfds)
218		Npollfd = Maxfds;
219	if ((Pollp = (struct pollfd *)
220	    malloc((unsigned)(Npollfd * sizeof (struct pollfd))))
221	    == (struct pollfd *)NULL)
222		fatal("malloc for Pollp failed");
223
224	(void) mod_ttydefs();	/* just to initialize Mtime */
225	if (check_version(TTYDEFS_VERS, TTYDEFS) != 0)
226		fatal("check /etc/ttydefs version failed");
227
228	read_ttydefs(NULL, FALSE);
229
230	/* initialize global variables, Uucp_uid & Tty_gid */
231	if ((pwdp = getpwnam(UUCP)) != NULL)
232		Uucp_uid = pwdp->pw_uid;
233	if ((gp = getgrnam(TTY)) == NULL)
234		log("no group entry for <tty>, default is used");
235	else
236		Tty_gid = gp->gr_gid;
237	endgrent();
238	endpwent();
239#ifdef	DEBUG
240	debug("Uucp_uid = %u, Tty_gid = %u", Uucp_uid, Tty_gid);
241#endif
242
243	log("Initialization Completed");
244
245	/* open the devices ttymon monitors */
246	Retry = TRUE;
247	while (Retry) {
248		Retry = FALSE;
249		for (tp = PMtab; tp; tp = tp->p_next) {
250			if ((tp->p_status > 0) && (tp->p_fd == 0) &&
251			    (tp->p_pid == 0) && !(tp->p_ttyflags & I_FLAG) &&
252			    (!((State == PM_DISABLED) &&
253			    ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
254				open_device(tp);
255				if (tp->p_fd > 0)
256					got_carrier(tp);
257			}
258		}
259	}
260	Initialized = TRUE;
261}
262
263/*
264 *	open_all - open devices in pmtab if the entry is
265 *	         - valid, fd = 0, and pid = 0
266 */
267static void
268open_all()
269{
270	struct	pmtab	*tp;
271	int	check_modtime;
272	static	void	free_defs();
273	sigset_t cset;
274	sigset_t tset;
275
276#ifdef	DEBUG
277	debug("in open_all");
278#endif
279	check_modtime = TRUE;
280
281	for (tp = PMtab; tp; tp = tp->p_next) {
282		if ((tp->p_status > 0) && (tp->p_fd == 0) &&
283		    (tp->p_pid == 0) &&
284		    !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) &&
285		    ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
286			/*
287			 * if we have not check modification time and
288			 * /etc/ttydefs was modified, need to re-read it
289			 */
290			if (check_modtime && mod_ttydefs()) {
291				check_modtime = FALSE;
292				(void) sigprocmask(SIG_SETMASK, NULL, &cset);
293				tset = cset;
294				(void) sigaddset(&tset, SIGCLD);
295				(void) sigprocmask(SIG_SETMASK, &tset, NULL);
296				free_defs();
297#ifdef	DEBUG
298				debug("/etc/ttydefs is modified, re-read it");
299#endif
300				read_ttydefs(NULL, FALSE);
301				(void) sigprocmask(SIG_SETMASK, &cset, NULL);
302			}
303			open_device(tp);
304			if (tp->p_fd > 0)
305				got_carrier(tp);
306		} else if (((tp->p_status == LOCKED) ||
307		    (tp->p_status == SESSION) ||
308		    (tp->p_status == UNACCESS)) &&
309		    (tp->p_fd > 0) &&
310		    (!((State == PM_DISABLED) &&
311		    ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
312			if (check_modtime && mod_ttydefs()) {
313				check_modtime = FALSE;
314				(void) sigprocmask(SIG_SETMASK, NULL, &cset);
315				tset = cset;
316				(void) sigaddset(&tset, SIGCLD);
317				(void) sigprocmask(SIG_SETMASK, &tset, NULL);
318				free_defs();
319#ifdef	DEBUG
320				debug("/etc/ttydefs is modified, re-read it");
321#endif
322				read_ttydefs(NULL, FALSE);
323				(void) sigprocmask(SIG_SETMASK, &cset, NULL);
324			}
325			tp->p_status = VALID;
326			open_device(tp);
327			if (tp->p_fd > 0)
328				got_carrier(tp);
329		}
330	}
331}
332
333void
334set_softcar(pmptr)
335struct	pmtab	*pmptr;
336{
337
338	int fd, val = 0;
339
340#ifdef	DEBUG
341	debug("in set_softcar");
342#endif
343	/*
344	 * If soft carrier is not set one way or
345	 * the other, leave it alone.
346	 */
347	if (*pmptr->p_softcar == '\0')
348		return;
349
350	if (*pmptr->p_softcar == 'y')
351		val = 1;
352
353	if ((fd = open(pmptr->p_device, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) {
354		log("open (%s) failed: %s", pmptr->p_device, strerror(errno));
355		return;
356	}
357
358	if (ioctl(fd, TIOCSSOFTCAR, &val) < 0)
359		log("set soft-carrier (%s) failed: %s", pmptr->p_device,
360		    strerror(errno));
361
362	close(fd);
363}
364
365
366/*
367 *	open_device(pmptr)	- open the device
368 *				- check device lock
369 *				- change owner of device
370 *				- push line disciplines
371 *				- set termio
372 */
373
374void
375open_device(pmptr)
376struct	pmtab	*pmptr;
377{
378	int	fd, tmpfd;
379	struct	sigaction	sigact;
380
381#ifdef	DEBUG
382	debug("in open_device");
383#endif
384
385	if (pmptr->p_status == GETTY) {
386		revokedevaccess(pmptr->p_device, 0, 0, 0);
387
388		if ((fd = open(pmptr->p_device, O_RDWR)) == -1)
389			fatal("open (%s) failed: %s", pmptr->p_device,
390			    strerror(errno));
391
392	} else {
393		if (check_spawnlimit(pmptr) == -1) {
394			pmptr->p_status = NOTVALID;
395			log("service <%s> is respawning too rapidly",
396			    pmptr->p_tag);
397			return;
398		}
399		if (pmptr->p_fd > 0) { /* file already open */
400			fd = pmptr->p_fd;
401			pmptr->p_fd = 0;
402		} else if ((fd = open(pmptr->p_device, O_RDWR|O_NONBLOCK))
403		    == -1) {
404			log("open (%s) failed: %s", pmptr->p_device,
405			    strerror(errno));
406			if ((errno ==  ENODEV) || (errno == EBUSY)) {
407				pmptr->p_status = UNACCESS;
408				Nlocked++;
409				if (Nlocked == 1) {
410				    sigact.sa_flags = 0;
411				    sigact.sa_handler = sigalarm;
412				    (void) sigemptyset(&sigact.sa_mask);
413				    (void) sigaction(SIGALRM, &sigact, NULL);
414				    (void) alarm(ALARMTIME);
415				}
416			} else
417				Retry = TRUE;
418			return;
419		}
420		/* set close-on-exec flag */
421		if (fcntl(fd, F_SETFD, 1) == -1)
422			fatal("F_SETFD fcntl failed: %s", strerror(errno));
423
424		if (tm_checklock(fd) != 0) {
425			pmptr->p_status = LOCKED;
426			(void) close(fd);
427			Nlocked++;
428			if (Nlocked == 1) {
429				sigact.sa_flags = 0;
430				sigact.sa_handler = sigalarm;
431				(void) sigemptyset(&sigact.sa_mask);
432				(void) sigaction(SIGALRM, &sigact, NULL);
433				(void) alarm(ALARMTIME);
434			}
435			return;
436		}
437		if (check_session(fd) != 0) {
438			if ((Initialized) && (pmptr->p_inservice != SESSION)) {
439				log("Warning -- active session exists on <%s>",
440				    pmptr->p_device);
441			} else {
442				/*
443				 * this may happen if a service is running
444				 * and ttymon dies and is restarted,
445				 * or another process is running on the
446				 * port.
447				 */
448				pmptr->p_status = SESSION;
449				pmptr->p_inservice = 0;
450				(void) close(fd);
451				Nlocked++;
452				if (Nlocked == 1) {
453					sigact.sa_flags = 0;
454					sigact.sa_handler = sigalarm;
455					(void) sigemptyset(&sigact.sa_mask);
456					(void) sigaction(SIGALRM, &sigact,
457					    NULL);
458					(void) alarm(ALARMTIME);
459				}
460				return;
461			}
462		}
463		pmptr->p_inservice = 0;
464	}
465
466	if (pmptr->p_ttyflags & H_FLAG) {
467		/* drop DTR */
468		(void) hang_up_line(fd);
469		/*
470		 * After hang_up_line, the stream is in STRHUP state.
471		 * We need to do another open to reinitialize streams
472		 * then we can close one fd
473		 */
474		if ((tmpfd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) {
475			log("open (%s) failed: %s", pmptr->p_device,
476			    strerror(errno));
477			Retry = TRUE;
478			(void) close(fd);
479			return;
480		}
481		(void) close(tmpfd);
482	}
483
484#ifdef DEBUG
485	debug("open_device (%s), fd = %d", pmptr->p_device, fd);
486#endif
487
488	/* Change ownership of the tty line to root/uucp and */
489	/* set protections to only allow root/uucp to read the line. */
490
491	if (pmptr->p_ttyflags & (B_FLAG|C_FLAG))
492		(void) fchown(fd, Uucp_uid, Tty_gid);
493	else
494		(void) fchown(fd, ROOTUID, Tty_gid);
495	(void) fchmod(fd, 0620);
496
497	if ((pmptr->p_modules != NULL)&&(*(pmptr->p_modules) != '\0')) {
498		if (push_linedisc(fd, pmptr->p_modules, pmptr->p_device)
499		    == -1) {
500			Retry = TRUE;
501			(void) close(fd);
502			return;
503		}
504	}
505
506	if (initial_termio(fd, pmptr) == -1)  {
507		Retry = TRUE;
508		(void) close(fd);
509		return;
510	}
511
512	di_devperm_logout((const char *)pmptr->p_device);
513	pmptr->p_fd = fd;
514}
515
516/*
517 *	set_poll(fdp)	- put all fd's in a pollfd array
518 *			- set poll event to POLLIN and POLLMSG
519 *			- return number of fd to be polled
520 */
521
522static	int
523set_poll(fdp)
524struct pollfd *fdp;
525{
526	struct	pmtab	*tp;
527	int 	nfd = 0;
528
529	for (tp = PMtab; tp; tp = tp->p_next) {
530		if (tp->p_fd > 0)  {
531			fdp->fd = tp->p_fd;
532			fdp->events = POLLIN;
533			fdp++;
534			nfd++;
535		}
536	}
537	return (nfd);
538}
539
540/*
541 *	check_spawnlimit	- return 0 if spawnlimit is not reached
542 *				- otherwise return -1
543 */
544static	int
545check_spawnlimit(pmptr)
546struct	pmtab	*pmptr;
547{
548	time_t	now;
549
550	(void) time(&now);
551	if (pmptr->p_time == 0L)
552		pmptr->p_time = now;
553	if (pmptr->p_respawn >= SPAWN_LIMIT) {
554		if ((now - pmptr->p_time) < SPAWN_INTERVAL) {
555			pmptr->p_time = now;
556			pmptr->p_respawn = 0;
557			return (-1);
558		}
559		pmptr->p_time = now;
560		pmptr->p_respawn = 0;
561	}
562	pmptr->p_respawn++;
563	return (0);
564}
565
566/*
567 * mod_ttydefs	- to check if /etc/ttydefs has been modified
568 *		- return TRUE if file modified
569 *		- otherwise, return FALSE
570 */
571static	int
572mod_ttydefs()
573{
574	struct	stat	statbuf;
575	extern	long	Mtime;
576	if (stat(TTYDEFS, &statbuf) == -1) {
577		/* if stat failed, don't bother reread ttydefs */
578		return (FALSE);
579	}
580	if ((long)statbuf.st_mtime != Mtime) {
581		Mtime = (long)statbuf.st_mtime;
582		return (TRUE);
583	}
584	return (FALSE);
585}
586
587/*
588 *	free_defs - free the Gdef table
589 */
590static	void
591free_defs()
592{
593	int	i;
594	struct	Gdef	*tp;
595	tp = &Gdef[0];
596	for (i = 0; i < Ndefs; i++, tp++) {
597		free(tp->g_id);
598		free(tp->g_iflags);
599		free(tp->g_fflags);
600		free(tp->g_nextid);
601		tp->g_id = NULL;
602		tp->g_iflags = NULL;
603		tp->g_fflags = NULL;
604		tp->g_nextid = NULL;
605	}
606	Ndefs = 0;
607}
608
609/*
610 * struct Gdef *get_speed(ttylabel)
611 *	- search "/etc/ttydefs" for speed and term. specification
612 *	  using "ttylabel". If "ttylabel" is NULL, default
613 *	  to DEFAULT
614 * arg:	  ttylabel - label/id of speed settings.
615 */
616
617struct Gdef *
618get_speed(char *ttylabel)
619{
620	register struct Gdef *sp;
621	extern   struct Gdef DEFAULT;
622
623	if ((ttylabel != NULL) && (*ttylabel != '\0')) {
624		if ((sp = find_def(ttylabel)) == NULL) {
625			log("unable to find <%s> in \"%s\"", ttylabel, TTYDEFS);
626			sp = &DEFAULT; /* use default */
627		}
628	} else sp = &DEFAULT; /* use default */
629	return (sp);
630}
631
632/*
633 * setup_PCpipe()	- setup the pipe between Parent and Children
634 *			- the pipe is used for a tmchild to send its
635 *			  pid to inform ttymon that it is about to
636 *			  invoke service
637 *			- the pipe also serves as a mean for tmchild
638 *			  to detect failure of ttymon
639 */
640void
641setup_PCpipe()
642{
643	int	flag = 0;
644
645	if (pipe(PCpipe) == -1)
646		fatal("pipe() failed: %s", strerror(errno));
647
648	/* set close-on-exec flag */
649	if (fcntl(PCpipe[0], F_SETFD, 1) == -1)
650		fatal("F_SETFD fcntl failed: %s", strerror(errno));
651
652	if (fcntl(PCpipe[1], F_SETFD, 1) == -1)
653		fatal("F_SETFD fcntl failed: %s", strerror(errno));
654
655	/* set O_NONBLOCK flag */
656	if (fcntl(PCpipe[0], F_GETFL, flag) == -1)
657		fatal("F_GETFL failed: %s", strerror(errno));
658
659	flag |= O_NONBLOCK;
660	if (fcntl(PCpipe[0], F_SETFL, flag) == -1)
661		fatal("F_SETFL failed: %s", strerror(errno));
662
663	/* set message discard mode */
664	if (ioctl(PCpipe[0], I_SRDOPT, RMSGD) == -1)
665		fatal("I_SRDOPT RMSGD failed: %s", strerror(errno));
666
667	/* register to receive SIGPOLL when data come */
668	if (ioctl(PCpipe[0], I_SETSIG, S_INPUT) == -1)
669		fatal("I_SETSIG S_INPUT failed: %s", strerror(errno));
670
671#ifdef 	DEBUG
672	log("PCpipe[0]\t = %d", PCpipe[0]);
673	log("PCpipe[1]\t = %d", PCpipe[1]);
674#endif
675}
676