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