refresh
[alioth/cvs.git] / vms / pipe.c
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 \f
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  */