xref: /openbsd-src/gnu/usr.bin/cvs/vms/pipe.c (revision 43c1707e6f6829177cb1974ee6615ce6c1307689)
150bf276cStholo /*
250bf276cStholo  * Copyright � 1994 the Free Software Foundation, Inc.
350bf276cStholo  *
450bf276cStholo  * Author: Roland B. Roberts (roberts@nsrl.rochester.edu)
550bf276cStholo  *
650bf276cStholo  * This file is a part of GNU VMSLIB, the GNU library for porting GNU
750bf276cStholo  * software to VMS.
850bf276cStholo  *
950bf276cStholo  * GNU VMSLIB is free software; you can redistribute it and/or modify
1050bf276cStholo  * it under the terms of the GNU General Public License as published by
1150bf276cStholo  * the Free Software Foundation; either version 2 of the License, or
1250bf276cStholo  * (at your option) any later version.
1350bf276cStholo  *
1450bf276cStholo  * GNU VMSLIB is distributed in the hope that it will be useful,
1550bf276cStholo  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1650bf276cStholo  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1750bf276cStholo  * GNU General Public License for more details.
1850bf276cStholo  */
1950bf276cStholo 
2050bf276cStholo /*
2150bf276cStholo  * Modification History
2250bf276cStholo  * 13 Sep 94 - RBR
2350bf276cStholo  *    Use event flag one -- zero seems to cause sys$synch to hang.
2450bf276cStholo  * 12 Sep 94 - RBR
2550bf276cStholo  *    All pipes now use event flag zero.
2650bf276cStholo  *    Removed the limit on the number of pipes.
2750bf276cStholo  *    Added members to PIPE structure and memory corruption tests.
2850bf276cStholo  */
2950bf276cStholo 
30*43c1707eStholo #ifndef __VMS_VER
31*43c1707eStholo #define __VMS_VER 0
32*43c1707eStholo #endif
33*43c1707eStholo #ifndef __DECC_VER
34*43c1707eStholo #define __DECC_VER 0
35*43c1707eStholo #endif
36*43c1707eStholo 
37*43c1707eStholo #if __VMS_VER < 70200000 || __DECC_VER < 50700000
38*43c1707eStholo 
3950bf276cStholo /* This won't work with GCC, but it won't cause any problems either.  */
4050bf276cStholo #define MODULE	PIPE
4150bf276cStholo #define VERSION "V1.5"
4250bf276cStholo 
4350bf276cStholo #ifdef __DECC
4450bf276cStholo #pragma module MODULE VERSION
4550bf276cStholo #else
4650bf276cStholo #ifdef VAXC
4750bf276cStholo #module MODULE VERSION
4850bf276cStholo #endif
4950bf276cStholo #endif
5050bf276cStholo 
5150bf276cStholo #include <stdio.h>
5250bf276cStholo #include <stdlib.h>
5350bf276cStholo #include <string.h>
5450bf276cStholo #include <iodef.h>
5550bf276cStholo #include <ssdef.h>
5650bf276cStholo #include <syidef.h>
5750bf276cStholo #include <clidef.h>
5850bf276cStholo #include <stsdef.h>
5950bf276cStholo #include <dvidef.h>
6050bf276cStholo #include <nam.h>
6150bf276cStholo #include <descrip.h>
6250bf276cStholo #include <errno.h>
6350bf276cStholo #include <file.h>
6450bf276cStholo #include <lib$routines.h>
6550bf276cStholo #include <starlet.h>
6650bf276cStholo #include <setjmp.h>
6750bf276cStholo #include "vms-types.h"
6850bf276cStholo 
6950bf276cStholo /* A linked list of pipes, for internal use only */
7050bf276cStholo struct PIPE
7150bf276cStholo {
7250bf276cStholo   struct PIPE *next;            /* next pipe in the chain */
7350bf276cStholo   struct PIPE *prev;            /* previous pipe in the chain */
7450bf276cStholo   struct PIPE *self;            /* self reference */
7550bf276cStholo   int mode;                     /* pipe I/O mode (read or write) */
7650bf276cStholo   long status;                  /* subprocess completion status */
7750bf276cStholo   struct IOSB iosb;             /* pipe I/O status block */
7850bf276cStholo   FILE *file;                   /* pipe file structure */
7950bf276cStholo   int pid;                      /* pipe process id */
8050bf276cStholo   short chan;                   /* pipe channel */
8150bf276cStholo   jmp_buf jmpbuf;		/* jump buffer, if needed */
8250bf276cStholo   int has_jmpbuf;		/* flag */
8350bf276cStholo };
8450bf276cStholo 
8550bf276cStholo /* Head of the pipe chain */
8650bf276cStholo static struct PIPE *phead = NULL, *ptail = NULL;
8750bf276cStholo 
8850bf276cStholo static unsigned char evf = 1;
8950bf276cStholo 
9050bf276cStholo /*
9150bf276cStholo  * Exit handler for current process, established by popen().
9250bf276cStholo  * Force the current process to wait for the completion of children
9350bf276cStholo  *   which were started via popen().
9450bf276cStholo  * Since
9550bf276cStholo  */
9650bf276cStholo static int
pwait(status)9750bf276cStholo pwait (status)
9850bf276cStholo   int status;
9950bf276cStholo {
10050bf276cStholo   struct IOSB iosb;
10150bf276cStholo   struct PIPE *this;
10250bf276cStholo   int ret = 0;
10350bf276cStholo 
10450bf276cStholo   this = phead;
10550bf276cStholo   while (this)
10650bf276cStholo     {
10750bf276cStholo       if (this->self != this)
10850bf276cStholo         {
10950bf276cStholo 	  ret = -1;
11050bf276cStholo 	  continue;
11150bf276cStholo 	}
11250bf276cStholo       if (!this->iosb.status)
11350bf276cStholo 	{
11450bf276cStholo 	  fflush (this->file);
11550bf276cStholo 	  if (this->mode == O_WRONLY)
11650bf276cStholo 	    sys$qio (0, this->chan, IO$_WRITEOF, &iosb,
11750bf276cStholo 		     0, 0, 0, 0, 0, 0, 0, 0);
11850bf276cStholo 	  fclose (this->file);
11950bf276cStholo 	  sys$synch (evf, &this->iosb);
12050bf276cStholo 	}
12150bf276cStholo       else
12250bf276cStholo 	fclose(this->file);
12350bf276cStholo       sys$dassgn (this->chan);
12450bf276cStholo       this = this->next;
12550bf276cStholo     }
12650bf276cStholo   return ret;
12750bf276cStholo }
12850bf276cStholo 
12950bf276cStholo /*
13050bf276cStholo  * Close a "pipe" created by popen()
13150bf276cStholo  * Return codes
13250bf276cStholo  * >0  VMS exit status of process
13350bf276cStholo  *  0  success, pipe was closed
13450bf276cStholo  * -1  stream not found in list of pipes
13550bf276cStholo  * -2  memory corruption detected
13650bf276cStholo  */
13750bf276cStholo int
pclose(stream)13850bf276cStholo pclose (stream)
13950bf276cStholo   FILE *stream;
14050bf276cStholo {
14150bf276cStholo   struct IOSB iosb;
14250bf276cStholo   struct PIPE *this = phead;
14350bf276cStholo 
14450bf276cStholo   while (this && this->self == this && this->file != stream)
14550bf276cStholo     this = this->next;
14650bf276cStholo 
14750bf276cStholo   /* Pipe not found or failed sanity check */
14850bf276cStholo   if (!this)
14950bf276cStholo     return -1;
15050bf276cStholo   else if (this->self != this)
15150bf276cStholo     return -2;
15250bf276cStholo 
15350bf276cStholo   /* Flush the I/O buffer and wait for the close to complete */
15450bf276cStholo   if (!this->iosb.status)
15550bf276cStholo     {
15650bf276cStholo       fflush (this->file);
15750bf276cStholo       if (this->mode == O_WRONLY)
15850bf276cStholo 	sys$qio (0, this->chan, IO$_WRITEOF, &iosb,
15950bf276cStholo 		 0, 0, 0, 0, 0, 0, 0, 0);
16050bf276cStholo       fclose (this->file);
16150bf276cStholo       sys$synch (evf, &this->iosb);
16250bf276cStholo     }
16350bf276cStholo   else
16450bf276cStholo     fclose (this->file);
16550bf276cStholo   sys$dassgn (this->chan);
16650bf276cStholo 
16750bf276cStholo   /* Remove `this' from the list of pipes and free its storage */
16850bf276cStholo   if (this == ptail)
16950bf276cStholo     ptail = this->prev;
17050bf276cStholo   if (this == phead)
17150bf276cStholo     phead = this->next;
17250bf276cStholo   if (this->prev)
17350bf276cStholo     this->prev->next = this->next;
17450bf276cStholo   if (this->next)
17550bf276cStholo     this->next->prev = this->prev;
17650bf276cStholo   free (this);
17750bf276cStholo 
17850bf276cStholo   if (this->status & STS$M_SUCCESS != STS$M_SUCCESS)
17950bf276cStholo     return this->status;
18050bf276cStholo   else
18150bf276cStholo     return 0;
18250bf276cStholo }
18350bf276cStholo 
18450bf276cStholo /*
18550bf276cStholo  * Subprocess AST completion routine
18650bf276cStholo  * Indicate successful completion in the iosb and clear the pid.
18750bf276cStholo  * Note that the channel is *not* deassigned and the file is
18850bf276cStholo  *   *not* closed.
18950bf276cStholo  */
19050bf276cStholo void
pdone(this)19150bf276cStholo pdone (this)
19250bf276cStholo   struct PIPE *this;
19350bf276cStholo {
19450bf276cStholo   struct IOSB iosb;
19550bf276cStholo 
19650bf276cStholo   if (this->self != this)
19750bf276cStholo     return;
19850bf276cStholo   this->iosb.status = 1;
19950bf276cStholo   this->pid  = 0;
20050bf276cStholo   if (this->has_jmpbuf)
20150bf276cStholo     {
20250bf276cStholo       this->has_jmpbuf = 0;
20350bf276cStholo       longjmp (this->jmpbuf, 1);
20450bf276cStholo     }
20550bf276cStholo }
20650bf276cStholo 
20750bf276cStholo int
pipe_set_fd_jmpbuf(fd,jmpbuf)20850bf276cStholo pipe_set_fd_jmpbuf (fd, jmpbuf)
20950bf276cStholo      int fd;
21050bf276cStholo      jmp_buf jmpbuf;
21150bf276cStholo {
21250bf276cStholo   struct PIPE *this = phead;
21350bf276cStholo 
21450bf276cStholo   while (this)
21550bf276cStholo     if (fileno (this->file) == fd)
21650bf276cStholo       {
21750bf276cStholo 	memcpy (this->jmpbuf, jmpbuf, sizeof (jmp_buf));
21850bf276cStholo 	this->has_jmpbuf = 1;
21950bf276cStholo 	if (this->pid == 0)
22050bf276cStholo 	  {
22150bf276cStholo 	    this->has_jmpbuf = 0;
22250bf276cStholo 	    longjmp (this->jmpbuf, 1);
22350bf276cStholo 	  }
22450bf276cStholo 	return 0;
22550bf276cStholo       }
22650bf276cStholo     else
22750bf276cStholo       this = this->next;
22850bf276cStholo   return 1;
22950bf276cStholo }
23050bf276cStholo 
pipe_unset_fd_jmpbuf(fd)23150bf276cStholo pipe_unset_fd_jmpbuf (fd)
23250bf276cStholo      int fd;
23350bf276cStholo {
23450bf276cStholo   struct PIPE *this = phead;
23550bf276cStholo 
23650bf276cStholo   while (this)
23750bf276cStholo     if (fileno (this->file) == fd)
23850bf276cStholo       {
23950bf276cStholo 	this->has_jmpbuf = 0;
24050bf276cStholo 	return 0;
24150bf276cStholo       }
24250bf276cStholo     else
24350bf276cStholo       this = this->next;
24450bf276cStholo   return 1;
24550bf276cStholo }
24650bf276cStholo 
24750bf276cStholo /* Exit handler control block for the current process. */
24850bf276cStholo static struct EXHCB pexhcb = { 0, pwait, 1, &pexhcb.exh$l_status, 0 };
24950bf276cStholo 
25050bf276cStholo struct Vstring
25150bf276cStholo {
25250bf276cStholo   short length;
25350bf276cStholo   char body[NAM$C_MAXRSS+1];
25450bf276cStholo };
25550bf276cStholo 
25650bf276cStholo /*
25750bf276cStholo  * Emulate a unix popen() call using lib$spawn
25850bf276cStholo  *
25950bf276cStholo  * if mode == "w", lib$spawn uses the mailbox for sys$input
26050bf276cStholo  * if mode == "r", lib$spawn uses the mailbox for sys$output
26150bf276cStholo  *
26250bf276cStholo  * Don't now how to handle both read and write
26350bf276cStholo  *
26450bf276cStholo  * Returns
26550bf276cStholo  *   FILE *  file pointer to the pipe
26650bf276cStholo  *   NULL    indicates an error ocurred, check errno value
26750bf276cStholo  */
26850bf276cStholo FILE *
popen(cmd,mode)26950bf276cStholo popen (cmd, mode)
27050bf276cStholo   const char *cmd;
27150bf276cStholo   const char *mode;
27250bf276cStholo {
27350bf276cStholo   int i, status, flags, mbxsize;
27450bf276cStholo   struct IOSB iosb;
27550bf276cStholo   struct dsc$descriptor_s cmddsc, mbxdsc;
27650bf276cStholo   struct Vstring mbxname = { sizeof(mbxname.body) };
27750bf276cStholo   struct itm$list3 mbxlist[2] = {
27850bf276cStholo     { sizeof(mbxname.body)-1, DVI$_DEVNAM, &mbxname.body, &mbxname.length },
27950bf276cStholo     { 0, 0, 0, 0} };
28050bf276cStholo   struct itm$list3 syilist[2] = {
28150bf276cStholo     { sizeof(mbxsize), SYI$_MAXBUF, &mbxsize, (void *) 0 },
28250bf276cStholo     { 0, 0, 0, 0} };
28350bf276cStholo   static int noExitHandler = 1;
28450bf276cStholo   struct PIPE *this;
28550bf276cStholo 
28650bf276cStholo   /* First allocate space for the new pipe */
28750bf276cStholo   this = (struct PIPE *) calloc (1, sizeof(struct PIPE));
28850bf276cStholo   if (!this)
28950bf276cStholo     {
29050bf276cStholo       errno = ENOMEM;
29150bf276cStholo       return NULL;
29250bf276cStholo     }
29350bf276cStholo 
29450bf276cStholo   /* Sanity check value */
29550bf276cStholo   this->self = this;
29650bf276cStholo 
29750bf276cStholo   /* Use the smaller of SYI$_MAXBUF and 2048 for the mailbox size */
29850bf276cStholo   status = sys$getsyiw(0, 0, 0, syilist, &iosb, 0, 0, 0);
29950bf276cStholo   if (status != SS$_NORMAL && !(iosb.status & STS$M_SUCCESS))
30050bf276cStholo     {
30150bf276cStholo       vaxc$errno = iosb.status;
30250bf276cStholo       errno = EVMSERR;
30350bf276cStholo       free (this);
30450bf276cStholo       perror ("popen, $GETSYIW failure for SYI$_MAXBUF");
30550bf276cStholo       return NULL;
30650bf276cStholo     }
30750bf276cStholo 
30850bf276cStholo   if (mbxsize > 2048)
30950bf276cStholo     mbxsize = 2048;
31050bf276cStholo 
31150bf276cStholo   status = sys$crembx (0, &this->chan, mbxsize, mbxsize, 0, 0, 0, 0);
31250bf276cStholo   if (status != SS$_NORMAL)
31350bf276cStholo     {
31450bf276cStholo       vaxc$errno = status;
31550bf276cStholo       errno = EVMSERR;
31650bf276cStholo       free (this);
31750bf276cStholo       perror ("popen, $CREMBX failure");
31850bf276cStholo       return NULL;
31950bf276cStholo     }
32050bf276cStholo 
32150bf276cStholo   /* Retrieve mailbox name, use for fopen */
32250bf276cStholo   status = sys$getdviw (0, this->chan, 0, &mbxlist, &iosb, 0, 0, 0);
32350bf276cStholo   if (status != SS$_NORMAL && !(iosb.status & STS$M_SUCCESS))
32450bf276cStholo     {
32550bf276cStholo       vaxc$errno = iosb.status;
32650bf276cStholo       errno = EVMSERR;
32750bf276cStholo       sys$dassgn (this->chan);
32850bf276cStholo       free (this);
32950bf276cStholo       perror ("popen, $GETDVIW failure");
33050bf276cStholo       return NULL;
33150bf276cStholo     }
33250bf276cStholo 
33350bf276cStholo   /* Spawn the command using the mailbox as the name for sys$input */
33450bf276cStholo   mbxname.body[mbxname.length] = 0;
33550bf276cStholo   mbxdsc.dsc$w_length  = mbxname.length;
33650bf276cStholo   mbxdsc.dsc$b_dtype   = DSC$K_DTYPE_T;
33750bf276cStholo   mbxdsc.dsc$b_class   = DSC$K_CLASS_S;
33850bf276cStholo   mbxdsc.dsc$a_pointer = mbxname.body;
33950bf276cStholo 
34050bf276cStholo   cmddsc.dsc$w_length  = strlen(cmd);
34150bf276cStholo   cmddsc.dsc$b_dtype   = DSC$K_DTYPE_T;
34250bf276cStholo   cmddsc.dsc$b_class   = DSC$K_CLASS_S;
34350bf276cStholo   cmddsc.dsc$a_pointer = (char *)cmd;
34450bf276cStholo   flags = CLI$M_NOWAIT;
34550bf276cStholo   if (strcmp(mode,"w") == 0)
34650bf276cStholo     {
34750bf276cStholo       status = lib$spawn (&cmddsc, &mbxdsc, 0, &flags, 0, &this->pid,
34850bf276cStholo                           &this->status, &evf, &pdone, this->self);
34950bf276cStholo       this->mode = O_WRONLY;
35050bf276cStholo     }
35150bf276cStholo   else
35250bf276cStholo     {
35350bf276cStholo       status = lib$spawn (&cmddsc, 0, &mbxdsc, &flags, 0, &this->pid,
35450bf276cStholo                           &this->status, &evf, &pdone, this->self);
35550bf276cStholo       this->mode = O_RDONLY;
35650bf276cStholo     }
35750bf276cStholo   if (status != SS$_NORMAL)
35850bf276cStholo     {
35950bf276cStholo       vaxc$errno = status;
36050bf276cStholo       errno = EVMSERR;
36150bf276cStholo       sys$dassgn (this->chan);
36250bf276cStholo       free (this);
36350bf276cStholo       perror("popen, LIB$SPAWN failure");
36450bf276cStholo       return NULL;
36550bf276cStholo     }
36650bf276cStholo 
36750bf276cStholo   /* Set up an exit handler so the subprocess isn't prematurely killed */
36850bf276cStholo   if (noExitHandler)
36950bf276cStholo     {
37050bf276cStholo       status = sys$dclexh (&pexhcb);
37150bf276cStholo       if (status != SS$_NORMAL)
37250bf276cStholo         {
37350bf276cStholo           vaxc$errno = status;
37450bf276cStholo           errno = EVMSERR;
37550bf276cStholo           sys$dassgn (this->chan);
37650bf276cStholo           sys$delprc (&this->pid, 0);
37750bf276cStholo           free (this);
37850bf276cStholo           perror("popen, $DCLEXH failure");
37950bf276cStholo           return NULL;
38050bf276cStholo         }
38150bf276cStholo       noExitHandler = 0;
38250bf276cStholo     }
38350bf276cStholo 
38450bf276cStholo   /* Pipes are always binary mode devices */
38550bf276cStholo   if (this->mode == O_WRONLY)
38650bf276cStholo     this->file = fopen (mbxname.body, "wb");
38750bf276cStholo   else
38850bf276cStholo     this->file = fopen (mbxname.body, "rb");
38950bf276cStholo 
39050bf276cStholo   /* Paranoia, check for failure again */
39150bf276cStholo   if (!this->file)
39250bf276cStholo     {
39350bf276cStholo       sys$dassgn (this->chan);
39450bf276cStholo       sys$delprc (this->pid);
39550bf276cStholo       free (this);
39650bf276cStholo       perror ("popen, fopen failure");
39750bf276cStholo       return NULL;
39850bf276cStholo     }
39950bf276cStholo 
40050bf276cStholo   this->has_jmpbuf = 0;
40150bf276cStholo 
40250bf276cStholo   /* Insert the new pipe into the list of open pipes */
40350bf276cStholo   if (phead)
40450bf276cStholo     {
40550bf276cStholo       ptail->next = this;
40650bf276cStholo       this->prev = ptail;
40750bf276cStholo       ptail = this;
40850bf276cStholo     }
40950bf276cStholo   else
41050bf276cStholo     phead = ptail = this;
41150bf276cStholo 
41250bf276cStholo   return (this->file);
41350bf276cStholo }
41450bf276cStholo 
41550bf276cStholo 
41650bf276cStholo #ifdef TEST_PIPE
41750bf276cStholo int
main(argc,argv)41850bf276cStholo main (argc, argv)
41950bf276cStholo   int argc;
42050bf276cStholo   char **argv;
42150bf276cStholo {
42250bf276cStholo   FILE *stdpipe;
42350bf276cStholo   char line[512];
42450bf276cStholo 
42550bf276cStholo   while (1)
42650bf276cStholo     {
42750bf276cStholo       printf ("\nEnter a command to run >> ");
42850bf276cStholo       fgets (line, 511, stdin);
42950bf276cStholo       if (!strlen(line))
43050bf276cStholo         exit (1);
43150bf276cStholo       line[strlen(line)-1] = 0;
43250bf276cStholo       stdpipe = popen (line, "r");
43350bf276cStholo       if (!stdpipe)
43450bf276cStholo         {
43550bf276cStholo           fprintf (stderr, "popen failed.\n");
43650bf276cStholo           exit(44);
43750bf276cStholo         }
43850bf276cStholo       do {
43950bf276cStholo           fgets (line, 511, stdpipe);
44050bf276cStholo           fputs (line, stdout);
44150bf276cStholo         } while (!feof(stdpipe));
44250bf276cStholo       pclose (stdpipe);
44350bf276cStholo     }
44450bf276cStholo }
44550bf276cStholo #endif
446*43c1707eStholo 
447*43c1707eStholo #else  /*  __VMS_VER >= 70200000 && __DECC_VER >= 50700000  */
448*43c1707eStholo #pragma message disable EMPTYFILE
449*43c1707eStholo #endif  /*  __VMS_VER >= 70200000 && __DECC_VER >= 50700000  */
450