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