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