#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)); } } }