home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 4
/
FreshFish_May-June1994.bin
/
new
/
util
/
edit
/
jade
/
src
/
unix_processes.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-04-19
|
26KB
|
996 lines
/* unix_processes.c -- Subprocess handling for Unix
Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
This file is part of Jade.
Jade is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Jade is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jade; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "jade.h"
#include "jade_protos.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
_PR void protect_procs(void);
_PR void unprotect_procs(void);
_PR void readfromproc(int);
_PR int writetoproc(VALUE, u_char *);
_PR void proc_mark(VALUE);
_PR void proc_sweep(void);
_PR void proc_prin(VALUE, VALUE);
_PR void sys_proc_init(void);
_PR void sys_proc_kill(void);
#define USE_SIGACTION
#ifdef USE_SIGACTION
static struct sigaction ChldAct;
static sigset_t ChldSet;
#endif
struct Proc
{
u_char pr_Type;
char pr_Status; /* PR_?? value */
struct Proc *pr_Next;
pid_t pr_Pid;
/* pr_Stdin is where we write, pr_Stdout where we read, they may be the
same. */
int pr_Stdin, pr_Stdout;
VALUE pr_OutputStream;
int pr_ExitStatus;
VALUE pr_ExitFunc;
VALUE pr_File;
VALUE pr_Argv;
};
/* <= 0 means process not running, > 0 means could be running... */
#define PR_STOPPED 2 /* waiting to be continued */
#define PR_RUNNING 1 /* running merrily */
#define PR_DEAD 0 /* nothing happening on this obj */
#define PR_EXITED -1 /* process dead but no EOF from pty */
/* Handy debugging macro */
#if 0
# define DB(x) fprintf x
#else
# define DB(x)
#endif
static struct Proc *ProcChain;
static int ProcRunCount;
static void
callexitfunc(struct Proc *pr)
{
if(!NILP(pr->pr_ExitFunc))
{
int oldgci = GCinhibit;
GCinhibit = TRUE;
calllisp1(pr->pr_ExitFunc, pr);
GCinhibit = oldgci;
}
}
/*
* Checks if any of my children are zombies, takes appropriate action.
*/
static void
checkforzombies(void)
{
int status;
pid_t pid;
if(!ProcRunCount)
return;
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
{
struct Proc *pr = ProcChain;
#ifdef DEBUG
settitlefmt("SIGCHLD: pid %d -- status 0x%x", pid, status);
#endif
while(pr)
{
if((pr->pr_Status > 0) && (pr->pr_Pid == pid))
break;
pr = pr->pr_Next;
}
if(pr)
{
if(WIFSTOPPED(status))
pr->pr_Status = PR_STOPPED;
else
{
pr->pr_ExitStatus = status;
ProcRunCount--;
#if 0
if(pr->pr_Stdout)
close(pr->pr_Stdout);
if(pr->pr_Stdin && (pr->pr_Stdin != pr->pr_Stdout))
close(pr->pr_Stdin);
pr->pr_Stdout = pr->pr_Stdin = 0;
pr->pr_Status = PR_DEAD;
#else
/* It seems that I can't just nuke the pty once the child's
dead -- there can be data pending on it still. So, I set
pr_Status to an in-between value and hope to get an eof
over pr_Stdin RSN
Another consideration is what happens if the process I ran
on the pty forked another process which is still using
my pty. This means that I don't get an EOF until it
exits (if it does). hmmm... */
if(pr->pr_Stdout || pr->pr_Stdin)
pr->pr_Status = PR_EXITED;
else
{
pr->pr_Status = PR_DEAD;
callexitfunc(pr);
pr->pr_File = pr->pr_Argv = sym_nil;
}
#endif
}
}
}
}
/*
* This semaphorey thing protects all operations done on process structures
* from SIGCHLD and the process reaping it causes.
*/
static int ProcMutex = -1;
static bool GotSig;
INLINE void
protect_procs(void)
{
ProcMutex++;
}
void
unprotect_procs(void)
{
if((ProcMutex == 0) && GotSig)
{
/* Have to leave (ProcMutex == 0) while looking for zombies. */
GotSig = FALSE;
checkforzombies();
}
ProcMutex--;
}
static void
sigchld_handler(int sig)
{
if(ProcMutex < 0)
checkforzombies();
else
GotSig = TRUE;
#ifndef USE_SIGACTION
signal(SIGCHLD, sigchld_handler);
#endif
}
void
readfromproc(int fd)
{
struct Proc *pr = ProcChain;
protect_procs();
while(pr)
{
if((pr->pr_Status != PR_DEAD) && (pr->pr_Stdout == fd))
break;
pr = pr->pr_Next;
}
if(pr)
{
u_char buf[1025];
int actual;
cursor(CurrVW, CURS_OFF);
do {
if((actual = read(fd, buf, 1024)) > 0)
{
buf[actual] = 0;
streamputs(pr->pr_OutputStream, buf, FALSE);
}
} while((actual > 0) || (errno == EINTR));
/* what happens when a child closes the pty slave (or dies)???
it seems that I get EIO on Linux. This might handle most
situations (or should I change !EWOULDBLOCK to EIO??) */
if((actual <= 0) && (errno != EWOULDBLOCK) && (errno != EAGAIN))
{
/* assume eof */
FD_CLR(pr->pr_Stdout, &FdReadSet);
FdReadAction[pr->pr_Stdout] = NULL;
close(pr->pr_Stdout);
if(pr->pr_Stdin && (pr->pr_Stdin != pr->pr_Stdout))
close(pr->pr_Stdin);
pr->pr_Stdout = pr->pr_Stdin = 0;
/* This means that the process has already exited and we were
just waiting for the dregs of its output. */
if(pr->pr_Status < 0)
{
pr->pr_Status = PR_DEAD;
callexitfunc(pr);
pr->pr_File = pr->pr_Argv = sym_nil;
}
}
refreshworld();
cursor(CurrVW, CURS_ON);
}
unprotect_procs();
}
int
writetoproc(VALUE pr, u_char *buf)
{
int act = 0;
if(!PROCESSP(pr))
return(0);
protect_procs();
if(VPROC(pr)->pr_Status == PR_RUNNING)
{
if(VPROC(pr)->pr_Stdin)
{
/* This will block. Needs to handle EINTR as well (oh well...) */
act = write(VPROC(pr)->pr_Stdin, buf, strlen(buf));
}
if(act < 0)
{
settitlefmt("Error: %s", VSTR(geterrstring()));
act = 0;
}
}
else
cmd_signal(sym_process_error, list_2(pr, MKSTR("Not running")));
unprotect_procs();
return(act);
}
static bool
signalprocess(struct Proc *pr, int sig)
{
bool rc = TRUE;
protect_procs();
if(pr->pr_Stdin)
{
pid_t gid;
#ifdef SIGNALS_VIA_CHARS
switch(sig)
{
struct termios term;
case SIGINT:
tcgetattr(pr->pr_Stdin, &term);
write(pr->pr_Stdin, &term.c_cc[VINTR], 1);
break;
case SIGQUIT:
tcgetattr(pr->pr_Stdin, &term);
write(pr->pr_Stdin, &term.c_cc[VQUIT], 1);
break;
case SIGTSTP:
/* This doesn't work?? sending SIGSTOP directly does :) */
tcgetattr(pr->pr_Stdin, &term);
write(pr->pr_Stdin, &term.c_cc[VSUSP], 1);
break;
default:
#endif
gid = tcgetpgrp(pr->pr_Stdin);
if(gid != -1)
killpg(gid, sig);
else if(pr->pr_Status != PR_DEAD)
killpg(pr->pr_Pid, sig);
else
rc = FALSE;
#ifdef SIGNALS_VIA_CHARS
}
#endif
}
else if(pr->pr_Status != PR_DEAD)
killpg(pr->pr_Pid, sig);
else
rc = FALSE;
unprotect_procs();
return(rc);
}
/*
* This is only called during GC, when the process isn't being referenced.
* it will already have been taken out of the chain
*/
static void
killproc(struct Proc *pr)
{
protect_procs();
if(pr->pr_Status != PR_DEAD)
{
if(pr->pr_Status == PR_RUNNING)
{
/* is this too heavy-handed?? */
if(!signalprocess(pr, SIGKILL))
killpg(pr->pr_Pid, SIGKILL);
waitpid(pr->pr_Pid, &pr->pr_ExitStatus, 0);
ProcRunCount--;
}
if(pr->pr_Stdout)
{
FD_CLR(pr->pr_Stdout, &FdReadSet);
FdReadAction[pr->pr_Stdout] = NULL;
close(pr->pr_Stdout);
}
if(pr->pr_Stdin && (pr->pr_Stdin != pr->pr_Stdout))
close(pr->pr_Stdin);
}
mystrfree(pr);
unprotect_procs();
}
static int
getpty(char *slavenam)
{
char c;
int i, master;
struct stat statb;
for(c = FIRST_PTY_LETTER; c < 'z'; c++)
{
for(i = 0; i < 16; i++)
{
sprintf(slavenam, "/dev/pty%c%x", c, i);
if(stat(slavenam, &statb) < 0)
goto none;