summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ttymon/tmhandler.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/ttymon/tmhandler.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/ttymon/tmhandler.c')
-rw-r--r--usr/src/cmd/ttymon/tmhandler.c658
1 files changed, 658 insertions, 0 deletions
diff --git a/usr/src/cmd/ttymon/tmhandler.c b/usr/src/cmd/ttymon/tmhandler.c
new file mode 100644
index 0000000000..b91b198c43
--- /dev/null
+++ b/usr/src/cmd/ttymon/tmhandler.c
@@ -0,0 +1,658 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+#include <termio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stropts.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include "ttymon.h"
+#include "tmstruct.h"
+#include "tmextern.h"
+#include "sac.h"
+
+extern int Retry;
+static struct pmtab *find_pid();
+static void kill_children();
+
+static struct pmtab *find_fd();
+static void pcsync_close();
+extern void sigalarm();
+extern void tmchild();
+
+/*
+ * fork_tmchild - fork child on the device
+ */
+static void
+fork_tmchild(pmptr)
+struct pmtab *pmptr;
+{
+ pid_t pid;
+ sigset_t cset;
+ sigset_t tset;
+ int pcpipe0[2], pcpipe1[2];
+ int p0;
+
+#ifdef DEBUG
+ debug("in fork_tmchild");
+#endif
+ pmptr->p_inservice = FALSE;
+
+ /*
+ * initialize pipe.
+ * Child has pcpipe[0] pipe fd for reading and writing
+ * and closes pcpipe[1]. Parent has pcpipe[1] pipe fd for
+ * reading and writing and closes pcpipe[0].
+ *
+ * This way if the child process exits the parent's block
+ * read on pipe will return immediately as the other end of
+ * the pipe has closed. Similarly if the parent process exits
+ * child's blocking read on the pipe will return immediately.
+ */
+
+ if (((p0 = pipe(pcpipe0)) == -1) || (pipe(pcpipe1) == -1)) {
+ if (p0 == 0) {
+ close(pcpipe0[0]);
+ close(pcpipe0[1]);
+ }
+ log("pipe() failed: %s", strerror(errno));
+ pmptr->p_status = VALID;
+ pmptr->p_pid = 0;
+ Retry = TRUE;
+ }
+
+ /* protect following region from SIGCLD */
+ (void)sigprocmask(SIG_SETMASK, NULL, &cset);
+ tset = cset;
+ (void)sigaddset(&tset, SIGCLD);
+ (void)sigprocmask(SIG_SETMASK, &tset, NULL);
+ if( (pid=fork()) == 0 ) {
+ /*
+ * Close all file descriptors except pmptr->p_fd
+ * Wait for the parent process to close its fd
+ */
+ pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd);
+ /* The CHILD */
+ tmchild(pmptr);
+ /* tmchild should never return */
+ fatal("tmchild for <%s> returns unexpected", pmptr->p_device);
+ }
+ else if (pid < 0) {
+ log("fork failed: %s", strerror(errno));
+ pmptr->p_status = VALID;
+ pmptr->p_pid = 0;
+ Retry = TRUE;
+ }
+ else {
+ /*
+ * The PARENT - store pid of child and close the device
+ */
+ pmptr->p_pid = pid;
+ }
+ if (pmptr->p_fd > 0) {
+ (void)close(pmptr->p_fd);
+ pmptr->p_fd = 0;
+ }
+ (void)sigprocmask(SIG_SETMASK, &cset, NULL);
+ /*
+ * Wait for child to close file descriptors
+ */
+ pcsync_close(pcpipe0, pcpipe1, pid, pmptr->p_fd);
+}
+
+/*
+ * got_carrier - carrier is detected on the stream
+ * - depends on the flags, different action is taken
+ * - R_FLAG - wait for data
+ * - C_FLAG - if port is not disabled, fork tmchild
+ * - A_FLAG - wait for data
+ * - otherwise - write out prompt, then wait for data
+ */
+void
+got_carrier(pmptr)
+struct pmtab *pmptr;
+{
+ flush_input(pmptr->p_fd);
+
+ if (pmptr->p_ttyflags & R_FLAG) {
+#ifdef DEBUG
+ debug("R_FLAG");
+#endif
+ return;
+ }
+ else if ((pmptr->p_ttyflags & (C_FLAG|B_FLAG)) &&
+ (State != PM_DISABLED) &&
+ (!(pmptr->p_flags & X_FLAG))) {
+ fork_tmchild(pmptr);
+ }
+ else if (pmptr->p_ttyflags & A_FLAG) {
+#ifdef DEBUG
+ debug("A_FLAG");
+#endif
+ return;
+ }
+ else if (pmptr->p_timeout) {
+ fork_tmchild(pmptr);
+ }
+ else if ( ! (pmptr->p_ttyflags & X_FLAG) ) {
+ write_prompt(pmptr->p_fd,pmptr,TRUE,TRUE);
+ }
+}
+
+/*
+ * got_data - data is detected on the stream, fork tmchild
+ */
+static void
+got_data(pmptr)
+struct pmtab *pmptr;
+{
+ struct sigaction sigact;
+
+ if (tm_checklock(pmptr->p_fd) != 0) {
+ pmptr->p_status = LOCKED;
+ (void)close(pmptr->p_fd);
+ pmptr->p_fd = 0;
+ Nlocked++;
+ if (Nlocked == 1) {
+ sigact.sa_flags = 0;
+ sigact.sa_handler = sigalarm;
+ (void)sigemptyset(&sigact.sa_mask);
+ (void)sigaction(SIGALRM, &sigact, NULL);
+ (void)alarm(ALARMTIME);
+ }
+ }
+ else
+ fork_tmchild(pmptr);
+}
+/*
+ * got_hup - stream hangup is detected, close the device
+ */
+static void
+got_hup(pmptr)
+struct pmtab *pmptr;
+{
+#ifdef DEBUG
+ debug("in got hup");
+#endif
+ (void)close(pmptr->p_fd);
+ pmptr->p_fd = 0;
+ pmptr->p_inservice = 0;
+ Retry = TRUE;
+}
+
+
+/*
+ * do_poll - poll device
+ * - if POLLHUP received, close the device
+ * - if POLLIN received, fork tmchild.
+ */
+void
+do_poll(fdp,nfds)
+struct pollfd *fdp;
+int nfds;
+{
+ int i,n;
+ struct pmtab *pmptr;
+
+ n = poll(fdp, (unsigned long)nfds, -1); /* blocked poll */
+#ifdef DEBUG
+ debug("poll return");
+#endif
+ if (n < 0) {
+ if (errno == EINTR) /* interrupt by signal */
+ return;
+ fatal("do_poll: poll failed: %s", strerror(errno));
+ }
+ for (i = 0; (i < nfds)&&(n); i++,fdp++) {
+ if (fdp->revents != 0) {
+ n--;
+ if ((pmptr = find_fd(fdp->fd)) == NULL) {
+ log("do_poll: cannot find fd %d in pmtab",
+ fdp->fd);
+ continue;
+ }
+ else if (fdp->revents & POLLHUP) {
+ got_hup(pmptr);
+ }
+ else if (fdp->revents & POLLIN) {
+#ifdef DEBUG
+ debug("got POLLIN");
+#endif
+ got_data(pmptr);
+ } else if (fdp->revents & POLLERR) {
+ fatal("ttymon[%d]: do_poll: POLLERR on fd %d",
+ getpid(), fdp->fd);
+ }
+ }
+ }
+}
+
+/*
+ * sigchild - handler for SIGCLD
+ * - find the pid of dead child
+ * - clean utmp if U_FLAG is set
+ */
+void
+/*ARGSUSED*/
+sigchild(n)
+int n; /* this is declared to make cc happy, but it is not used */
+{
+ struct pmtab *pmptr;
+ struct sigaction sigact;
+ siginfo_t info;
+ int status;
+ pid_t pid;
+ int rcode;
+
+#ifdef DEBUG
+ debug("in sigchild");
+#endif
+
+ /* find all processes that died */
+ for (;;) {
+ rcode = waitid(P_ALL, 0, &info, WNOHANG|WEXITED);
+ if (rcode == -1 && errno == EINTR)
+ continue;
+
+ /* If no more children have exited, just return */
+ if (rcode == -1 || (pid = info.si_pid) == 0)
+ break;
+
+ /* construct status as returned from waitid() */
+ status = info.si_status & 0377;
+ switch (info.si_code) {
+ case CLD_EXITED:
+ status <<= 8;
+ break;
+ case CLD_DUMPED:
+ status |= WCOREFLG;
+ break;
+ case CLD_KILLED:
+ break;
+ }
+
+ if ((pmptr = find_pid(pid)) == NULL) {
+#ifdef DEBUG
+ log("cannot find dead child (%ld) in pmtab", pid);
+#endif
+ /*
+ * This may happen if the entry is deleted from pmtab
+ * before the service exits.
+ * We try to cleanup utmp entry
+ */
+ cleanut(pid, status);
+ } else {
+ if (pmptr->p_flags & U_FLAG)
+ cleanut(pid, status);
+ pmptr->p_status = VALID;
+ pmptr->p_fd = 0;
+ pmptr->p_pid = 0;
+ pmptr->p_inservice = 0;
+ Retry = TRUE;
+ }
+ }
+}
+
+/*
+ * sigterm - handler for SIGTERM
+ */
+void
+sigterm()
+{
+ fatal("caught SIGTERM");
+}
+
+/*
+ * state_change - this is called when ttymon changes
+ * its internal state between enabled and disabled
+ */
+void
+state_change()
+{
+ struct pmtab *pmptr;
+
+#ifdef DEBUG
+ debug("in state_change");
+#endif
+
+ /*
+ * closing PCpipe will cause attached non-service children
+ * to get SIGPOLL and exit
+ */
+ (void)close(PCpipe[0]);
+ (void)close(PCpipe[1]);
+
+ /* reopen PCpipe */
+ setup_PCpipe();
+
+ /*
+ * also close all open ports so ttymon can start over
+ * with new internal state
+ */
+ for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
+ if ((pmptr->p_fd > 0) && (pmptr->p_pid == 0)) {
+ (void)close(pmptr->p_fd);
+ pmptr->p_fd = 0;
+ }
+ }
+ Retry = TRUE;
+
+}
+
+/*
+ * re_read - reread pmtab
+ * - kill tmchild if entry changed
+ */
+void
+re_read()
+{
+ extern struct pollfd *Pollp;
+ sigset_t cset;
+ sigset_t tset;
+
+ (void)sigprocmask(SIG_SETMASK, NULL, &cset);
+ tset = cset;
+ (void)sigaddset(&tset, SIGCLD);
+ (void)sigprocmask(SIG_SETMASK, &tset, NULL);
+ if (Nlocked > 0) {
+ alarm(0);
+ Nlocked = 0;
+ }
+ read_pmtab();
+ kill_children();
+ (void)sigprocmask(SIG_SETMASK, &cset, NULL);
+ purge();
+
+ if (Nentries > Npollfd) {
+#ifdef DEBUG
+ debug("Nentries > Npollfd, reallocating pollfds");
+#endif
+ /* need to malloc more pollfd structure */
+ free((char *)Pollp);
+ Npollfd = Nentries + 10;
+ if (Npollfd > Maxfds)
+ Npollfd = Maxfds;
+ if ((Pollp = (struct pollfd *)
+ malloc((unsigned)(Npollfd * sizeof(struct pollfd))))
+ == (struct pollfd *)NULL)
+ fatal("malloc for Pollp failed");
+ }
+ Retry = TRUE;
+}
+
+/*
+ * find_pid(pid) - find the corresponding pmtab entry for the pid
+ */
+static struct pmtab *
+find_pid(pid)
+pid_t pid;
+{
+ struct pmtab *pmptr;
+
+ for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
+ if (pmptr->p_pid == pid) {
+ return(pmptr);
+ }
+ }
+ return((struct pmtab *)NULL);
+}
+
+/*
+ * find_fd(fd) - find the corresponding pmtab entry for the fd
+ */
+static struct pmtab *
+find_fd(fd)
+int fd;
+{
+ struct pmtab *pmptr;
+
+ for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
+ if (pmptr->p_fd == fd) {
+ return(pmptr);
+ }
+ }
+ return((struct pmtab *)NULL);
+}
+
+/*
+ * kill_children() - if the pmtab entry has been changed,
+ * kill tmchild if it is not in service.
+ * - close the device if there is no tmchild
+ */
+static void
+kill_children()
+{
+ struct pmtab *pmptr;
+ for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
+ if (pmptr->p_status == VALID)
+ continue;
+ if ((pmptr->p_fd > 0) && (pmptr->p_pid == 0)) {
+ (void)close(pmptr->p_fd);
+ pmptr->p_fd = 0;
+ }
+ else if ((pmptr->p_fd == 0) && (pmptr->p_pid > 0)
+ && (pmptr->p_inservice == FALSE)) {
+ (void)kill(pmptr->p_pid, SIGTERM);
+ }
+ }
+}
+
+static void
+mark_service(pid)
+pid_t pid;
+{
+ struct pmtab *pmptr;
+#ifdef DEBUG
+ debug("in mark_service");
+#endif
+ if ((pmptr = find_pid(pid)) == NULL) {
+ log("mark_service: cannot find child (%ld) in pmtab", pid);
+ return;
+ }
+ pmptr->p_inservice = TRUE;
+ return;
+}
+
+/*
+ * read_pid(fd) - read pid info from PCpipe
+ */
+static void
+read_pid(fd)
+int fd;
+{
+ int ret;
+ pid_t pid;
+
+ for (;;) {
+ if ((ret = read(fd,&pid,sizeof(pid))) < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return;
+ fatal("read PCpipe failed: %s", strerror(errno));
+ }
+ if (ret == 0)
+ return;
+ if (ret != sizeof(pid))
+ fatal("read return size incorrect, ret = %d", ret);
+
+ mark_service(pid);
+ }
+}
+
+/*
+ * sipoll_catch() - signal handle of SIGPOLL for ttymon
+ * - it will check both PCpipe and pmpipe
+ */
+void
+sigpoll_catch()
+{
+ int ret;
+ struct pollfd pfd[2];
+
+#ifdef DEBUG
+ debug("in sigpoll_catch");
+#endif
+
+ pfd[0].fd = PCpipe[0];
+ pfd[1].fd = Pfd;
+ pfd[0].events = POLLIN;
+ pfd[1].events = POLLIN;
+ if ((ret = poll(pfd, 2, 0)) < 0)
+ fatal("sigpoll_catch: poll failed: %s", strerror(errno));
+
+ if (ret > 0) {
+ if (pfd[0].revents & POLLIN)
+ read_pid(pfd[0].fd);
+ if (pfd[1].revents & POLLIN)
+ sacpoll();
+ }
+}
+
+/*ARGSUSED*/
+void
+sigalarm(signo)
+int signo;
+{
+ struct pmtab *pmptr;
+ struct sigaction sigact;
+ int fd;
+ extern int check_session();
+
+#ifdef DEBUG
+ debug("in sigalarm, Nlocked = %d", Nlocked);
+#endif
+ for (pmptr = PMtab; pmptr; pmptr = pmptr->p_next) {
+ if ((pmptr->p_status == LOCKED) && (pmptr->p_fd == 0)) {
+ if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){
+ log("open (%s) failed: %s", pmptr->p_device,
+ strerror(errno));
+ pmptr->p_status = VALID;
+ Nlocked--;
+ Retry = TRUE;
+ }
+ else {
+ if (tm_checklock(fd) == 0) {
+ Nlocked--;
+ pmptr->p_fd = fd;
+ Retry = TRUE;
+ }
+ else
+ (void)close(fd);
+ }
+ }
+ else if ((pmptr->p_status == SESSION) && (pmptr->p_fd == 0)) {
+ if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){
+ log("open (%s) failed: %s", pmptr->p_device,
+ strerror(errno));
+ pmptr->p_status = VALID;
+ Nlocked--;
+ Retry = TRUE;
+ }
+ else {
+ if (check_session(fd) == 0) {
+ Nlocked--;
+ pmptr->p_fd = fd;
+ Retry = TRUE;
+ }
+ else
+ (void)close(fd);
+ }
+ }
+ else if ((pmptr->p_status == UNACCESS) && (pmptr->p_fd == 0)) {
+ if ((fd=open(pmptr->p_device,O_RDWR|O_NONBLOCK)) == -1){
+ log("open (%s) failed: %s", pmptr->p_device,
+ strerror(errno));
+ pmptr->p_status = VALID;
+ Nlocked--;
+ Retry = TRUE;
+ }
+ else {
+ Nlocked--;
+ pmptr->p_fd = fd;
+ Retry = TRUE;
+ }
+ }
+ }
+ if (Nlocked > 0) {
+ sigact.sa_flags = 0;
+ sigact.sa_handler = sigalarm;
+ (void)sigemptyset(&sigact.sa_mask);
+ (void)sigaction(SIGALRM, &sigact, NULL);
+ (void)alarm(ALARMTIME);
+ }
+ else {
+ sigact.sa_flags = 0;
+ sigact.sa_handler = SIG_IGN;
+ (void)sigemptyset(&sigact.sa_mask);
+ (void)sigaction(SIGALRM, &sigact, NULL);
+ }
+}
+
+/*
+ * pcsync_close - For the child process close all open fd's except
+ * the one that is passed to the routine. Coordinate the reads and
+ * writes to the pipes by the parent and child process to ensure
+ * the parent and child processes have closed all the file descriptors
+ * that are not needed any more.
+ */
+static void
+pcsync_close(p0, p1, pid, fd)
+int *p0;
+int *p1;
+int pid;
+{
+ char ch;
+
+ if (pid == 0) { /* Child */
+ struct pmtab *tp;
+ for (tp = PMtab; tp; tp = tp->p_next)
+ if ((tp->p_fd > 0) && (tp->p_fd != fd))
+ close(tp->p_fd);
+ close(p0[1]); close(p1[0]);
+ if (read(p0[0], &ch, 1) == 1)
+ write(p1[1], "a", 1);
+ close(p0[0]); close(p1[1]);
+ } else { /* Parent */
+ close(p0[0]); close(p1[1]);
+ if (write(p0[1], "a", 1) == 1)
+ read(p1[0], &ch, 1);
+ close(p0[1]); close(p1[0]);
+ }
+}