xref: /openbsd-src/gnu/usr.bin/cvs/vms/pipe.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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