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