about summary refs log tree commit diff
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 0000000000..53595f5b54
--- /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;
+}