1 /* 2 * Copyright � 1994 the Free Software Foundation, Inc. 3 * 4 * Author: Roland B. Roberts (roberts@nsrl.rochester.edu) 5 * 6 * This file is a part of GNU VMSLIB, the GNU library for porting GNU 7 * software to VMS. 8 * 9 * GNU VMSLIB is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * GNU VMSLIB is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20 /* 21 * Modification History 22 * 13 Sep 94 - RBR 23 * Use event flag one -- zero seems to cause sys$synch to hang. 24 * 12 Sep 94 - RBR 25 * All pipes now use event flag zero. 26 * Removed the limit on the number of pipes. 27 * Added members to PIPE structure and memory corruption tests. 28 */ 29 30 /* This won't work with GCC, but it won't cause any problems either. */ 31 #define MODULE PIPE 32 #define VERSION "V1.5" 33 34 #ifdef __DECC 35 #pragma module MODULE VERSION 36 #else 37 #ifdef VAXC 38 #module MODULE VERSION 39 #endif 40 #endif 41 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <iodef.h> 46 #include <ssdef.h> 47 #include <syidef.h> 48 #include <clidef.h> 49 #include <stsdef.h> 50 #include <dvidef.h> 51 #include <nam.h> 52 #include <descrip.h> 53 #include <errno.h> 54 #include <file.h> 55 #include <lib$routines.h> 56 #include <starlet.h> 57 #include <setjmp.h> 58 #include "vms-types.h" 59 60 /* A linked list of pipes, for internal use only */ 61 struct PIPE 62 { 63 struct PIPE *next; /* next pipe in the chain */ 64 struct PIPE *prev; /* previous pipe in the chain */ 65 struct PIPE *self; /* self reference */ 66 int mode; /* pipe I/O mode (read or write) */ 67 long status; /* subprocess completion status */ 68 struct IOSB iosb; /* pipe I/O status block */ 69 FILE *file; /* pipe file structure */ 70 int pid; /* pipe process id */ 71 short chan; /* pipe channel */ 72 jmp_buf jmpbuf; /* jump buffer, if needed */ 73 int has_jmpbuf; /* flag */ 74 }; 75 76 /* Head of the pipe chain */ 77 static struct PIPE *phead = NULL, *ptail = NULL; 78 79 static unsigned char evf = 1; 80 81 /* 82 * Exit handler for current process, established by popen(). 83 * Force the current process to wait for the completion of children 84 * which were started via popen(). 85 * Since 86 */ 87 static int 88 pwait (status) 89 int status; 90 { 91 struct IOSB iosb; 92 struct PIPE *this; 93 int ret = 0; 94 95 this = phead; 96 while (this) 97 { 98 if (this->self != this) 99 { 100 ret = -1; 101 continue; 102 } 103 if (!this->iosb.status) 104 { 105 fflush (this->file); 106 if (this->mode == O_WRONLY) 107 sys$qio (0, this->chan, IO$_WRITEOF, &iosb, 108 0, 0, 0, 0, 0, 0, 0, 0); 109 fclose (this->file); 110 sys$synch (evf, &this->iosb); 111 } 112 else 113 fclose(this->file); 114 sys$dassgn (this->chan); 115 this = this->next; 116 } 117 return ret; 118 } 119 120 /* 121 * Close a "pipe" created by popen() 122 * Return codes 123 * >0 VMS exit status of process 124 * 0 success, pipe was closed 125 * -1 stream not found in list of pipes 126 * -2 memory corruption detected 127 */ 128 int 129 pclose (stream) 130 FILE *stream; 131 { 132 struct IOSB iosb; 133 struct PIPE *this = phead; 134 135 while (this && this->self == this && this->file != stream) 136 this = this->next; 137 138 /* Pipe not found or failed sanity check */ 139 if (!this) 140 return -1; 141 else if (this->self != this) 142 return -2; 143 144 /* Flush the I/O buffer and wait for the close to complete */ 145 if (!this->iosb.status) 146 { 147 fflush (this->file); 148 if (this->mode == O_WRONLY) 149 sys$qio (0, this->chan, IO$_WRITEOF, &iosb, 150 0, 0, 0, 0, 0, 0, 0, 0); 151 fclose (this->file); 152 sys$synch (evf, &this->iosb); 153 } 154 else 155 fclose (this->file); 156 sys$dassgn (this->chan); 157 158 /* Remove `this' from the list of pipes and free its storage */ 159 if (this == ptail) 160 ptail = this->prev; 161 if (this == phead) 162 phead = this->next; 163 if (this->prev) 164 this->prev->next = this->next; 165 if (this->next) 166 this->next->prev = this->prev; 167 free (this); 168 169 if (this->status & STS$M_SUCCESS != STS$M_SUCCESS) 170 return this->status; 171 else 172 return 0; 173 } 174 175 /* 176 * Subprocess AST completion routine 177 * Indicate successful completion in the iosb and clear the pid. 178 * Note that the channel is *not* deassigned and the file is 179 * *not* closed. 180 */ 181 void 182 pdone (this) 183 struct PIPE *this; 184 { 185 struct IOSB iosb; 186 187 if (this->self != this) 188 return; 189 this->iosb.status = 1; 190 this->pid = 0; 191 if (this->has_jmpbuf) 192 { 193 this->has_jmpbuf = 0; 194 longjmp (this->jmpbuf, 1); 195 } 196 } 197 198 int 199 pipe_set_fd_jmpbuf (fd, jmpbuf) 200 int fd; 201 jmp_buf jmpbuf; 202 { 203 struct PIPE *this = phead; 204 205 while (this) 206 if (fileno (this->file) == fd) 207 { 208 memcpy (this->jmpbuf, jmpbuf, sizeof (jmp_buf)); 209 this->has_jmpbuf = 1; 210 if (this->pid == 0) 211 { 212 this->has_jmpbuf = 0; 213 longjmp (this->jmpbuf, 1); 214 } 215 return 0; 216 } 217 else 218 this = this->next; 219 return 1; 220 } 221 222 pipe_unset_fd_jmpbuf (fd) 223 int fd; 224 { 225 struct PIPE *this = phead; 226 227 while (this) 228 if (fileno (this->file) == fd) 229 { 230 this->has_jmpbuf = 0; 231 return 0; 232 } 233 else 234 this = this->next; 235 return 1; 236 } 237 238 /* Exit handler control block for the current process. */ 239 static struct EXHCB pexhcb = { 0, pwait, 1, &pexhcb.exh$l_status, 0 }; 240 241 struct Vstring 242 { 243 short length; 244 char body[NAM$C_MAXRSS+1]; 245 }; 246 247 /* 248 * Emulate a unix popen() call using lib$spawn 249 * 250 * if mode == "w", lib$spawn uses the mailbox for sys$input 251 * if mode == "r", lib$spawn uses the mailbox for sys$output 252 * 253 * Don't now how to handle both read and write 254 * 255 * Returns 256 * FILE * file pointer to the pipe 257 * NULL indicates an error ocurred, check errno value 258 */ 259 FILE * 260 popen (cmd, mode) 261 const char *cmd; 262 const char *mode; 263 { 264 int i, status, flags, mbxsize; 265 struct IOSB iosb; 266 struct dsc$descriptor_s cmddsc, mbxdsc; 267 struct Vstring mbxname = { sizeof(mbxname.body) }; 268 struct itm$list3 mbxlist[2] = { 269 { sizeof(mbxname.body)-1, DVI$_DEVNAM, &mbxname.body, &mbxname.length }, 270 { 0, 0, 0, 0} }; 271 struct itm$list3 syilist[2] = { 272 { sizeof(mbxsize), SYI$_MAXBUF, &mbxsize, (void *) 0 }, 273 { 0, 0, 0, 0} }; 274 static int noExitHandler = 1; 275 struct PIPE *this; 276 277 /* First allocate space for the new pipe */ 278 this = (struct PIPE *) calloc (1, sizeof(struct PIPE)); 279 if (!this) 280 { 281 errno = ENOMEM; 282 return NULL; 283 } 284 285 /* Sanity check value */ 286 this->self = this; 287 288 /* Use the smaller of SYI$_MAXBUF and 2048 for the mailbox size */ 289 status = sys$getsyiw(0, 0, 0, syilist, &iosb, 0, 0, 0); 290 if (status != SS$_NORMAL && !(iosb.status & STS$M_SUCCESS)) 291 { 292 vaxc$errno = iosb.status; 293 errno = EVMSERR; 294 free (this); 295 perror ("popen, $GETSYIW failure for SYI$_MAXBUF"); 296 return NULL; 297 } 298 299 if (mbxsize > 2048) 300 mbxsize = 2048; 301 302 status = sys$crembx (0, &this->chan, mbxsize, mbxsize, 0, 0, 0, 0); 303 if (status != SS$_NORMAL) 304 { 305 vaxc$errno = status; 306 errno = EVMSERR; 307 free (this); 308 perror ("popen, $CREMBX failure"); 309 return NULL; 310 } 311 312 /* Retrieve mailbox name, use for fopen */ 313 status = sys$getdviw (0, this->chan, 0, &mbxlist, &iosb, 0, 0, 0); 314 if (status != SS$_NORMAL && !(iosb.status & STS$M_SUCCESS)) 315 { 316 vaxc$errno = iosb.status; 317 errno = EVMSERR; 318 sys$dassgn (this->chan); 319 free (this); 320 perror ("popen, $GETDVIW failure"); 321 return NULL; 322 } 323 324 /* Spawn the command using the mailbox as the name for sys$input */ 325 mbxname.body[mbxname.length] = 0; 326 mbxdsc.dsc$w_length = mbxname.length; 327 mbxdsc.dsc$b_dtype = DSC$K_DTYPE_T; 328 mbxdsc.dsc$b_class = DSC$K_CLASS_S; 329 mbxdsc.dsc$a_pointer = mbxname.body; 330 331 cmddsc.dsc$w_length = strlen(cmd); 332 cmddsc.dsc$b_dtype = DSC$K_DTYPE_T; 333 cmddsc.dsc$b_class = DSC$K_CLASS_S; 334 cmddsc.dsc$a_pointer = (char *)cmd; 335 flags = CLI$M_NOWAIT; 336 if (strcmp(mode,"w") == 0) 337 { 338 status = lib$spawn (&cmddsc, &mbxdsc, 0, &flags, 0, &this->pid, 339 &this->status, &evf, &pdone, this->self); 340 this->mode = O_WRONLY; 341 } 342 else 343 { 344 status = lib$spawn (&cmddsc, 0, &mbxdsc, &flags, 0, &this->pid, 345 &this->status, &evf, &pdone, this->self); 346 this->mode = O_RDONLY; 347 } 348 if (status != SS$_NORMAL) 349 { 350 vaxc$errno = status; 351 errno = EVMSERR; 352 sys$dassgn (this->chan); 353 free (this); 354 perror("popen, LIB$SPAWN failure"); 355 return NULL; 356 } 357 358 /* Set up an exit handler so the subprocess isn't prematurely killed */ 359 if (noExitHandler) 360 { 361 status = sys$dclexh (&pexhcb); 362 if (status != SS$_NORMAL) 363 { 364 vaxc$errno = status; 365 errno = EVMSERR; 366 sys$dassgn (this->chan); 367 sys$delprc (&this->pid, 0); 368 free (this); 369 perror("popen, $DCLEXH failure"); 370 return NULL; 371 } 372 noExitHandler = 0; 373 } 374 375 /* Pipes are always binary mode devices */ 376 if (this->mode == O_WRONLY) 377 this->file = fopen (mbxname.body, "wb"); 378 else 379 this->file = fopen (mbxname.body, "rb"); 380 381 /* Paranoia, check for failure again */ 382 if (!this->file) 383 { 384 sys$dassgn (this->chan); 385 sys$delprc (this->pid); 386 free (this); 387 perror ("popen, fopen failure"); 388 return NULL; 389 } 390 391 this->has_jmpbuf = 0; 392 393 /* Insert the new pipe into the list of open pipes */ 394 if (phead) 395 { 396 ptail->next = this; 397 this->prev = ptail; 398 ptail = this; 399 } 400 else 401 phead = ptail = this; 402 403 return (this->file); 404 } 405 406 407 #ifdef TEST_PIPE 408 int 409 main (argc, argv) 410 int argc; 411 char **argv; 412 { 413 FILE *stdpipe; 414 char line[512]; 415 416 while (1) 417 { 418 printf ("\nEnter a command to run >> "); 419 fgets (line, 511, stdin); 420 if (!strlen(line)) 421 exit (1); 422 line[strlen(line)-1] = 0; 423 stdpipe = popen (line, "r"); 424 if (!stdpipe) 425 { 426 fprintf (stderr, "popen failed.\n"); 427 exit(44); 428 } 429 do { 430 fgets (line, 511, stdpipe); 431 fputs (line, stdout); 432 } while (!feof(stdpipe)); 433 pclose (stdpipe); 434 } 435 } 436 #endif 437