xref: /illumos-gate/usr/src/cmd/ttymon/tmhandler.c (revision b1e2e3fb17324e9ddf43db264a0c64da7756d9e6)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 /*
29  * Copyright (c) 2018, Joyent, Inc.
30  */
31 
32 
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <poll.h>
38 #include <string.h>
39 #include <termio.h>
40 #include <signal.h>
41 #include <sys/types.h>
42 #include <sys/stropts.h>
43 #include <unistd.h>
44 #include <sys/wait.h>
45 #include "ttymon.h"
46 #include "tmstruct.h"
47 #include "tmextern.h"
48 #include "sac.h"
49 
50 extern	int	Retry;
51 static	struct	pmtab	*find_pid();
52 static	void	kill_children();
53 
54 static 	struct	pmtab	*find_fd();
55 static	void	pcsync_close();
56 extern  void	sigalarm();
57 extern	void	tmchild();
58 
59 /*
60  *	fork_tmchild	- fork child on the device
61  */
62 static	void
63 fork_tmchild(pmptr)
64 struct	pmtab	*pmptr;
65 {
66 	pid_t	pid;
67 	sigset_t	cset;
68 	sigset_t	tset;
69 	int	pcpipe0[2], pcpipe1[2];
70 	int	p0;
71 
72 #ifdef	DEBUG
73 	debug("in fork_tmchild");
74 #endif
75 	pmptr->p_inservice = FALSE;
76 
77 	/*
78 	 * initialize pipe.
79 	 * Child has pcpipe[0] pipe fd for reading and writing
80 	 * and closes pcpipe[1]. Parent has pcpipe[1] pipe fd for
81 	 * reading and writing and closes pcpipe[0].
82 	 *
83 	 * This way if the child process exits the parent's block
84 	 * read on pipe will return immediately as the other end of
85 	 * the pipe has closed. Similarly if the parent process exits
86 	 * child's blocking read on the pipe will return immediately.
87 	 */
88 
89 	if (((p0 = pipe(pcpipe0)) == -1) || (pipe(pcpipe1) == -1))  {
90 		if (p0 == 0) {
91 			close(pcpipe0[0]);
92 			close(pcpipe0[1]);
93 		}
94 		log("pipe() failed: %s", strerror(errno));
95 		pmptr->p_status = VALID;
96 		pmptr->p_pid = 0;
97 		Retry = TRUE;
98 	}
99 
100 	/* protect following region from SIGCLD */
101 	(void)sigprocmask(SIG_SETMASK, NULL, &cset);
102 	tset = cset;
103 	(void)sigaddset(&tset, SIGCLD);
104 	(void)sigprocmask(SIG_SETMASK, &tset, NULL);
105 	if( (pid=fork()) == 0 ) {
106 		/*
107 		 * Close all file descriptors except pmptr->p_fd
108 		 * Wait for the parent process to close its fd
109 		 */
110 		pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd);
111 	 	/* The CHILD */
112 		tmchild(pmptr);
113 		/* tmchild should never return */
114 		fatal("tmchild for <%s> returns unexpected", pmptr->p_device);
115 	}
116 	else if (pid < 0) {
117 		log("fork failed: %s", strerror(errno));
118 		pmptr->p_status = VALID;
119 		pmptr->p_pid = 0;
120 		Retry = TRUE;
121 	}
122 	else {
123 		/*
124 		 * The PARENT - store pid of child and close the device
125 		 */
126 		pmptr->p_pid = pid;
127 	}
128 	if (pmptr->p_fd > 0) {
129 		(void)close(pmptr->p_fd);
130 		pmptr->p_fd = 0;
131 	}
132 	(void)sigprocmask(SIG_SETMASK, &cset, NULL);
133 	/*
134 	 * Wait for child to close file descriptors
135 	 */
136 	pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd);
137 }
138 
139 /*
140  * got_carrier - carrier is detected on the stream
141  *	       - depends on the flags, different action is taken
142  *	       - R_FLAG - wait for data
143  *	       - C_FLAG - if port is not disabled, fork tmchild
144  *	       - A_FLAG - wait for data
145  *	       - otherwise - write out prompt, then wait for data
146  */
147 void
148 got_carrier(pmptr)
149 struct	pmtab	*pmptr;
150 {
151 	flush_input(pmptr->p_fd);
152 
153 	if (pmptr->p_ttyflags & R_FLAG) {
154 #ifdef	DEBUG
155 	debug("R_FLAG");
156 #endif
157 		return;
158 	}
159 	else if ((pmptr->p_ttyflags & (C_FLAG|B_FLAG)) &&
160 		(State != PM_DISABLED) &&
161 		(!(pmptr->p_flags & X_FLAG))) {
162 		fork_tmchild(pmptr);
163 	}
164 	else if (pmptr->p_ttyflags & A_FLAG) {
165 #ifdef	DEBUG
166 	debug("A_FLAG");
167 #endif
168 		return;
169 	}
170 	else if (pmptr->p_timeout) {
171 		fork_tmchild(pmptr);
172 	}
173 	else if ( ! (pmptr->p_ttyflags & X_FLAG) ) {
174 		write_prompt(pmptr->p_fd,pmptr,TRUE,TRUE);
175 	}
176 }
177 
178 /*
179  * got_data - data is detected on the stream, fork tmchild
180  */
181 static void
182 got_data(pmptr)
183 struct	pmtab	*pmptr;
184 {
185 	struct	sigaction sigact;
186 
187 	if (tm_checklock(pmptr->p_fd) != 0) {
188 		pmptr->p_status = LOCKED;
189 		(void)close(pmptr->p_fd);
190 		pmptr->p_fd = 0;
191 		Nlocked++;
192 		if (Nlocked == 1) {
193 			sigact.sa_flags = 0;
194 			sigact.sa_handler = sigalarm;
195 			(void)sigemptyset(&sigact.sa_mask);
196 			(void)sigaction(SIGALRM, &sigact, NULL);
197 			(void)alarm(ALARMTIME);
198 		}
199 	}
200 	else
201 		fork_tmchild(pmptr);
202 }
203 /*
204  * got_hup - stream hangup is detected, close the device
205  */
206 static void
207 got_hup(pmptr)
208 struct	pmtab	*pmptr;
209 {
210 #ifdef	DEBUG
211 	debug("in got hup");
212 #endif
213 	(void)close(pmptr->p_fd);
214 	pmptr->p_fd = 0;
215 	pmptr->p_inservice = 0;
216 	Retry = TRUE;
217 }
218 
219 
220 /*
221  *	do_poll	- poll device
222  *		- if POLLHUP received, close the device
223  *		- if POLLIN received, fork tmchild.
224  */
225 void
226 do_poll(fdp,nfds)
227 struct 	pollfd *fdp;
228 int 	nfds;
229 {
230 	int	i,n;
231 	struct	pmtab	*pmptr;
232 
233 	n = poll(fdp, (unsigned long)nfds, -1);	/* blocked poll */
234 #ifdef	DEBUG
235 	debug("poll return");
236 #endif
237 	if (n < 0) {
238 		if (errno == EINTR)	/* interrupt by signal */
239 			return;
240 		fatal("do_poll: poll failed: %s", strerror(errno));
241 	}
242 	for (i = 0; (i < nfds)&&(n); i++,fdp++) {
243 		if (fdp->revents != 0) {
244 			n--;
245 			if ((pmptr = find_fd(fdp->fd)) == NULL) {
246 				log("do_poll: cannot find fd %d in pmtab",
247 				    fdp->fd);
248 				continue;
249 			}
250 			else if (fdp->revents & POLLHUP) {
251 				got_hup(pmptr);
252 			}
253 			else if (fdp->revents & POLLIN) {
254 #ifdef	DEBUG
255 				debug("got POLLIN");
256 #endif
257 				got_data(pmptr);
258 			} else if (fdp->revents & POLLERR) {
259 				fatal("ttymon[%d]: do_poll: POLLERR on fd %d",
260 				    getpid(), fdp->fd);
261 			}
262 		}
263 	}
264 }
265 
266 /*
267  *	sigchild	- handler for SIGCLD
268  *			- find the pid of dead child
269  *			- clean utmp if U_FLAG is set
270  */
271 void
272 /*ARGSUSED*/
273 sigchild(n)
274 int	n;	/* this is declared to make cc happy, but it is not used */
275 {
276 	struct	pmtab	*pmptr;
277 	struct	sigaction	sigact;
278 	siginfo_t	info;
279 	int 	status;
280 	pid_t 	pid;
281 	int	rcode;
282 
283 #ifdef	DEBUG
284 	debug("in sigchild");
285 #endif
286 
287 	/* find all processes that died */
288 	for (;;) {
289 		rcode = waitid(P_ALL, 0, &info, WNOHANG|WEXITED);
290 		if (rcode == -1 && errno == EINTR)
291 			continue;
292 
293 		/* If no more children have exited, just return */
294 		if (rcode == -1 || (pid = info.si_pid) == 0)
295 			break;
296 
297 		/* construct status as returned from waitid() */
298 		status = info.si_status & 0377;
299 		switch (info.si_code) {
300 		case CLD_EXITED:
301 			status <<= 8;
302 			break;
303 		case CLD_DUMPED:
304 			status |= WCOREFLG;
305 			break;
306 		case CLD_KILLED:
307 			break;
308 		}
309 
310 		if ((pmptr = find_pid(pid)) == NULL) {
311 #ifdef	DEBUG
312 			log("cannot find dead child (%ld) in pmtab", pid);
313 #endif
314 			/*
315 			 * This may happen if the entry is deleted from pmtab
316 			 * before the service exits.
317 			 * We try to cleanup utmp entry
318 			 */
319 			cleanut(pid, status);
320 		} else {
321 			if (pmptr->p_flags & U_FLAG)
322 				cleanut(pid, status);
323 			pmptr->p_status = VALID;
324 			pmptr->p_fd = 0;
325 			pmptr->p_pid = 0;
326 			pmptr->p_inservice = 0;
327 			Retry = TRUE;
328 		}
329 	}
330 }
331 
332 /*
333  *	sigterm	- handler for SIGTERM
334  */
335 void
336 sigterm()
337 {
338 	fatal("caught SIGTERM");
339 }
340 
341 /*
342  *	state_change	- this is called when ttymon changes
343  *			  its internal state between enabled and disabled
344  */
345 void
346 state_change()
347 {
348 	struct pmtab *pmptr;
349 
350 #ifdef	DEBUG
351 	debug("in state_change");
352 #endif
353 
354 	/*
355 	 * closing PCpipe will cause attached non-service children
356 	 * to get SIGPOLL and exit
357 	 */
358 	(void)close(PCpipe[0]);
359 	(void)close(PCpipe[1]);
360 
361 	/* reopen PCpipe */
362 	setup_PCpipe();
363 
364 	/*
365 	 * also close all open ports so ttymon can start over
366 	 * with new internal state
367 	 */
368 	for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
369 		if ((pmptr->p_fd > 0) && (pmptr->p_pid == 0)) {
370 			(void)close(pmptr->p_fd);
371 			pmptr->p_fd = 0;
372 		}
373 	}
374 	Retry = TRUE;
375 
376 }
377 
378 /*
379  *	re_read	- reread pmtab
380  *		- kill tmchild if entry changed
381  */
382 void
383 re_read()
384 {
385 	extern	struct	pollfd	*Pollp;
386 	sigset_t	cset;
387 	sigset_t	tset;
388 
389 	(void)sigprocmask(SIG_SETMASK, NULL, &cset);
390 	tset = cset;
391 	(void)sigaddset(&tset, SIGCLD);
392 	(void)sigprocmask(SIG_SETMASK, &tset, NULL);
393 	if (Nlocked > 0) {
394 		alarm(0);
395 		Nlocked = 0;
396 	}
397 	read_pmtab();
398 	kill_children();
399 	(void)sigprocmask(SIG_SETMASK, &cset, NULL);
400 	purge();
401 
402 	if (Nentries > Npollfd) {
403 #ifdef	DEBUG
404 		debug("Nentries > Npollfd, reallocating pollfds");
405 #endif
406 		/* need to malloc more pollfd structure */
407 		free((char *)Pollp);
408 		Npollfd = Nentries + 10;
409 		if (Npollfd > Maxfds)
410 			Npollfd = Maxfds;
411 		if ((Pollp = (struct pollfd *)
412 		    malloc((unsigned)(Npollfd * sizeof(struct pollfd))))
413 		    == (struct pollfd *)NULL)
414 			fatal("malloc for Pollp failed");
415 	}
416 	Retry = TRUE;
417 }
418 
419 /*
420  *	find_pid(pid)	- find the corresponding pmtab entry for the pid
421  */
422 static	struct pmtab *
423 find_pid(pid)
424 pid_t	pid;
425 {
426 	struct pmtab *pmptr;
427 
428 	for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
429 		if (pmptr->p_pid == pid) {
430 			return(pmptr);
431 		}
432 	}
433 	return((struct pmtab *)NULL);
434 }
435 
436 /*
437  *	find_fd(fd)	- find the corresponding pmtab entry for the fd
438  */
439 static struct pmtab *
440 find_fd(fd)
441 int	fd;
442 {
443 	struct pmtab *pmptr;
444 
445 	for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
446 		if (pmptr->p_fd == fd) {
447 			return(pmptr);
448 		}
449 	}
450 	return((struct pmtab *)NULL);
451 }
452 
453 /*
454  *	kill_children()	- if the pmtab entry has been changed,
455  *			  kill tmchild if it is not in service.
456  *			- close the device if there is no tmchild
457  */
458 static	void
459 kill_children()
460 {
461 	struct pmtab *pmptr;
462 	for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
463 		if (pmptr->p_status == VALID)
464 			continue;
465 		if ((pmptr->p_fd > 0) && (pmptr->p_pid == 0)) {
466 			(void)close(pmptr->p_fd);
467 			pmptr->p_fd = 0;
468 		}
469 		else if ((pmptr->p_fd == 0) && (pmptr->p_pid > 0)
470 			&& (pmptr->p_inservice == FALSE)) {
471 			(void)kill(pmptr->p_pid, SIGTERM);
472 		}
473 	}
474 }
475 
476 static	void
477 mark_service(pid)
478 pid_t	pid;
479 {
480 	struct	pmtab	*pmptr;
481 #ifdef	DEBUG
482 	debug("in mark_service");
483 #endif
484 	if ((pmptr = find_pid(pid)) == NULL) {
485 		log("mark_service: cannot find child (%ld) in pmtab", pid);
486 		return;
487 	}
488 	pmptr->p_inservice = TRUE;
489 	return;
490 }
491 
492 /*
493  * read_pid(fd)	- read pid info from PCpipe
494  */
495 static	void
496 read_pid(fd)
497 int	fd;
498 {
499 	int	ret;
500 	pid_t	pid;
501 
502 	for (;;) {
503 		if ((ret = read(fd,&pid,sizeof(pid))) < 0) {
504 			if (errno == EINTR)
505 				continue;
506 			if (errno == EAGAIN)
507 				return;
508 			fatal("read PCpipe failed: %s", strerror(errno));
509 		}
510 		if (ret == 0)
511 			return;
512 		if (ret != sizeof(pid))
513 			fatal("read return size incorrect, ret = %d", ret);
514 
515 		mark_service(pid);
516 	}
517 }
518 
519 /*
520  * sipoll_catch()	- signal handle of SIGPOLL for ttymon
521  *			- it will check both PCpipe and pmpipe
522  */
523 void
524 sigpoll_catch()
525 {
526 	int	ret;
527 	struct	pollfd	pfd[2];
528 
529 #ifdef	DEBUG
530 	debug("in sigpoll_catch");
531 #endif
532 
533 	pfd[0].fd = PCpipe[0];
534 	pfd[1].fd = Pfd;
535 	pfd[0].events = POLLIN;
536 	pfd[1].events = POLLIN;
537 	if ((ret = poll(pfd, 2, 0)) < 0)
538 		fatal("sigpoll_catch: poll failed: %s", strerror(errno));
539 
540 	if (ret > 0) {
541 		if (pfd[0].revents & POLLIN)
542 			read_pid(pfd[0].fd);
543 		if (pfd[1].revents & POLLIN)
544 			sacpoll();
545 	}
546 }
547 
548 /*ARGSUSED*/
549 void
550 sigalarm(signo)
551 int	signo;
552 {
553 	struct pmtab *pmptr;
554 	struct sigaction sigact;
555 	int	fd;
556 	extern	int	check_session();
557 
558 #ifdef	DEBUG
559 	debug("in sigalarm, Nlocked = %d", Nlocked);
560 #endif
561 	for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
562 		if ((pmptr->p_status == LOCKED) && (pmptr->p_fd == 0)) {
563 			if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){
564 				log("open (%s) failed: %s", pmptr->p_device,
565 				    strerror(errno));
566 				pmptr->p_status = VALID;
567 				Nlocked--;
568 				Retry = TRUE;
569 			}
570 			else {
571 				if (tm_checklock(fd) == 0) {
572 					Nlocked--;
573 					pmptr->p_fd = fd;
574 					Retry = TRUE;
575 				}
576 				else
577 					(void)close(fd);
578 			}
579 		}
580 		else if ((pmptr->p_status == SESSION) && (pmptr->p_fd == 0)) {
581 			if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){
582 				log("open (%s) failed: %s", pmptr->p_device,
583 				    strerror(errno));
584 				pmptr->p_status = VALID;
585 				Nlocked--;
586 				Retry = TRUE;
587 			}
588 			else {
589 				if (check_session(fd) == 0) {
590 					Nlocked--;
591 					pmptr->p_fd = fd;
592 					Retry = TRUE;
593 				}
594 				else
595 					(void)close(fd);
596 			}
597 		}
598 		else if ((pmptr->p_status == UNACCESS) && (pmptr->p_fd == 0)) {
599 			if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){
600 				log("open (%s) failed: %s", pmptr->p_device,
601 				    strerror(errno));
602 				pmptr->p_status = VALID;
603 				Nlocked--;
604 				Retry = TRUE;
605 			}
606 			else {
607 				Nlocked--;
608 				pmptr->p_fd = fd;
609 				Retry = TRUE;
610 			}
611 		}
612 	}
613 	if (Nlocked > 0) {
614 		sigact.sa_flags = 0;
615 		sigact.sa_handler = sigalarm;
616 		(void)sigemptyset(&sigact.sa_mask);
617 		(void)sigaction(SIGALRM, &sigact, NULL);
618 		(void)alarm(ALARMTIME);
619 	}
620 	else {
621 		sigact.sa_flags = 0;
622 		sigact.sa_handler = SIG_IGN;
623 		(void)sigemptyset(&sigact.sa_mask);
624 		(void)sigaction(SIGALRM, &sigact, NULL);
625 	}
626 }
627 
628 /*
629  * pcsync_close -  For the child process close all open fd's except
630  * the one that is passed to the routine. Coordinate the reads and
631  * writes to the pipes by the parent and child process to ensure
632  * the parent and child processes have closed all the file descriptors
633  * that are not needed any more.
634  */
635 static void
636 pcsync_close(int *p0, int *p1, int pid, int fd)
637 {
638 	char	ch;
639 
640 	if (pid == 0) {				/* Child */
641 		struct  pmtab   *tp;
642 		for (tp = PMtab; tp; tp = tp->p_next)
643 			if ((tp->p_fd > 0) && (tp->p_fd != fd))
644 				close(tp->p_fd);
645 		close(p0[1]); close(p1[0]);
646 		if (read(p0[0], &ch, 1) == 1)
647 			write(p1[1], "a", 1);
648 		close(p0[0]); close(p1[1]);
649 	} else {				/* Parent */
650 		close(p0[0]); close(p1[1]);
651 		if (write(p0[1], "a", 1) == 1)
652 			read(p1[0], &ch, 1);
653 		close(p0[1]); close(p1[0]);
654 	}
655 }
656