mirror of
https://github.com/Interlisp/maiko.git
synced 2026-01-15 07:54:13 +00:00
436 lines
14 KiB
C
436 lines
14 KiB
C
/* $Id: unixfork.c,v 1.6 2001/12/26 22:17:05 sybalsky Exp $ (C) Copyright Venue, All Rights Reserved
|
|
*/
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* Code to fork a subprocess for Unix communication */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* (C) Copyright 1989-1998 Venue. All Rights Reserved. */
|
|
/* Manufactured in the United States of America. */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
#include "version.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <termios.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef OS5
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stropts.h>
|
|
#endif
|
|
|
|
#include "dbprint.h"
|
|
#include "unixfork.h"
|
|
|
|
#ifdef DEBUG
|
|
/* required by DBPRINT from dbprint.h */
|
|
int flushing = 0;
|
|
#endif
|
|
|
|
/* Used to communicate between Unix subprocesses and LISP */
|
|
|
|
static long StartTime; /* Time, for creating pipe filenames */
|
|
static char shcom[512]; /* Here because I'm suspicious of */
|
|
/* large allocations on the stack */
|
|
|
|
|
|
static inline ssize_t SAFEREAD(int f, char *b, size_t c)
|
|
{
|
|
ssize_t res;
|
|
do {
|
|
res = read(f, b, c);
|
|
if (res >= 0) return (res);
|
|
} while (errno == EINTR || errno == EAGAIN);
|
|
perror("reading UnixPipeIn");
|
|
return (res);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* F o r k U n i x S h e l l */
|
|
/* */
|
|
/* Fork a PTY connection to a C-shell process. */
|
|
/* Returns PID of process, or -1 if something failed */
|
|
/* */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
/* Creates a PTY connection to a shell */
|
|
|
|
static int ForkUnixShell(int slot, char *PtySlave, char *termtype, char *shellarg)
|
|
{
|
|
int PID, SlaveFD;
|
|
struct termios tio;
|
|
char *argvec[4] = {NULL, NULL, NULL, NULL};
|
|
char *shell = NULL;
|
|
char *userShell = NULL;
|
|
|
|
PID = fork();
|
|
|
|
if (PID != 0) {
|
|
if (shellarg != shcom) free(shellarg);
|
|
return (PID);
|
|
}
|
|
|
|
if (0 > setsid()) /* create us a new session for tty purposes */
|
|
perror("setsid");
|
|
|
|
/* Open the slave side */
|
|
SlaveFD = open(PtySlave, O_RDWR);
|
|
if (SlaveFD == -1) {
|
|
perror("Slave Open");
|
|
perror(PtySlave);
|
|
exit(0);
|
|
}
|
|
|
|
#ifdef OS5
|
|
ioctl(SlaveFD, I_PUSH, "ptem");
|
|
ioctl(SlaveFD, I_PUSH, "ldterm");
|
|
#endif /* OS5 */
|
|
|
|
/* Set up as basic display terminal: canonical erase,
|
|
kill processing, echo, backspace to erase, echo ctrl
|
|
chars as ^x, kill line by backspacing */
|
|
tcgetattr(SlaveFD, &tio);
|
|
tio.c_lflag |= ICANON | ECHO | ECHOE | ECHOCTL | ECHOKE;
|
|
tcsetattr(SlaveFD, TCSANOW, &tio);
|
|
|
|
(void)dup2(SlaveFD, 0);
|
|
(void)dup2(SlaveFD, 1);
|
|
(void)dup2(SlaveFD, 2);
|
|
(void)close(SlaveFD);
|
|
|
|
/* set the LDESHELL variable so the underlying shell initialization can see it and
|
|
configure the shell appropriately, though this may not be so important any more */
|
|
setenv("LDESHELL", "YES", 1);
|
|
|
|
if (termtype[0] != 0) { /* set the TERM environment var */
|
|
setenv("TERM", termtype, 1);
|
|
}
|
|
/* Start up shell -- use SHELL environment variable as long as it's in /etc/shells */
|
|
shell = getenv("SHELL");
|
|
if (shell == NULL) /* shell of last resort */
|
|
shell = "/bin/sh";
|
|
for (userShell = getusershell(); userShell != NULL && strcmp(shell, userShell) != 0; userShell = getusershell());
|
|
if (userShell == NULL) {
|
|
perror("$(SHELL) not found in /etc/shells");
|
|
exit(0);
|
|
}
|
|
|
|
/* argvec entries initialized to NULL */
|
|
argvec[0] = strrchr(userShell, '/') + 1;
|
|
if (shellarg[0] != 0) { /* setup to run command */
|
|
argvec[1] = "-c"; /* read commands from next arg */
|
|
argvec[2] = shellarg;
|
|
}
|
|
|
|
execv(userShell, argvec);
|
|
|
|
/* Should never get here */
|
|
perror("execv");
|
|
exit(0);
|
|
}
|
|
|
|
/* fork_Unix is the secondary process spawned right after LISP is
|
|
started, to avoid having TWO 8 mbyte images sitting around. It listens
|
|
to the pipe LispToUnix waiting for requests, and responds on UnixToLisp.
|
|
The data passed through this pipe is in 6 byte packets, of the form:
|
|
|
|
Byte 0: Command character, one of:
|
|
S: Fork PTY (shell) process. This is used for CHAT windows.
|
|
P: New version of S, takes 2 string args.
|
|
F: Fork piped shell, takes 1 string arg.
|
|
K: Kill process
|
|
E: Exit (kill all subprocesses)
|
|
C: Close stdin to subprocess
|
|
Byte 1: Process number (0 to NPROCS - 1)
|
|
Not used for S, F, and E commands
|
|
[For S&P, pty letter]
|
|
[For F, process # for pipe naming]
|
|
Byte 2: Value, used as follows:
|
|
Only used for W command, contains byte to write
|
|
[For S&P, pty number]
|
|
Byte 3: Slot number.
|
|
Byte 4: unused.
|
|
Byte 5: unused.
|
|
|
|
|
|
In the case of F & P commands, additional data follows the 6 byte packet.
|
|
This consists of 2 bytes representing the length of the shell command
|
|
string, and the string itself.
|
|
|
|
fork_Unix will return another 6 byte packet. The bytes are the same as those
|
|
of the packet received except:
|
|
|
|
F, S, P: Bytes 1, 2, 4, and 5 are the Unix process id
|
|
Byte 3 is 1 if successful, 0 if not
|
|
R: Byte 2 is value of byte read from stdin, if any
|
|
Byte 3 is 1 if successful, 2 if EOF, 0 if nothing waiting
|
|
W: Bytes 0, 1, 4, 5 are the Process ID of the terminated process
|
|
Bytes 2 & 3 are the high & low bytes of the exit status.
|
|
K: Bytes 1 and 2 are the high and low bytes of the exit status
|
|
of the process.
|
|
Byte 3 is 1 if an exit status was available.
|
|
C: Always the same
|
|
*/
|
|
|
|
int fork_Unix(void) {
|
|
int LispToUnix[2], /* Incoming pipe from LISP */
|
|
UnixToLisp[2], /* Outgoing pipe to LISP */
|
|
UnixPID, LispPipeIn, LispPipeOut, slot;
|
|
pid_t pid;
|
|
sigset_t signals;
|
|
|
|
char IOBuf[6]; /* XXX: signed, because slot comes from here */
|
|
unsigned short tmp = 0;
|
|
char *cmdstring;
|
|
|
|
/* Pipes between LISP subr and process */
|
|
if (pipe(LispToUnix) == -1) {
|
|
perror("pipe");
|
|
exit(-1);
|
|
}
|
|
if (pipe(UnixToLisp) == -1) {
|
|
perror("pipe");
|
|
exit(-1);
|
|
}
|
|
|
|
StartTime = time(0); /* Save the time, to create filenames with */
|
|
StartTime &= 0xFFFFFF; /* as a positive number! */
|
|
|
|
/* interrupts need to be blocked here so subprocess won't see them */
|
|
sigemptyset(&signals);
|
|
sigaddset(&signals, SIGVTALRM);
|
|
sigaddset(&signals, SIGIO);
|
|
sigaddset(&signals, SIGALRM);
|
|
sigaddset(&signals, SIGXFSZ);
|
|
sigaddset(&signals, SIGFPE);
|
|
sigprocmask(SIG_BLOCK, &signals, NULL);
|
|
|
|
if ((UnixPID = fork()) == -1) { /* Fork off small version of the emulator */
|
|
perror("fork");
|
|
exit(-1);
|
|
}
|
|
|
|
if (UnixPID != 0) {
|
|
/* JRB - fork_Unix is now called in ldeboot; leave UnixPipe{In,Out} open
|
|
and put their numbers in the environment so parent can find them */
|
|
/* JDS - NB that sprintf doesn't always return a string! */
|
|
|
|
char tempstring[30];
|
|
|
|
snprintf(tempstring, sizeof(tempstring), "%d", UnixToLisp[0]);
|
|
setenv("LDEPIPEIN", tempstring, 1);
|
|
|
|
snprintf(tempstring, sizeof(tempstring), "%d", LispToUnix[1]);
|
|
setenv("LDEPIPEOUT", tempstring, 1);
|
|
|
|
snprintf(tempstring, sizeof(tempstring), "%ld", StartTime);
|
|
setenv("LDESTARTTIME", tempstring, 1);
|
|
|
|
snprintf(tempstring, sizeof(tempstring), "%d", UnixPID);
|
|
setenv("LDEUNIXPID", tempstring, 1);
|
|
|
|
close(LispToUnix[0]);
|
|
close(UnixToLisp[1]);
|
|
return (1);
|
|
}
|
|
|
|
LispPipeIn = LispToUnix[0];
|
|
LispPipeOut = UnixToLisp[1];
|
|
close(LispToUnix[1]);
|
|
close(UnixToLisp[0]);
|
|
|
|
fcntl(LispPipeIn, F_SETFL, fcntl(LispPipeIn, F_GETFL, 0) & ~O_NONBLOCK);
|
|
|
|
while (1) {
|
|
ssize_t len;
|
|
len = SAFEREAD(LispPipeIn, IOBuf, 6);
|
|
if (len < 0) {
|
|
perror("Error reading packet by slave");
|
|
exit(0);
|
|
} else if (len != 6) {
|
|
DBPRINT(("Input packet wrong length: %zd", len));
|
|
exit(0);
|
|
}
|
|
slot = IOBuf[3];
|
|
IOBuf[3] = 1; /* Start by signalling success in return-code */
|
|
|
|
switch (IOBuf[0]) {
|
|
case 'S':
|
|
case 'P': /* Fork PTY shell */
|
|
if (slot >= 0) { /* Found a free slot */
|
|
char termtype[64];
|
|
char slavepty[32]; /* For slave pty name */
|
|
|
|
if (SAFEREAD(LispPipeIn, (char *)&tmp, 2) < 0) perror("Slave reading slave pty len");
|
|
if (SAFEREAD(LispPipeIn, slavepty, tmp) < 0) perror("Slave reading slave pty id");
|
|
|
|
if (IOBuf[0] == 'P') { /* The new style, which takes term type & command to csh */
|
|
if (SAFEREAD(LispPipeIn, (char *)&tmp, 2) < 0) perror("Slave reading cmd length");
|
|
if (tmp > 63) perror("Slave termtype length too long");
|
|
if (SAFEREAD(LispPipeIn, termtype, tmp) < 0) perror("Slave reading termtype");
|
|
termtype[tmp] = '\0';
|
|
if (SAFEREAD(LispPipeIn, (char *)&tmp, 2) < 0) perror("Slave reading cmd length");
|
|
if (tmp > 510)
|
|
cmdstring = (char *)malloc(tmp + 5);
|
|
else
|
|
cmdstring = shcom;
|
|
|
|
if (SAFEREAD(LispPipeIn, cmdstring, tmp) < 0) perror("Slave reading shcom");
|
|
} else /* old style, no args */
|
|
{
|
|
termtype[0] = 0;
|
|
cmdstring = shcom;
|
|
cmdstring[0] = 0;
|
|
}
|
|
|
|
/* Alloc a PTY and fork */
|
|
pid = ForkUnixShell(slot, slavepty, termtype, cmdstring);
|
|
|
|
if (pid == -1) {
|
|
printf("Impossible failure from ForkUnixShell??\n");
|
|
fflush(stdout);
|
|
IOBuf[3] = 0;
|
|
} else {
|
|
/* ForkUnixShell sets the pid and standard in/out variables */
|
|
IOBuf[1] = (pid >> 8) & 0xFF;
|
|
IOBuf[2] = pid & 0xFF;
|
|
IOBuf[4] = (pid >> 16) & 0xFF;
|
|
IOBuf[5] = (pid >> 24) & 0xFF;
|
|
}
|
|
} else {
|
|
printf("Can't get process slot for PTY shell.\n");
|
|
fflush(stdout);
|
|
IOBuf[3] = 0;
|
|
}
|
|
break;
|
|
|
|
case 'F': /* Fork pipe command */
|
|
if (slot >= 0) {
|
|
/* Read in the length of the shell command, and then the command */
|
|
if (SAFEREAD(LispPipeIn, (char *)&tmp, 2) < 0) perror("Slave reading cmd length");
|
|
if (tmp > 510)
|
|
cmdstring = (char *)malloc(tmp + 5);
|
|
else
|
|
cmdstring = shcom;
|
|
if (SAFEREAD(LispPipeIn, cmdstring, tmp) < 0) perror("Slave reading cmd");
|
|
DBPRINT(("Cmd len = %d.\n", tmp));
|
|
DBPRINT(("Rev'd cmd string: %s\n", cmdstring));
|
|
pid = fork(); /* Fork */
|
|
|
|
if (pid == 0) {
|
|
int i;
|
|
int status, sock;
|
|
struct sockaddr_un addr;
|
|
char PipeName[40];
|
|
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (sock < 0) {
|
|
perror("slave socket");
|
|
exit(0);
|
|
}
|
|
sprintf(PipeName, "/tmp/LPU%ld-%d", StartTime, slot);
|
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strcpy(addr.sun_path, PipeName);
|
|
status =
|
|
connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));
|
|
if (status < 0) {
|
|
perror("slave connect");
|
|
printf("Name = %s.\n", PipeName);
|
|
fflush(stdout);
|
|
exit(0);
|
|
} else {
|
|
DBPRINT(("Slave connected on %s.\n", PipeName));
|
|
}
|
|
|
|
/* Copy the pipes onto stdin, stdout, and stderr */
|
|
dup2(sock, 0);
|
|
dup2(sock, 1);
|
|
dup2(sock, 2);
|
|
|
|
/* Make sure everything else is closed. */
|
|
for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) close(i);
|
|
|
|
/* Run the shell command and get the result */
|
|
status = system(cmdstring);
|
|
if (cmdstring != shcom) free(cmdstring);
|
|
/* Comment out to fix USAR 11302 (FXAR 320)
|
|
unlink(PipeName);
|
|
*/
|
|
_exit((status & ~0xff) ? (status >> 8) : status);
|
|
}
|
|
|
|
/* Check for error doing the fork */
|
|
if (pid == (pid_t)-1) {
|
|
perror("unixcomm: fork");
|
|
IOBuf[3] = 0;
|
|
} else {
|
|
IOBuf[1] = (pid >> 8) & 0xFF;
|
|
IOBuf[2] = pid & 0xFF;
|
|
IOBuf[4] = (pid >> 16) & 0xFF;
|
|
IOBuf[5] = (pid >> 24) & 0xFF;
|
|
}
|
|
} else {
|
|
printf("No process slots available.\n");
|
|
IOBuf[3] = 0; /* Couldn't get a process slot */
|
|
}
|
|
break;
|
|
|
|
case 'W': /* Wait for a process to die. */
|
|
{
|
|
int status;
|
|
|
|
status = 0;
|
|
|
|
IOBuf[0] = 0;
|
|
IOBuf[1] = 0;
|
|
DBPRINT(("About to wait for processes.\n"));
|
|
retry1:
|
|
pid = waitpid(-1, &status, WNOHANG);
|
|
if (pid == -1 && errno == EINTR) goto retry1;
|
|
if (pid > 0) {
|
|
/* Ignore processes which are suspended but haven't exited
|
|
(this shouldn't happen) */
|
|
if (WIFSTOPPED(status)) break;
|
|
IOBuf[5] = (pid >> 24) & 0xFF;
|
|
IOBuf[4] = (pid >> 16) & 0xFF;
|
|
IOBuf[3] = status >> 8;
|
|
IOBuf[2] = status & 0xFF;
|
|
IOBuf[1] = pid & 0xFF;
|
|
IOBuf[0] = (pid >> 8) & 0xFF;
|
|
}
|
|
DBPRINT(("wait3 returned pid = %ld.\n", (long)pid));
|
|
}
|
|
|
|
break;
|
|
|
|
case 'C': /* Close stdin to subprocess */ break;
|
|
|
|
case 'K': /* Kill subprocess */ break;
|
|
} /* End of switch */
|
|
|
|
/* Return the status/data packet */
|
|
write(LispPipeOut, IOBuf, 6);
|
|
}
|
|
}
|
|
|