|
Packit |
a69f91 |
/* exp_pty.c - generic routines to allocate and test ptys
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
Written by: Don Libes, NIST, 3/9/93
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
Design and implementation of this program was paid for by U.S. tax
|
|
Packit |
a69f91 |
dollars. Therefore it is public domain. However, the author and NIST
|
|
Packit |
a69f91 |
would appreciate credit if this program or parts of it are used.
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
*/
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#include "expect_cf.h"
|
|
Packit |
a69f91 |
#ifdef HAVE_UNISTD_H
|
|
Packit |
a69f91 |
# include <unistd.h>
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
#ifdef HAVE_SYS_FCNTL_H
|
|
Packit |
a69f91 |
# include <sys/fcntl.h>
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
# include <fcntl.h>
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
#include <sys/types.h>
|
|
Packit |
a69f91 |
#include <sys/stat.h>
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#ifdef TIME_WITH_SYS_TIME
|
|
Packit |
a69f91 |
# include <sys/time.h>
|
|
Packit |
a69f91 |
# include <time.h>
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
# if HAVE_SYS_TIME_H
|
|
Packit |
a69f91 |
# include <sys/time.h>
|
|
Packit |
a69f91 |
# else
|
|
Packit |
a69f91 |
# include <time.h>
|
|
Packit |
a69f91 |
# endif
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#include <signal.h>
|
|
Packit |
a69f91 |
#include <setjmp.h>
|
|
Packit |
a69f91 |
#include <sys/file.h>
|
|
Packit |
a69f91 |
#include "tcl.h"
|
|
Packit |
a69f91 |
#include "exp_int.h"
|
|
Packit |
a69f91 |
#include "expect_comm.h"
|
|
Packit |
a69f91 |
#include "exp_rename.h"
|
|
Packit |
a69f91 |
#include "exp_pty.h"
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#include <errno.h>
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#if 0
|
|
Packit |
a69f91 |
void expDiagLog();
|
|
Packit |
a69f91 |
void expDiagLogU();
|
|
Packit |
a69f91 |
void expDiagLogPtrSet();
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#ifndef TRUE
|
|
Packit |
a69f91 |
#define TRUE 1
|
|
Packit |
a69f91 |
#define FALSE 0
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#ifdef O_NOCTTY
|
|
Packit |
a69f91 |
#define RDWR ((O_RDWR)|(O_NOCTTY))
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
#define RDWR O_RDWR
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
static int locked = FALSE;
|
|
Packit |
a69f91 |
static char lock[] = "/tmp/ptylock.XXXX"; /* XX is replaced by pty id */
|
|
Packit |
a69f91 |
static char locksrc[50] = "/tmp/expect.pid"; /* pid is replaced by real pid */
|
|
Packit |
a69f91 |
/* locksrc is used as the link source, i.e., something to link from */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
static int i_read_errno;/* place to save errno, if i_read() == -1, so it
|
|
Packit |
a69f91 |
doesn't get overwritten before we get to read it */
|
|
Packit |
a69f91 |
#ifdef HAVE_SIGLONGJMP
|
|
Packit |
a69f91 |
static sigjmp_buf env; /* for interruptable read() */
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
static jmp_buf env; /* for interruptable read() */
|
|
Packit |
a69f91 |
#endif /* HAVE_SIGLONGJMP */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
static int env_valid = FALSE; /* whether we can longjmp or not */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* sigalarm_handler and i_read are here just for supporting the sanity */
|
|
Packit |
a69f91 |
/* checking of pty slave devices. I have only seen this happen on BSD */
|
|
Packit |
a69f91 |
/* systems, but it may need to be done to the other pty implementations */
|
|
Packit |
a69f91 |
/* as well. */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* Note that this code is virtually replicated from other code in expect */
|
|
Packit |
a69f91 |
/* At some point, I'll dump one, but not until I'm satisfied no other */
|
|
Packit |
a69f91 |
/* changes are needed */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/*ARGSUSED*/
|
|
Packit |
a69f91 |
static RETSIGTYPE
|
|
Packit |
a69f91 |
sigalarm_handler(n)
|
|
Packit |
a69f91 |
int n; /* unused, for compatibility with STDC */
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
#ifdef REARM_SIG
|
|
Packit |
a69f91 |
signal(SIGALRM,sigalarm_handler);
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* check env_valid first to protect us from the alarm occurring */
|
|
Packit |
a69f91 |
/* in the window between i_read and alarm(0) */
|
|
Packit |
a69f91 |
#ifdef HAVE_SIGLONGJMP
|
|
Packit |
a69f91 |
if (env_valid) siglongjmp(env,1);
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
if (env_valid) longjmp(env,1);
|
|
Packit |
a69f91 |
#endif /* HAVE_SIGLONGJMP */
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* interruptable read */
|
|
Packit |
a69f91 |
static int
|
|
Packit |
a69f91 |
i_read(fd,buffer,length,timeout)
|
|
Packit |
a69f91 |
int fd;
|
|
Packit |
a69f91 |
char *buffer;
|
|
Packit |
a69f91 |
int length;
|
|
Packit |
a69f91 |
int timeout;
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
int cc = -2;
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* since setjmp insists on returning 1 upon longjmp(,0), */
|
|
Packit |
a69f91 |
/* longjmp(,2) instead. */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* restart read if setjmp returns 0 (first time) or 2. */
|
|
Packit |
a69f91 |
/* abort if setjmp returns 1. */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
alarm(timeout);
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#ifdef HAVE_SIGLONGJMP
|
|
Packit |
a69f91 |
if (1 != sigsetjmp(env,1)) {
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
if (1 != setjmp(env)) {
|
|
Packit |
a69f91 |
#endif /* HAVE_SIGLONGJMP */
|
|
Packit |
a69f91 |
env_valid = TRUE;
|
|
Packit |
a69f91 |
cc = read(fd,buffer,length);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
env_valid = FALSE;
|
|
Packit |
a69f91 |
i_read_errno = errno; /* errno can be overwritten by the */
|
|
Packit |
a69f91 |
/* time we return */
|
|
Packit |
a69f91 |
alarm(0);
|
|
Packit |
a69f91 |
return(cc);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
static RETSIGTYPE (*oldAlarmHandler)();
|
|
Packit |
a69f91 |
static RETSIGTYPE (*oldHupHandler)();
|
|
Packit |
a69f91 |
static time_t current_time; /* time when testing began */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* if TRUE, begin testing, else end testing */
|
|
Packit |
a69f91 |
/* returns -1 for failure, 0 for success */
|
|
Packit |
a69f91 |
int
|
|
Packit |
a69f91 |
exp_pty_test_start()
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
int lfd; /* locksrc file descriptor */
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
oldAlarmHandler = signal(SIGALRM,sigalarm_handler);
|
|
Packit |
a69f91 |
#ifndef O_NOCTTY
|
|
Packit |
a69f91 |
/* Ignore hangup signals generated by pty testing */
|
|
Packit |
a69f91 |
/* when running in background with no control tty. */
|
|
Packit |
a69f91 |
/* Very few systems don't define O_NOCTTY. Only one */
|
|
Packit |
a69f91 |
/* I know of is Next. */
|
|
Packit |
a69f91 |
oldAlarmHandler = signal(SIGHUP,SIG_IGN);
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
time(¤t_time);
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* recreate locksrc to prevent locks from 'looking old', so */
|
|
Packit |
a69f91 |
/* that they are not deleted (later on in this code) */
|
|
Packit |
a69f91 |
sprintf(locksrc,"/tmp/expect.%d",getpid());
|
|
Packit |
a69f91 |
(void) unlink(locksrc);
|
|
Packit |
a69f91 |
/* stanislav shalunov <shalunov@mccme.ru> notes that creat allows */
|
|
Packit |
a69f91 |
/* race - someone could link to important file which root could then */
|
|
Packit |
a69f91 |
/* smash. */
|
|
Packit |
a69f91 |
/* if (-1 == (lfd = creat(locksrc,0777))) { */
|
|
Packit |
a69f91 |
if (-1 == (lfd = open(locksrc,O_RDWR|O_CREAT|O_EXCL,0777))) {
|
|
Packit |
a69f91 |
static char buf[256];
|
|
Packit |
a69f91 |
exp_pty_error = buf;
|
|
Packit |
a69f91 |
sprintf(exp_pty_error,"can't create %s, errno = %d\n",locksrc, errno);
|
|
Packit |
a69f91 |
return(-1);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
close(lfd);
|
|
Packit |
a69f91 |
return 0;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
exp_pty_test_end()
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
signal(SIGALRM,oldAlarmHandler);
|
|
Packit |
a69f91 |
#ifndef O_NOCTTY
|
|
Packit |
a69f91 |
signal(SIGALRM,oldHupHandler);
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
(void) unlink(locksrc);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* returns non-negative if successful */
|
|
Packit |
a69f91 |
int
|
|
Packit |
a69f91 |
exp_pty_test(
|
|
Packit |
a69f91 |
char *master_name,
|
|
Packit |
a69f91 |
char *slave_name,
|
|
Packit |
a69f91 |
char bank,
|
|
Packit |
a69f91 |
char *num) /* string representation of number */
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
int master, slave;
|
|
Packit |
a69f91 |
int cc;
|
|
Packit |
a69f91 |
char c;
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* make a lock file to prevent others (for now only */
|
|
Packit |
a69f91 |
/* expects) from allocating pty while we are playing */
|
|
Packit |
a69f91 |
/* with it. This allows us to rigorously test the */
|
|
Packit |
a69f91 |
/* pty is usable. */
|
|
Packit |
a69f91 |
if (exp_pty_lock(bank,num) == 0) {
|
|
Packit |
a69f91 |
expDiagLogPtrStr("pty master (%s) is locked...skipping\r\n",master_name);
|
|
Packit |
a69f91 |
return(-1);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
/* verify no one else is using slave by attempting */
|
|
Packit |
a69f91 |
/* to read eof from master side */
|
|
Packit |
a69f91 |
if (0 > (master = open(master_name,RDWR))) return(-1);
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#ifdef __QNX__
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* QNX ptys don't have a lot of the same properties such as
|
|
Packit |
a69f91 |
read 0 at EOF, etc */
|
|
Packit |
a69f91 |
/* if 1 should pacify C compiler without using nested ifdefs */
|
|
Packit |
a69f91 |
if (1) return master;
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
#ifdef HAVE_PTYTRAP
|
|
Packit |
a69f91 |
if (access(slave_name, R_OK|W_OK) != 0) {
|
|
Packit |
a69f91 |
expDiagLogPtrStr("could not open slave for pty master (%s)...skipping\r\n",
|
|
Packit |
a69f91 |
master_name);
|
|
Packit |
a69f91 |
(void) close(master);
|
|
Packit |
a69f91 |
return -1;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
return(master);
|
|
Packit |
a69f91 |
#else
|
|
Packit |
a69f91 |
if (0 > (slave = open(slave_name,RDWR))) {
|
|
Packit |
a69f91 |
(void) close(master);
|
|
Packit |
a69f91 |
return -1;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
(void) close(slave);
|
|
Packit |
a69f91 |
cc = i_read(master,&c,1,10);
|
|
Packit |
a69f91 |
(void) close(master);
|
|
Packit |
a69f91 |
if (!(cc == 0 || cc == -1)) {
|
|
Packit |
a69f91 |
expDiagLogPtrStr("%s slave open, skipping\r\n",slave_name);
|
|
Packit |
a69f91 |
locked = FALSE; /* leave lock file around so Expect's avoid */
|
|
Packit |
a69f91 |
/* retrying this pty for near future */
|
|
Packit |
a69f91 |
return -1;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* verify no one else is using master by attempting */
|
|
Packit |
a69f91 |
/* to read eof from slave side */
|
|
Packit |
a69f91 |
if (0 > (master = open(master_name,RDWR))) return(-1);
|
|
Packit |
a69f91 |
if (0 > (slave = open(slave_name,RDWR))) {
|
|
Packit |
a69f91 |
(void) close(master);
|
|
Packit |
a69f91 |
return -1;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
(void) close(master);
|
|
Packit |
a69f91 |
cc = i_read(slave,&c,1,10);
|
|
Packit |
a69f91 |
(void) close(slave);
|
|
Packit |
a69f91 |
if (!(cc == 0 || cc == -1)) {
|
|
Packit |
a69f91 |
expDiagLogPtrStr("%s master open, skipping\r\n",master_name);
|
|
Packit |
a69f91 |
return -1;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* seems ok, let's use it */
|
|
Packit |
a69f91 |
expDiagLogPtrStr("using master pty %s\n",master_name);
|
|
Packit |
a69f91 |
return(open(master_name,RDWR));
|
|
Packit |
a69f91 |
#endif
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
exp_pty_unlock(void)
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
if (locked) {
|
|
Packit |
a69f91 |
(void) unlink(lock);
|
|
Packit |
a69f91 |
locked = FALSE;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/* returns 1 if successfully locked, 0 otherwise */
|
|
Packit |
a69f91 |
int
|
|
Packit |
a69f91 |
exp_pty_lock(
|
|
Packit |
a69f91 |
char bank,
|
|
Packit |
a69f91 |
char *num) /* string representation of number */
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
struct stat statbuf;
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
if (locked) {
|
|
Packit |
a69f91 |
unlink(lock);
|
|
Packit |
a69f91 |
locked = FALSE;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
sprintf(lock,"/tmp/ptylock.%c%s",bank,num);
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
if ((0 == stat(lock,&statbuf)) &&
|
|
Packit |
a69f91 |
(statbuf.st_mtime+3600 < current_time)) {
|
|
Packit |
a69f91 |
(void) unlink(lock);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
if (-1 == (link(locksrc,lock))) locked = FALSE;
|
|
Packit |
a69f91 |
else locked = TRUE;
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
return locked;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
/*
|
|
Packit |
a69f91 |
* expDiagLog needs a different definition, depending on whether its
|
|
Packit |
a69f91 |
* called inside of Expect or the clib. Allow it to be set using this
|
|
Packit |
a69f91 |
* function. It's done here because this file (and pty_XXX.c) are the
|
|
Packit |
a69f91 |
* ones that call expDiagLog from the two different environments.
|
|
Packit |
a69f91 |
*/
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
static void (*expDiagLogPtrVal) _ANSI_ARGS_((char *));
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
expDiagLogPtrSet(fn)
|
|
Packit |
a69f91 |
void (*fn) _ANSI_ARGS_((char *));
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
expDiagLogPtrVal = fn;
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
expDiagLogPtr(str)
|
|
Packit |
a69f91 |
char *str;
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
(*expDiagLogPtrVal)(str);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
expDiagLogPtrX(fmt,num)
|
|
Packit |
a69f91 |
char *fmt;
|
|
Packit |
a69f91 |
int num;
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
static char buf[1000];
|
|
Packit |
a69f91 |
sprintf(buf,fmt,num);
|
|
Packit |
a69f91 |
(*expDiagLogPtrVal)(buf);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
expDiagLogPtrStr(fmt,str1)
|
|
Packit |
a69f91 |
char *fmt;
|
|
Packit |
a69f91 |
char *str1;
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
static char buf[1000];
|
|
Packit |
a69f91 |
sprintf(buf,fmt,str1);
|
|
Packit |
a69f91 |
(*expDiagLogPtrVal)(buf);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
expDiagLogPtrStrStr(fmt,str1,str2)
|
|
Packit |
a69f91 |
char *fmt;
|
|
Packit |
a69f91 |
char *str1, *str2;
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
static char buf[1000];
|
|
Packit |
a69f91 |
sprintf(buf,fmt,str1,str2);
|
|
Packit |
a69f91 |
(*expDiagLogPtrVal)(buf);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
static char * (*expErrnoMsgVal) _ANSI_ARGS_((int));
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
char *
|
|
Packit |
a69f91 |
expErrnoMsg(errorNo)
|
|
Packit |
a69f91 |
int errorNo;
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
return (*expErrnoMsgVal)(errorNo);
|
|
Packit |
a69f91 |
}
|
|
Packit |
a69f91 |
|
|
Packit |
a69f91 |
void
|
|
Packit |
a69f91 |
expErrnoMsgSet(fn)
|
|
Packit |
a69f91 |
char * (*fn) _ANSI_ARGS_((int));
|
|
Packit |
a69f91 |
{
|
|
Packit |
a69f91 |
expErrnoMsgVal = fn;
|
|
Packit |
a69f91 |
}
|