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