1/* exp_pty.c - generic routines to allocate and test ptys
2
3Written by: Don Libes, NIST,  3/9/93
4
5Design and implementation of this program was paid for by U.S. tax
6dollars.  Therefore it is public domain.  However, the author and NIST
7would appreciate credit if this program or parts of it are used.
8
9*/
10
11#include "expect_cf.h"
12#ifdef HAVE_UNISTD_H
13#  include <unistd.h>
14#endif
15#ifdef HAVE_SYS_FCNTL_H
16#  include <sys/fcntl.h>
17#else
18#  include <fcntl.h>
19#endif
20#include <sys/types.h>
21#include <sys/stat.h>
22
23#ifdef TIME_WITH_SYS_TIME
24# include <sys/time.h>
25# include <time.h>
26#else
27# if HAVE_SYS_TIME_H
28#  include <sys/time.h>
29# else
30#  include <time.h>
31# endif
32#endif
33
34#include <signal.h>
35#include <setjmp.h>
36#include <sys/file.h>
37#include "tcl.h"
38#include "exp_int.h"
39#include "expect_comm.h"
40#include "exp_rename.h"
41#include "exp_pty.h"
42
43#include <errno.h>
44
45#if 0
46void expDiagLog();
47void expDiagLogU();
48void expDiagLogPtrSet();
49#endif
50
51#ifndef TRUE
52#define TRUE 1
53#define FALSE 0
54#endif
55
56#ifdef O_NOCTTY
57#define RDWR ((O_RDWR)|(O_NOCTTY))
58#else
59#define RDWR O_RDWR
60#endif
61
62static int locked = FALSE;
63static char lock[] = "/tmp/ptylock.XXXX";	/* XX is replaced by pty id */
64static 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 */
66
67static 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
70static sigjmp_buf env;                /* for interruptable read() */
71#else
72static jmp_buf env;		/* for interruptable read() */
73#endif  /* HAVE_SIGLONGJMP */
74
75static int env_valid = FALSE;	/* whether we can longjmp or not */
76
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 */
80/* as well. */
81
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 */
85
86/*ARGSUSED*/
87static RETSIGTYPE
88sigalarm_handler(n)
89int n;		/* unused, for compatibility with STDC */
90{
91#ifdef REARM_SIG
92	signal(SIGALRM,sigalarm_handler);
93#endif
94
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);
99#else
100	if (env_valid) longjmp(env,1);
101#endif  /* HAVE_SIGLONGJMP */
102}
103
104/* interruptable read */
105static int
106i_read(fd,buffer,length,timeout)
107int fd;
108char *buffer;
109int length;
110int timeout;
111{
112	int cc = -2;
113
114	/* since setjmp insists on returning 1 upon longjmp(,0), */
115	/* longjmp(,2) instead. */
116
117	/* restart read if setjmp returns 0 (first time) or 2. */
118	/* abort if setjmp returns 1. */
119
120	alarm(timeout);
121
122#ifdef HAVE_SIGLONGJMP
123	if (1 != sigsetjmp(env,1)) {
124#else
125	if (1 != setjmp(env)) {
126#endif  /* HAVE_SIGLONGJMP */
127		env_valid = TRUE;
128		cc = read(fd,buffer,length);
129	}
130	env_valid = FALSE;
131	i_read_errno = errno;	/* errno can be overwritten by the */
132				/* time we return */
133	alarm(0);
134	return(cc);
135}
136
137static RETSIGTYPE (*oldAlarmHandler)();
138static RETSIGTYPE (*oldHupHandler)();
139static time_t current_time;	/* time when testing began */
140
141/* if TRUE, begin testing, else end testing */
142/* returns -1 for failure, 0 for success */
143int
144exp_pty_test_start()
145{
146	int lfd;	/* locksrc file descriptor */
147
148	oldAlarmHandler = signal(SIGALRM,sigalarm_handler);
149#ifndef O_NOCTTY
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);
155#endif
156
157	time(&current_time);
158
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 */
165	/* smash. */
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];
169		exp_pty_error = buf;
170		sprintf(exp_pty_error,"can't create %s, errno = %d\n",locksrc, errno);
171		return(-1);
172	}
173	close(lfd);
174	return 0;
175}
176
177void
178exp_pty_test_end()
179{
180	signal(SIGALRM,oldAlarmHandler);
181#ifndef O_NOCTTY
182	signal(SIGALRM,oldHupHandler);
183#endif
184	(void) unlink(locksrc);
185}
186
187/* returns non-negative if successful */
188int
189exp_pty_test(
190     char *master_name,
191     char *slave_name,
192     char bank,
193     char *num)	/* string representation of number */
194{
195	int master, slave;
196	int cc;
197	char c;
198
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 */
202	/* pty is usable. */
203	if (exp_pty_lock(bank,num) == 0) {
204		expDiagLogPtrStr("pty master (%s) is locked...skipping\r\n",master_name);
205		return(-1);
206	}
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);
210
211#ifdef __QNX__
212
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;
217#endif
218
219#ifdef HAVE_PTYTRAP
220	if (access(slave_name, R_OK|W_OK) != 0) {
221		expDiagLogPtrStr("could not open slave for pty master (%s)...skipping\r\n",
222			master_name);
223		(void) close(master);
224		return -1;
225	}
226	return(master);
227#else
228	if (0 > (slave = open(slave_name,RDWR))) {
229		(void) close(master);
230		return -1;
231	}
232	(void) close(slave);
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 */
239		return -1;
240	}
241
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);
247		return -1;
248	}
249	(void) close(master);
250	cc = i_read(slave,&c,1,10);
251	(void) close(slave);
252	if (!(cc == 0 || cc == -1)) {
253		expDiagLogPtrStr("%s master open, skipping\r\n",master_name);
254		return -1;
255	}
256
257	/* seems ok, let's use it */
258	expDiagLogPtrStr("using master pty %s\n",master_name);
259	return(open(master_name,RDWR));
260#endif
261}
262
263void
264exp_pty_unlock(void)
265{
266	if (locked) {
267		(void) unlink(lock);
268		locked = FALSE;
269	}
270}
271
272/* returns 1 if successfully locked, 0 otherwise */
273int
274exp_pty_lock(
275     char bank,
276     char *num)	/* string representation of number */
277{
278	struct stat statbuf;
279
280	if (locked) {
281		unlink(lock);
282		locked = FALSE;
283	}
284
285	sprintf(lock,"/tmp/ptylock.%c%s",bank,num);
286
287	if ((0 == stat(lock,&statbuf)) &&
288	    (statbuf.st_mtime+3600 < current_time)) {
289		(void) unlink(lock);
290	}
291
292	if (-1 == (link(locksrc,lock))) locked = FALSE;
293	else locked = TRUE;
294
295	return locked;
296}
297
298/*
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.
303 */
304
305static void		(*expDiagLogPtrVal) _ANSI_ARGS_((char *));
306
307void
308expDiagLogPtrSet(fn)
309     void (*fn) _ANSI_ARGS_((char *));
310{
311  expDiagLogPtrVal = fn;
312}
313
314void
315expDiagLogPtr(str)
316     char *str;
317{
318  (*expDiagLogPtrVal)(str);
319}
320
321
322
323void
324expDiagLogPtrX(fmt,num)
325     char *fmt;
326     int num;
327{
328  static char buf[1000];
329  sprintf(buf,fmt,num);
330  (*expDiagLogPtrVal)(buf);
331}
332
333
334void
335expDiagLogPtrStr(fmt,str1)
336     char *fmt;
337     char *str1;
338{
339  static char buf[1000];
340  sprintf(buf,fmt,str1);
341  (*expDiagLogPtrVal)(buf);
342}
343
344void
345expDiagLogPtrStrStr(fmt,str1,str2)
346     char *fmt;
347     char *str1, *str2;
348{
349  static char buf[1000];
350  sprintf(buf,fmt,str1,str2);
351  (*expDiagLogPtrVal)(buf);
352}
353
354static char *		(*expErrnoMsgVal) _ANSI_ARGS_((int));
355
356char *
357expErrnoMsg(errorNo)
358int errorNo;
359{
360  return (*expErrnoMsgVal)(errorNo);
361}
362
363void
364expErrnoMsgSet(fn)
365     char * (*fn) _ANSI_ARGS_((int));
366{
367  expErrnoMsgVal = fn;
368}
369