popen

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  #include <errno.h>
  #include <signal.h>

  /*
   * A problem existed whereby a process would run several scripts with popen, and if the parent
   * had to shut down, it was unable to terminate it's child processes for a more graceful exit,
   * thus leaving child processes running.
   *
   * Since popen doesn't provide the pid of a process it runs, there was no way to gracefully
   * exit the parent without leaving running children.
   *
   * The solution was to write a replacement for popen that would run a process, and store its
   * pid in an element of a global array passed to it from the parent.
   * With the pid now available, if the parent needs to shut down, it can terminate all running
   * children first to facilitate a more graceful exit.
   */

  int pid[3] = {0};
  FILE *fptr;
 
  FILE *myPopen (char *cmd, int len, int *pid, char *mode);
  void killStrayProcs (int sig);

  int main (int argc, char** argv) {
    int i = 1,
        j = 0,
        n;
    char readBuffer[3],
         finalBuffer[5000],
         command [80];

    memset (readBuffer, 0, 3 * sizeof (char));
    memset (finalBuffer, 0, 5000 * sizeof (char));
    memset (command, 0, 80 * sizeof (char))

    if (argc > 1) {
      for (i = 1; i < argc; i++ ) {
        if (i == 1) {
          memcpy (command, argv [i], 80 * sizeof (char));
        } else {
          memcpy (&command [strlen (command)], " ", sizeof (char) + 1);
          memcpy (&command [strlen (command)], argv [i], 80 * sizeof (char));
        }
      }
    } else {
      memcpy (command, "date '+%m/%d/%Y %I:%M:%S %p'", 28);
    }

    /* Handle signals: SIGHUP, SIGINT, SIGQUIT, SIGTERM */
    signal (SIGHUP, killStrayProcs);  // 1
    signal (SIGINT, killStrayProcs);  // 2
    signal (SIGQUIT, killStrayProcs); // 3
    signal (SIGTERM, killStrayProcs); // 15

    if ((fptr = myPopen (command, strlen (command), &(pid[0]), "r")) != (FILE *) NULL) {
      while ((n = fread (readBuffer, 1, 1, fptr)) > 0) {
        if (n < 0) {
          fprintf (stderr, "Read failed, error: %d (%s)\n", errno, strerror (errno));
          break;
        } else if (n == 0) {
          fprintf (stdout, "EOF\n");
          break;
        }
        finalBuffer[j++] = readBuffer[0];
        if (readBuffer[0] == '\n') {
          // End of line, reset readBuffer
          memset (readBuffer, 0, sizeof (readBuffer));
        }
      }
    } else {
        fprintf (stderr, "myPopen failed, error: %d (%s)\n", errno, strerror (errno));
        exit (errno);
    }

    fprintf (stdout, "\n%s\n", finalBuffer);

    fclose (fptr);
    return (0);
  }

  // This version is always being used in read only mode.
  // The mode argument is included for future use and for compatibility with popen.
  // len is included for extra safety against buffer overflow
  // pid is the child pid, the whole reason for creating this function
  FILE *myPopen (char *cmd, int len, int *pid, char *mode) {
    int i = 0, j, ret, myPid;
    int fifo[2];
    char tmpCmd[256];
    char *arg0, *start, *end;
    char *args[256] = {NULL};
    char *startQuote = 0, *endQuote = 0;
    int errorCode = 0;

    memset (tmpCmd, 0, 256 * sizeof (char));
    memcpy (tmpCmd, cmd, len);
    start = arg0 = tmpCmd;

    if ((end = strtok (tmpCmd, " ")) != NULL) {
      args[i++] = end; // argv [0]
      end += strlen (end) + 1;

      if ((startQuote = (char *) strpbrk (end, "'\"")) != NULL) {
        args [i++] = startQuote + 1;
        if ((endQuote = (char *) strpbrk (startQuote + 1, "'\"")) != NULL) {
          *endQuote = 0;
        }
      } else {
        arg0 = cmd;
      }

      if (pipe (fifo) == -1) {
        fprintf (stderr, "Pipe failed, error: %d (%s)\n", errno, strerror (errno));
        exit (errno);
      }

      if ((*pid = fork ()) == -1) {
        fprintf (stderr, "Fork failed, error: %d (%s)\n", errno, strerror (errno));
        exit (errno);
      } else {
        if (*pid == 0) { // Child
          close (fileno (stdout));
          dup (fifo[1]); // pipe child's stdout to parent's stdin
          close (fileno (stderr));
          dup (fifo[1]); // pipe child's stderr to parent's stderr
          myPid = getpid ();
          setpgrp ();

          if (DEBUG) {
            fprintf (stderr, "\nIn child, my pid: %d, my grp: %d, cmd: %s\n\n", myPid, getpgrp (), cmd);
          }
          fflush (stdout);
          execvp (arg0, args);
          errorCode = errno;
          fprintf (stderr, "Exec failed, cmd: %s, error: %d (%s)\n", cmd, errorCode, strerror (errorCode));
          exit (errorCode);
        } else { // Parent
          close (fifo[1]);
          close (fileno (stdin));
          dup (fifo[0]); // get parent's input from childs output
          wait (&ret);
          fflush (stdout);
          fflush (stderr);
          return (fdopen (0, mode));
        }
      }
    }