/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) tty.c (Gunther Schadow) 11/15/96"); #include "socket.h" #include #include #include #include #include #include #include #include #include #ifdef _AIX # include #endif #define TTY_REOPEN_DELAY 2 /* seconds */ #ifndef LOCK_DIR # ifdef hpux # define LOCK_DIR "/usr/spool/uucp" # else # define LOCK_DIR "/var/spool/lock" # endif #endif static char *tty_fname[NOFILE]; static char *tty_lockname[NOFILE]; static result configure_tty(int fd); bool accept_tty_immediately = FALSE; static void fnsplit(char *fname, char **basename, char **dirname) { if(dirname != NULL) *dirname = fname; do { *basename = fname; fname = strchr(*basename, '/'); } while(fname++ != NULL); if(dirname != NULL && *basename != *dirname) *(*basename - 1) = 0; } static int open_tty(const char *address) { int fd, lck; char *basename, *dirname; char *namebuf = strdup(address); char *fname = strdup(address); char *lockname = (char *)malloc(sizeof(LOCK_DIR "/" "LCK..") + strlen(address)); DBG(syslog(LOG_DEBUG, "opening tty '%s'", address)); fnsplit(namebuf, &basename, &dirname); strcpy(lockname, LOCK_DIR "/LCK.."); strcat(lockname, basename); open_lock_again: lck = open(lockname, O_WRONLY | O_CREAT | O_EXCL, 0644); if(lck == FAIL) switch(errno) { case EINTR: goto open_lock_again; case EEXIST: /* lock exists already */ { /* check whether the lock is stale: */ pid_t pid; char pidbuf[11]; int n; /* - get the lock holder's process id */ lck = open(lockname, O_RDONLY); if(lck == FAIL) { syslog(LOG_ERR, __FUNCTION__ ":open %s: %m", lockname); goto fail; } read_again: n = read(lck, pidbuf, 10); switch(n) { case FAIL: if(errno == EINTR) goto read_again; syslog(LOG_ERR, __FUNCTION__ ":read from %s: %m", lockname); goto fail; case 0: syslog(LOG_ERR, __FUNCTION__ ":lockfile %s is empty", lockname); goto unlink_lock; default: { char *endp; pid = (pid_t)strtoul(pidbuf, &endp, 0); if(endp == pidbuf) { syslog(LOG_ERR, __FUNCTION__ ":lockfile %s contains junk:" "`%s'", lockname, pidbuf); goto unlink_lock; } } } close(lck); /* - test if process is still alive */ if(kill(pid, 0) == SUCCESS) { syslog(LOG_ERR, __FUNCTION__ "device %s is locked", fname); return FAIL; } else if(errno != ESRCH) { syslog(LOG_ERR, __FUNCTION__ ":kill %d: %m", pid); goto fail; } unlink_lock: syslog(LOG_NOTICE, "removing stale lock %s (pid %d terminated)", lockname, pid); if(unlink(lockname) == FAIL) { syslog(LOG_ERR, __FUNCTION__ ":unlink %s: %m", lockname); goto fail; } goto open_lock_again; } default: syslog(LOG_ERR, __FUNCTION__ ":open %s: %m", lockname); goto fail; } else /* lck is the valid lock file descriptor */ { char buf[12]; sprintf(buf, "%10d\n", (int)getpid()); write(lck, buf, strlen(buf)); close(lck); } open_tty_again: fd = open(fname, O_RDWR); if(fd == FAIL) { switch(errno) { case EINTR: goto open_tty_again; default: syslog(LOG_ERR, "open %s: %m", fname); unlink(lockname); goto fail; } } tty_fname[fd] = fname; tty_lockname[fd] = lockname; if(configure_tty(fd) == FAIL) { close(fd); goto fail; } free(namebuf); return fd; fail: free(fname); free(namebuf); free(lockname); return FAIL; } int connect_tty(const char *address) { return open_tty(address); } int bind_tty(const char *address) { return open_tty(address); } int accept_tty(int fd) { fd_set rcfds; int nfds; int lck; /* Update the lock holder's process number. This is a hack! * There is a vulnerable phase before the first accept, and * it is superflous afterwards. FIXME! */ if((lck = open(tty_lockname[fd], O_WRONLY)) == FAIL) { syslog(LOG_ERR, __FUNCTION__ ": can't open my lockfile %s: %s", tty_lockname[fd], sys_errlist[errno]); return FAIL; } else { char buf[12]; sprintf(buf, "%10d\n", (int)getpid()); write(lck, buf, strlen(buf)); close(lck); } if(!accept_tty_immediately) { syslog(LOG_INFO, __FUNCTION__ ": listening ..."); FD_ZERO(&rcfds); FD_SET(fd, &rcfds); select_again: nfds = select(FD_SETSIZE, &rcfds, NULL, NULL, NULL); if (nfds == FAIL) { if(errno == EINTR) goto select_again; else { syslog(LOG_ERR, __FUNCTION__ ":select: %m"); return FAIL; } } /* * Select returned with data ready. At this time we will not read * anything, since our job was only to listen if something happens. */ } return dup(fd); } result peername_tty(int s, peername_t peer) { strcpy(peer, "tty"); return SUCCESS; } result shutdown_tty(int fd) { close(fd); unlink(tty_lockname[fd]); free(tty_fname[fd]); free(tty_lockname[fd]); return SUCCESS; } int reset_tty(int fd) { int nfd; /* The mfd file desciptor must be the last one that referes to the * tty. Otherwise the client will never notice that we just closed * the file. */ close(fd); sleep(TTY_REOPEN_DELAY); unlink(tty_lockname[fd]); if((fd = open_tty(tty_fname[fd])) == FAIL) { free(tty_fname[fd]); unlink(tty_lockname[fd]); free(tty_lockname[fd]); return FAIL; } if(fd != nfd) { free(tty_fname[fd]); free(tty_lockname[fd]); } return fd; } /* TTY MODES */ tcflag_t tty_iflags = ( IGNBRK | IGNPAR ); tcflag_t tty_oflags = 0; tcflag_t tty_cflags = ( CS8 | CREAD | HUPCL ); result configure_tty(int fd) { struct termios tio; CHK(tcgetattr(fd, &tio), FAIL, return FAIL); tio.c_iflag = tty_iflags; tio.c_oflag = tty_oflags; tio.c_cflag = tty_cflags; tio.c_lflag = 0; tio.c_cc[VMIN] = 0; tio.c_cc[VTIME] = 0; CHK(cfsetspeed(&tio, 9600), FAIL, return FAIL); CHK(tcsetattr(fd, TCSANOW, &tio), FAIL, return FAIL); return SUCCESS; }