update from main archive 961217
[glibc.git] / sysdeps / posix / pipestream.c
blobd954f1412c29f5226c3546edb85b5f7756a48c30
1 /* Copyright (C) 1991, 1992, 1993, 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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. */
19 #include <errno.h>
20 #include <stddef.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 #include <fcntl.h>
29 #define SH_PATH "/bin/sh" /* Shell to run. */
30 #define SH_NAME "sh" /* Name to give it. */
32 /* Structure describing a popen child. */
33 struct child
35 pid_t pid; /* PID of the child. */
36 __ptr_t cookie; /* Original cookie from fdopen. */
37 __io_functions funcs; /* Original functions from fdopen. */
40 /* io_functions for pipe streams.
41 These all simply call the corresponding
42 original function with the original cookie. */
44 #define FUNC(type, name, args) \
45 static type __CONCAT(child_,name) args __CONCAT(name,decl) \
46 { \
47 struct child *c = (struct child *) cookie; \
48 { \
49 __ptr_t cookie = c->cookie; \
50 return (*c->funcs.__CONCAT(__,name)) args; \
51 } \
54 #define readdecl void *cookie; register char *buf; register size_t n;
55 FUNC (int, read, (cookie, buf, n))
56 #define writedecl void *cookie; register const char *buf; register size_t n;
57 FUNC (int, write, (cookie, buf, n))
58 #define seekdecl void *cookie; fpos_t *pos; int whence;
59 FUNC (int, seek, (cookie, pos, whence))
60 #define closedecl void *cookie;
61 FUNC (int, close, (cookie))
62 #define filenodecl void *cookie;
63 FUNC (int, fileno, (cookie))
65 static const __io_functions child_funcs
66 = { child_read, child_write, child_seek, child_close, child_fileno };
68 /* Open a new stream that is a one-way pipe to a
69 child process running the given shell command. */
70 FILE *
71 popen (command, mode)
72 const char *command;
73 const char *mode;
75 pid_t pid;
76 int pipedes[2];
77 FILE *stream;
78 struct child *child;
80 if (command == NULL || mode == NULL || (*mode != 'r' && *mode != 'w'))
82 __set_errno (EINVAL);
83 return NULL;
86 /* Create the pipe. */
87 if (pipe (pipedes) < 0)
88 return NULL;
90 /* Fork off the child. */
91 pid = __vfork ();
92 if (pid == (pid_t) -1)
94 /* The fork failed. */
95 (void) close (pipedes[0]);
96 (void) close (pipedes[1]);
97 return NULL;
99 else if (pid == (pid_t) 0)
101 /* We are the child side. Make the write side of
102 the pipe be stdin or the read side be stdout. */
104 const char *new_argv[4];
106 if ((*mode == 'w' ? dup2(pipedes[STDIN_FILENO], STDIN_FILENO) :
107 dup2 (pipedes[STDOUT_FILENO], STDOUT_FILENO)) < 0)
108 _exit (127);
110 /* Close the pipe descriptors. */
111 (void) close (pipedes[STDIN_FILENO]);
112 (void) close (pipedes[STDOUT_FILENO]);
114 /* Exec the shell. */
115 new_argv[0] = SH_NAME;
116 new_argv[1] = "-c";
117 new_argv[2] = command;
118 new_argv[3] = NULL;
119 (void) execve (SH_PATH, (char *const *) new_argv, environ);
120 /* Die if it failed. */
121 _exit (127);
124 /* We are the parent side. */
126 /* Close the irrelevant side of the pipe and open the relevant side as a
127 new stream. Mark our side of the pipe to close on exec, so new children
128 won't see it. */
129 if (*mode == 'r')
131 (void) close (pipedes[STDOUT_FILENO]);
132 (void) fcntl (pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
133 stream = fdopen (pipedes[STDIN_FILENO], mode);
135 else
137 (void) close (pipedes[STDIN_FILENO]);
138 (void) fcntl (pipedes[STDOUT_FILENO], F_SETFD, FD_CLOEXEC);
139 stream = fdopen (pipedes[STDOUT_FILENO], mode);
142 if (stream == NULL)
143 goto error;
145 child = (struct child *) malloc (sizeof (struct child));
146 if (child == NULL)
147 goto error;
150 /* Make sure STREAM has its functions set before
151 we try to squirrel them away in CHILD. */
152 extern void __stdio_check_funcs __P ((FILE *));
153 __stdio_check_funcs (stream);
156 child->pid = pid;
157 child->cookie = stream->__cookie;
158 child->funcs = stream->__io_funcs;
159 stream->__cookie = (void *) child;
160 stream->__io_funcs = child_funcs;
161 stream->__ispipe = 1;
162 return stream;
164 error:
166 /* The stream couldn't be opened or the child structure couldn't be
167 allocated. Kill the child and close the other side of the pipe. */
168 int save = errno;
169 (void) kill (pid, SIGKILL);
170 if (stream == NULL)
171 (void) close (pipedes[*mode == 'r' ? STDOUT_FILENO : STDIN_FILENO]);
172 else
173 (void) fclose (stream);
174 #ifndef NO_WAITPID
175 (void) waitpid (pid, (int *) NULL, 0);
176 #else
178 pid_t dead;
180 dead = wait ((int *) NULL);
181 while (dead > 0 && dead != pid);
183 #endif
184 __set_errno (save);
185 return NULL;
189 /* Close a stream opened by popen and return its status.
190 Returns -1 if the stream was not opened by popen. */
192 pclose (stream)
193 register FILE *stream;
195 struct child *c;
196 pid_t pid, dead;
197 int status;
199 if (!__validfp (stream) || !stream->__ispipe)
201 __set_errno (EINVAL);
202 return -1;
205 c = (struct child *) stream->__cookie;
206 pid = c->pid;
207 stream->__cookie = c->cookie;
208 stream->__io_funcs = c->funcs;
209 free ((void *) c);
210 stream->__ispipe = 0;
211 if (fclose (stream))
212 return -1;
214 #ifndef NO_WAITPID
215 dead = waitpid (pid, &status, 0);
216 #else
218 dead = wait (&status);
219 while (dead > 0 && dead != pid);
220 #endif
221 if (dead != pid)
222 status = -1;
224 return status;