home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD1.bin
/
new
/
util
/
edit
/
jade
/
src
/
unix_processes.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-10-03
|
35KB
|
1,407 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 bool proc_notification(void);
static void check_for_zombies(void);
static void read_from_process(int);
_PR int write_to_process(VALUE, u_char *, int);
_PR void proc_mark(VALUE);
_PR void proc_sweep(void);
_PR void proc_prin(VALUE, VALUE);
_PR void sigchld_restart(bool);
_PR void proc_init(void);
_PR void proc_kill(void);
static struct sigaction chld_sigact;
static sigset_t chld_sigset;
struct Proc
{
u_char pr_Type;
char pr_Status; /* PR_?? value */
struct Proc *pr_Next;
/* Chain of all processes waiting to be notified of a change of state. */
struct Proc *pr_NotifyNext;
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_NotifyFun;
VALUE pr_Prog;
VALUE pr_Args;
VALUE pr_Dir;
VALUE pr_ConnType;
};
/* <= 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 */
/* Connection types, pty-echo is a pty with the ECHO bit set in c_lflag */
static VALUE sym_pipe, sym_pty, sym_pty_echo;
#define PR_CONN_PTY_P(p) \
(((p)->pr_ConnType == sym_pty) || ((p)->pr_ConnType == sym_pty_echo))
#define PR_CONN_PTY_ECHO_P(p) \
((p)->pr_ConnType == sym_pty_echo)
#define PR_CONN_PIPE_P(p) \
((p)->pr_ConnType == sym_pipe)
/* Handy debugging macro */
#if 0
# define DB(x) fprintf x
#else
# define DB(x)
#endif
static struct Proc *process_chain;
static struct Proc *notify_chain;
static int process_run_count;
/* This semaphorey thing protects all operations done on process structures
from SIGCHLD and the process reaping it causes. */
static int process_mutex = -1;
static bool got_sigchld;
INLINE void
protect_procs(void)
{
process_mutex++;
}
void
unprotect_procs(void)
{
if((process_mutex == 0) && got_sigchld)
{
/* Have to leave (process_mutex == 0) while looking for zombies. */
got_sigchld = FALSE;
check_for_zombies();
}
process_mutex--;
}
/* PR's NotifyFun will be called when possible. This function is safe
to call from signal handlers. */
static void
queue_notify(struct Proc *pr)
{
if(pr->pr_NotifyNext == NULL)
{
pr->pr_NotifyNext = notify_chain;
notify_chain = pr;
}
}
/* Dispatch all queued notification. */
bool
proc_notification(void)
{
if(!notify_chain)
return(FALSE);
protect_procs();
cursor(curr_vw, CURS_OFF);
while(notify_chain != NULL)
{
struct Proc *pr = notify_chain;
notify_chain = pr->pr_NotifyNext;
pr->pr_NotifyNext = NULL;
if(pr->pr_NotifyFun && !NILP(pr->pr_NotifyFun))
funcall(pr->pr_NotifyFun, sym_nil);
}
cursor(curr_vw, CURS_ON);
unprotect_procs();
return(TRUE);
}
/* Checks if any of my children are zombies, takes appropriate action. */
static void
check_for_zombies(void)
{
int status;
pid_t pid;
if(!process_run_count)
return;
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
{
struct Proc *pr = process_chain;
#ifdef DEBUG
messagef("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;
queue_notify(pr);
}
else
{
pr->pr_ExitStatus = status;
process_run_count--;
/* 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 */
if((pr->pr_Stdout != 0) || (pr->pr_Stdin != 0))
pr->pr_Status = PR_EXITED;
else
{
/* No file handles open so just die */
pr->pr_Status = PR_DEAD;
queue_notify(pr);
}
}
}
}
}
static void
sigchld_handler(int sig)
{
if(process_mutex < 0)
check_for_zombies();
else
got_sigchld = TRUE;
}
static void
read_from_process(int fd)
{
struct Proc *pr = process_chain;
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(curr_vw, CURS_OFF);
do {
if((actual = read(fd, buf, 1024)) > 0)
{
buf[actual] = 0;
if(!NILP(pr->pr_OutputStream))
stream_puts(pr->pr_OutputStream, buf, actual, FALSE);
}
} while((actual > 0) || (errno == EINTR));
if((actual <= 0) && (errno != EWOULDBLOCK) && (errno != EAGAIN))
{
/* We assume EOF */
#ifdef HAVE_X11
FD_CLR(pr->pr_Stdout, &x11_fd_read_set);
x11_fd_read_action[pr->pr_Stdout] = NULL;
#endif
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;
queue_notify(pr);
}
}
cursor(curr_vw, CURS_ON);
}
unprotect_procs();
}
int
write_to_process(VALUE pr, u_char *buf, int bufLen)
{
int act = 0;
if(!PROCESSP(pr))
return(0);
protect_procs();
if(VPROC(pr)->pr_Status > 0)
{
if(VPROC(pr)->pr_Stdin == 0)
cmd_signal(sym_process_error, list_2(pr, MKSTR("No link to input")));
else
{
/* This will block */
act = write(VPROC(pr)->pr_Stdin, buf, bufLen);
if(act < 0)
{
signal_file_error(pr);
act = 0;
}
}
}
else
cmd_signal(sym_process_error, list_2(pr, MKSTR("Not running")));
unprotect_procs();
return(act);
}
static bool
signal_process(struct Proc *pr, int sig, bool do_grp)
{
bool rc = TRUE;
protect_procs();
if(do_grp)
{
if(pr->pr_Stdin && PR_CONN_PTY_P(pr))
{
pid_t gid = tcgetpgrp(pr->pr_Stdin);
if(gid != -1)
kill(-gid, sig);
else if(pr->pr_Status != PR_DEAD)
kill(-pr->pr_Pid, sig);
else
rc = FALSE;
}
else
{
if(pr->pr_Status > 0)
kill(-pr->pr_Pid, sig);
else
rc = FALSE;
}
}
else
{
if(pr->pr_Status > 0)
kill(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
kill_process(struct Proc *pr)
{
protect_procs();
if(pr->pr_Status != PR_DEAD)
{
if(pr->pr_Status == PR_RUNNING)
{
/* is this too heavy-handed?? */
if(!signal_process(pr, SIGKILL, TRUE))
kill(-pr->pr_Pid, SIGKILL);
waitpid(pr->pr_Pid, &pr->pr_ExitStatus, 0);
process_run_count--;
}
if(pr->pr_Stdout)
{
#ifdef HAVE_X11
FD_CLR(pr->pr_Stdout, &x11_fd_read_set);
x11_fd_read_action[pr->pr_Stdout] = NULL;
#endif
close(pr->pr_Stdout);
}
if(pr->pr_Stdin && (pr->pr_Stdin !=