Formerly posix/pipestream.c.~7~
[kopensolaris-gnu/glibc.git] / sysdeps / posix / pipestream.c
1 /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ansidecl.h>
20 #include <errno.h>
21 #include <stddef.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29
30 #define SH_PATH "/bin/sh"       /* Shell to run.  */
31 #define SH_NAME "sh"            /* Name to give it.  */
32
33 /* Structure describing a popen child.  */
34 struct child
35   {
36     pid_t pid;                  /* PID of the child.  */
37     __ptr_t cookie;             /* Original cookie from fdopen.  */
38     __io_functions funcs;       /* Original functions from fdopen.  */
39   };
40
41 /* io_functions for pipe streams.
42    These all simply call the corresponding
43    original function with the original cookie.  */
44
45 #define FUNC(type, name, args, argdecl)                                       \
46   static type DEFUN(__CONCAT(child_,name), args, argdecl)                     \
47   {                                                                           \
48     struct child *c = (struct child *) cookie;                                \
49     __ptr_t cookie = c->cookie;                                               \
50     return (*c->cookie.funcs.__CONCAT(__,name)) args;                         \
51   }
52
53 FUNC (int, read, (cookie, buf, n),
54       PTR cookie AND register char *buf AND register size_t n)
55 FUNC (int, write, (cookie, buf, n),
56       PTR cookie AND register CONST char *buf AND register size_t n)
57 FUNC (int, seek, (cookie, pos, whence),
58       PTR cookie AND fpos_t *pos AND int whence)
59 FUNC (int, close, (cookie), PTR cookie)
60 FUNC (int, fileno, (cookie), PTR cookie)
61
62 static const __io_functions child_funcs
63   = { child_read, child_write, child_seek, child_close, child_fileno };
64 \f
65 /* Open a new stream that is a one-way pipe to a
66    child process running the given shell command.  */
67 FILE *
68 DEFUN(popen, (command, mode), CONST char *command AND CONST char *mode)
69 {
70   pid_t pid;
71   int pipedes[2];
72   FILE *stream;
73   struct child *child;
74
75   if (command == NULL || mode == NULL || (*mode != 'r' && *mode != 'w'))
76     {
77       errno = EINVAL;
78       return NULL;
79     }
80
81   /* Create the pipe.  */
82   if (pipe(pipedes) < 0)
83     return NULL;
84
85   /* Fork off the child.  */
86   pid = __vfork ();
87   if (pid == (pid_t) -1)
88     {
89       /* The fork failed.  */
90       (void) close (pipedes[0]);
91       (void) close (pipedes[1]);
92       return NULL;
93     }
94   else if (pid == (pid_t) 0)
95     {
96       /* We are the child side.  Make the write side of
97          the pipe be stdin or the read side be stdout.  */
98
99       CONST char *new_argv[4];
100
101       if ((*mode == 'w' ? dup2(pipedes[STDIN_FILENO], STDIN_FILENO) :
102           dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO)) < 0)
103         _exit(127);
104
105       /* Close the pipe descriptors.  */
106       (void) close(pipedes[STDIN_FILENO]);
107       (void) close(pipedes[STDOUT_FILENO]);
108
109       /* Exec the shell.  */
110       new_argv[0] = SH_NAME;
111       new_argv[1] = "-c";
112       new_argv[2] = command;
113       new_argv[3] = NULL;
114       (void) execve(SH_PATH, (char *CONST *) new_argv, environ);
115       /* Die if it failed.  */
116       _exit(127);
117     }
118
119   /* We are the parent side.  */
120
121   /* Close the irrelevant side of the pipe and open the relevant side as a
122      new stream.  Mark our side of the pipe to close on exec, so new children
123      won't see it.  */
124   if (*mode == 'r')
125     {
126       (void) close(pipedes[STDOUT_FILENO]);
127       (void) fcntl (pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
128       stream = fdopen(pipedes[STDIN_FILENO], mode);
129     }
130   else
131     {
132       (void) close(pipedes[STDIN_FILENO]);
133       (void) fcntl (pipedes[STDOUT_FILENO], F_SETFD, FD_CLOEXEC);
134       stream = fdopen(pipedes[STDOUT_FILENO], mode);
135     }
136
137   if (stream == NULL)
138     goto error;
139
140   child = (struct child *) malloc(sizeof(struct child));
141   if (child == NULL)
142     goto error;
143   child->pid = pid;
144   child->cookie = stream->__cookie;
145   child->funcs = stream->__io_funcs;
146   stream->__cookie = (PTR) child;
147   stream->__io_funcs = child_funcs;
148   stream->__ispipe = 1;
149   return stream;
150
151  error:;
152   {
153     /* The stream couldn't be opened or the child structure couldn't be
154        allocated.  Kill the child and close the other side of the pipe.  */
155     int save = errno;
156     (void) kill(pid, SIGKILL);
157     if (stream == NULL)
158       (void) close(pipedes[*mode == 'r' ? STDOUT_FILENO : STDIN_FILENO]);
159     else
160       (void) fclose(stream);
161 #ifndef NO_WAITPID
162     (void) waitpid(pid, (int *) NULL, 0);
163 #else
164     {
165       pid_t dead;
166       do
167         dead = wait ((int *) NULL);
168       while (dead > 0 && dead != pid);
169     }
170 #endif
171     errno = save;
172     return NULL;
173   }
174 }
175 \f
176 /* Close a stream opened by popen and return its status.
177    Returns -1 if the stream was not opened by popen.  */
178 int
179 DEFUN(pclose, (stream), register FILE *stream)
180 {
181   struct child *c;
182   pid_t pid, dead;
183   int status;
184
185   if (!__validfp(stream) || !stream->__ispipe)
186     {
187       errno = EINVAL;
188       return -1;
189     }
190
191   c = (struct child *) stream->__cookie;
192   pid = c->pid;
193   stream->__cookie = c->cookie;
194   stream->__io_funcs = c->funcs;
195   free(stream->__cookie);
196   stream->__ispipe = 0;
197   if (fclose(stream))
198     return -1;
199
200 #ifndef NO_WAITPID
201   dead = waitpid (pid, &status, 0);
202 #else
203   do
204     dead = wait (&status);
205   while (dead > 0 && dead != pid);
206 #endif
207   if (dead != pid)
208     status = -1;
209
210   return status;
211 }