1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
include <knet.h>
include <config.h>
include <syserr.h>
include <fset.h>
include <xwhen.h>
include <prstat.h>
# PROPCPR -- Open a connected subprocess, i.e., spawn the subprocess and
# connect the two IPC channels to FIO file descriptors. No i/o is done on
# the IPC channels, hence this relatively low level command is independent
# of the IPC protocol used. PROPEN should be used if the standard IPC
# protocol is followed, so that the environment and current working directory
# may be passed to the subprocess.
int procedure propcpr (process, in, out)
char process[ARB] # filename of executable process file
int in # fd of IPC input from child (output)
int out # fd of IPC output to child (output)
bool first_time
int pr, loc_onipc
pointer sp, fname
int fopnbf()
extern pr_onipc(), pr_dummy_open(), pr_zclspr()
extern zardpr(), zawrpr(), zawtpr(), zsttpr()
errchk xwhen, fmapfn, syserr
include "prc.com"
define cleanup1_ 91
define cleanup2_ 92
define cleanup3_ 93
data first_time /true/
begin
call smark (sp)
call salloc (fname, SZ_PATHNAME, TY_CHAR)
# Initialize the process table. Post exception handler to recover
# from write to IPC with no reader.
if (first_time) {
do pr = 1, MAX_CHILDPROCS
pr_pid[pr] = NULL
pr_lastio = 1 # any legal slot number will do
pr_last_exit_code = OK
call zlocpr (pr_onipc, loc_onipc)
call xwhen (X_IPC, loc_onipc, pr_oldipc)
first_time = false
}
# Find empty process slot.
for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
if (pr_pid[pr] == NULL)
break
if (pr > MAX_CHILDPROCS)
call syserr (SYS_PROVFL)
pr_index = pr
# Initialize the mapping of pseudofile codes to file descriptor numbers.
# PS codes begin at 1, corresponding to STDIN.
pr_pstofd[pr,STDIN] = STDIN
pr_pstofd[pr,STDOUT] = STDOUT
pr_pstofd[pr,STDERR] = STDERR
pr_pstofd[pr,STDGRAPH] = STDGRAPH
pr_pstofd[pr,STDIMAGE] = STDIMAGE
pr_pstofd[pr,STDPLOT] = STDPLOT
# Spawn process and open IPC channels.
call fmapfn (process, Memc[fname], SZ_PATHNAME)
call zopcpr (Memc[fname], pr_inchan[pr], pr_outchan[pr], pr_pid[pr])
if (pr_pid[pr] == ERR)
goto cleanup3_
pr_nopen[pr] = 2
# Set up file descriptors for the two IPC channels.
call strcpy (process, Memc[fname], SZ_PATHNAME)
call strcat (".in", Memc[fname], SZ_PATHNAME)
iferr (in = fopnbf (Memc[fname], READ_ONLY,
pr_dummy_open, zardpr, zawrpr, zawtpr, zsttpr, pr_zclspr))
goto cleanup2_
pr_infd[pr] = in
call strcpy (process, Memc[fname], SZ_PATHNAME)
call strcat (".out", Memc[fname], SZ_PATHNAME)
iferr (out = fopnbf (Memc[fname], WRITE_ONLY,
pr_dummy_open, zardpr, zawrpr, zawtpr, zsttpr, pr_zclspr))
goto cleanup1_
pr_outfd[pr] = out
pr_status[pr] = P_RUNNING
call sfree (sp)
return (pr_pid[pr])
cleanup1_
iferr (call close (out))
;
cleanup2_
iferr (call close (in))
;
cleanup3_
call sfree (sp)
pr_pid[pr] = NULL
call syserrs (SYS_PROPEN, process)
end
# PR_DUMMY_OPEN -- Dummy ZOPNPR procedure called by FIO to "open" the IPC
# channel to a subprocess. Our only function is to return the appropriate
# channel code to FIO, since the channel has already been opened by ZOPCPR.
procedure pr_dummy_open (osfn, mode, chan)
char osfn[ARB] # not used
int mode # used to select read or write IPC channel
int chan # returned to FIO
include "prc.com"
begin
if (mode == READ_ONLY)
chan = pr_inchan[pr_index]
else
chan = pr_outchan[pr_index]
end
# PR_ZCLSPR -- Dummy "zclspr" routine called by FIO to close an IPC channel
# when CLOSE is called on the corresponding FIO file descriptor. A subprocess
# opened with PROPCPR is normally closed with PRCLCPR, but if error recovery
# takes place CLOSE will automatically be called by the system to close both
# file descriptors. We decrement the count of open channels and close the
# process and both IPC channels when the count reaches zero. Hence, a
# connected subprocess may be closed either by calling PRCLCPR or by closing
# the IN and OUT file descriptors.
procedure pr_zclspr (chan, status)
int chan # either inchan or outchan of process
int status # OK or ERR on output
int pr
include "prc.com"
begin
do pr = 1, MAX_CHILDPROCS
if (pr_pid[pr] != NULL)
if (pr_inchan[pr] == chan || pr_outchan[pr] == chan) {
pr_nopen[pr] = pr_nopen[pr] - 1
if (pr_nopen[pr] == 0) {
call zclcpr (pr_pid[pr], pr_last_exit_code)
pr_pid[pr] = NULL
}
status = OK
return
}
status = ERR
end
# PR_ONIPC -- Exception handler for the X_IPC (write to IPC with no reader)
# exception. This exception occurs when the child process dies unexpectedly.
# Determine what process was being written to, cancel any output (to prevent
# a cascade of onipcs trying to flush the output buffer), and cause FIO to
# return a file write error. We cannot do any more than that w/o reentrancy
# problems.
procedure pr_onipc (vex, next_handler)
int vex # virtual exception
int next_handler # next handler in chain
int pr, fd
int fstati()
include "prc.com"
begin
# Chain to next exception handler, if any.
next_handler = pr_oldipc
# Get the FD of the file being written to at the time that the
# exception occurred. We assume that the write operation is still
# in progress when the exception takes place.
fd = fstati (0, F_LASTREFFILE)
for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
if (pr_pid[pr] != NULL)
if (pr_outfd[pr] == fd)
break
if (pr > MAX_CHILDPROCS)
return
# Cancel any buffered output and remove write permission to ensure
# that we will not get a cascade of X_IPC exceptions.
call fseti (fd, F_CANCEL, ERR)
call fseti (fd, F_MODE, READ_ONLY)
pr_status[pr] = P_DEAD
end
|