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