home *** CD-ROM | disk | FTP | other *** search
- /* exp_pty.c - generic routines to allocate and test ptys
-
- Written by: Don Libes, NIST, 3/9/93
-
- Design and implementation of this program was paid for by U.S. tax
- dollars. Therefore it is public domain. However, the author and NIST
- would appreciate credit if this program or parts of it are used.
-
- */
-
- #include "exp_conf.h"
- #ifdef HAVE_UNISTD_H
- # include <unistd.h>
- #endif
- #ifdef HAVE_SYS_FCNTL_H
- # include <sys/fcntl.h>
- #else
- # include <fcntl.h>
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <sys/file.h>
- #include "exp_rename.h"
- #include "exp_pty.h"
-
- extern int errno;
-
- void debuglog();
-
- #ifndef TRUE
- #define TRUE 1
- #define FALSE 0
- #endif
-
- #ifdef O_NOCTTY
- #define RDWR ((O_RDWR)|(O_NOCTTY))
- #else
- #define RDWR O_RDWR
- #endif
-
- static int locked = FALSE;
- static char lock[] = "/tmp/ptylock.XX"; /* XX is replaced by pty id */
- static char locksrc[50] = "/tmp/expect.pid"; /* pid is replaced by real pid */
- /* locksrc is used as the link source, i.e., something to link from */
-
- static int i_read_errno;/* place to save errno, if i_read() == -1, so it
- doesn't get overwritten before we get to read it */
- static jmp_buf env; /* for interruptable read() */
- static int env_valid = FALSE; /* whether we can longjmp or not */
-
- /* sigalarm_handler and i_read are here just for supporting the sanity */
- /* checking of pty slave devices. I have only seen this happen on BSD */
- /* systems, but it may need to be done to the other pty implementations */
- /* as well. */
-
- /* Note that this code is virtually replicated from other code in expect */
- /* At some point, I'll dump one, but not until I'm satisfied no other */
- /* changes are needed */
-
- /*ARGSUSED*/
- static RETSIGTYPE
- sigalarm_handler(n)
- int n; /* unused, for compatibility with STDC */
- {
- #ifdef REARM_SIG
- signal(SIGALRM,sigalarm_handler);
- #endif
-
- /* check env_valid first to protect us from the alarm occurring */
- /* in the window between i_read and alarm(0) */
- if (env_valid) longjmp(env,1);
- }
-
- /* interruptable read */
- static int
- i_read(fd,buffer,length,timeout)
- int fd;
- char *buffer;
- int length;
- int timeout;
- {
- int cc = -2;
-
- /* since setjmp insists on returning 1 upon longjmp(,0), */
- /* longjmp(,2) instead. */
-
- /* restart read if setjmp returns 0 (first time) or 2. */
- /* abort if setjmp returns 1. */
-
- alarm(timeout);
-
- if (1 != setjmp(env)) {
- env_valid = TRUE;
- cc = read(fd,buffer,length);
- }
- env_valid = FALSE;
- i_read_errno = errno; /* errno can be overwritten by the */
- /* time we return */
- alarm(0);
- return(cc);
- }
-
- static RETSIGTYPE (*func)(); /* save old sigalarm handler */
- static time_t current_time; /* time when testing began */
-
- /* if TRUE, begin testing, else end testing */
- /* returns -1 for failure, 0 for success */
- int
- exp_pty_test_start()
- {
- int lfd; /* locksrc file descriptor */
-
- func = signal(SIGALRM,sigalarm_handler);
-
- time(¤t_time);
-
- /* recreate locksrc to prevent locks from 'looking old', so */
- /* that they are not deleted (later on in this code) */
- sprintf(locksrc,"/tmp/expect.%d",getpid());
- (void) unlink(locksrc);
- if (-1 == (lfd = creat(locksrc,0777))) {
- debuglog("can't create %s, errno = %d\n",locksrc, errno);
- return(-1);
- }
- close(lfd);
- return 0;
- }
-
- void
- exp_pty_test_end()
- {
- signal(SIGALRM,func);
- (void) unlink(locksrc);
- }
-
- /* returns non-negative if successful */
- int
- exp_pty_test(master_name,slave_name,bank,num)
- char *master_name;
- char *slave_name;
- int bank;
- int num;
- {
- int master, slave;
- int cc;
- char c;
-
- /* make a lock file to prevent others (for now only */
- /* expects) from allocating pty while we are playing */
- /* with it. This allows us to rigorously test the */
- /* pty is usable. */
- if (exp_pty_lock(bank,num) == 0) {
- debuglog("pty master (%s) is locked...skipping\r\n",master_name);
- return(-1);
- }
- /* verify no one else is using slave by attempting */
- /* to read eof from master side */
- if (0 > (master = open(master_name,RDWR))) return(-1);
- #ifdef HAVE_PTYTRAP
- if (access(slave_name, R_OK|W_OK) != 0) {
- debuglog("could not open slave for pty master (%s)...skipping\r\n",
- master_name);
- (void) close(master);
- return -1;
- }
- return(master);
- #else
- if (0 > (slave = open(slave_name,RDWR))) {
- (void) close(master);
- return -1;
- }
- (void) close(slave);
- cc = i_read(master,&c,1,10);
- (void) close(master);
- if (!(cc == 0 || cc == -1)) {
- debuglog("%s slave open, skipping\r\n",slave_name);
- locked = FALSE; /* leave lock file around so Expect's avoid */
- /* retrying this pty for near future */
- return -1;
- }
-
- /* verify no one else is using master by attempting */
- /* to read eof from slave side */
- if (0 > (master = open(master_name,RDWR))) return(-1);
- if (0 > (slave = open(slave_name,RDWR))) {
- (void) close(master);
- return -1;
- }
- (void) close(master);
- cc = i_read(slave,&c,1,10);
- (void) close(slave);
- if (!(cc == 0 || cc == -1)) {
- debuglog("%s master open, skipping\r\n",master_name);
- return -1;
- }
-
- /* seems ok, let's use it */
- return(open(master_name,RDWR));
- #endif
- }
-
- void
- exp_pty_unlock()
- {
- if (locked) {
- (void) unlink(lock);
- locked = FALSE;
- }
- }
-
- /* returns 1 if successfully locked, 0 otherwise */
- int
- exp_pty_lock(bank,num)
- int bank;
- int num;
- {
- struct stat statbuf;
-
- if (locked) {
- unlink(lock);
- locked = FALSE;
- }
-
- sprintf(lock,"/tmp/ptylock.%c%c",bank,num);
-
- if ((0 == stat(lock,&statbuf)) &&
- (statbuf.st_mtime+3600 < current_time)) {
- (void) unlink(lock);
- }
-
- if (-1 == (link(locksrc,lock))) locked = FALSE;
- else locked = TRUE;
-
- return locked;
- }
-
-