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(¤t_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