aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/posix/pipestream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/posix/pipestream.c')
-rw-r--r--sysdeps/posix/pipestream.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/sysdeps/posix/pipestream.c b/sysdeps/posix/pipestream.c
new file mode 100644
index 0000000..53595f5
--- /dev/null
+++ b/sysdeps/posix/pipestream.c
@@ -0,0 +1,223 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <ansidecl.h>
+#include <errno.h>
+#include <stddef.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define SH_PATH "/bin/sh" /* Shell to run. */
+#define SH_NAME "sh" /* Name to give it. */
+
+/* Structure describing a popen child. */
+struct child
+ {
+ pid_t pid; /* PID of the child. */
+ __ptr_t cookie; /* Original cookie from fdopen. */
+ __io_functions funcs; /* Original functions from fdopen. */
+ };
+
+/* io_functions for pipe streams.
+ These all simply call the corresponding
+ original function with the original cookie. */
+
+#define FUNC(type, name, args) \
+ static type DEFUN(__CONCAT(child_,name), args, __CONCAT(name,decl)) \
+ { \
+ struct child *c = (struct child *) cookie; \
+ { \
+ __ptr_t cookie = c->cookie; \
+ return (*c->funcs.__CONCAT(__,name)) args; \
+ } \
+ }
+
+#define readdecl PTR cookie AND register char *buf AND register size_t n
+FUNC (int, read, (cookie, buf, n))
+#define writedecl PTR cookie AND register CONST char *buf AND register size_t n
+FUNC (int, write, (cookie, buf, n))
+#define seekdecl PTR cookie AND fpos_t *pos AND int whence
+FUNC (int, seek, (cookie, pos, whence))
+#define closedecl PTR cookie
+FUNC (int, close, (cookie))
+#define filenodecl PTR cookie
+FUNC (int, fileno, (cookie))
+
+static const __io_functions child_funcs
+ = { child_read, child_write, child_seek, child_close, child_fileno };
+
+/* Open a new stream that is a one-way pipe to a
+ child process running the given shell command. */
+FILE *
+DEFUN(popen, (command, mode), CONST char *command AND CONST char *mode)
+{
+ pid_t pid;
+ int pipedes[2];
+ FILE *stream;
+ struct child *child;
+
+ if (command == NULL || mode == NULL || (*mode != 'r' && *mode != 'w'))
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Create the pipe. */
+ if (pipe(pipedes) < 0)
+ return NULL;
+
+ /* Fork off the child. */
+ pid = __vfork ();
+ if (pid == (pid_t) -1)
+ {
+ /* The fork failed. */
+ (void) close (pipedes[0]);
+ (void) close (pipedes[1]);
+ return NULL;
+ }
+ else if (pid == (pid_t) 0)
+ {
+ /* We are the child side. Make the write side of
+ the pipe be stdin or the read side be stdout. */
+
+ CONST char *new_argv[4];
+
+ if ((*mode == 'w' ? dup2(pipedes[STDIN_FILENO], STDIN_FILENO) :
+ dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO)) < 0)
+ _exit(127);
+
+ /* Close the pipe descriptors. */
+ (void) close(pipedes[STDIN_FILENO]);
+ (void) close(pipedes[STDOUT_FILENO]);
+
+ /* Exec the shell. */
+ new_argv[0] = SH_NAME;
+ new_argv[1] = "-c";
+ new_argv[2] = command;
+ new_argv[3] = NULL;
+ (void) execve(SH_PATH, (char *CONST *) new_argv, environ);
+ /* Die if it failed. */
+ _exit(127);
+ }
+
+ /* We are the parent side. */
+
+ /* Close the irrelevant side of the pipe and open the relevant side as a
+ new stream. Mark our side of the pipe to close on exec, so new children
+ won't see it. */
+ if (*mode == 'r')
+ {
+ (void) close (pipedes[STDOUT_FILENO]);
+ (void) fcntl (pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
+ stream = fdopen (pipedes[STDIN_FILENO], mode);
+ }
+ else
+ {
+ (void) close (pipedes[STDIN_FILENO]);
+ (void) fcntl (pipedes[STDOUT_FILENO], F_SETFD, FD_CLOEXEC);
+ stream = fdopen (pipedes[STDOUT_FILENO], mode);
+ }
+
+ if (stream == NULL)
+ goto error;
+
+ child = (struct child *) malloc (sizeof (struct child));
+ if (child == NULL)
+ goto error;
+
+ {
+ /* Make sure STREAM has its functions set before
+ we try to squirrel them away in CHILD. */
+ extern void __stdio_check_funcs __P ((FILE *));
+ __stdio_check_funcs (stream);
+ }
+
+ child->pid = pid;
+ child->cookie = stream->__cookie;
+ child->funcs = stream->__io_funcs;
+ stream->__cookie = (PTR) child;
+ stream->__io_funcs = child_funcs;
+ stream->__ispipe = 1;
+ return stream;
+
+ error:
+ {
+ /* The stream couldn't be opened or the child structure couldn't be
+ allocated. Kill the child and close the other side of the pipe. */
+ int save = errno;
+ (void) kill (pid, SIGKILL);
+ if (stream == NULL)
+ (void) close (pipedes[*mode == 'r' ? STDOUT_FILENO : STDIN_FILENO]);
+ else
+ (void) fclose (stream);
+#ifndef NO_WAITPID
+ (void) waitpid (pid, (int *) NULL, 0);
+#else
+ {
+ pid_t dead;
+ do
+ dead = wait ((int *) NULL);
+ while (dead > 0 && dead != pid);
+ }
+#endif
+ errno = save;
+ return NULL;
+ }
+}
+
+/* Close a stream opened by popen and return its status.
+ Returns -1 if the stream was not opened by popen. */
+int
+DEFUN(pclose, (stream), register FILE *stream)
+{
+ struct child *c;
+ pid_t pid, dead;
+ int status;
+
+ if (!__validfp(stream) || !stream->__ispipe)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ c = (struct child *) stream->__cookie;
+ pid = c->pid;
+ stream->__cookie = c->cookie;
+ stream->__io_funcs = c->funcs;
+ free ((PTR) c);
+ stream->__ispipe = 0;
+ if (fclose (stream))
+ return -1;
+
+#ifndef NO_WAITPID
+ dead = waitpid (pid, &status, 0);
+#else
+ do
+ dead = wait (&status);
+ while (dead > 0 && dead != pid);
+#endif
+ if (dead != pid)
+ status = -1;
+
+ return status;
+}