Imported Upstream version 5.45
[platform/upstream/expect.git] / exp_pty.c
1 /* exp_pty.c - generic routines to allocate and test ptys
2
3 Written by: Don Libes, NIST,  3/9/93
4
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.
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
46 void expDiagLog();
47 void expDiagLogU();
48 void 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
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 */
66
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() */
71 #else
72 static jmp_buf env;             /* for interruptable read() */
73 #endif  /* HAVE_SIGLONGJMP */
74
75 static 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*/
87 static RETSIGTYPE
88 sigalarm_handler(n)
89 int 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 */
105 static int
106 i_read(fd,buffer,length,timeout)
107 int fd;
108 char *buffer;
109 int length;
110 int 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
137 static RETSIGTYPE (*oldAlarmHandler)();
138 static RETSIGTYPE (*oldHupHandler)();
139 static 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 */
143 int
144 exp_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
177 void
178 exp_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 */
188 int
189 exp_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
263 void
264 exp_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 */
273 int
274 exp_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
305 static void             (*expDiagLogPtrVal) _ANSI_ARGS_((char *));
306
307 void
308 expDiagLogPtrSet(fn)
309      void (*fn) _ANSI_ARGS_((char *));
310 {
311   expDiagLogPtrVal = fn;
312 }
313
314 void
315 expDiagLogPtr(str)
316      char *str;
317 {
318   (*expDiagLogPtrVal)(str);
319 }
320
321
322
323 void
324 expDiagLogPtrX(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
334 void
335 expDiagLogPtrStr(fmt,str1)
336      char *fmt;
337      char *str1;
338 {
339   static char buf[1000];
340   sprintf(buf,fmt,str1);
341   (*expDiagLogPtrVal)(buf);
342 }
343
344 void
345 expDiagLogPtrStrStr(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
354 static char *           (*expErrnoMsgVal) _ANSI_ARGS_((int));
355
356 char *
357 expErrnoMsg(errorNo)
358 int errorNo;
359 {
360   return (*expErrnoMsgVal)(errorNo);
361 }
362
363 void
364 expErrnoMsgSet(fn)
365      char * (*fn) _ANSI_ARGS_((int));
366 {
367   expErrnoMsgVal = fn;
368 }