Changes for pyramid from sbanner1@cheops (S. John Banner).
[unix-history] / usr / src / usr.bin / tn3270 / sys_curses / system.c
index 01ab229..a64e92f 100644 (file)
+/*
+ *     Copyright (c) 1984-1987 by the Regents of the
+ *     University of California and by Gregory Glenn Minshall.
+ *
+ *     Permission to use, copy, modify, and distribute these
+ *     programs and their documentation for any purpose and
+ *     without fee is hereby granted, provided that this
+ *     copyright and permission appear on all copies and
+ *     supporting documentation, the name of the Regents of
+ *     the University of California not be used in advertising
+ *     or publicity pertaining to distribution of the programs
+ *     without specific prior permission, and notice be given in
+ *     supporting documentation that copying and distribution is
+ *     by permission of the Regents of the University of California
+ *     and by Gregory Glenn Minshall.  Neither the Regents of the
+ *     University of California nor Gregory Glenn Minshall make
+ *     representations about the suitability of this software
+ *     for any purpose.  It is provided "as is" without
+ *     express or implied warranty.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)system.c   3.2 (Berkeley) %G%";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#if     defined(pyr)
+#define fd_set fdset_t
+#endif  /* defined(pyr) */
+
+#if    !defined(sun) && !defined(pyr)
+#include <sys/inode.h>
+#else  /* !defined(sun) */
+#define        IREAD   00400
+#define        IWRITE  00200
+#endif /* !defined(sun) */
+
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 #include <sys/wait.h>
 
 #include <sys/wait.h>
 
+#include <errno.h>
+extern int errno;
+
+#include <netdb.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdio.h>
+#include <pwd.h>
 
 #include "../general/general.h"
 
 #include "../general/general.h"
-#include "../api/api.h"
+#include "../ctlr/api.h"
+#include "../api/api_exch.h"
 
 #include "../general/globals.h"
 
 
 #include "../general/globals.h"
 
+#ifndef        FD_SETSIZE
+/*
+ * The following is defined just in case someone should want to run
+ * this telnet on a 4.2 system.
+ *
+ */
+
+#define        FD_SET(n, p)    ((p)->fds_bits[0] |= (1<<(n)))
+#define        FD_CLR(n, p)    ((p)->fds_bits[0] &= ~(1<<(n)))
+#define        FD_ISSET(n, p)  ((p)->fds_bits[0] & (1<<(n)))
+#define FD_ZERO(p)     ((p)->fds_bits[0] = 0)
 
 
+#endif
+\f
 static int shell_pid = 0;
 static int shell_pid = 0;
+static char key[50];                   /* Actual key */
+static char *keyname;                  /* Name of file with key in it */
+
+static char *ourENVlist[200];          /* Lots of room */
 
 static int
 
 static int
-child_died()
+    sock = -1,                         /* Connected socket */
+    serversock;                                /* Server (listening) socket */
+
+static enum { DEAD, UNCONNECTED, CONNECTED } state;
+
+static int
+    storage_location,          /* Address we have */
+    storage_length = 0,                /* Length we have */
+    storage_must_send = 0,     /* Storage belongs on other side of wire */
+    storage_accessed = 0;      /* The storage is accessed (so leave alone)! */
+
+static long storage[1000];
+
+static union REGS inputRegs;
+static struct SREGS inputSregs;
+
+
+static void
+kill_connection()
 {
 {
-    union wait *status;
+    state = UNCONNECTED;
+    if (sock != -1) {
+       (void) close(sock);
+       sock = -1;
+    }
+}
 
 
-    while ((pid = wait3(status, WNOHANG, 0)) > 0) {
-       if (pid == shell_pid) {
-           shell_active = 0;
+
+static int
+nextstore()
+{
+    struct storage_descriptor sd;
+
+    if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+       storage_length = 0;
+       return -1;
+    }
+    storage_length = sd.length;
+    storage_location = sd.location;
+    if (storage_length > sizeof storage) {
+       fprintf(stderr, "API client tried to send too much storage (%d).\n",
+               storage_length);
+       storage_length = 0;
+       return -1;
+    }
+    if (api_exch_intype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
+                                                       == -1) {
+       storage_length = 0;
+       return -1;
+    }
+    return 0;
+}
+
+
+static int
+doreject(message)
+char   *message;
+{
+    struct storage_descriptor sd;
+    int length = strlen(message);
+
+    if (api_exch_outcommand(EXCH_CMD_REJECTED) == -1) {
+       return -1;
+    }
+    sd.length = length;
+    if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+       return -1;
+    }
+    if (api_exch_outtype(EXCH_TYPE_BYTES, length, message) == -1) {
+       return -1;
+    }
+    return 0;
+}
+
+
+/*
+ * doassociate()
+ *
+ * Negotiate with the other side and try to do something.
+ *
+ * Returns:
+ *
+ *     -1:     Error in processing
+ *      0:     Invalid password entered
+ *      1:     Association OK
+ */
+
+static int
+doassociate()
+{
+    struct passwd *pwent;
+    char
+       promptbuf[100],
+       buffer[200];
+    int length;
+    int was;
+    struct storage_descriptor sd;
+
+    if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+       return -1;
+    }
+    sd.length = sd.length;
+    if (sd.length > sizeof buffer) {
+       doreject("(internal error) Authentication key too long");
+       return -1;
+    }
+    if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+       return -1;
+    }
+    buffer[sd.length] = 0;
+
+    if (strcmp(buffer, key) != 0) {
+       if ((pwent = getpwuid(geteuid())) == 0) {
+           return -1;
+       }
+       sprintf(promptbuf, "Enter password for user %s:", pwent->pw_name);
+       if (api_exch_outcommand(EXCH_CMD_SEND_AUTH) == -1) {
+           return -1;
+       }
+       sd.length = strlen(promptbuf);
+       if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+                                                                       == -1) {
+           return -1;
+       }
+       if (api_exch_outtype(EXCH_TYPE_BYTES, strlen(promptbuf), promptbuf)
+                                                                       == -1) {
+           return -1;
+       }
+       sd.length = strlen(pwent->pw_name);
+       if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+                                                                       == -1) {
+           return -1;
+       }
+       if (api_exch_outtype(EXCH_TYPE_BYTES,
+                           strlen(pwent->pw_name), pwent->pw_name) == -1) {
+           return -1;
+       }
+       if (api_exch_incommand(EXCH_CMD_AUTH) == -1) {
+           return -1;
+       }
+       if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+                                                                       == -1) {
+           return -1;
+       }
+       sd.length = sd.length;
+       if (sd.length > sizeof buffer) {
+           doreject("Password entered was too long");
+           return -1;
+       }
+       if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+           return -1;
+       }
+       buffer[sd.length] = 0;
+
+       /* Is this the correct password? */
+       if (strlen(pwent->pw_name)) {
+           char *ptr;
+           int i;
+
+           ptr = pwent->pw_name;
+           i = 0;
+           while (i < sd.length) {
+               buffer[i++] ^= *ptr++;
+               if (*ptr == 0) {
+                   ptr = pwent->pw_name;
+               }
+           }
+       }
+       if (strcmp(crypt(buffer, pwent->pw_passwd), pwent->pw_passwd) != 0) {
+           doreject("Invalid password");
+           sleep(10);          /* Don't let us do too many of these */
+           return 0;
        }
     }
        }
     }
-    signal(SIGCHLD, child_died);
+    if (api_exch_outcommand(EXCH_CMD_ASSOCIATED) == -1) {
+       return -1;
+    } else {
+       return 1;
+    }
+}
+
+
+void
+freestorage()
+{
+    char buffer[40];
+    struct storage_descriptor sd;
+
+    if (storage_accessed) {
+       fprintf(stderr, "Internal error - attempt to free accessed storage.\n");
+       fprintf(stderr, "(Encountered in file %s at line %d.)\n",
+                       __FILE__, __LINE__);
+       quit();
+    }
+    if (storage_must_send == 0) {
+       return;
+    }
+    storage_must_send = 0;
+    if (api_exch_outcommand(EXCH_CMD_HEREIS) == -1) {
+       kill_connection();
+       return;
+    }
+    sd.length = storage_length;
+    sd.location = storage_location;
+    if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+       kill_connection();
+       return;
+    }
+    if (api_exch_outtype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
+                                                           == -1) {
+       kill_connection();
+       return;
+    }
+}
+
+
+static int
+getstorage(address, length, copyin)
+int
+    address,
+    length,
+    copyin;
+{
+    struct storage_descriptor sd;
+    char buffer[40];
+
+    freestorage();
+    if (storage_accessed) {
+       fprintf(stderr,
+               "Internal error - attempt to get while storage accessed.\n");
+       fprintf(stderr, "(Encountered in file %s at line %d.)\n",
+                       __FILE__, __LINE__);
+       quit();
+    }
+    storage_must_send = 0;
+    if (api_exch_outcommand(EXCH_CMD_GIMME) == -1) {
+       kill_connection();
+       return -1;
+    }
+    storage_location = address;
+    storage_length = length;
+    if (copyin) {
+       sd.location = storage_location;
+       sd.length = storage_length;
+       if (api_exch_outtype(EXCH_TYPE_STORE_DESC,
+                                       sizeof sd, (char *)&sd) == -1) {
+           kill_connection();
+           return -1;
+       }
+       if (api_exch_incommand(EXCH_CMD_HEREIS) == -1) {
+           fprintf(stderr, "Bad data from other side.\n");
+           fprintf(stderr, "(Encountered at %s, %d.)\n", __FILE__, __LINE__);
+           return -1;
+       }
+       if (nextstore() == -1) {
+           kill_connection();
+           return -1;
+       }
+    }
+    return 0;
+}
+
+void
+movetous(local, es, di, length)
+char
+    *local;
+int
+    es,
+    di;
+int
+    length;
+{
+    if (length > sizeof storage) {
+       fprintf(stderr, "Internal API error - movetous() length too long.\n");
+       fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
+       quit();
+    } else if (length == 0) {
+       return;
+    }
+    getstorage(di, length, 1);
+    memcpy(local, storage+(di-storage_location), length);
+}
+
+void
+movetothem(es, di, local, length)
+int
+    es,
+    di;
+char
+    *local;
+int
+    length;
+{
+    if (length > sizeof storage) {
+       fprintf(stderr, "Internal API error - movetothem() length too long.\n");
+       fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
+       quit();
+    } else if (length == 0) {
+       return;
+    }
+    freestorage();
+    memcpy((char *)storage, local, length);
+    storage_length = length;
+    storage_location = di;
+    storage_must_send = 1;
+}
+
+
+char *
+access_api(location, length, copyin)
+int
+    location,
+    length,
+    copyin;                    /* Do we need to copy in initially? */
+{
+    if (storage_accessed) {
+       fprintf(stderr, "Internal error - storage accessed twice\n");
+       fprintf(stderr, "(Encountered in file %s, line %d.)\n",
+                               __FILE__, __LINE__);
+       quit();
+    } else if (length != 0) {
+       freestorage();
+       getstorage(location, length, copyin);
+       storage_accessed = 1;
+    }
+    return (char *) storage;
+}
+
+unaccess_api(location, local, length, copyout)
+int    location;
+char   *local;
+int    length;
+{
+    if (storage_accessed == 0) {
+       fprintf(stderr, "Internal error - unnecessary unaccess_api call.\n");
+       fprintf(stderr, "(Encountered in file %s, line %d.)\n",
+                       __FILE__, __LINE__);
+       quit();
+    }
+    storage_accessed = 0;
+    storage_must_send = copyout;       /* if needs to go back */
+}
+
+/*
+ * Accept a connection from an API client, aborting if the child dies.
+ */
+
+static int
+doconnect()
+{
+    fd_set fdset;
+    int i;
+
+    sock = -1;
+    FD_ZERO(&fdset);
+    while (shell_active && (sock == -1)) {
+       FD_SET(serversock, &fdset);
+       if ((i = select(serversock+1, &fdset, 0, 0, 0)) < 0) {
+           if (errno = EINTR) {
+               continue;
+           } else {
+               perror("in select waiting for API connection");
+               return -1;
+           }
+       } else {
+           i = accept(serversock, 0, 0);
+           if (i == -1) {
+               perror("accepting API connection");
+               return -1;
+           }
+           sock = i;
+       }
+    }
+    /* If the process has already exited, we may need to close */
+    if ((shell_active == 0) && (sock != -1)) {
+       (void) close(sock);
+       sock = -1;
+       setcommandmode();       /* In case child_died sneaked in */
+    }
 }
 
 /*
 }
 
 /*
@@ -33,10 +468,108 @@ child_died()
 int
 shell_continue()
 {
 int
 shell_continue()
 {
+    int i;
+
+    switch (state) {
+    case DEAD:
+       pause();                        /* Nothing to do */
+       break;
+    case UNCONNECTED:
+       if (doconnect() == -1) {
+           kill_connection();
+           return -1;
+       }
+       if (api_exch_init(sock, "server") == -1) {
+           return -1;
+       }
+       while (state == UNCONNECTED) {
+           if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
+               kill_connection();
+               return -1;
+           } else {
+               switch (doassociate()) {
+               case -1:
+                   kill_connection();
+                   return -1;
+               case 0:
+                   break;
+               case 1:
+                   state = CONNECTED;
+               }
+           }
+       }
+       break;
+    case CONNECTED:
+       switch (i = api_exch_nextcommand()) {
+       case EXCH_CMD_REQUEST:
+           if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
+                                   (char *)&inputRegs) == -1) {
+               kill_connection();
+           } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
+                                   (char *)&inputSregs) == -1) {
+               kill_connection();
+           } else if (nextstore() == -1) {
+               kill_connection();
+           } else {
+               handle_api(&inputRegs, &inputSregs);
+               freestorage();                  /* Send any storage back */
+               if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
+                   kill_connection();
+               } else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
+                                   (char *)&inputRegs) == -1) {
+                   kill_connection();
+               } else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
+                                   (char *)&inputSregs) == -1) {
+                   kill_connection();
+               }
+               /* Done, and it all worked! */
+           }
+           break;
+       case EXCH_CMD_DISASSOCIATE:
+           kill_connection();
+           break;
+       default:
+           if (i != -1) {
+               fprintf(stderr,
+                       "Looking for a REQUEST or DISASSOCIATE command\n");
+               fprintf(stderr, "\treceived 0x%02x.\n", i);
+           }
+           kill_connection();
+           break;
+       }
+    }
     return shell_active;
 }
 
 
     return shell_active;
 }
 
 
+static int
+child_died()
+{
+    union wait *status;
+    register int pid;
+
+    while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
+       if (pid == shell_pid) {
+           char inputbuffer[100];
+
+           shell_active = 0;
+           if (sock != -1) {
+               (void) close(sock);
+               sock = -1;
+           }
+           printf("[Hit return to continue]");
+           fflush(stdout);
+           (void) gets(inputbuffer);
+           setconnmode();
+           ConnectScreen();    /* Turn screen on (if need be) */
+           (void) close(serversock);
+           (void) unlink(keyname);
+       }
+    }
+    signal(SIGCHLD, child_died);
+}
+
+
 /*
  * Called from telnet.c to fork a lower command.com.  We
  * use the spint... routines so that we can pick up
 /*
  * Called from telnet.c to fork a lower command.com.  We
  * use the spint... routines so that we can pick up
@@ -49,9 +582,106 @@ shell(argc,argv)
 int    argc;
 char   *argv[];
 {
 int    argc;
 char   *argv[];
 {
-    if ((shell_pid = fork()) {
-       shell_active = 1;               /* We are running down below */
-       child_killed();                 /* Start up signal handler */
+    int length;
+    struct sockaddr_in server;
+    char sockNAME[100];
+    static char **whereAPI = 0;
+    int fd;
+    struct timeval tv;
+    long ikey;
+    extern long random();
+    extern char *mktemp();
+
+    /* First, create verification file. */
+    do {
+       keyname = mktemp("/tmp/apiXXXXXX");
+       fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE);
+    } while ((fd == -1) && (errno == EEXIST));
+
+    if (fd == -1) {
+       perror("open");
+       return 0;
+    }
+
+    /* Now, get seed for random */
+
+    if (gettimeofday(&tv, 0) == -1) {
+       perror("gettimeofday");
+       return 0;
+    }
+    srandom(tv.tv_usec);               /* seed random number generator */
+    do {
+       ikey = random();
+    } while (ikey == 0);
+    sprintf(key, "%lu\n", ikey);
+    if (write(fd, key, strlen(key)) != strlen(key)) {
+       perror("write");
+       return 0;
+    }
+    key[strlen(key)-1] = 0;            /* Get rid of newline */
+
+    if (close(fd) == -1) {
+       perror("close");
+       return 0;
+    }
+
+    /* Next, create the socket which will be connected to */
+    serversock = socket(AF_INET, SOCK_STREAM, 0);
+    if (serversock < 0) {
+       perror("opening API socket");
+       return 0;
+    }
+    server.sin_family = AF_INET;
+    server.sin_addr.s_addr = INADDR_ANY;
+    server.sin_port = 0;
+    if (bind(serversock, &server, sizeof server) < 0) {
+       perror("binding API socket");
+       return 0;
+    }
+    length = sizeof server;
+    if (getsockname(serversock, &server, &length) < 0) {
+       perror("getting API socket name");
+       (void) close(serversock);
+    }
+    listen(serversock, 1);
+    /* Get name to advertise in address list */
+    strcpy(sockNAME, "API3270=");
+    gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
+    if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) {
+       fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
+       strcpy(sockNAME, "localhost");
+    }
+    sprintf(sockNAME+strlen(sockNAME), ":%d", ntohs(server.sin_port));
+    sprintf(sockNAME+strlen(sockNAME), ":%s", keyname);
+
+    if (whereAPI == 0) {
+       char **ptr, **nextenv;
+       extern char **environ;
+
+       ptr = environ;
+       nextenv = ourENVlist;
+       while (*ptr) {
+           if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
+               fprintf(stderr, "Too many environmental variables\n");
+               break;
+           }
+           *nextenv++ = *ptr++;
+       }
+       whereAPI = nextenv++;
+       *nextenv++ = 0;
+       environ = ourENVlist;           /* New environment */
+    }
+    *whereAPI = sockNAME;
+
+    child_died();                      /* Start up signal handler */
+    shell_active = 1;                  /* We are running down below */
+    if (shell_pid = vfork()) {
+       if (shell_pid == -1) {
+           perror("vfork");
+           (void) close(serversock);
+       } else {
+           state = UNCONNECTED;
+       }
     } else {                           /* New process */
        register int i;
 
     } else {                           /* New process */
        register int i;
 
@@ -60,6 +690,7 @@ char *argv[];
        }
        if (argc == 1) {                /* Just get a shell */
            char *cmdname;
        }
        if (argc == 1) {                /* Just get a shell */
            char *cmdname;
+           extern char *getenv();
 
            cmdname = getenv("SHELL");
            execlp(cmdname, cmdname, 0);
 
            cmdname = getenv("SHELL");
            execlp(cmdname, cmdname, 0);
@@ -72,5 +703,5 @@ char *argv[];
        }
        /*NOTREACHED*/
     }
        }
        /*NOTREACHED*/
     }
-    return 1;                  /* Go back to main loop */
+    return shell_active;               /* Go back to main loop */
 }
 }