home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
mitsch75.zip
/
scheme-7_5_17-src.zip
/
scheme-7.5.17
/
src
/
microcode
/
uxproc.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-05-03
|
22KB
|
794 lines
/* -*-C-*-
$Id: uxproc.c,v 1.27 2001/05/03 20:14:51 cph Exp $
Copyright (c) 1990-2001 Massachusetts Institute of Technology
This program 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 of the License, or (at
your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include "ux.h"
#include "uxproc.h"
#include "uxio.h"
#include "osterm.h"
#include "ostop.h"
#ifndef HAVE_DUP2
#include "error: can't hack subprocess I/O without dup2() or equivalent"
#endif
extern void EXFUN ((*subprocess_death_hook), (pid_t pid, int * status));
extern void EXFUN ((*stop_signal_hook), (int signo));
extern void EXFUN (stop_signal_default, (int signo));
extern int EXFUN (OS_ctty_fd, (void));
extern void EXFUN (UX_initialize_child_signals, (void));
static void EXFUN (subprocess_death, (pid_t pid, int * status));
static void EXFUN (stop_signal_handler, (int signo));
static void EXFUN (give_terminal_to, (Tprocess process));
static void EXFUN (get_terminal_back, (void));
static void EXFUN (process_wait, (Tprocess process));
static int EXFUN (child_setup_tty, (int fd));
size_t OS_process_table_size;
struct process * process_table;
enum process_jc_status scheme_jc_status;
static int scheme_ctty_fd;
static Tprocess foreground_child_process;
static long process_tick;
static long sync_tick;
#define NEW_RAW_STATUS(process, status, reason) \
{ \
(PROCESS_RAW_STATUS (process)) = (status); \
(PROCESS_RAW_REASON (process)) = (reason); \
(PROCESS_TICK (process)) = (++process_tick); \
}
#define PROCESS_STATUS_SYNC(process) \
{ \
(PROCESS_STATUS (process)) = (PROCESS_RAW_STATUS (process)); \
(PROCESS_REASON (process)) = (PROCESS_RAW_REASON (process)); \
(PROCESS_SYNC_TICK (process)) = (PROCESS_TICK (process)); \
}
/* This macro should only be used when
(scheme_jc_status == process_jc_status_jc). */
#define SCHEME_IN_FOREGROUND() \
((UX_tcgetpgrp (scheme_ctty_fd)) == (UX_getpgrp ()))
#ifdef HAVE_POSIX_SIGNALS
static void
DEFUN (restore_signal_mask, (environment), PTR environment)
{
UX_sigprocmask (SIG_SETMASK, ((sigset_t *) environment), 0);
}
static void
DEFUN_VOID (block_sigchld)
{
sigset_t * outside = (dstack_alloc (sizeof (sigset_t)));
sigset_t sigchld;
UX_sigemptyset (&sigchld);
UX_sigaddset ((&sigchld), SIGCHLD);
UX_sigprocmask (SIG_BLOCK, (&sigchld), outside);
transaction_record_action (tat_always, restore_signal_mask, outside);
}
static void
DEFUN_VOID (block_jc_signals)
{
sigset_t * outside = (dstack_alloc (sizeof (sigset_t)));
sigset_t jc_signals;
UX_sigemptyset (&jc_signals);
UX_sigaddset ((&jc_signals), SIGCHLD);
UX_sigaddset ((&jc_signals), SIGTTOU);
UX_sigaddset ((&jc_signals), SIGTTIN);
UX_sigaddset ((&jc_signals), SIGTSTP);
UX_sigaddset ((&jc_signals), SIGSTOP);
UX_sigprocmask (SIG_BLOCK, (&jc_signals), outside);
transaction_record_action (tat_always, restore_signal_mask, outside);
}
static sigset_t grabbed_signal_mask;
static void
DEFUN_VOID (grab_signal_mask)
{
UX_sigprocmask (SIG_BLOCK, 0, (&grabbed_signal_mask));
}
#else /* not HAVE_POSIX_SIGNALS */
#ifdef HAVE_SIGHOLD
static void
DEFUN (release_sigchld, (environment), PTR environment)
{
UX_sigrelse (SIGCHLD);
}
static void
DEFUN_VOID (block_sigchld)
{
UX_sighold (SIGCHLD);
transaction_record_action (tat_always, release_sigchld, 0);
}
#else /* not HAVE_SIGHOLD */
#define block_sigchld()
#endif /* not HAVE_SIGHOLD */
#define block_jc_signals block_sigchld
#define grab_signal_mask()
#endif /* not HAVE_POSIX_SIGNALS */
void
DEFUN_VOID (UX_initialize_processes)
{
OS_process_table_size = (UX_SC_CHILD_MAX ());
process_table =
(UX_malloc (OS_process_table_size * (sizeof (struct process))));
if (process_table == 0)
{
fprintf (stderr, "\nUnable to allocate process table.\n");
fflush (stderr);
termination_init_error ();
}
{
Tprocess process;
for (process = 0; (process < OS_process_table_size); process += 1)
OS_process_deallocate (process);
}
scheme_ctty_fd = (OS_ctty_fd ());
scheme_jc_status =
((scheme_ctty_fd < 0)
? process_jc_status_no_ctty
: (UX_SC_JOB_CONTROL ())
? process_jc_status_jc
: process_jc_status_no_jc);
foreground_child_process = NO_PROCESS;
subprocess_death_hook = subprocess_death;
stop_signal_hook = stop_signal_handler;
process_tick = 0;
sync_tick = 0;
}
void
DEFUN_VOID (UX_reset_processes)
{
UX_free (process_table);
process_table = 0;
OS_process_table_size = 0;
}
static void
DEFUN (process_allocate_abort, (environment), PTR environment)
{
Tprocess process = (* ((Tprocess *) environment));
switch (PROCESS_RAW_STATUS (process))
{
case process_status_stopped:
case process_status_running:
UX_kill ((PROCESS_ID (process)), SIGKILL);
break;
default:
break;
}
OS_process_deallocate (process);
}
static Tprocess
DEFUN_VOID (process_allocate)
{
Tprocess process;
for (process = 0; (process < OS_process_table_size); process += 1)
if ((PROCESS_RAW_STATUS (process)) == process_status_free)
{
Tprocess * pp = (dstack_alloc (sizeof (Tprocess)));
(*pp) = process;
transaction_record_action (tat_abort, process_allocate_abort, pp);
(PROCESS_RAW_STATUS (process)) = process_status_allocated;
return (process);
}
error_out_of_processes ();
return (NO_PROCESS);
}
void
DEFUN (OS_process_deallocate, (process), Tprocess process)
{
(PROCESS_ID (process)) = 0;
(PROCESS_RAW_STATUS (process)) = process_status_free;
}
Tprocess
DEFUN (OS_make_subprocess,
(filename, argv, envp, working_directory,
ctty_type, ctty_name,
channel_in_type, channel_in,
channel_out_type, channel_out,
channel_err_type, channel_err),
CONST char * filename AND
CONST char ** argv AND
CONST char ** VOLATILE envp AND
CONST char * working_directory AND
enum process_ctty_type ctty_type AND
char * ctty_name AND
enum process_channel_type channel_in_type AND
Tchannel channel_in AND
enum process_channel_type channel_out_type AND
Tchannel channel_out AND
enum process_channel_type channel_err_type AND
Tchannel channel_err)
{
pid_t child_pid;
Tprocess child;
VOLATILE enum process_jc_status child_jc_status = process_jc_status_no_ctty;
if (envp == 0)
envp = ((CONST char **) environ);
switch (ctty_type)
{
case process_ctty_type_none:
child_jc_status = process_jc_status_no_ctty;
break;
case process_ctty_type_explicit:
child_jc_status = process_jc_status_unrelated;
break;
case process_ctty_type_inherit_bg:
case process_ctty_type_inherit_fg:
child_jc_status = scheme_jc_status;
break;
}
transaction_begin ();
child = (process_allocate ());
/* Flush streams so that output won't be duplicated after the fork. */
fflush (stdout);
fflush (stderr);
grab_signal_mask ();
if (ctty_type == process_ctty_type_inherit_fg)
block_jc_signals ();
else
block_sigchld ();
STD_UINT_SYSTEM_CALL (syscall_vfork, child_pid, (UX_vfork ()));
if (child_pid > 0)
{
/* In the parent process. */
(PROCESS_ID (child)) = child_pid;
(PROCESS_JC_STATUS (child)) = child_jc_status;
(PROCESS_RAW_STATUS (child)) = process_status_running;
(PROCESS_RAW_REASON (child)) = 0;
(PROCESS_TICK (child)) = process_tick;
PROCESS_STATUS_SYNC (child);
if (child_jc_status == process_jc_status_jc)
STD_VOID_SYSTEM_CALL
(syscall_setpgid, (UX_setpgid (child_pid, child_pid)));
if (ctty_type == process_ctty_type_inherit_fg)
{
give_terminal_to (child);
process_wait (child);
}
transaction_commit ();
return (child);
}
/* In the child process -- if any errors occur, just exit. */
child_pid = (UX_getpid ());
/* Don't do `transaction_commit ()' here. Because we used `vfork'
to spawn the child, the side-effects that are performed by
`transaction_commit' will occur in the parent as well. */
if (working_directory != 0)
UX_chdir (working_directory);
{
int in_fd = (-1);
int out_fd = (-1);
int err_fd = (-1);
if (channel_in_type == process_channel_type_explicit)
in_fd = (CHANNEL_DESCRIPTOR (channel_in));
if (channel_out_type == process_channel_type_explicit)
out_fd = (CHANNEL_DESCRIPTOR (channel_out));
if (channel_err_type == process_channel_type_explicit)
err_fd = (CHANNEL_DESCRIPTOR (channel_err));
if ((ctty_type == process_ctty_type_inherit_bg)
|| (ctty_type == process_ctty_type_inherit_fg))
{
/* If the control terminal is inherited and job control is
available, force the child into a different process group. */
if (child_jc_status == process_jc_status_jc)
{
if (((UX_setpgid (child_pid, child_pid)) < 0)
|| ((ctty_type == process_ctty_type_inherit_fg)
&& (SCHEME_IN_FOREGROUND ())
&& ((UX_tcsetpgrp (scheme_ctty_fd, child_pid)) < 0)))
goto kill_child;
}
}
else
{
/* If the control terminal is not inherited, force the child
into a different session. */
if ((UX_setsid ()) < 0)
goto kill_child;
/* If the control terminal is explicit, open the given device
now so it becomes the control terminal. */
if (ctty_type == process_ctty_type_explicit)
{
int fd = (UX_open (ctty_name, O_RDWR, 0));
if ((fd < 0)
#ifdef SLAVE_PTY_P
|| ((SLAVE_PTY_P (ctty_name)) && (!UX_setup_slave_pty (fd)))
#endif
|| (!isatty (fd))
#ifdef TIOCSCTTY
|| ((UX_ioctl (fd, TIOCSCTTY, 0)) < 0)
#endif
/* Tell the controlling terminal its process group. */
|| (((UX_tcsetpgrp (fd, child_pid)) < 0) && (errno != ENOSYS))
|| ((child_setup_tty (fd)) < 0))
goto kill_child;
/* Use CTTY for standard I/O if requested. */
if (channel_in_type == process_channel_type_ctty)
in_fd = fd;
if (channel_out_type == process_channel_type_ctty)
out_fd = fd;
if (channel_err_type == process_channel_type_ctty)
err_fd = fd;
}
}
/* Install the new standard I/O channels. */
if ((in_fd >= 0) && (in_fd != STDIN_FILENO))
{
if ((out_fd == STDIN_FILENO) && ((out_fd = (UX_dup (out_fd))) < 0))
goto kill_child;
if ((err_fd == STDIN_FILENO) && ((err_fd = (UX_dup (err_fd))) < 0))
goto kill_child;
if ((UX_dup2 (in_fd, STDIN_FILENO)) < 0)
goto kill_child;
}
if ((out_fd >= 0) && (out_fd != STDOUT_FILENO))
{
if ((err_fd == STDOUT_FILENO) && ((err_fd = (UX_dup (err_fd))) < 0))
goto kill_child;
if ((UX_dup2 (out_fd, STDOUT_FILENO)) < 0)
goto kill_child;
}
if ((err_fd >= 0) && (err_fd != STDERR_FILENO))
{
if ((UX_dup2 (err_fd, STDERR_FILENO)) < 0)
goto kill_child;
}
}
{
/* Close all other file descriptors. */
int fd = 0;
int open_max = (UX_SC_OPEN_MAX ());
while (fd < open_max)
{
if ((fd == STDIN_FILENO)
? (channel_in_type == process_channel_type_none)
: (fd == STDOUT_FILENO)
? (channel_out_type == process_channel_type_none)
: (fd == STDERR_FILENO)
? (channel_err_type == process_channel_type_none)
: 1)
UX_close (fd);
fd += 1;
}
}
/* Put the signal mask and handlers in a normal state. */
UX_initialize_child_signals ();
/* Start the process. */
execve (filename, ((char * CONST *) argv), ((char * CONST *) envp));
kill_child:
_exit (1);
}
#define DEFUN_PROCESS_ACCESSOR(name, result_type, accessor) \
result_type \
DEFUN (name, (process), Tprocess process) \
{ \
return (accessor (process)); \
}
DEFUN_PROCESS_ACCESSOR (OS_process_id, pid_t, PROCESS_ID)
DEFUN_PROCESS_ACCESSOR (OS_process_status, enum process_status, PROCESS_STATUS)
DEFUN_PROCESS_ACCESSOR (OS_process_reason, unsigned short, PROCESS_REASON)
DEFUN_PROCESS_ACCESSOR
(OS_process_jc_status, enum process_jc_status, PROCESS_JC_STATUS)
int
DEFUN (OS_process_valid_p, (process), Tprocess process)
{
switch (PROCESS_RAW_STATUS (process))
{
case process_status_exited:
case process_status_signalled:
case process_status_stopped:
case process_status_running:
return (1);
default:
return (0);
}
}
int
DEFUN (OS_process_continuable_p, (process), Tprocess process)
{
switch (PROCESS_RAW_STATUS (process))
{
case process_status_stopped:
case process_status_running:
return (1);
default:
return (0);
}
}
int
DEFUN (OS_process_foregroundable_p, (process), Tprocess process)
{
switch (PROCESS_JC_STATUS (process))
{
case process_jc_status_no_jc:
case process_jc_status_jc:
return (1);
default:
return (0);
}
}
int
DEFUN (OS_process_status_sync, (process), Tprocess process)
{
transaction_begin ();
block_sigchld ();
{
int result = ((PROCESS_TICK (process)) != (PROCESS_SYNC_TICK (process)));
if (result) PROCESS_STATUS_SYNC (process);
transaction_commit ();
return (result);
}
}
int
DEFUN_VOID (OS_process_status_sync_all)
{
transaction_begin ();
block_sigchld ();
{
int result = (process_tick != sync_tick);
if (result) sync_tick = process_tick;
transaction_commit ();
return (result);
}
}
int
DEFUN_VOID (OS_process_any_status_change)
{
return (process_tick != sync_tick);
}
void
DEFUN (OS_process_send_signal, (process, sig), Tprocess process AND int sig)
{
STD_VOID_SYSTEM_CALL
(syscall_kill,
(UX_kill ((((PROCESS_JC_STATUS (process)) == process_jc_status_jc)
? (- (PROCESS_ID (process)))
: (PROCESS_ID (process))),
sig)));
}
void
DEFUN (OS_process_kill, (process), Tprocess process)
{
OS_process_send_signal (process, SIGKILL);
}
void
DEFUN (OS_process_stop, (process), Tprocess process)
{
OS_process_send_signal (process, SIGTSTP);
}
void
DEFUN (OS_process_interrupt, (process), Tprocess process)
{
OS_process_send_signal (process, SIGINT);
}
void
DEFUN (OS_process_quit, (process), Tprocess process)
{
OS_process_send_signal (process, SIGQUIT);
}
void
DEFUN (OS_process_hangup, (process), Tprocess process)
{
OS_process_send_signal (process, SIGHUP);
}
void
DEFUN (OS_process_continue_background, (process), Tprocess process)
{
transaction_begin ();
block_sigchld ();
if ((PROCESS_RAW_STATUS (process)) == process_status_stopped)
{
NEW_RAW_STATUS (process, process_status_running, 0);
OS_process_send_signal (process, SIGCONT);
}
transaction_commit ();
}
void
DEFUN (OS_process_continue_foreground, (process), Tprocess process)
{
transaction_begin ();
grab_signal_mask ();
block_jc_signals ();
give_terminal_to (process);
if ((PROCESS_RAW_STATUS (process)) == process_status_stopped)
{
NEW_RAW_STATUS (process, process_status_running, 0);
OS_process_send_signal (process, SIGCONT);
}
process_wait (process);
transaction_commit ();
}
void
DEFUN (OS_process_wait, (process), Tprocess process)
{
transaction_begin ();
grab_signal_mask ();
block_jc_signals ();
process_wait (process);
transaction_commit ();
}
static void
DEFUN (get_terminal_back_1, (environment), PTR environment)
{
get_terminal_back ();
}
static void
DEFUN (give_terminal_to, (process), Tprocess process)
{
if (((PROCESS_JC_STATUS (process)) == process_jc_status_jc)
&& (SCHEME_IN_FOREGROUND ()))
{
transaction_record_action (tat_always, get_terminal_back_1, 0);
foreground_child_process = process;
OS_save_internal_state ();
OS_restore_external_state ();
UX_tcsetpgrp (scheme_ctty_fd, (PROCESS_ID (process)));
}
}
static void
DEFUN_VOID (get_terminal_back)
{
if (foreground_child_process != NO_PROCESS)
{
UX_tcsetpgrp (scheme_ctty_fd, (UX_getpgrp ()));
OS_save_external_state ();
OS_restore_internal_state ();
foreground_child_process = NO_PROCESS;
}
}
static void
DEFUN (process_wait, (process), Tprocess process)
{
#ifdef HAVE_POSIX_SIGNALS
while (((PROCESS_RAW_STATUS (process)) == process_status_running)
&& (! (pending_interrupts_p ())))
UX_sigsuspend (&grabbed_signal_mask);
#else /* not HAVE_POSIX_SIGNALS */
enum process_status status = (PROCESS_RAW_STATUS (process));
while ((status == process_status_running)
&& (! (pending_interrupts_p ())))
{
/* INTERRUPTABLE_EXTENT eliminates the interrupt window between
PROCESS_RAW_STATUS and `pause'. */
int scr;
INTERRUPTABLE_EXTENT
(scr,
((((status = (PROCESS_RAW_STATUS (process)))
== process_status_running)
&& (! (pending_interrupts_p ())))
? (UX_pause ())
: ((errno = EINTR), (-1))));
}
#endif /* not HAVE_POSIX_SIGNALS */
}
static Tprocess
DEFUN (find_process, (pid), pid_t pid)
{
Tprocess process;
for (process = 0; (process < OS_process_table_size); process += 1)
if ((PROCESS_ID (process)) == pid)
return (process);
return (NO_PROCESS);
}
static void
DEFUN (subprocess_death, (pid, status), pid_t pid AND int * status)
{
Tprocess process = (find_process (pid));
if (process != NO_PROCESS)
{
if (WIFEXITED (*status))
{
NEW_RAW_STATUS
(process, process_status_exited, (WEXITSTATUS (*status)));
}
else if (WIFSTOPPED (*status))
{
NEW_RAW_STATUS
(process, process_status_stopped, (WSTOPSIG (*status)));
}
else if (WIFSIGNALED (*status))
{
NEW_RAW_STATUS
(process, process_status_signalled, (WTERMSIG (*status)));
}
}
}
static void
DEFUN (stop_signal_handler, (signo), int signo)
{
/* If Scheme gets a stop signal while waiting on a foreground
subprocess, it must grab the terminal back from the subprocess
before stopping. The caller guarantees that the job-control
signals are blocked when this procedure is called. */
get_terminal_back ();
stop_signal_default (signo);
}
/* Set up the terminal at the other end of a pseudo-terminal that we
will be controlling an inferior through. */
#ifdef HAVE_TERMIOS_H
/* POSIX.1 doesn't require (or even mention) these symbols, but we
must disable them if they are present. */
#ifndef IUCLC
# define IUCLC 0
#endif
#ifndef OLCUC
# define OLCUC 0
#endif
#ifndef NLDLY
# define NLDLY 0
#endif
#ifndef CRDLY
# define CRDLY 0
#endif
#ifndef TABDLY
# define TABDLY 0
#endif
#ifndef BSDLY
# define BSDLY 0
#endif
#ifndef VTDLY
# define VTDLY 0
#endif
#ifndef FFDLY
# define FFDLY 0
#endif
#ifndef ONLCR
# define ONLCR 0
#endif
static int
DEFUN (child_setup_tty, (fd), int fd)
{
cc_t disabled_char = (UX_PC_VDISABLE (fd));
struct termios s;
if ((UX_tcgetattr (fd, (&s))) < 0)
return (-1);
(s . c_iflag) &=~ IUCLC;
(s . c_oflag) |= OPOST;
(s . c_oflag) &=~
(OLCUC | ONLCR | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY);
(s . c_lflag) &=~ (ECHO | ECHOE | ECHOK | ECHONL);
(s . c_lflag) |= (ICANON | ISIG);
((s . c_cc) [VEOF]) = '\004';
((s . c_cc) [VERASE]) = disabled_char;
((s . c_cc) [VKILL]) = disabled_char;
cfsetispeed ((&s), B9600);
cfsetospeed ((&s), B9600);
return (UX_tcsetattr (fd, TCSADRAIN, (&s)));
}
#else /* not HAVE_TERMIOS_H */
#ifdef HAVE_TERMIO_H
static int
DEFUN (child_setup_tty, (fd), int fd)
{
cc_t disabled_char = (UX_PC_VDISABLE (fd));
struct termio s;
if ((ioctl (fd, TCGETA, (&s))) < 0)
return (-1);
(s . c_iflag) &=~ IUCLC;
(s . c_oflag) |= OPOST;
(s . c_oflag) &=~
(OLCUC | ONLCR | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY);
(s . c_lflag) &=~ (ECHO | ECHOE | ECHOK | ECHONL);
(s . c_lflag) |= (ICANON | ISIG);
((s . c_cc) [VEOF]) = '\004';
((s . c_cc) [VERASE]) = disabled_char;
((s . c_cc) [VKILL]) = disabled_char;
(s . c_cflag) = (((s . c_cflag) &~ CBAUD) | B9600);
#ifdef _AIX
/* AIX enhanced edit loses NULs, so disable it.
Also, PTY overloads NUL and BREAK.
don't ignore break, but don't signal either, so it looks like NUL.
This really serves a purpose only if running in an XTERM window
or via TELNET or the like, but does no harm elsewhere. */
(s . c_line) = 0;
(s . c_iflag) &=~ (ASCEDIT | IGNBRK | BRKINT);
/* QUIT and INTR work better as signals, so disable character forms */
(s . c_lflag) &=~ ISIG;
((s . c_cc) [VQUIT]) = disabled_char;
((s . c_cc) [VINTR]) = disabled_char;
((s . c_cc) [VEOL]) = disabled_char;
#endif /* _AIX */
return (ioctl (fd, TCSETAW, (&s)));
}
#else /* not HAVE_TERMIO_H */
#ifdef HAVE_SGTTY_H
static int
DEFUN (child_setup_tty, (fd), int fd)
{
struct sgttyb s;
if ((ioctl (fd, TIOCGETP, (&s))) < 0)
return (-1);
(s . sg_flags) &=~
(ECHO | CRMOD | ANYP | ALLDELAY | RAW | LCASE | CBREAK | TANDEM);
return (ioctl (fd, TIOCSETN, (&s)));
}
#endif /* HAVE_SGTTY_H */
#endif /* HAVE_TERMIO_H */
#endif /* HAVE_TERMIOS_H */