1 /*
2 * piped_child.c
3 *
4 * An experimental VMS implementation of the same routine in [-.src]run.c
5 * <benjamin@cyclic.com>
6 *
7 * Derived in part from pipe.c, in this directory.
8 */
9
10 #include "vms.h"
11 #include "vms-types.h"
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <iodef.h>
17 #include <ssdef.h>
18 #include <syidef.h>
19 #include <clidef.h>
20 #include <stsdef.h>
21 #include <dvidef.h>
22 #include <nam.h>
23 #include <descrip.h>
24 #include <errno.h>
25 #include <file.h>
26 #include <lib$routines.h>
27 #include <starlet.h>
28
29 extern int trace;
30
31 /* Subprocess IO structure */
32 typedef struct _SUBIO {
33 struct _SUBIO *self;
34 struct _SUBIO *prev;
35 struct _SUBIO *next;
36 short read_chan;
37 short write_chan;
38 FILE *read_fp;
39 FILE *write_fp;
40 struct IOSB read_iosb;
41 struct IOSB write_iosb;
42 int pid;
43 int return_status;
44 unsigned long event_flag;
45 unsigned char event_flag_byte;
46 } SUBIO;
47
48 static SUBIO *siop_head = NULL, *siop_tail = NULL;
49
50 static int piped_child_exith(int);
51
52 static struct EXHCB piped_child_exit_handler_block =
53 {0, piped_child_exith, 1, &piped_child_exit_handler_block.exh$l_status, 0};
54
55 typedef struct
56 {
57 short length;
58 char body[NAM$C_MAXRSS+1];
59 } Vstring;
60
61 /* Subprocess Completion AST */
piped_child_done(siop)62 void piped_child_done(siop)
63 SUBIO *siop;
64 {
65 struct IOSB iosb;
66 int status;
67
68 if (siop->self != siop)
69 return;
70 siop->read_iosb.status = SS$_NORMAL;
71 siop->write_iosb.status = SS$_NORMAL;
72
73 }
74
75 /* Exit handler, established by piped_child() */
76 static int
piped_child_exith(status)77 piped_child_exith(status)
78 int status;
79 {
80 struct IOSB iosb;
81 SUBIO *siop;
82 int return_value = 0;
83
84 siop = siop_head;
85 while (siop)
86 {
87 if (siop->self != siop)
88 {
89 return_value = -1;
90 continue;
91 }
92
93 /* Finish pending reads and shutdown */
94 if(!siop->read_iosb.status)
95 {
96 fflush (siop->read_fp);
97 fclose (siop->read_fp);
98 }
99 else
100 fclose (siop->read_fp);
101 sys$dassgn (siop->read_chan);
102
103 /* Finish pending writes and shutdown */
104 if(!siop->write_iosb.status)
105 {
106 fflush (siop->write_fp);
107 sys$qio (0, siop->write_chan, IO$_WRITEOF, &iosb,
108 0, 0, 0, 0, 0, 0, 0, 0);
109 fclose (siop->write_fp);
110 }
111 else
112 fclose (siop->write_fp);
113 sys$dassgn (siop->write_chan);
114
115 sys$synch (siop->event_flag, &siop->write_iosb);
116
117 siop = siop->next;
118 }
119 return return_value;
120 }
121
piped_child(command,tofdp,fromfdp)122 int piped_child(command, tofdp, fromfdp)
123 char **command;
124 int *tofdp, *fromfdp;
125 {
126 static int exit_handler = 0;
127 struct IOSB iosb1, iosb2;
128 int rs1, rs2, i;
129 unsigned long flags, vmspid, return_status;
130 char cmd[1024];
131 struct dsc$descriptor_s cmddsc;
132 struct dsc$descriptor_s read_mbxdsc, write_mbxdsc;
133 SUBIO *siop;
134 static Vstring read_mbxname, write_mbxname;
135 static struct itm$list3 write_mbxlist[2] = {
136 {sizeof(write_mbxname.body)-1, DVI$_DEVNAM,
137 &write_mbxname.body, (size_t *) &write_mbxname.length},
138 {0, 0, 0, 0} };
139 static struct itm$list3 read_mbxlist[2] = {
140 {sizeof(read_mbxname.body)-1, DVI$_DEVNAM,
141 &read_mbxname.body, (size_t *) &read_mbxname.length},
142 {0, 0, 0, 0} };
143
144 read_mbxname.length = sizeof(read_mbxname.body);
145 write_mbxname.length = sizeof(write_mbxname.body);
146
147 siop = (SUBIO *) calloc(1, sizeof(SUBIO));
148 if (!siop)
149 {
150 perror("piped_child: malloc failed\n");
151 return -1;
152 }
153
154 siop->self = siop;
155
156 /* Construct command line by concatenating argument list */
157 strcpy(cmd, command[0]);
158 for(i=1; command[i] != NULL; i++)
159 {
160 strcat(cmd, " ");
161 strcat(cmd, command[i]);
162 }
163
164 if(trace)
165 fprintf(stderr, "piped_child: running '%s'\n", cmd);
166
167 /* Allocate a pair of temporary mailboxes (2kB each) */
168 rs1 = sys$crembx (0, &siop->read_chan, 2048, 2048, 0, 0, 0, 0);
169 rs2 = sys$crembx (0, &siop->write_chan, 2048, 2048, 0, 0, 0, 0);
170
171 if (rs1 != SS$_NORMAL || rs2 != SS$_NORMAL)
172 {
173 vaxc$errno = rs1 | rs2;
174 errno = EVMSERR;
175 free (siop);
176 perror ("piped_child: $CREMBX failure");
177 return -1;
178 }
179
180 /* Get mailbox names, so we can fopen() them */
181 rs1 = sys$getdviw (0, siop->read_chan, 0, &read_mbxlist,
182 &iosb1, 0, 0, 0);
183
184 rs2 = sys$getdviw (0, siop->write_chan, 0, &write_mbxlist,
185 &iosb2, 0, 0, 0);
186
187 if ((rs1 != SS$_NORMAL && !(iosb1.status & STS$M_SUCCESS)) ||
188 (rs2 != SS$_NORMAL && !(iosb2.status & STS$M_SUCCESS)))
189 {
190 vaxc$errno = iosb1.status | iosb2.status;
191 errno = EVMSERR;
192 sys$dassgn (siop->read_chan);
193 sys$dassgn (siop->write_chan);
194 free (siop);
195 perror ("piped_child: $GETDVIW failure, could not get mailbox names");
196 return -1;
197 }
198
199 if (trace)
200 {
201 fprintf(stderr, "piped_child: $GETDVIW succeeded, got mailbox names\n");
202 fprintf(stderr, "piped_child: ReadMBX: %s, WriteMBX: %s\n",
203 read_mbxname.body, write_mbxname.body);
204 }
205
206 /* Make C happy */
207 write_mbxname.body[write_mbxname.length] = '\0';
208 read_mbxname.body[read_mbxname.length] = '\0';
209
210 /* Make VMS happy */
211 write_mbxdsc.dsc$w_length = write_mbxname.length;
212 write_mbxdsc.dsc$b_dtype = DSC$K_DTYPE_T;
213 write_mbxdsc.dsc$b_class = DSC$K_CLASS_S;
214 write_mbxdsc.dsc$a_pointer = write_mbxname.body;
215
216 read_mbxdsc.dsc$w_length = read_mbxname.length;
217 read_mbxdsc.dsc$b_dtype = DSC$K_DTYPE_T;
218 read_mbxdsc.dsc$b_class = DSC$K_CLASS_S;
219 read_mbxdsc.dsc$a_pointer = read_mbxname.body;
220
221 /* Build descriptor for command line */
222 cmddsc.dsc$w_length = strlen(cmd);
223 cmddsc.dsc$b_dtype = DSC$K_DTYPE_T;
224 cmddsc.dsc$b_class = DSC$K_CLASS_S;
225 cmddsc.dsc$a_pointer = (char *) cmd;
226
227 flags = CLI$M_NOWAIT;
228
229 /* Allocate an event flag to signal process termination */
230 rs1 = lib$get_ef(&siop->event_flag);
231 if (rs1 != SS$_NORMAL)
232 {
233 vaxc$errno = rs1;
234 errno = EVMSERR;
235 sys$dassgn(siop->read_chan);
236 sys$dassgn(siop->write_chan);
237 perror("piped_child: LIB$GET_EF failed");
238 return -1;
239 }
240
241 /* Save the EFN as a byte for later calls to other routines */
242 siop->event_flag_byte = 0xff & siop->event_flag;
243
244 if (trace)
245 fprintf(stderr, "piped_child: Got an EFN: %d\n", siop->event_flag_byte);
246
247 rs1 = lib$spawn(&cmddsc, &write_mbxdsc, &read_mbxdsc, &flags, 0,
248 &siop->pid, &siop->return_status, &siop->event_flag_byte,
249 &piped_child_done, siop->self);
250
251 if (rs1 != SS$_NORMAL)
252 {
253 vaxc$errno = rs1;
254 errno = EVMSERR;
255 sys$dassgn(siop->read_chan);
256 sys$dassgn(siop->write_chan);
257 perror("piped_child: LIB$SPAWN failure");
258 return -1;
259 }
260
261 if (trace)
262 fprintf(stderr, "piped_child: LIB$SPAWN succeeded, pid is %08x.\n",
263 siop->pid);
264
265 /* Establish an exit handler so the process isn't prematurely terminated */
266 if (!exit_handler)
267 {
268 rs1 = sys$dclexh (&piped_child_exit_handler_block);
269 if (rs1 != SS$_NORMAL)
270 {
271 vaxc$errno = rs1;
272 errno = EVMSERR;
273 sys$dassgn (siop->read_chan);
274 sys$dassgn (siop->write_chan);
275 sys$delprc (siop->pid, 0);
276 free (siop);
277 perror("piped_child: $DCLEXH failure");
278 return -1;
279 }
280 exit_handler = 1;
281 }
282
283 /* Let's open some files */
284 siop->read_fp = fopen (read_mbxname.body, "r");
285 siop->write_fp = fopen (write_mbxname.body, "w");
286
287 if (!siop->read_fp || !siop->write_fp)
288 {
289 sys$dassgn (siop->read_chan);
290 sys$dassgn (siop->write_chan);
291 sys$delprc (siop->pid);
292 free (siop);
293 perror("piped_child: fopen() failed");
294 return -1;
295 }
296
297 *fromfdp = fileno(siop->read_fp);
298 *tofdp = fileno(siop->write_fp);
299
300 if (trace)
301 fprintf(stderr, "piped_child: file open successful: tofd=%d fromfd=%d\n",
302 *tofdp, *fromfdp);
303
304 /* Keep track of active subprocess I/O (SUBIO) structures */
305 if (siop_head)
306 {
307 siop_tail->next = siop;
308 siop->prev = siop_tail;
309 siop_tail = siop;
310 }
311 else
312 siop_head = siop_tail = siop;
313
314 return siop->pid;
315 }
316
317 /*
318 * Return codes
319 * >0 VMS exit status of subprocess
320 * 0 success, subprocess was shutdown
321 * -1 pid not found in list of subprocesses
322 * -2 memory corruption detected
323 */
324 int
piped_child_shutdown(pid)325 piped_child_shutdown(pid)
326 pid_t pid;
327 {
328 int return_status;
329 struct IOSB iosb;
330 SUBIO *siop = siop_head;
331
332 while (siop && siop->self == siop && siop->pid != pid)
333 siop = siop->next;
334
335 if (!siop)
336 return -1;
337 else if (siop->self != siop)
338 return -2;
339
340 /* Finish reading and writing and shutdown */
341 if (siop->read_iosb.status)
342 {
343 fflush (siop->read_fp);
344 fclose (siop->read_fp);
345 }
346 else
347 fclose(siop->read_fp);
348 sys$dassgn (siop->read_chan);
349
350 if (siop->write_iosb.status)
351 {
352 fflush (siop->write_fp);
353 sys$qio (0, siop->write_chan, IO$_WRITEOF, &iosb,
354 0, 0, 0, 0, 0, 0, 0, 0);
355 fclose (siop->write_fp);
356 }
357 else
358 fclose(siop->write_fp);
359 sys$dassgn (siop->write_chan);
360
361 sys$synch (siop->event_flag, &siop->write_iosb);
362 lib$free_ef(&siop->event_flag);
363
364 /* Ditch SUBIO structure */
365 if (siop == siop_tail)
366 siop_tail = siop->prev;
367 if (siop == siop_head)
368 siop_head = siop->next;
369 if (siop->prev)
370 siop->prev->next = siop->next;
371 if (siop->next)
372 siop->next->prev = siop->prev;
373
374 if (siop->return_status)
375 return_status = siop->return_status;
376 else
377 return_status = 0;
378
379 free (siop);
380
381 return return_status;
382 }
383