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