xref: /openbsd/gnu/usr.bin/cvs/vms/piped_child.c (revision fc61954a)
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 */
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
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 
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
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