imported 9.0.299 Beta.01
[ckermit.git] / ckupty.c
index 705914c..9906555 100644 (file)
--- a/ckupty.c
+++ b/ckupty.c
@@ -1,5 +1,9 @@
+char *ckptyv = "Pseudoterminal support, 9.0.101, 13 Jun 2011";
+
 /*  C K U P T Y  --  C-Kermit pseudoterminal control functions for UNIX  */
 
 /*  C K U P T Y  --  C-Kermit pseudoterminal control functions for UNIX  */
 
+/* Last update: Mon Jun 13 11:32:52 2011 */
+
 /*
   Copyright 1995 by the Massachusetts Institute of Technology.
 
 /*
   Copyright 1995 by the Massachusetts Institute of Technology.
 
   Jeffrey Altman <jaltman@secure-endpoints.com>
   Secure Endpoints Inc., New York City
   November 1999
   Jeffrey Altman <jaltman@secure-endpoints.com>
   Secure Endpoints Inc., New York City
   November 1999
+
+  Parameterized for pty file descriptor and function code,
+  Frank da Cruz, Columbia University, New York City
+  Dec 2006 - Sep 2009
 */
 
 /*
 */
 
 /*
@@ -31,7 +39,9 @@
    . HP-UX 9.00 and later
    . IRIX 6.0 and later
    . Linux
    . HP-UX 9.00 and later
    . IRIX 6.0 and later
    . Linux
+   . Mac OS X 10.4
    . NeXTSTEP 3.x
    . NeXTSTEP 3.x
+   . OpenBSD
    . QNX 4.25 (except PTY process termination not detected)
    . SCO OSR5.0.5
    . SCO Unixware 7
    . QNX 4.25 (except PTY process termination not detected)
    . SCO OSR5.0.5
    . SCO Unixware 7
@@ -39,9 +49,6 @@
    . Solaris 2.x and 7
    . SunOS 4.1.3
 
    . Solaris 2.x and 7
    . SunOS 4.1.3
 
-  Included but not tested yet in:
-   . Macintosh OSX, OpenBSD, and any other BSD44-based system not listed above
-
   Failures include:
    . SCO UNIX 3.2v4.2 (compile fails with syntax error in <memory.h>)
    . HP-UX 8.00 and earlier (no vhangup or ptsname routines)
   Failures include:
    . SCO UNIX 3.2v4.2 (compile fails with syntax error in <memory.h>)
    . HP-UX 8.00 and earlier (no vhangup or ptsname routines)
@@ -56,7 +63,7 @@ char * ptyver = "No PTY support";
 
 #else  /* (rest of this module...) */
 
 
 #else  /* (rest of this module...) */
 
-char * ptyver = "PTY support 8.0.014, 20 Aug 2002";
+char * ptyver = "PTY support 8.0.016, 22 Aug 2007";
 
 /* These will no doubt need adjustment... */
 
 
 /* These will no doubt need adjustment... */
 
@@ -67,6 +74,10 @@ char * ptyver = "PTY support 8.0.014, 20 Aug 2002";
 #define HAVE_TTYNAME
 #define HAVE_WAITPID
 
 #define HAVE_TTYNAME
 #define HAVE_WAITPID
 
+#ifdef SUNOS41
+#define BSD44ORPOSIX
+#endif /* SUNOS41 */
+
 #ifndef USE_TERMIO
 #ifdef LINUX
 #define USE_TERMIO
 #ifndef USE_TERMIO
 #ifdef LINUX
 #define USE_TERMIO
@@ -292,6 +303,7 @@ char * ptyver = "PTY support 8.0.014, 20 Aug 2002";
 #ifdef HAVE_TTY_H
 #include <tty.h>
 #endif /* HAVE_TTY_H */
 #ifdef HAVE_TTY_H
 #include <tty.h>
 #endif /* HAVE_TTY_H */
+
 /*
   Because of the way ptyibuf is used with streams messages, we need
   ptyibuf+1 to be on a full-word boundary.  The following weirdness
 /*
   Because of the way ptyibuf is used with streams messages, we need
   ptyibuf+1 to be on a full-word boundary.  The following weirdness
@@ -380,9 +392,21 @@ static int spty = -1;
 
 #endif /* USE_TERMIO */
 
 
 #endif /* USE_TERMIO */
 
-extern int ttyfd;                       /* Standard Kermit usage */
+#ifdef QNX                             /* 299 */
+#ifndef IXANY
+#define IXANY 0
+#endif /* IXANY */
+#endif /* QNX */
+
 static int msg = 0;
 
 static int msg = 0;
 
+/* Variables available to other modules */
+
+int pty_fork_active = 0;               /* pty fork is active */
+PID_T pty_fork_pid = -1;               /* pty fork pid */
+int pty_slave_fd = -1;                 /* pty slave file descriptor */
+int pty_master_fd = -1;                        /* pty master file descriptor */
+
 /* termbuf routines (begin) */
 /*
   init_termbuf()
 /* termbuf routines (begin) */
 /*
   init_termbuf()
@@ -395,8 +419,33 @@ static int msg = 0;
   set_termbuf() writes the structure into the kernel.
 */
 VOID
   set_termbuf() writes the structure into the kernel.
 */
 VOID
-init_termbuf() {
+init_termbuf(fd) int fd; {
+    int ttyfd;
     int rc = 0;
     int rc = 0;
+
+    ttyfd = fd;
+
+#ifdef HAVE_STREAMS
+    debug(F100,"init_termbuf HAVE_STREAMS","",0);
+#else
+    debug(F100,"init_termbuf HAVE_STREAMS NOT DEFINED","",0);
+#endif /* HAVE_STREAMS */
+#ifdef STREAMSPTY
+    debug(F100,"init_termbuf STREAMSPTY","",0);
+#else
+    debug(F100,"init_termbuf STREAMSPTY NOT DEFINED","",0);
+#endif /* STREAMSPTY */
+#ifdef INIT_SPTY
+    debug(F100,"init_termbuf INIT_SPTY","",0);
+#else
+    debug(F100,"init_termbuf INIT_SPTY NOT DEFINED","",0);
+#endif /* INIT_SPTY */
+
+    debug(F101,"init_termbuf ttyfd","",ttyfd);
+#ifdef INIT_SPTY
+    debug(F101,"init_termbuf spty","",spty);
+#endif /* INIT_SPTY */
+
     memset(&termbuf,0,sizeof(termbuf));
     memset(&termbuf2,0,sizeof(termbuf2));
 #ifndef        USE_TERMIO
     memset(&termbuf,0,sizeof(termbuf));
     memset(&termbuf2,0,sizeof(termbuf2));
 #ifndef        USE_TERMIO
@@ -431,8 +480,18 @@ copy_termbuf(cp, len) char *cp; int len; {
 #endif /* TIOCPKT_IOCTL */
 
 VOID
 #endif /* TIOCPKT_IOCTL */
 
 VOID
-set_termbuf() {                                /* Only make the necessary changes. */
+set_termbuf(fd) int fd; {              /* Only make the necessary changes. */
+    int x;
+    int ttyfd;
+    ttyfd = fd;
+
+    debug(F101,"set_termbuf ttyfd","",ttyfd);
+#ifdef INIT_SPTY
+    debug(F101,"set_termbuf spty","",spty);
+#endif /* INIT_SPTY */
+
 #ifndef        USE_TERMIO
 #ifndef        USE_TERMIO
+    debug(F100,"set_termbuf USE_TERMIO","",0);
     if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg)))
       ioctl(ttyfd, TIOCSETN, (char *)&termbuf.sg);
     if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc)))
     if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg)))
       ioctl(ttyfd, TIOCSETN, (char *)&termbuf.sg);
     if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc)))
@@ -443,13 +502,18 @@ set_termbuf() {                           /* Only make the necessary changes. */
     if (termbuf.lflags != termbuf2.lflags)
       ioctl(ttyfd, TIOCLSET, (char *)&termbuf.lflags);
 #else  /* USE_TERMIO */
     if (termbuf.lflags != termbuf2.lflags)
       ioctl(ttyfd, TIOCLSET, (char *)&termbuf.lflags);
 #else  /* USE_TERMIO */
-    if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) {
+    x = memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf));
+    debug(F101,"set_termbuf !USE_TERMIO memcmp","",x);
+    x = 1;                             /* Force this */
+    if (x) {
        int x;
        errno = 0;
 #ifdef INIT_SPTY
        int x;
        errno = 0;
 #ifdef INIT_SPTY
+       debug(F100,"set_termbuf INIT_SPTY","",0);
        x = tcsetattr(spty, TCSANOW, &termbuf);
        debug(F111,"set_termbuf tcsetattr(spty)",ckitoa(x),errno);
 #else
        x = tcsetattr(spty, TCSANOW, &termbuf);
        debug(F111,"set_termbuf tcsetattr(spty)",ckitoa(x),errno);
 #else
+       debug(F100,"set_termbuf !INIT_SPTY","",0);
        x = tcsetattr(ttyfd, TCSANOW, &termbuf);
        debug(F111,"set_termbuf tcsetattr(ttyfd)",ckitoa(x),errno);
 #endif /* INIT_SPTY */
        x = tcsetattr(ttyfd, TCSANOW, &termbuf);
        debug(F111,"set_termbuf tcsetattr(ttyfd)",ckitoa(x),errno);
 #endif /* INIT_SPTY */
@@ -481,7 +545,7 @@ ptyint_vhangup() {
 
 /*
   This routine is called twice.  It's not particularly important that the
 
 /*
   This routine is called twice.  It's not particularly important that the
-  setsid() or TIOCSTTY ioctls succeed (they may not the second time), but
+  setsid() or TIOCSCTTY ioctls succeed (they may not the second time), but
   rather that we have a controlling terminal at the end.  It is assumed that
   vhangup doesn't exist and confuse the process's notion of controlling
   terminal on any system without TIOCNOTTY.  That is, either vhangup() leaves
   rather that we have a controlling terminal at the end.  It is assumed that
   vhangup doesn't exist and confuse the process's notion of controlling
   terminal on any system without TIOCNOTTY.  That is, either vhangup() leaves
@@ -587,7 +651,7 @@ pty_cleanup(slave, pid, update_utmp) char *slave; int pid; int update_utmp; {
             return errno;
          case 0:
             ptyint_void_association();
             return errno;
          case 0:
             ptyint_void_association();
-            if (retval = (pty_open_ctty(slave, &fd)))
+            if (retval = (pty_open_ctty(slave, &fd, -1)))
              exit(retval);
             ptyint_vhangup();
             exit(0);
              exit(retval);
             ptyint_vhangup();
             exit(0);
@@ -636,6 +700,7 @@ pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; {
 #ifdef HAVE_OPENPTY
     int slavefd;
 
 #ifdef HAVE_OPENPTY
     int slavefd;
 
+    pty_master_fd = -1;
     debug(F100,"HAVE_OPENPTY","",0);
     if (openpty(fd,
                &slavefd,
     debug(F100,"HAVE_OPENPTY","",0);
     if (openpty(fd,
                &slavefd,
@@ -643,8 +708,10 @@ pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; {
                (struct termios *)0,
                (struct winsize *)0
                )
                (struct termios *)0,
                (struct winsize *)0
                )
-       )
-      return(1);
+       ) {
+       pty_master_fd = *fd;
+       return(1);
+    }
     close(slavefd);
     return(0);
 
     close(slavefd);
     return(0);
 
@@ -699,7 +766,11 @@ pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; {
         debug(F110,"pty_getpty()","open(/dev/pty) success",0);
 
   have_fd:
         debug(F110,"pty_getpty()","open(/dev/pty) success",0);
 
   have_fd:
+    /* This would be the pty master */
+    debug(F101,"pty_getpty fd(A)","",*fd);
     if (*fd >= 0) {
     if (*fd >= 0) {
+       pty_master_fd = *fd;
+
 #ifdef HAVE_GRANTPT
 #ifdef HAVE_PTMX
         debug(F100,"HAVE_GRANTPT","",0);
 #ifdef HAVE_GRANTPT
 #ifdef HAVE_PTMX
         debug(F100,"HAVE_GRANTPT","",0);
@@ -718,7 +789,7 @@ pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; {
        p = ttyname(*fd);
         debug(F110,"pty_getpty() ttyname()",p,0);
 #else
        p = ttyname(*fd);
         debug(F110,"pty_getpty() ttyname()",p,0);
 #else
-       /* XXX If we don't have either what do we do? */
+       /* If we don't have either what do we do? */
        return(PTY_GETPTY_NOPTY);       /* punt */
 #endif /* HAVE_TTYNAME */
 #endif /* HAVE_PTSNAME */
        return(PTY_GETPTY_NOPTY);       /* punt */
 #endif /* HAVE_TTYNAME */
 #endif /* HAVE_PTSNAME */
@@ -754,18 +825,23 @@ pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; {
              break;
            for (i = 0; i < 16; i++) {
                slavebuf[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
              break;
            for (i = 0; i < 16; i++) {
                slavebuf[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
+               errno = 0;
                *fd = open(slavebuf, O_RDWR|O_NDELAY);
                *fd = open(slavebuf, O_RDWR|O_NDELAY);
-               if (*fd < 0)
-                 continue;
-                debug(F110,"pty_getpty() found pty master",slavebuf,0);
+               if (*fd < 0) {
+                   debug(F111,"pty_getpty() pty master open error",
+                         slavebuf,errno);
+                   continue;
+               }
+                debug(F111,"pty_getpty() found pty master",slavebuf,*fd);
                slavebuf[sizeof("/dev/") - 1] = 't'; /* got pty */
                if (strlen(slavebuf) > slavelength -1) {
                    close(*fd);
                    *fd = -1;
                    return(PTY_GETPTY_SLAVE_TOOLONG);
                }
                slavebuf[sizeof("/dev/") - 1] = 't'; /* got pty */
                if (strlen(slavebuf) > slavelength -1) {
                    close(*fd);
                    *fd = -1;
                    return(PTY_GETPTY_SLAVE_TOOLONG);
                }
-                debug(F110,"pty_getpty() slavebuf [2]",slavebuf,0);
                ckstrncpy(slave, slavebuf, slavelength);
                ckstrncpy(slave, slavebuf, slavelength);
+                debug(F110,"pty_getpty slave name",slave,0);
+               pty_master_fd = *fd;
                return(0);
            }
        }
                return(0);
            }
        }
@@ -925,7 +1001,7 @@ pty_logwtmp (tty, user, host) char *user, *tty, *host; {
 
 /*
   This routine is called twice.  It's not particularly important that the
 
 /*
   This routine is called twice.  It's not particularly important that the
-  setsid() or TIOCSTTY ioctls succeed (they may not the second time), but
+  setsid() or TIOCSCTTY ioctls succeed (they may not the second time), but
   rather that we have a controlling terminal at the end.  It is assumed that
   vhangup doesn't exist and confuse the process's notion of controlling
   terminal on any system without TIOCNOTTY.  That is, either vhangup() leaves
   rather that we have a controlling terminal at the end.  It is assumed that
   vhangup doesn't exist and confuse the process's notion of controlling
   terminal on any system without TIOCNOTTY.  That is, either vhangup() leaves
@@ -935,7 +1011,7 @@ pty_logwtmp (tty, user, host) char *user, *tty, *host; {
   effect controlling terminals, so this condition is met.
 */
 long
   effect controlling terminals, so this condition is met.
 */
 long
-pty_open_ctty(slave, fd) char * slave; int *fd; {
+pty_open_ctty(slave, fd, fc) char * slave; int *fd; int fc; {
     int retval;
 
     debug(F110,"pty_open_ctty() slave",slave,0);
     int retval;
 
     debug(F110,"pty_open_ctty() slave",slave,0);
@@ -950,41 +1026,63 @@ pty_open_ctty(slave, fd) char * slave; int *fd; {
              );
        return(retval);
     }
              );
        return(retval);
     }
-
 #ifdef MUST_SETPGRP
 /*
   The Ultrix (and other BSD tty drivers) require the process group
   to be zero in order to acquire the new tty as a controlling tty.
 */
     setpgrp(0,0);
 #ifdef MUST_SETPGRP
 /*
   The Ultrix (and other BSD tty drivers) require the process group
   to be zero in order to acquire the new tty as a controlling tty.
 */
     setpgrp(0,0);
+    debug(F101,"pty_open_ctty MUST_SETPGRP setpgrp(0,0)","",errno);
 #endif /* MUST_SETPGRP */
 
     errno = 0;
     *fd = open(slave, O_RDWR);
 #endif /* MUST_SETPGRP */
 
     errno = 0;
     *fd = open(slave, O_RDWR);
+    debug(F111,"pty_open_ctty open(slave) fd",slave,*fd);
     if (*fd < 0) {
        debug(F111,"pty_open_ctty() open failure", slave, errno);
        return(PTY_OPEN_SLAVE_OPENFAIL);
     }
     if (*fd < 0) {
        debug(F111,"pty_open_ctty() open failure", slave, errno);
        return(PTY_OPEN_SLAVE_OPENFAIL);
     }
+#ifdef SOLARIS
+    /* This forces the job to have a controlling terminal. */
+    close(*fd);
+    *fd = open(slave, O_RDWR);
+    debug(F111,"pty_open_ctty close/open(slave) fd",slave,*fd);
 #ifdef DEBUG
 #ifdef DEBUG
-    else if (deblog) {
-       debug(F110, "pty_open_ctty() open ok", slave, 0);
+    /* This shows that /dev/tty exists */
+if (deblog) {
+       int x;
+       x = open("/dev/tty", O_RDWR);
+       debug(F111,"pty_open_ctty open(/dev/tty) fd",slave,x);
+       if (x < 0) debug(F111,"pty_open_ctty open(/dev/tty) errno","",errno);
+       debug(F110,"pty_open_ctty ttyname(/dev/tty)",ttyname(x),0);
+       if (x > -1) close(x);
     }
     }
-#endif /* DEBUG */
+#endif /* DEBUG */
+#endif /* SOLARIS */
 
 #ifdef MUST_SETPGRP
     setpgrp(0, getpid());
 #endif /* MUST_SETPGRP */
 
 #ifdef TIOCSCTTY
 
 #ifdef MUST_SETPGRP
     setpgrp(0, getpid());
 #endif /* MUST_SETPGRP */
 
 #ifdef TIOCSCTTY
-    errno = 0;
-    retval = ioctl(*fd, TIOCSCTTY, 0); /* Don't check return.*/
-    debug(F111,"pty_open_ctty() ioctl TIOCSCTTY",ckitoa(retval),errno);
-#endif /* TIOCSTTY */
+    if (
+#ifdef COMMENT
+       fc == 0
+#else
+       1
+#endif /* COMMENT */
+       ) {
+       /* TIOCSCTTY = Make this the job's controlling terminal */
+       errno = 0;
+       retval = ioctl(*fd, TIOCSCTTY, 0); /* Don't check return.*/
+       debug(F111,"pty_open_ctty() ioctl TIOCSCTTY",ckitoa(retval),errno);
+    }
+#endif /* TIOCSCTTY */
     return(0L);
 }
 
 long
     return(0L);
 }
 
 long
-pty_open_slave(slave, fd) char *slave; int *fd; {
+pty_open_slave(slave, fd, fc) char *slave; int *fd; int fc; {
     int vfd, testfd;
     long retval;
 #ifdef CK_POSIX_SIG
     int vfd, testfd;
     long retval;
 #ifdef CK_POSIX_SIG
@@ -1003,7 +1101,7 @@ pty_open_slave(slave, fd) char *slave; int *fd; {
   use a descriptor if we can.
 */
 #ifdef VHANG_FIRST
   use a descriptor if we can.
 */
 #ifdef VHANG_FIRST
-    if ((retval = pty_open_ctty(slave, &vfd)) != 0) {
+    if ((retval = pty_open_ctty(slave, &vfd, fc)) != 0) {
         debug(F111,
              "pty_open_slave() VHANG_FIRST",
              "pty_open_ctty() failed",
         debug(F111,
              "pty_open_slave() VHANG_FIRST",
              "pty_open_ctty() failed",
@@ -1059,28 +1157,24 @@ pty_open_slave(slave, fd) char *slave; int *fd; {
 
 /* Open the pty for real. */
 
 
 /* Open the pty for real. */
 
-    retval = pty_open_ctty(slave, fd);
+    retval = pty_open_ctty(slave, fd, fc);
+    debug(F111,"pty_open_slave retval",slave,retval);
+    debug(F111,"pty_open_slave fd",slave,*fd);
     if (retval != 0) {
         debug(F111,"pty_open_slave()","pty_open_ctty() failed",retval);
        return(PTY_OPEN_SLAVE_OPENFAIL);
     }
     if (retval != 0) {
         debug(F111,"pty_open_slave()","pty_open_ctty() failed",retval);
        return(PTY_OPEN_SLAVE_OPENFAIL);
     }
+    pty_slave_fd = *fd;                   /* This is not visible to the upper fork */
+    debug(F111,"pty_open_slave fd ctty'd",slave,pty_slave_fd);
     retval = pty_initialize_slave(*fd);
     retval = pty_initialize_slave(*fd);
+    debug(F111,"pty_open_slave fd init'd",slave,pty_slave_fd);
     if (retval) {
         debug(F111,"pty_open_slave()","pty_initialize_slave() failed",retval);
         return(retval);
     }
     if (retval) {
         debug(F111,"pty_open_slave()","pty_initialize_slave() failed",retval);
         return(retval);
     }
-#ifndef NO_DEVTTY
-    errno = 0;
-    testfd = open("/dev/tty", O_RDWR|O_NDELAY);
-    if (testfd < 0) {
-        debug(F111,"pty_open_slave() open failed","/dev/tty",errno);
-       close(*fd);
-       *fd = -1;
-       return(PTY_OPEN_SLAVE_NOCTTY);
-    }
-    close(testfd);
-#endif /* NO_DEVTTY */
-    debug(F110,"pty_open_slave()","success",0);
+    /* (VOID)pty_make_raw(*fd); */
+
+    debug(F100,"pty_open_slave OK","",*fd);
     return(0L);
 }
 
     return(0L);
 }
 
@@ -1409,20 +1503,35 @@ ptyint_update_wtmp(ent,host,user) struct utmp *ent; char *host; char *user; {
        close(fd);
     }
 #endif /* HAVE_UPDWTMP */
        close(fd);
     }
 #endif /* HAVE_UPDWTMP */
-    return(0); /* no current failure cases; file not found is not failure!*/
+    return(0); /* no current failure cases; file not found is not failure! */
 }
 #endif /* WANT_UTMP */
 
 }
 #endif /* WANT_UTMP */
 
-static char Xline[17] = { 0, 0 };
-int pty_fork_pid = -1;
+/* This is for ancient Unixes that don't have these tty symbols defined. */
+
+#ifndef PENDIN
+#define PENDIN ICANON
+#endif /* PENDIN */
+#ifndef FLUSHO
+#define FLUSHO ICANON
+#endif /* FLUSHO */
+#ifndef IMAXBEL
+#define IMAXBEL ICANON
+#endif /* IMAXBEL */
+#ifndef EXTPROC
+#define EXTPROC ICANON
+#endif /* EXTPROC */
 
 
+static char Xline[17] = { 0, 0 };
 /*
   getptyslave()
   Open the slave side of the pty, and do any initialization that is necessary.
 /*
   getptyslave()
   Open the slave side of the pty, and do any initialization that is necessary.
-  The return value is a file descriptor for the slave side.
+  The return value fd is a file descriptor for the slave side.
+  fc = function code from do_pty() (q.v.)
 */
 int
 */
 int
-getptyslave() {
+getptyslave(fd, fc) int * fd, fc; {
+    int ttyfd;
     int t = -1;
     long retval;
 #ifdef TIOCGWINSZ
     int t = -1;
     long retval;
 #ifdef TIOCGWINSZ
@@ -1430,8 +1539,8 @@ getptyslave() {
     extern int cmd_rows, cmd_cols;
 #endif /* TIOCGWINSZ */
 
     extern int cmd_rows, cmd_cols;
 #endif /* TIOCGWINSZ */
 
-    debug(F100,"getptyslave()","",0);
-
+    ttyfd = *fd;
+    debug(F111,"getptyslave()","ttyfd",ttyfd);
     /*
      * Opening the slave side may cause initilization of the
      * kernel tty structure.  We need remember the state of:
     /*
      * Opening the slave side may cause initilization of the
      * kernel tty structure.  We need remember the state of:
@@ -1440,17 +1549,16 @@ getptyslave() {
      *      terminal speed
      * so that we can reset them if we need to.
      */
      *      terminal speed
      * so that we can reset them if we need to.
      */
-    if ((retval = pty_open_slave(Xline, &t)) != 0) {
+    if ((retval = pty_open_slave(Xline, &t, fc)) != 0) {
        perror(Xline);
        msg++;
         debug(F111,"getptyslave()","Unable to open slave",retval);
         return(-1);
     }
        perror(Xline);
        msg++;
         debug(F111,"getptyslave()","Unable to open slave",retval);
         return(-1);
     }
-
-    debug(F111,"getptyslave","ttyfd",ttyfd);
     debug(F111,"getptyslave","t",t);
 #ifdef INIT_SPTY
     spty = t;
     debug(F111,"getptyslave","t",t);
 #ifdef INIT_SPTY
     spty = t;
+    debug(F111,"getptyslave","spty",spty);
 #endif /* INIT_SPTY */
 #ifdef STREAMSPTY
     if (ioctl(t,I_PUSH,"pckt") < 0) {
 #endif /* INIT_SPTY */
 #ifdef STREAMSPTY
     if (ioctl(t,I_PUSH,"pckt") < 0) {
@@ -1462,78 +1570,178 @@ getptyslave() {
 #endif /* STREAMSPTY */
 
     /* Set up the tty modes as we like them to be. */
 #endif /* STREAMSPTY */
 
     /* Set up the tty modes as we like them to be. */
-    init_termbuf();
+#ifdef COMMENT
+    /* Originally like this... But this is the master - we want the slave */
+    /* Anyway, this fails on Solaris and probably other System V OS's */
+    init_termbuf(ttyfd);
+#else
+    init_termbuf(t);
+#endif /* COMMENT */
 #ifdef TIOCGWINSZ
     if (cmd_rows || cmd_cols) {
         memset((char *)&ws, 0, sizeof(ws));
         ws.ws_col = cmd_cols;
         ws.ws_row = cmd_rows;
 #ifdef TIOCGWINSZ
     if (cmd_rows || cmd_cols) {
         memset((char *)&ws, 0, sizeof(ws));
         ws.ws_col = cmd_cols;
         ws.ws_row = cmd_rows;
+       debug(F101,"getptyslave() doing TIOCSWINSZ...","",t);
         ioctl(t, TIOCSWINSZ, (char *)&ws);
     }
 #endif /* TIOCGWINSZ */
 
         ioctl(t, TIOCSWINSZ, (char *)&ws);
     }
 #endif /* TIOCGWINSZ */
 
-    /* Settings for sgtty based systems */
+    /* For external protocols, put the pty in no-echo mode */
+    if (fc == 1) {
+       debug(F100,"getptyslave() setting rawmode","",0);
+       /* iflags */
+       termbuf.c_iflag &= ~(PARMRK|ISTRIP|BRKINT|INLCR|IGNCR|ICRNL);
+       termbuf.c_iflag &= ~(INPCK|IGNPAR|IMAXBEL|IXANY|IXON|IXOFF);
+       termbuf.c_iflag |= IGNBRK;
+#ifdef IUCLC
+       termbuf.c_iflag &= ~IUCLC;
+#endif /* IUCLC */
+
+       /* oflags */
+       termbuf.c_oflag &= ~OPOST;
+#ifdef OXTABS
+       termbuf.c_oflag &= ~OXTABS;
+#endif /* OXTABS */
+#ifdef ONOCR
+       termbuf.c_oflag &= ~ONOCR;
+#endif /* ONOCR */
+#ifdef ONLRET
+       termbuf.c_oflag &= ~ONLRET;
+#endif /* ONLRET */
+#ifdef ONLCR
+       termbuf.c_oflag &= ~ONLCR;
+#endif /* ONLCR */
+
+       /* lflags */
+       termbuf.c_lflag &= ~ECHO;
+#ifdef ECHOE
+       termbuf.c_lflag &= ~ECHOE;
+#endif /* ECHOE */
+#ifdef ECHONL
+       termbuf.c_lflag &= ~ECHONL;
+#endif /* ECHONL */
+#ifdef ECHOPRT
+       termbuf.c_lflag &= ~ECHOPRT;
+#endif /* ECHOPRT */
+#ifdef ECHOKE
+       termbuf.c_lflag &= ~ECHOKE;
+#endif /* ECHOKE */
+#ifdef ECHOCTL
+       termbuf.c_lflag &= ~ECHOCTL;
+#endif /* ECHOCTL */
+#ifdef ALTWERASE
+       termbuf.c_lflag &= ~ALTWERASE;
+#endif /* ALTWERASE */
+#ifdef EXTPROC
+       termbuf.c_lflag &= ~EXTPROC;
+#endif /* EXTPROC */
+       termbuf.c_lflag &= ~(ICANON|ISIG|IEXTEN|TOSTOP|FLUSHO|PENDIN);
+
+#ifdef NOKERNINFO
+       termbuf.c_lflag |= NOKERNINFO;
+#endif /* NOKERNINFO */
+       /* termbuf.c_lflag |= NOFLSH; */
+       termbuf.c_lflag &= ~NOFLSH;
+
+       /* cflags */
+       termbuf.c_cflag &= ~(CSIZE|PARENB|PARODD);
+       termbuf.c_cflag |= CS8|CREAD;
+#ifdef VMIN
+       termbuf.c_cc[VMIN] = 1;
+#endif /* VMIN */
+    } else {                           /* Regular interactive use */
+       debug(F100,"getptyslave() setting cooked mode","",0);
+
+       /* Settings for sgtty based systems */
 
 #ifndef USE_TERMIO
 
 #ifndef USE_TERMIO
-    termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS;
+       termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS;
 #endif /* USE_TERMIO */
 
 #ifndef OXTABS
 #define OXTABS 0
 #endif /* OXTABS */
 
 #endif /* USE_TERMIO */
 
 #ifndef OXTABS
 #define OXTABS 0
 #endif /* OXTABS */
 
-    /* Settings for UNICOS and HPUX */
+       /* Settings for UNICOS and HPUX */
 
 #ifdef CRAY
 
 #ifdef CRAY
-    termbuf.c_oflag = OPOST|ONLCR|TAB3;
-    termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
-    termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
-    termbuf.c_cflag = EXTB|HUPCL|CS8;
+       termbuf.c_oflag = OPOST|ONLCR|TAB3;
+       termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
+       termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
+       termbuf.c_cflag = EXTB|HUPCL|CS8;
 #else /* CRAY */
 #ifdef HPUX
 #else /* CRAY */
 #ifdef HPUX
-    termbuf.c_oflag = OPOST|ONLCR|TAB3;
-    termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
-    termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
-    termbuf.c_cflag = EXTB|HUPCL|CS8;
+       termbuf.c_oflag = OPOST|ONLCR|TAB3;
+       termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
+       termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
+       termbuf.c_cflag = EXTB|HUPCL|CS8;
 #else /* HPUX */
 #ifdef USE_TERMIO
 #else /* HPUX */
 #ifdef USE_TERMIO
-    /*
-    Settings for all other termios/termio based systems, other than 4.4BSD.
-    In 4.4BSD the kernel does the initial terminal setup.
-    */
+       /*
+         Settings for all other termios/termio based systems, other than 
+         4.4BSD.  In 4.4BSD the kernel does the initial terminal setup.
+       */
 #ifdef BSD42
 #ifndef BSD44
 #ifdef BSD42
 #ifndef BSD44
-    termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG;
-    termbuf.c_oflag |= ONLCR|OXTABS|OPOST;
-    termbuf.c_iflag |= ICRNL|IGNPAR;
-    termbuf.c_cflag |= HUPCL;
-    termbuf.c_iflag &= ~IXOFF;
+       termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG;
+       termbuf.c_oflag |= ONLCR|OXTABS|OPOST;
+       termbuf.c_iflag |= ICRNL|IGNPAR;
+       termbuf.c_cflag |= HUPCL;
+       termbuf.c_iflag &= ~IXOFF;
 #endif /* BSD44 */
 #else /* BSD42 */
 #endif /* BSD44 */
 #else /* BSD42 */
-    termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG;
-    termbuf.c_oflag |= ONLCR|OXTABS|OPOST;
-    termbuf.c_iflag |= ICRNL|IGNPAR;
-    termbuf.c_cflag |= HUPCL;
-    termbuf.c_iflag &= ~IXOFF;
+       termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG;
+       termbuf.c_oflag |= ONLCR|OXTABS|OPOST;
+       termbuf.c_iflag |= ICRNL|IGNPAR;
+       termbuf.c_cflag |= HUPCL;
+       termbuf.c_iflag &= ~IXOFF;
 #endif /* BSD42 */
 #endif /* USE_TERMIO */
 #endif /* HPUX */
 #endif /* CRAY */
 #endif /* BSD42 */
 #endif /* USE_TERMIO */
 #endif /* HPUX */
 #endif /* CRAY */
+    }
 
 
-    set_termbuf();  /* Set the tty modes, and make this our controlling tty. */
+    /* Set the tty modes, and make this our controlling tty. */
+#ifdef COMMENT
+    /* But this is the master - we want the slave */
+    set_termbuf(ttyfd);
+#else
+    set_termbuf(t);
+#endif /* COMMENT */
 
     if (t != 0)
       dup2(t, 0);
     if (t != 1)
       dup2(t, 1);
 
     if (t != 0)
       dup2(t, 0);
     if (t != 1)
       dup2(t, 1);
-    if (t != 2)
-      dup2(t, 2);
+    if (t != 2) {
+       if (fc == 0) {
+           dup2(t, 2);
+       } else if (fc == 1) {
+           /* For external protocols, send stderr to /dev/null */
+#ifdef COMMENT
+           int xx;
+#ifndef COMMENT
+           char * s = "/dev/null";
+           errno = 0;
+           xx = open(s, O_WRONLY);
+#else
+           char * s = "pty.log";
+           errno = 0;
+           xx = open(s, O_CREAT, 0644);
+#endif /* COMMENT */
+           debug(F111,"getptyslave redirect stderr",s,errno);
+           dup2(xx,2);
+#endif /* COMMENT */
+       }
+    }
     if (t > 2)
       close(t);
 
     if (ttyfd > 2) {
        close(ttyfd);
         ttyfd = -1;
     if (t > 2)
       close(t);
 
     if (ttyfd > 2) {
        close(ttyfd);
         ttyfd = -1;
+       *fd = ttyfd;
     }
     return(0);
 }
     }
     return(0);
 }
@@ -1606,20 +1814,53 @@ exec_cmd(s) char * s; {
     if (!q) return;
 
     args = q->a_head + 1;
     if (!q) return;
 
     args = q->a_head + 1;
+
+#ifdef DEBUG    
+    {
+       int i, n;
+       n = q->a_size;
+       for (i = 0; i <= n; i++) {
+           if (!args[i]) {
+               debug(F111,"exec_cmd arg","NULL",i);
+               break;
+           } else {
+               debug(F111,"exec_cmd arg",args[i],i);
+               if (i == n && args[i]) {
+                   debug(F101,"exec_cmd SUBSTITUTING NULL","",i);
+                   if (strlen(args[i]) == 0)
+                     makestr(&(args[i]),NULL);
+               }
+
+           }
+       }           
+    }
+#endif /* DEBUG */
+
     execvp(args[0],args);
 }
 
 /* Get a pty, scan input lines. */
     execvp(args[0],args);
 }
 
 /* Get a pty, scan input lines. */
+/* fc = 0 for interactive access; fc = 1 for running external protocols */
+
+static int pty_fc = -1;                        /* Global copy of fc */
 
 int
 
 int
-do_pty(cmd) char * cmd; {
+do_pty(fd, cmd, fc) int * fd; char * cmd; int fc; {
     long retval;
     int syncpipe[2];
     long retval;
     int syncpipe[2];
-    int i;
+    int i, ttyfd;
 #ifdef HAVE_PTYTRAP
     int x;
 #endif /* HAVE_PTYTRAP */
 
 #ifdef HAVE_PTYTRAP
     int x;
 #endif /* HAVE_PTYTRAP */
 
+    debug(F101,"CKUPTY.C do_pty fc","",fc);
+
+    ttyfd = *fd;
+
+    pty_master_fd = -2;
+    pty_slave_fd = -2;
+    pty_fork_pid = -2;
+
     msg = 0;                           /* Message counter */
     pty_init();                                /* Find an available pty to use. */
     errno = 0;
     msg = 0;                           /* Message counter */
     pty_init();                                /* Find an available pty to use. */
     errno = 0;
@@ -1628,9 +1869,11 @@ do_pty(cmd) char * cmd; {
        if (msg++ == 0)
          perror(Xline);
         debug(F111,"do_pty()","pty_getpty() fails",retval);
        if (msg++ == 0)
          perror(Xline);
         debug(F111,"do_pty()","pty_getpty() fails",retval);
+       *fd = ttyfd;
         return(-1);
     }
         return(-1);
     }
-    debug(F110,"do_pty() Xline",Xline,0);
+    *fd = ttyfd;
+    debug(F111,"do_pty() Xline",Xline,ttyfd);
 
 #ifdef SIGTTOU
 /*
 
 #ifdef SIGTTOU
 /*
@@ -1671,6 +1914,7 @@ do_pty(cmd) char * cmd; {
             return(-1);
         }
         pty_fork_pid = i;              /* So we can clean it up later */
             return(-1);
         }
         pty_fork_pid = i;              /* So we can clean it up later */
+       pty_fork_active = 1;
        debug(F101,"do_pty pty_fork_pid","",pty_fork_pid);
 #ifdef HAVE_PTYTRAP
         /* HPUX does not allow the master to read end of file.  */
        debug(F101,"do_pty pty_fork_pid","",pty_fork_pid);
 #ifdef HAVE_PTYTRAP
         /* HPUX does not allow the master to read end of file.  */
@@ -1683,8 +1927,13 @@ do_pty(cmd) char * cmd; {
         debug(F111,"do_pty()","synchronized - pty_fork_pid",pty_fork_pid);
         close(syncpipe[0]);
     } else {
         debug(F111,"do_pty()","synchronized - pty_fork_pid",pty_fork_pid);
         close(syncpipe[0]);
     } else {
+       int x;
+       debug(F101,"do_pty getptyslave ttyfd A","",ttyfd);
         debug(F110,"do_pty()","Slave starts",0);
         debug(F110,"do_pty()","Slave starts",0);
-        if (getptyslave() == 0) {
+       x = getptyslave(&ttyfd,fc);
+       debug(F101,"do_pty getptyslave","",x);
+        if (x == 0) {
+           debug(F101,"do_pty getptyslave ttyfd B","",ttyfd);
 #ifdef WANT_UTMP
             pty_update_utmp(PTY_USER_PROCESS,
                            getpid(),
 #ifdef WANT_UTMP
             pty_update_utmp(PTY_USER_PROCESS,
                            getpid(),
@@ -1700,23 +1949,31 @@ do_pty(cmd) char * cmd; {
             close(syncpipe[0]);
             close(syncpipe[1]);
 
             close(syncpipe[0]);
             close(syncpipe[1]);
 
+           debug(F110,"do_pty cmd",cmd,"");
             exec_cmd(cmd);
             debug(F111,"do_pty()","exec_cmd() returns - why?",errno);
         }
             exec_cmd(cmd);
             debug(F111,"do_pty()","exec_cmd() returns - why?",errno);
         }
+       *fd = ttyfd;
         debug(F110,"do_pty()","getptyslave() fails - exiting",0);
         exit(1);
     }
         debug(F110,"do_pty()","getptyslave() fails - exiting",0);
         exit(1);
     }
-    return(0);
+    *fd = ttyfd;
+    pty_fc = fc;
+    return(getpid());
 } /* end of do_pty() */
 
 
 VOID
 end_pty() {
     msg = 0;                           /* Message counter */
 } /* end of do_pty() */
 
 
 VOID
 end_pty() {
     msg = 0;                           /* Message counter */
+    debug(F101,"end_pty pty_fork_pid","",pty_fork_pid);
     if (Xline[0] && pty_fork_pid >= 0) {
         pty_cleanup(Xline,pty_fork_pid,1);
         Xline[0] = '\0';
         pty_fork_pid = -1;
     if (Xline[0] && pty_fork_pid >= 0) {
         pty_cleanup(Xline,pty_fork_pid,1);
         Xline[0] = '\0';
         pty_fork_pid = -1;
+       pty_fork_active = 0;
+       debug(F101,"end_pty pty_fork_active","",pty_fork_active);
     }
     }
+    pty_fc = -1;
 }
 #endif /* NETPTY */
 }
 #endif /* NETPTY */