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.
14 #if defined(SIGCLD) && !defined(SIGCHLD)
15 #define SIGCHLD SIGCLD
18 #include "expect_cf.h"
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();
31 #if defined(HAVE_PTMX_BSD) && defined(HAVE_PTMX)
33 * Some systems have both PTMX and PTMX_BSD.
34 * In fact, alphaev56-dec-osf4.0e has /dev/pts, /dev/pty, /dev/ptym,
35 * /dev/ptm, /dev/ptmx, and /dev/ptmx_bsd
36 * Suggestion from Martin Buchholz <martin@xemacs.org> is that BSD
37 * is usually deprecated and so should be here.
42 /* Linux and Digital systems can be configured to have both.
43 According to Ashley Pittman <ashley@ilo.dec.com>, Digital works better
44 with openpty which supports 4000 while ptmx supports 60. */
45 #if defined(HAVE_OPENPTY) && defined(HAVE_PTMX)
49 #if defined(HAVE_PTYM) && defined(HAVE_PTMX)
51 * HP-UX 10.0 with streams (optional) have both PTMX and PTYM. I don't
52 * know which is preferred but seeing as how the HP trap stuff is so
53 * unusual, it is probably safer to stick with the native HP pty support,
62 #ifdef HAVE_INTTYPES_H
63 # include <inttypes.h>
65 #include <sys/types.h>
69 #include "../compat/stdlib.h"
77 #ifdef HAVE_SYSMACROS_H
78 #include <sys/sysmacros.h>
82 #include <sys/ptyio.h>
87 #ifdef HAVE_SYS_FCNTL_H
88 # include <sys/fcntl.h>
93 #if defined(_SEQUENT_)
94 # include <sys/strpty.h>
97 #if defined(HAVE_PTMX) && defined(HAVE_STROPTS_H)
98 # include <sys/stropts.h>
103 #include "exp_tty_in.h"
104 #include "exp_rename.h"
108 void expDiagLogPtr();
111 /*extern char *sys_errlist[];*/
118 /* Convex getpty is different than older-style getpty */
119 /* Convex getpty is really just a cover function that does the traversal */
120 /* across the domain of pty names. It makes no attempt to verify that */
121 /* they can actually be used. Indded, the logic in the man page is */
122 /* wrong because it will allow you to allocate ptys that your own account */
123 /* already has in use. */
124 #if defined(HAVE_GETPTY) && defined(CONVEX)
126 #define HAVE_CONVEX_GETPTY
127 extern char *getpty();
128 static char *master_name;
129 static char slave_name[] = "/dev/ptyXX";
130 static char *tty_bank; /* ptr to char [p-z] denoting
132 static char *tty_num; /* ptr to char [0-f] denoting
133 which number it is */
136 #if defined(_SEQUENT_) && !defined(HAVE_PTMX)
137 /* old-style SEQUENT, new-style uses ptmx */
138 static char *master_name, *slave_name;
139 #endif /* _SEQUENT */
141 /* very old SGIs prefer _getpty over ptc */
142 #if defined(HAVE__GETPTY) && defined(HAVE_PTC) && !defined(HAVE_GETPTY)
146 #if defined(HAVE_PTC)
147 static char slave_name[] = "/dev/ttyqXXX";
148 /* some machines (e.g., SVR4.0 StarServer) have all of these and */
149 /* HAVE_PTC works best */
154 #if defined(HAVE__GETPTY) || defined(HAVE_PTC_PTS) || defined(HAVE_PTMX)
155 static char *slave_name;
158 #if defined(HAVE_GETPTY)
160 static char master_name[MAXPTYNAMELEN];
161 static char slave_name[MAXPTYNAMELEN];
164 #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)
166 /* strange order and missing d is intentional */
167 static char banks[] = "pqrstuvwxyzabcefghijklo";
168 static char master_name[] = "/dev/ptym/ptyXXXX";
169 static char slave_name[] = "/dev/pty/ttyXXXX";
170 static char *slave_bank;
171 static char *slave_num;
173 static char banks[] = "pqrstuvwxyzPQRSTUVWXYZ";
174 static char master_name[] = "/dev/ptyXX";
175 static char slave_name [] = "/dev/ttyXX";
176 #endif /* HAVE_PTYM */
178 static char *tty_type; /* ptr to char [pt] denoting
179 whether it is a pty or tty */
180 static char *tty_bank; /* ptr to char [p-z] denoting
182 static char *tty_num; /* ptr to char [0-f] denoting
183 which number it is */
186 #if defined(HAVE_SCO_CLIST_PTYS)
187 # define MAXPTYNAMELEN 64
188 static char master_name[MAXPTYNAMELEN];
189 static char slave_name[MAXPTYNAMELEN];
190 #endif /* HAVE_SCO_CLIST_PTYS */
193 static char master_name[64];
194 static char slave_name[64];
197 char *exp_pty_slave_name;
203 char *s; /* args to stty */
204 char *name; /* name of pty */
206 #define MAX_ARGLIST 10240
207 char buf[MAX_ARGLIST]; /* overkill is easier */
208 RETSIGTYPE (*old)(); /* save old sigalarm handler */
211 old = signal(SIGCHLD, SIG_DFL);
212 switch (pid = fork()) {
214 exec_stty(STTY_BIN,STTY_BIN,s);
217 default: /* parent */
222 signal(SIGCHLD, old); /* restore signal handler */
232 int in_token = FALSE; /* TRUE if we are reading a token */
237 if (*s == '\\' && *(s+1) == '"') { /* quoted quote */
238 s++; /* get past " */
240 } else if (*s == '\"') { /* close quote */
243 } else *cp++ = *s++; /* suck up anything */
244 } else if (*s == '\"') { /* open quote */
248 } else if (isspace(*s)) {
256 args[argi] = (char *) 0; /* terminate argv */
257 execvp(args[0],args);
263 char *s; /* args to stty */
264 char *name; /* name of pty */
266 #define MAX_ARGLIST 10240
267 char buf[MAX_ARGLIST]; /* overkill is easier */
268 RETSIGTYPE (*old)(); /* save old sigalarm handler */
270 #ifdef STTY_READS_STDOUT
271 sprintf(buf,"%s %s > %s",STTY_BIN,s,name);
273 sprintf(buf,"%s %s < %s",STTY_BIN,s,name);
275 old = signal(SIGCHLD, SIG_DFL);
277 signal(SIGCHLD, old); /* restore signal handler */
280 int exp_dev_tty; /* file descriptor to /dev/tty or -1 if none */
281 static int knew_dev_tty;/* true if we had our hands on /dev/tty at any time */
283 exp_tty exp_tty_original;
285 #define GET_TTYTYPE 0
286 #define SET_TTYTYPE 1
288 ttytype(request,fd,ttycopy,ttyinit,s)
291 /* following are used only if request == SET_TTYTYPE */
292 int ttycopy; /* true/false, copy from /dev/tty */
293 int ttyinit; /* if true, initialize to sane state */
294 char *s; /* stty args */
296 if (request == GET_TTYTYPE) {
297 #ifdef HAVE_TCSETATTR
298 if (-1 == tcgetattr(fd, &exp_tty_original)) {
300 if (-1 == ioctl(fd, TCGETS, (char *)&exp_tty_original)) {
302 knew_dev_tty = FALSE;
305 exp_window_size_get(fd);
306 } else { /* type == SET_TTYTYPE */
307 if (ttycopy && knew_dev_tty) {
308 #ifdef HAVE_TCSETATTR
309 (void) tcsetattr(fd, TCSADRAIN, &exp_tty_current);
311 (void) ioctl(fd, TCSETS, (char *)&exp_tty_current);
314 exp_window_size_set(fd);
317 #ifdef __CENTERLINE__
319 #define DFLT_STTY "sane"
322 /* Apollo Domain doesn't need this */
325 /* overlay parms originally supplied by Makefile */
326 /* As long as BSD stty insists on stdout == stderr, we can no longer write */
327 /* diagnostics to parent stderr, since stderr has is now child's */
328 /* Maybe someday they will fix stty? */
329 /* expDiagLogPtrStr("exp_getptyslave: (default) stty %s\n",DFLT_STTY);*/
330 pty_stty(DFLT_STTY,slave_name);
334 /* lastly, give user chance to override any terminal parms */
336 /* give user a chance to override any terminal parms */
337 /* expDiagLogPtrStr("exp_getptyslave: (user-requested) stty %s\n",s);*/
338 pty_stty(s,slave_name);
346 #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)
349 tty_bank = &master_name[strlen("/dev/ptym/pty")];
350 tty_num = &master_name[strlen("/dev/ptym/ptyX")];
351 slave_bank = &slave_name[strlen("/dev/pty/tty")];
352 slave_num = &slave_name[strlen("/dev/pty/ttyX")];
354 tty_bank = &master_name[strlen("/dev/pty")];
355 tty_num = &master_name[strlen("/dev/ptyp")];
356 tty_type = &slave_name[strlen("/dev/")];
359 #endif /* HAVE_PTYM */
362 exp_dev_tty = open("/dev/tty",O_RDWR);
363 knew_dev_tty = (exp_dev_tty != -1);
364 if (knew_dev_tty) ttytype(GET_TTYTYPE,exp_dev_tty,0,0,(char *)0);
368 /* 3b2 doesn't define these according to jthomas@nmsu.edu. */
377 struct stat stat_buf;
386 #if defined(HAVE_PTMX) || defined(HAVE_PTMX_BSD)
388 #if defined(HAVE_PTMX_BSD)
389 if ((master = open("/dev/ptmx_bsd", O_RDWR)) == -1) return(-1);
391 if ((master = open("/dev/ptmx", O_RDWR)) == -1) return(-1);
393 if ((slave_name = (char *)ptsname(master)) == NULL) {
397 if (grantpt(master)) {
398 static char buf[500];
400 sprintf(exp_pty_error,"grantpt(%s) 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.", expErrnoMsg(errno));
404 if (-1 == (int)unlockpt(master)) {
405 static char buf[500];
407 sprintf(exp_pty_error,"unlockpt(%s) failed.", expErrnoMsg(errno));
412 (void) ioctl(master,TIOCFLUSH,(char *)0);
413 #endif /* TIOCFLUSH */
415 exp_pty_slave_name = slave_name;
419 #if defined(HAVE__GETPTY) /* SGI needs it this way */
421 slave_name = _getpty(&master, O_RDWR, 0600, 0);
422 if (slave_name == NULL)
424 exp_pty_slave_name = slave_name;
428 #if defined(HAVE_PTC) && !defined(HAVE__GETPTY) /* old SGI, version 3 */
430 master = open("/dev/ptc", O_RDWR);
434 if (fstat(master, &stat_buf) < 0) {
438 ptynum = minor(stat_buf.st_rdev);
439 sprintf(slave_name,"/dev/ttyq%d",ptynum);
441 exp_pty_slave_name = slave_name;
445 #if defined(HAVE_GETPTY) && !defined(HAVE__GETPTY)
447 master = getpty(master_name, slave_name, O_RDWR);
448 /* is it really necessary to verify slave side is usable? */
449 exp_pty_slave_name = slave_name;
453 #if defined(HAVE_PTC_PTS)
455 master = open("/dev/ptc",O_RDWR);
458 slave_name = ttyname(master);
460 exp_pty_slave_name = slave_name;
464 #if defined(_SEQUENT_) && !defined(HAVE_PTMX)
466 /* old-style SEQUENT, new-style uses ptmx */
467 master = getpseudotty(&slave_name, &master_name);
468 exp_pty_slave_name = slave_name;
470 #endif /* _SEQUENT_ */
472 #if defined(HAVE_OPENPTY)
474 if (openpty(&master, &slave, master_name, 0, 0) != 0) {
479 strcpy(slave_name, ttyname(slave));
480 exp_pty_slave_name = slave_name;
483 #endif /* HAVE_OPENPTY */
485 #if defined(TEST_PTY)
487 * all pty allocation mechanisms after this require testing
489 if (exp_pty_test_start() == -1) return -1;
491 #if !defined(HAVE_CONVEX_GETPTY) && !defined(HAVE_PTYM) && !defined(HAVE_SCO_CLIST_PTYS)
492 for (bank = banks;*bank;bank++) {
495 if (stat(master_name, &stat_buf) < 0) break;
496 for (hex = "0123456789abcdef";*hex;hex++) {
498 strcpy(slave_name,master_name);
500 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
501 if (master >= 0) goto done;
506 #ifdef HAVE_SCO_CLIST_PTYS
507 for (num = 0; ; num++) {
510 sprintf (num_str, "%d", num);
511 sprintf (master_name, "%s%s", "/dev/ptyp", num_str);
512 if (stat (master_name, &stat_buf) < 0)
514 sprintf (slave_name, "%s%s", "/dev/ttyp", num_str);
516 master = exp_pty_test(master_name,slave_name,'0',num_str);
523 /* systems with PTYM follow this idea:
525 /dev/ptym/pty[a-ce-z][0-9a-f] master pseudo terminals
526 /dev/pty/tty[a-ce-z][0-9a-f] slave pseudo terminals
527 /dev/ptym/pty[a-ce-z][0-9][0-9] master pseudo terminals
528 /dev/pty/tty[a-ce-z][0-9][0-9] slave pseudo terminals
530 SPPUX (Convex's HPUX compatible) follows the PTYM convention but
533 /dev/ptym/pty[a-ce-z][0-9][0-9][0-9] master pseudo terminals
534 /dev/pty/tty[a-ce-z][0-9][0-9][0-9] slave pseudo terminals
536 The code does not distinguish between HPUX and SPPUX because there
537 is no reason to. HPUX will merely fail the extended SPPUX tests.
538 In fact, most SPPUX systems will fail simply because few systems
539 will actually have the extended ptys. However, the tests are
540 fast so it is no big deal.
544 * pty[a-ce-z][0-9a-f]
547 for (bank = banks;*bank;bank++) {
549 sprintf(tty_num,"0");
550 if (stat(master_name, &stat_buf) < 0) break;
551 *(slave_num+1) = '\0';
552 for (hex = "0123456789abcdef";*hex;hex++) {
554 *slave_bank = *tty_bank;
555 *slave_num = *tty_num;
556 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
557 if (master >= 0) goto done;
562 * tty[p-za-ce-o][0-9][0-9]
565 for (bank = banks;*bank;bank++) {
567 sprintf(tty_num,"00");
568 if (stat(master_name, &stat_buf) < 0) break;
569 for (num = 0; num<100; num++) {
570 *slave_bank = *tty_bank;
571 sprintf(tty_num,"%02d",num);
572 strcpy(slave_num,tty_num);
573 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
574 if (master >= 0) goto done;
579 * tty[p-za-ce-o][0-9][0-9][0-9]
581 for (bank = banks;*bank;bank++) {
583 sprintf(tty_num,"000");
584 if (stat(master_name, &stat_buf) < 0) break;
585 for (num = 0; num<1000; num++) {
586 *slave_bank = *tty_bank;
587 sprintf(tty_num,"%03d",num);
588 strcpy(slave_num,tty_num);
589 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
590 if (master >= 0) goto done;
594 #endif /* HAVE_PTYM */
596 #if defined(HAVE_CONVEX_GETPTY)
598 if ((master_name = getpty()) == NULL) return -1;
600 strcpy(slave_name,master_name);
601 slave_name[5] = 't';/* /dev/ptyXY ==> /dev/ttyXY */
603 tty_bank = &slave_name[8];
604 tty_num = &slave_name[9];
605 master = exp_pty_test(master_name,slave_name,*tty_bank,tty_num);
606 if (master >= 0) goto done;
612 exp_pty_slave_name = slave_name;
615 #endif /* defined(TEST_PTY) */
618 /* if slave is opened in a child, slave_control(1) must be executed after */
619 /* master is opened (when child is opened is irrelevent) */
620 /* if slave is opened in same proc as master, slave_control(1) must executed */
621 /* after slave is opened */
624 exp_slave_control(master,control)
626 int control; /* if 1, enable pty trapping of close/open/ioctl */
629 ioctl(master, TIOCTRAP, &control);
630 #endif /* HAVE_PTYTRAP */
637 CONST char *stty_args)
642 if (0 > (slave = open(slave_name, O_RDWR))) {
643 static char buf[500];
645 sprintf(exp_pty_error,"open(%s,rw) = %d (%s)",slave_name,slave,expErrnoMsg(errno));
649 #if defined(HAVE_PTMX_BSD)
650 if (ioctl (slave, I_LOOK, buf) != 0)
651 if (ioctl (slave, I_PUSH, "ldterm")) {
652 expDiagLogPtrStrStr("ioctl(%d,I_PUSH,\"ldterm\") = %s\n",slave,expErrnoMsg(errno));
655 #if defined(HAVE_PTMX)
656 if (ioctl(slave, I_PUSH, "ptem")) {
657 expDiagLogPtrStrStr("ioctl(%d,I_PUSH,\"ptem\") = %s\n",slave,expErrnoMsg(errno));
659 if (ioctl(slave, I_PUSH, "ldterm")) {
660 expDiagLogPtrStrStr("ioctl(%d,I_PUSH,\"ldterm\") = %s\n",slave,expErrnoMsg(errno));
662 if (ioctl(slave, I_PUSH, "ttcompat")) {
663 expDiagLogPtrStrStr("ioctl(%d,I_PUSH,\"ttcompat\") = %s\n",slave,expErrnoMsg(errno));
669 /* if opened in a new process, slave will be 0 (and */
670 /* ultimately, 1 and 2 as well) */
672 /* duplicate 0 onto 1 and 2 to prepare for stty */
677 ttytype(SET_TTYTYPE,slave,ttycopy,ttyinit,stty_args);
681 /* do another open, to tell master that slave is done fiddling */
682 /* with pty and master does not have to wait to do further acks */
683 if (0 > (slave2 = open(slave_name, O_RDWR))) return(-1);
685 #endif /* HAVE_PTYTRAP */
688 (void) exp_pty_unlock();
693 #include <sys/ptyio.h>
694 #include <sys/time.h>
696 /* This function attempts to deal with HP's pty interface. This
697 function simply returns an indication of what was trapped (or -1 for
698 failure), the parent deals with the details.
700 Originally, I tried to just trap open's but that is not enough. When
701 the pty is initialized, ioctl's are generated and if not trapped will
702 hang the child if no further trapping is done. (This could occur if
703 parent spawns a process and then immediatley does a close.) So
704 instead, the parent must trap the ioctl's. It probably suffices to
705 trap the write ioctl's (and tiocsctty which some hp's need) -
706 conceivably, stty could be smart enough not to do write's if the tty
707 settings are already correct. In that case, we'll have to rethink
710 Suggestions from HP engineers encouraged. I cannot imagine how this
711 interface was intended to be used!
716 exp_wait_for_slave_open(fd)
721 struct request_info ioctl_info;
725 int maxfds = sysconf(_SC_OPEN_MAX);
727 t.tv_sec = 30; /* 30 seconds */
734 (SELECT_MASK_TYPE *)0,
735 (SELECT_MASK_TYPE *)0,
736 (SELECT_MASK_TYPE *)&excep,
739 expDiagLogPtrStr("spawned process never started: %s\r\n",expErrnoMsg(errno));
742 if (ioctl(fd,TIOCREQCHECK,&ioctl_info) < 0) {
743 expDiagLogPtrStr("ioctl(TIOCREQCHECK) failed: %s\r\n",expErrnoMsg(errno));
747 found = ioctl_info.request;
749 expDiagLogPtrX("trapped pty op = %x",found);
750 if (found == TIOCOPEN) {
751 expDiagLogPtr(" TIOCOPEN");
752 } else if (found == TIOCCLOSE) {
753 expDiagLogPtr(" TIOCCLOSE");
757 if (found == TIOCSCTTY) {
758 expDiagLogPtr(" TIOCSCTTY");
762 if (found & IOC_IN) {
763 expDiagLogPtr(" IOC_IN (set)");
764 } else if (found & IOC_OUT) {
765 expDiagLogPtr(" IOC_OUT (get)");
770 if (ioctl(fd, TIOCREQSET, &ioctl_info) < 0) {
771 expDiagLogPtrStr("ioctl(TIOCREQSET) failed: %s\r\n",expErrnoMsg(errno));
781 /* a stub so we can do weird things on the cray */