1 /* pty_termios.c - routines to allocate ptys - termios version
3 Written by: Don Libes, NIST, 2/6/90
5 This file is in the public domain. However, the author and NIST
6 would appreciate credit if you use this file or parts of it.
10 /* Must be first, so that _XOPEN_SOURCE works. */
11 #include "expect_cf.h"
16 #if defined(SIGCLD) && !defined(SIGCHLD)
17 #define SIGCHLD SIGCLD
21 The following functions are linked from the Tcl library. They
22 don't cause anything else in the library to be dragged in, so it
23 shouldn't cause any problems (e.g., bloat).
25 The functions are relatively small but painful enough that I don't care
26 to recode them. You may, if you absolutely want to get rid of any
29 extern char *TclGetRegError();
33 #if defined(HAVE_PTYM) && defined(HAVE_PTMX)
35 * HP-UX 10.0 with streams (optional) have both PTMX and PTYM. I don't
36 * know which is preferred but seeing as how the HP trap stuff is so
37 * unusual, it is probably safer to stick with the native HP pty support,
46 #ifdef HAVE_INTTYPES_H
47 # include <inttypes.h>
49 #include <sys/types.h>
53 #include "../compat/stdlib.h"
58 #ifdef HAVE_SYSMACROS_H
59 #include <sys/sysmacros.h>
63 #include <sys/ptyio.h>
68 #ifdef HAVE_SYS_FCNTL_H
69 # include <sys/fcntl.h>
74 #if defined(_SEQUENT_)
75 # include <sys/strpty.h>
78 #if defined(HAVE_PTMX) && !defined(__CYGWIN__)
79 # include <sys/stropts.h>
84 #include "exp_tty_in.h"
85 #include "exp_rename.h"
91 /*extern char *sys_errlist[];*/
98 /* Convex getpty is different than older-style getpty */
99 /* Convex getpty is really just a cover function that does the traversal */
100 /* across the domain of pty names. It makes no attempt to verify that */
101 /* they can actually be used. Indded, the logic in the man page is */
102 /* wrong because it will allow you to allocate ptys that your own account */
103 /* already has in use. */
104 #if defined(HAVE_GETPTY) && defined(CONVEX)
106 #define HAVE_CONVEX_GETPTY
107 extern char *getpty();
108 static char *master_name;
109 static char slave_name[] = "/dev/ptyXX";
110 static char *tty_bank; /* ptr to char [p-z] denoting
112 static char *tty_num; /* ptr to char [0-f] denoting
113 which number it is */
116 #if defined(_SEQUENT_) && !defined(HAVE_PTMX)
117 /* old-style SEQUENT, new-style uses ptmx */
118 static char *master_name, *slave_name;
119 #endif /* _SEQUENT */
121 /* very old SGIs prefer _getpty over ptc */
122 #if defined(HAVE__GETPTY) && defined(HAVE_PTC) && !defined(HAVE_GETPTY)
126 #if defined(HAVE_PTC)
127 static char slave_name[] = "/dev/ttyqXXX";
128 /* some machines (e.g., SVR4.0 StarServer) have all of these and */
129 /* HAVE_PTC works best */
134 #if defined(HAVE__GETPTY) || defined(HAVE_PTC_PTS) || defined(HAVE_PTMX)
135 static char *slave_name;
138 #if defined(HAVE_GETPTY)
140 static char master_name[MAXPTYNAMELEN];
141 static char slave_name[MAXPTYNAMELEN];
144 #if !defined(HAVE_GETPTY) && !defined(HAVE__GETPTY) && !defined(HAVE_PTC) && !defined(HAVE_PTC_PTS) && !defined(HAVE_PTMX) && !defined(HAVE_CONVEX_GETPTY) && !defined(_SEQUENT_) && !defined(HAVE_SCO_CLIST_PTYS) && !defined(HAVE_OPENPTY)
146 /* strange order and missing d is intentional */
147 static char banks[] = "pqrstuvwxyzabcefghijklo";
148 static char master_name[] = "/dev/ptym/ptyXXXX";
149 static char slave_name[] = "/dev/pty/ttyXXXX";
150 static char *slave_bank;
151 static char *slave_num;
153 static char banks[] = "pqrstuvwxyzPQRSTUVWXYZ";
154 static char master_name[] = "/dev/ptyXX";
155 static char slave_name [] = "/dev/ttyXX";
156 #endif /* HAVE_PTYM */
158 static char *tty_type; /* ptr to char [pt] denoting
159 whether it is a pty or tty */
160 static char *tty_bank; /* ptr to char [p-z] denoting
162 static char *tty_num; /* ptr to char [0-f] denoting
163 which number it is */
166 #if defined(HAVE_SCO_CLIST_PTYS)
167 # define MAXPTYNAMELEN 64
168 static char master_name[MAXPTYNAMELEN];
169 static char slave_name[MAXPTYNAMELEN];
170 #endif /* HAVE_SCO_CLIST_PTYS */
173 static char master_name[64];
174 static char slave_name[64];
177 char *exp_pty_slave_name;
183 char *s; /* args to stty */
184 char *name; /* name of pty */
186 #define MAX_ARGLIST 10240
187 char buf[MAX_ARGLIST]; /* overkill is easier */
188 RETSIGTYPE (*old)(); /* save old sigalarm handler */
191 old = signal(SIGCHLD, SIG_DFL);
192 switch (pid = fork()) {
194 exec_stty("/bin/stty","/bin/stty",s);
197 default: /* parent */
202 signal(SIGCHLD, old); /* restore signal handler */
212 int in_token = FALSE; /* TRUE if we are reading a token */
217 if (*s == '\\' && *(s+1) == '"') { /* quoted quote */
218 s++; /* get past " */
220 } else if (*s == '\"') { /* close quote */
223 } else *cp++ = *s++; /* suck up anything */
224 } else if (*s == '\"') { /* open quote */
228 } else if (isspace(*s)) {
236 args[argi] = (char *) 0; /* terminate argv */
237 execvp(args[0],args);
243 char *s; /* args to stty */
244 char *name; /* name of pty */
246 #define MAX_ARGLIST 10240
247 char buf[MAX_ARGLIST]; /* overkill is easier */
248 RETSIGTYPE (*old)(); /* save old sigalarm handler */
250 #ifdef STTY_READS_STDOUT
251 sprintf(buf,"/bin/stty %s > %s",s,name);
254 sprintf(buf,"stty %s < %s",s,name);
256 sprintf(buf,"/bin/stty %s < %s",s,name);
259 old = signal(SIGCHLD, SIG_DFL);
261 signal(SIGCHLD, old); /* restore signal handler */
264 int exp_dev_tty; /* file descriptor to /dev/tty or -1 if none */
265 static int knew_dev_tty;/* true if we had our hands on /dev/tty at any time */
267 exp_tty exp_tty_original;
269 #define GET_TTYTYPE 0
270 #define SET_TTYTYPE 1
272 ttytype(request,fd,ttycopy,ttyinit,s)
275 /* following are used only if request == SET_TTYTYPE */
276 int ttycopy; /* true/false, copy from /dev/tty */
277 int ttyinit; /* if true, initialize to sane state */
278 char *s; /* stty args */
280 if (request == GET_TTYTYPE) {
281 #ifdef HAVE_TCSETATTR
282 if (-1 == tcgetattr(fd, &exp_tty_original)) {
284 if (-1 == ioctl(fd, TCGETS, (char *)&exp_tty_original)) {
286 knew_dev_tty = FALSE;
289 exp_window_size_get(fd);
290 } else { /* type == SET_TTYTYPE */
291 if (ttycopy && knew_dev_tty) {
292 #ifdef HAVE_TCSETATTR
293 (void) tcsetattr(fd, TCSADRAIN, &exp_tty_current);
295 (void) ioctl(fd, TCSETS, (char *)&exp_tty_current);
298 exp_window_size_set(fd);
301 #ifdef __CENTERLINE__
303 #define DFLT_STTY "sane"
306 /* Apollo Domain doesn't need this */
309 /* overlay parms originally supplied by Makefile */
310 /* As long as BSD stty insists on stdout == stderr, we can no longer write */
311 /* diagnostics to parent stderr, since stderr has is now child's */
312 /* Maybe someday they will fix stty? */
313 /* debuglog("getptyslave: (default) stty %s\n",DFLT_STTY);*/
314 pty_stty(DFLT_STTY,slave_name);
318 /* lastly, give user chance to override any terminal parms */
320 /* give user a chance to override any terminal parms */
321 /* debuglog("getptyslave: (user-requested) stty %s\n",s);*/
322 pty_stty(s,slave_name);
330 #if !defined(HAVE_GETPTY) && !defined(HAVE__GETPTY) && !defined(HAVE_PTC) && !defined(HAVE_PTC_PTS) && !defined(HAVE_PTMX) && !defined(HAVE_CONVEX_GETPTY) && !defined(_SEQUENT_) && !defined(HAVE_SCO_CLIST_PTYS) && !defined(HAVE_OPENPTY)
333 tty_bank = &master_name[strlen("/dev/ptym/pty")];
334 tty_num = &master_name[strlen("/dev/ptym/ptyX")];
335 slave_bank = &slave_name[strlen("/dev/pty/tty")];
336 slave_num = &slave_name[strlen("/dev/pty/ttyX")];
338 tty_bank = &master_name[strlen("/dev/pty")];
339 tty_num = &master_name[strlen("/dev/ptyp")];
340 tty_type = &slave_name[strlen("/dev/")];
343 #endif /* HAVE_PTYM */
346 exp_dev_tty = open("/dev/tty",O_RDWR);
347 knew_dev_tty = (exp_dev_tty != -1);
348 if (knew_dev_tty) ttytype(GET_TTYTYPE,exp_dev_tty,0,0,(char *)0);
352 /* 3b2 doesn't define these according to jthomas@nmsu.edu. */
361 struct stat stat_buf;
370 #if defined(HAVE_PTMX) || defined(HAVE_PTMX_BSD)
372 #if defined(HAVE_PTMX_BSD)
373 if ((master = open("/dev/ptmx_bsd", O_RDWR)) == -1) return(-1);
375 if ((master = open("/dev/ptmx", O_RDWR)) == -1) return(-1);
377 if ((slave_name = (char *)ptsname(master)) == NULL || unlockpt(master)) {
380 } else if (grantpt(master)) {
381 static char buf[500];
383 sprintf(exp_pty_error,"grantpt(%d) failed - likely reason is that your system administrator (in a rage of blind passion to rid the system of security holes) removed setuid from the utility used internally by grantpt to change pty permissions. Tell your system admin to reestablish setuid on the utility. Get the utility name by running Expect under truss or trace.");
388 (void) ioctl(master,TIOCFLUSH,(char *)0);
389 #endif /* TIOCFLUSH */
391 exp_pty_slave_name = slave_name;
395 #if defined(HAVE__GETPTY) /* SGI needs it this way */
397 slave_name = _getpty(&master, O_RDWR, 0600, 0);
398 if (slave_name == NULL)
400 exp_pty_slave_name = slave_name;
404 #if defined(HAVE_PTC) && !defined(HAVE__GETPTY) /* old SGI, version 3 */
406 master = open("/dev/ptc", O_RDWR);
410 if (fstat(master, &stat_buf) < 0) {
414 ptynum = minor(stat_buf.st_rdev);
415 sprintf(slave_name,"/dev/ttyq%d",ptynum);
417 exp_pty_slave_name = slave_name;
421 #if defined(HAVE_GETPTY) && !defined(HAVE__GETPTY)
423 master = getpty(master_name, slave_name, O_RDWR);
424 /* is it really necessary to verify slave side is usable? */
425 exp_pty_slave_name = slave_name;
429 #if defined(HAVE_PTC_PTS)
431 master = open("/dev/ptc",O_RDWR);
434 slave_name = ttyname(master);
436 exp_pty_slave_name = slave_name;
440 #if defined(_SEQUENT_) && !defined(HAVE_PTMX)
442 /* old-style SEQUENT, new-style uses ptmx */
443 master = getpseudotty(&slave_name, &master_name);
444 exp_pty_slave_name = slave_name;
446 #endif /* _SEQUENT_ */
448 #if defined(HAVE_OPENPTY)
450 if (openpty(&master, &slave, master_name, 0, 0) != 0) {
455 strcpy(slave_name, ttyname(slave));
456 exp_pty_slave_name = slave_name;
459 #endif /* HAVE_OPENPTY */
461 #if defined(TEST_PTY)
463 * all pty allocation mechanisms after this require testing
465 if (exp_pty_test_start() == -1) return -1;
467 #if !defined(HAVE_CONVEX_GETPTY) && !defined(HAVE_PTYM) && !defined(HAVE_SCO_CLIST_PTYS)
468 for (bank = banks;*bank;bank++) {
471 if (stat(master_name, &stat_buf) < 0) break;
472 for (hex = "0123456789abcdef";*hex;hex++) {
474 strcpy(slave_name,master_name);
476 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
477 if (master >= 0) goto done;
482 #ifdef HAVE_SCO_CLIST_PTYS
483 for (num = 0; ; num++) {
486 sprintf (num_str, "%d", num);
487 sprintf (master_name, "%s%s", "/dev/ptyp", num_str);
488 if (stat (master_name, &stat_buf) < 0)
490 sprintf (slave_name, "%s%s", "/dev/ttyp", num_str);
492 master = exp_pty_test (master_name, slave_name, 0, num_str);
499 /* systems with PTYM follow this idea:
501 /dev/ptym/pty[a-ce-z][0-9a-f] master pseudo terminals
502 /dev/pty/tty[a-ce-z][0-9a-f] slave pseudo terminals
503 /dev/ptym/pty[a-ce-z][0-9][0-9] master pseudo terminals
504 /dev/pty/tty[a-ce-z][0-9][0-9] slave pseudo terminals
506 SPPUX (Convex's HPUX compatible) follows the PTYM convention but
509 /dev/ptym/pty[a-ce-z][0-9][0-9][0-9] master pseudo terminals
510 /dev/pty/tty[a-ce-z][0-9][0-9][0-9] slave pseudo terminals
512 The code does not distinguish between HPUX and SPPUX because there
513 is no reason to. HPUX will merely fail the extended SPPUX tests.
514 In fact, most SPPUX systems will fail simply because few systems
515 will actually have the extended ptys. However, the tests are
516 fast so it is no big deal.
520 * pty[a-ce-z][0-9a-f]
523 for (bank = banks;*bank;bank++) {
525 sprintf(tty_num,"0");
526 if (stat(master_name, &stat_buf) < 0) break;
527 *(slave_num+1) = '\0';
528 for (hex = "0123456789abcdef";*hex;hex++) {
530 *slave_bank = *tty_bank;
531 *slave_num = *tty_num;
532 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
533 if (master >= 0) goto done;
538 * tty[p-za-ce-o][0-9][0-9]
541 for (bank = banks;*bank;bank++) {
543 sprintf(tty_num,"00");
544 if (stat(master_name, &stat_buf) < 0) break;
545 for (num = 0; num<100; num++) {
546 *slave_bank = *tty_bank;
547 sprintf(tty_num,"%02d",num);
548 strcpy(slave_num,tty_num);
549 master = exp_pty_test(master_name,slave_name,tty_bank,tty_num);
550 if (master >= 0) goto done;
555 * tty[p-za-ce-o][0-9][0-9][0-9]
557 for (bank = banks;*bank;bank++) {
559 sprintf(tty_num,"000");
560 if (stat(master_name, &stat_buf) < 0) break;
561 for (num = 0; num<1000; num++) {
562 *slave_bank = *tty_bank;
563 sprintf(tty_num,"%03d",num);
564 strcpy(slave_num,tty_num);
565 master = exp_pty_test(master_name,slave_name,tty_bank,tty_num);
566 if (master >= 0) goto done;
570 #endif /* HAVE_PTYM */
572 #if defined(HAVE_CONVEX_GETPTY)
574 if ((master_name = getpty()) == NULL) return -1;
576 strcpy(slave_name,master_name);
577 slave_name[5] = 't';/* /dev/ptyXY ==> /dev/ttyXY */
579 tty_bank = &slave_name[8];
580 tty_num = &slave_name[9];
581 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
582 if (master >= 0) goto done;
588 exp_pty_slave_name = slave_name;
591 #endif /* defined(TEST_PTY) */
594 /* if slave is opened in a child, slave_control(1) must be executed after */
595 /* master is opened (when child is opened is irrelevent) */
596 /* if slave is opened in same proc as master, slave_control(1) must executed */
597 /* after slave is opened */
600 exp_slave_control(master,control)
602 int control; /* if 1, enable pty trapping of close/open/ioctl */
605 ioctl(master, TIOCTRAP, &control);
606 #endif /* HAVE_PTYTRAP */
610 getptyslave(ttycopy,ttyinit,stty_args)
618 if (0 > (slave = open(slave_name, O_RDWR))) return(-1);
620 #if defined(HAVE_PTMX_BSD)
621 if (ioctl (slave, I_LOOK, buf) != 0)
622 if (ioctl (slave, I_PUSH, "ldterm")) {
623 debuglog("ioctl(%s,I_PUSH,\"ldterm\") = %s\n",Tcl_ErrnoMsg(errno));
626 #if defined(HAVE_PTMX) && !defined(__CYGWIN__)
627 if (ioctl(slave, I_PUSH, "ptem")) {
628 debuglog("ioctl(%s,I_PUSH,\"ptem\") = %s\n",Tcl_ErrnoMsg(errno));
630 if (ioctl(slave, I_PUSH, "ldterm")) {
631 debuglog("ioctl(%s,I_PUSH,\"ldterm\") = %s\n",Tcl_ErrnoMsg(errno));
633 if (ioctl(slave, I_PUSH, "ttcompat")) {
634 debuglog("ioctl(%s,I_PUSH,\"ttcompat\") = %s\n",Tcl_ErrnoMsg(errno));
640 /* if opened in a new process, slave will be 0 (and */
641 /* ultimately, 1 and 2 as well) */
643 /* duplicate 0 onto 1 and 2 to prepare for stty */
648 ttytype(SET_TTYTYPE,slave,ttycopy,ttyinit,stty_args);
652 /* do another open, to tell master that slave is done fiddling */
653 /* with pty and master does not have to wait to do further acks */
654 if (0 > (slave2 = open(slave_name, O_RDWR))) return(-1);
656 #endif /* HAVE_PTYTRAP */
659 (void) exp_pty_unlock();
664 #include <sys/ptyio.h>
665 #include <sys/time.h>
667 /* This function attempts to deal with HP's pty interface. This
668 function simply returns an indication of what was trapped (or -1 for
669 failure), the parent deals with the details.
671 Originally, I tried to just trap open's but that is not enough. When
672 the pty is initialized, ioctl's are generated and if not trapped will
673 hang the child if no further trapping is done. (This could occur if
674 parent spawns a process and then immediatley does a close.) So
675 instead, the parent must trap the ioctl's. It probably suffices to
676 trap the write ioctl's (and tiocsctty which some hp's need) -
677 conceivably, stty could be smart enough not to do write's if the tty
678 settings are already correct. In that case, we'll have to rethink
681 Suggestions from HP engineers encouraged. I cannot imagine how this
682 interface was intended to be used!
687 exp_wait_for_slave_open(fd)
692 struct request_info ioctl_info;
696 int maxfds = sysconf(_SC_OPEN_MAX);
698 t.tv_sec = 30; /* 30 seconds */
705 (SELECT_MASK_TYPE *)0,
706 (SELECT_MASK_TYPE *)0,
707 (SELECT_MASK_TYPE *)&excep,
710 debuglog("spawned process never started, errno = %d\n",errno);
713 if (ioctl(fd,TIOCREQCHECK,&ioctl_info) < 0) {
714 debuglog("ioctl(TIOCREQCHECK) failed, errno = %d\n",errno);
718 found = ioctl_info.request;
720 debuglog("trapped pty op = %x",found);
721 if (found == TIOCOPEN) {
722 debuglog(" TIOCOPEN");
723 } else if (found == TIOCCLOSE) {
724 debuglog(" TIOCCLOSE");
728 if (found == TIOCSCTTY) {
729 debuglog(" TIOCSCTTY");
733 if (found & IOC_IN) {
734 debuglog(" IOC_IN (set)");
735 } else if (found & IOC_OUT) {
736 debuglog(" IOC_OUT (get)");
741 if (ioctl(fd, TIOCREQSET, &ioctl_info) < 0) {
742 debuglog("ioctl(TIOCREQSET) failed, errno = %d\n",errno);
752 /* a stub so we can do weird things on the cray */