Blame exp_pty.c

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(&current_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
}