1 /* exp_pty.c - generic routines to allocate and test ptys
3 Written by: Don Libes, NIST, 3/9/93
5 Design and implementation of this program was paid for by U.S. tax
6 dollars. Therefore it is public domain. However, the author and NIST
7 would appreciate credit if this program or parts of it are used.
11 #include "expect_cf.h"
15 #ifdef HAVE_SYS_FCNTL_H
16 # include <sys/fcntl.h>
20 #include <sys/types.h>
23 #ifdef TIME_WITH_SYS_TIME
24 # include <sys/time.h>
28 # include <sys/time.h>
39 #include "expect_comm.h"
40 #include "exp_rename.h"
48 void expDiagLogPtrSet();
57 #define RDWR ((O_RDWR)|(O_NOCTTY))
62 static int locked = FALSE;
63 static char lock[] = "/tmp/ptylock.XXXX"; /* XX is replaced by pty id */
64 static char locksrc[50] = "/tmp/expect.pid"; /* pid is replaced by real pid */
65 /* locksrc is used as the link source, i.e., something to link from */
67 static int i_read_errno;/* place to save errno, if i_read() == -1, so it
68 doesn't get overwritten before we get to read it */
69 #ifdef HAVE_SIGLONGJMP
70 static sigjmp_buf env; /* for interruptable read() */
72 static jmp_buf env; /* for interruptable read() */
73 #endif /* HAVE_SIGLONGJMP */
75 static int env_valid = FALSE; /* whether we can longjmp or not */
77 /* sigalarm_handler and i_read are here just for supporting the sanity */
78 /* checking of pty slave devices. I have only seen this happen on BSD */
79 /* systems, but it may need to be done to the other pty implementations */
82 /* Note that this code is virtually replicated from other code in expect */
83 /* At some point, I'll dump one, but not until I'm satisfied no other */
84 /* changes are needed */
89 int n; /* unused, for compatibility with STDC */
92 signal(SIGALRM,sigalarm_handler);
95 /* check env_valid first to protect us from the alarm occurring */
96 /* in the window between i_read and alarm(0) */
97 #ifdef HAVE_SIGLONGJMP
98 if (env_valid) siglongjmp(env,1);
100 if (env_valid) longjmp(env,1);
101 #endif /* HAVE_SIGLONGJMP */
104 /* interruptable read */
106 i_read(fd,buffer,length,timeout)
114 /* since setjmp insists on returning 1 upon longjmp(,0), */
115 /* longjmp(,2) instead. */
117 /* restart read if setjmp returns 0 (first time) or 2. */
118 /* abort if setjmp returns 1. */
122 #ifdef HAVE_SIGLONGJMP
123 if (1 != sigsetjmp(env,1)) {
125 if (1 != setjmp(env)) {
126 #endif /* HAVE_SIGLONGJMP */
128 cc = read(fd,buffer,length);
131 i_read_errno = errno; /* errno can be overwritten by the */
137 static RETSIGTYPE (*oldAlarmHandler)();
138 static RETSIGTYPE (*oldHupHandler)();
139 static time_t current_time; /* time when testing began */
141 /* if TRUE, begin testing, else end testing */
142 /* returns -1 for failure, 0 for success */
146 int lfd; /* locksrc file descriptor */
148 oldAlarmHandler = signal(SIGALRM,sigalarm_handler);
150 /* Ignore hangup signals generated by pty testing */
151 /* when running in background with no control tty. */
152 /* Very few systems don't define O_NOCTTY. Only one */
153 /* I know of is Next. */
154 oldAlarmHandler = signal(SIGHUP,SIG_IGN);
159 /* recreate locksrc to prevent locks from 'looking old', so */
160 /* that they are not deleted (later on in this code) */
161 sprintf(locksrc,"/tmp/expect.%d",getpid());
162 (void) unlink(locksrc);
163 /* stanislav shalunov <shalunov@mccme.ru> notes that creat allows */
164 /* race - someone could link to important file which root could then */
166 /* if (-1 == (lfd = creat(locksrc,0777))) { */
167 if (-1 == (lfd = open(locksrc,O_RDWR|O_CREAT|O_EXCL,0777))) {
168 static char buf[256];
170 sprintf(exp_pty_error,"can't create %s, errno = %d\n",locksrc, errno);
180 signal(SIGALRM,oldAlarmHandler);
182 signal(SIGALRM,oldHupHandler);
184 (void) unlink(locksrc);
187 /* returns non-negative if successful */
193 char *num) /* string representation of number */
199 /* make a lock file to prevent others (for now only */
200 /* expects) from allocating pty while we are playing */
201 /* with it. This allows us to rigorously test the */
203 if (exp_pty_lock(bank,num) == 0) {
204 expDiagLogPtrStr("pty master (%s) is locked...skipping\r\n",master_name);
207 /* verify no one else is using slave by attempting */
208 /* to read eof from master side */
209 if (0 > (master = open(master_name,RDWR))) return(-1);
213 /* QNX ptys don't have a lot of the same properties such as
214 read 0 at EOF, etc */
215 /* if 1 should pacify C compiler without using nested ifdefs */
216 if (1) return master;
220 if (access(slave_name, R_OK|W_OK) != 0) {
221 expDiagLogPtrStr("could not open slave for pty master (%s)...skipping\r\n",
223 (void) close(master);
228 if (0 > (slave = open(slave_name,RDWR))) {
229 (void) close(master);
233 cc = i_read(master,&c,1,10);
234 (void) close(master);
235 if (!(cc == 0 || cc == -1)) {
236 expDiagLogPtrStr("%s slave open, skipping\r\n",slave_name);
237 locked = FALSE; /* leave lock file around so Expect's avoid */
238 /* retrying this pty for near future */
242 /* verify no one else is using master by attempting */
243 /* to read eof from slave side */
244 if (0 > (master = open(master_name,RDWR))) return(-1);
245 if (0 > (slave = open(slave_name,RDWR))) {
246 (void) close(master);
249 (void) close(master);
250 cc = i_read(slave,&c,1,10);
252 if (!(cc == 0 || cc == -1)) {
253 expDiagLogPtrStr("%s master open, skipping\r\n",master_name);
257 /* seems ok, let's use it */
258 expDiagLogPtrStr("using master pty %s\n",master_name);
259 return(open(master_name,RDWR));
272 /* returns 1 if successfully locked, 0 otherwise */
276 char *num) /* string representation of number */
285 sprintf(lock,"/tmp/ptylock.%c%s",bank,num);
287 if ((0 == stat(lock,&statbuf)) &&
288 (statbuf.st_mtime+3600 < current_time)) {
292 if (-1 == (link(locksrc,lock))) locked = FALSE;
299 * expDiagLog needs a different definition, depending on whether its
300 * called inside of Expect or the clib. Allow it to be set using this
301 * function. It's done here because this file (and pty_XXX.c) are the
302 * ones that call expDiagLog from the two different environments.
305 static void (*expDiagLogPtrVal) _ANSI_ARGS_((char *));
309 void (*fn) _ANSI_ARGS_((char *));
311 expDiagLogPtrVal = fn;
318 (*expDiagLogPtrVal)(str);
324 expDiagLogPtrX(fmt,num)
328 static char buf[1000];
329 sprintf(buf,fmt,num);
330 (*expDiagLogPtrVal)(buf);
335 expDiagLogPtrStr(fmt,str1)
339 static char buf[1000];
340 sprintf(buf,fmt,str1);
341 (*expDiagLogPtrVal)(buf);
345 expDiagLogPtrStrStr(fmt,str1,str2)
349 static char buf[1000];
350 sprintf(buf,fmt,str1,str2);
351 (*expDiagLogPtrVal)(buf);
354 static char * (*expErrnoMsgVal) _ANSI_ARGS_((int));
360 return (*expErrnoMsgVal)(errorNo);
365 char * (*fn) _ANSI_ARGS_((int));