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
pwait(status)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
pclose(stream)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
pdone(this)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
pipe_set_fd_jmpbuf(fd,jmpbuf)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
pipe_unset_fd_jmpbuf(fd)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 *
popen(cmd,mode)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
main(argc,argv)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