diff options
Diffstat (limited to 'exp_pty.c')
-rw-r--r-- | exp_pty.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/exp_pty.c b/exp_pty.c new file mode 100644 index 0000000..0970211 --- /dev/null +++ b/exp_pty.c @@ -0,0 +1,368 @@ +/* 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 "expect_cf.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> + +#ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#include <signal.h> +#include <setjmp.h> +#include <sys/file.h> +#include "tcl.h" +#include "exp_int.h" +#include "expect_comm.h" +#include "exp_rename.h" +#include "exp_pty.h" + +#include <errno.h> + +#if 0 +void expDiagLog(); +void expDiagLogU(); +void expDiagLogPtrSet(); +#endif + +#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.XXXX"; /* 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 */ +#ifdef HAVE_SIGLONGJMP +static sigjmp_buf env; /* for interruptable read() */ +#else +static jmp_buf env; /* for interruptable read() */ +#endif /* HAVE_SIGLONGJMP */ + +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) */ +#ifdef HAVE_SIGLONGJMP + if (env_valid) siglongjmp(env,1); +#else + if (env_valid) longjmp(env,1); +#endif /* HAVE_SIGLONGJMP */ +} + +/* 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); + +#ifdef HAVE_SIGLONGJMP + if (1 != sigsetjmp(env,1)) { +#else + if (1 != setjmp(env)) { +#endif /* HAVE_SIGLONGJMP */ + 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 (*oldAlarmHandler)(); +static RETSIGTYPE (*oldHupHandler)(); +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 */ + + oldAlarmHandler = signal(SIGALRM,sigalarm_handler); +#ifndef O_NOCTTY + /* Ignore hangup signals generated by pty testing */ + /* when running in background with no control tty. */ + /* Very few systems don't define O_NOCTTY. Only one */ + /* I know of is Next. */ + oldAlarmHandler = signal(SIGHUP,SIG_IGN); +#endif + + 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); + /* stanislav shalunov <shalunov@mccme.ru> notes that creat allows */ + /* race - someone could link to important file which root could then */ + /* smash. */ +/* if (-1 == (lfd = creat(locksrc,0777))) { */ + if (-1 == (lfd = open(locksrc,O_RDWR|O_CREAT|O_EXCL,0777))) { + static char buf[256]; + exp_pty_error = buf; + sprintf(exp_pty_error,"can't create %s, errno = %d\n",locksrc, errno); + return(-1); + } + close(lfd); + return 0; +} + +void +exp_pty_test_end() +{ + signal(SIGALRM,oldAlarmHandler); +#ifndef O_NOCTTY + signal(SIGALRM,oldHupHandler); +#endif + (void) unlink(locksrc); +} + +/* returns non-negative if successful */ +int +exp_pty_test( + char *master_name, + char *slave_name, + char bank, + char *num) /* string representation of number */ +{ + 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) { + expDiagLogPtrStr("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 __QNX__ + + /* QNX ptys don't have a lot of the same properties such as + read 0 at EOF, etc */ + /* if 1 should pacify C compiler without using nested ifdefs */ + if (1) return master; +#endif + +#ifdef HAVE_PTYTRAP + if (access(slave_name, R_OK|W_OK) != 0) { + expDiagLogPtrStr("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)) { + expDiagLogPtrStr("%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)) { + expDiagLogPtrStr("%s master open, skipping\r\n",master_name); + return -1; + } + + /* seems ok, let's use it */ + expDiagLogPtrStr("using master pty %s\n",master_name); + return(open(master_name,RDWR)); +#endif +} + +void +exp_pty_unlock(void) +{ + if (locked) { + (void) unlink(lock); + locked = FALSE; + } +} + +/* returns 1 if successfully locked, 0 otherwise */ +int +exp_pty_lock( + char bank, + char *num) /* string representation of number */ +{ + struct stat statbuf; + + if (locked) { + unlink(lock); + locked = FALSE; + } + + sprintf(lock,"/tmp/ptylock.%c%s",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; +} + +/* + * expDiagLog needs a different definition, depending on whether its + * called inside of Expect or the clib. Allow it to be set using this + * function. It's done here because this file (and pty_XXX.c) are the + * ones that call expDiagLog from the two different environments. + */ + +static void (*expDiagLogPtrVal) _ANSI_ARGS_((char *)); + +void +expDiagLogPtrSet(fn) + void (*fn) _ANSI_ARGS_((char *)); +{ + expDiagLogPtrVal = fn; +} + +void +expDiagLogPtr(str) + char *str; +{ + (*expDiagLogPtrVal)(str); +} + + + +void +expDiagLogPtrX(fmt,num) + char *fmt; + int num; +{ + static char buf[1000]; + sprintf(buf,fmt,num); + (*expDiagLogPtrVal)(buf); +} + + +void +expDiagLogPtrStr(fmt,str1) + char *fmt; + char *str1; +{ + static char buf[1000]; + sprintf(buf,fmt,str1); + (*expDiagLogPtrVal)(buf); +} + +void +expDiagLogPtrStrStr(fmt,str1,str2) + char *fmt; + char *str1, *str2; +{ + static char buf[1000]; + sprintf(buf,fmt,str1,str2); + (*expDiagLogPtrVal)(buf); +} + +static char * (*expErrnoMsgVal) _ANSI_ARGS_((int)); + +char * +expErrnoMsg(errorNo) +int errorNo; +{ + return (*expErrnoMsgVal)(errorNo); +} + +void +expErrnoMsgSet(fn) + char * (*fn) _ANSI_ARGS_((int)); +{ + expErrnoMsgVal = fn; +} |