1#include <sys/time.h>
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <sys/wait.h>
5#include <fcntl.h>
6#include <unistd.h>
7#include <errno.h>
8#include <signal.h>
9
10#include <stdlib.h>
11#include <stdio.h>
12
13pid_t childpid;
14
15void kill_child_exit(int ignored)
16{
17  kill(childpid, SIGKILL);
18  exit(1);
19}
20
21
22int main(int argc, char *argv[])
23{
24  char *logfile;
25  int timeout = argc > 3 ? atoi(argv[3]) : 5;
26  int pipe_fds[2];
27  if (argc < 3) {
28    fprintf(stderr, "Usage: %s dp-program logfile [timeout]\n", argv[0]);
29    exit(1);
30  }
31  logfile = argv[2];
32  if (timeout <= 0) {
33    fprintf(stderr, "Timeout must be positive non-zero\n");
34    exit(1);
35  }
36
37  if (pipe(pipe_fds) != 0) {
38    perror("Creating pipe in main");
39    exit(1);
40  }
41  childpid = fork();
42  if (childpid < 0) {
43    perror("forking in main");
44    exit(1);
45  }
46  if (childpid == 0) {
47    /* in child */
48    /* want to write stdout to pipe, stderr to log file */
49    /* stdin must be the file test_coopers.sml */
50    int logfile_fd;
51    char *child_args[2] = {argv[1], 0};
52    if (dup2(pipe_fds[1], STDOUT_FILENO) == -1) {
53      perror("dup2 for stdout in child");
54      exit(1);
55    }
56    close(STDIN_FILENO);
57    logfile_fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
58    if (logfile_fd == -1) {
59      perror("child opening logfile");
60      exit(1);
61    }
62    if (dup2(logfile_fd, STDERR_FILENO) == -1) {
63      perror("dup2 for stderr in child");
64      exit(1);
65    }
66    /* can now exec hol */
67    execv(child_args[0], child_args);
68    perror("exec in child");
69    exit(1);
70  } else {
71    /* in parent */
72    fd_set to_watch;
73    struct timeval tv;
74    char buffer[80];
75    int numread, read_anything = 0;
76    signal(SIGINT, kill_child_exit);
77    FD_ZERO(&to_watch);
78    FD_SET(pipe_fds[0], &to_watch);
79    select(pipe_fds[0] + 1, &to_watch, 0, 0, &tv);
80    while (1) {
81      int retval;
82      FD_SET(pipe_fds[0], &to_watch);
83      /* need to do this potentially redundant assignment to tv because
84         select leaves the value of tv undefined */
85      tv.tv_sec = timeout;
86      tv.tv_usec = 0;
87      retval = select(pipe_fds[0] + 1, &to_watch, 0, 0, &tv);
88      if (retval == -1) {
89        perror("select in parent");
90        exit(1);
91      }
92      if (retval == 0) {
93        /* nothing new to read - maybe the child has exited,
94           otherwise interrupt the child */
95        int status, wait_retval;
96        wait_retval = waitpid(childpid, &status, WNOHANG);
97        if (wait_retval == 0) {
98          if (read_anything) {
99            /* child still fine, interrupt it to wake it up */
100            if (kill(childpid, SIGINT) == -1) {
101              perror("kill in parent");
102              exit(1);
103            }
104          }
105          continue;
106        } else if (wait_retval < 0) {
107          perror("parent waiting");
108          exit(1);
109        }
110        if (WIFEXITED(status)) {
111          fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(status));
112          exit(1);
113        } else /* (WIFSIGNALLED(status)) is true */ {
114          fprintf(stderr, "Child was stopped by signal %d\n",
115                  WTERMSIG(status));
116          exit(1);
117        }
118      }
119      /* retval was one */
120      numread = read(pipe_fds[0], buffer, 80);
121      if (numread < 0) {
122        if (errno == EINTR) continue;
123        perror("read in parent");
124        exit(1);
125      }
126      if (numread == 0) { exit(0); }
127      read_anything = 1;
128      write(STDOUT_FILENO, buffer, numread);
129    }
130  }
131}
132
133
134
135
136
137