Update codebase to remove clang warnings (and a couple of legit errors
[typhoon.git] / src / autoplay / child_process.cc
1 #include "child_process.h"
2
3 #include <vector>
4 #include <cstdio>
5 #include <cstdlib>
6 #include <unistd.h>
7 #include <signal.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10
11 namespace util {
12
13 void ChildProcess::ParseCommandline(vector<char *> *args) {
14   char *p = commandline_;
15   bool in_quote;
16   do {
17     // Find the start of a chunk.
18     while(*p && isspace(*p)) p++;
19     if (!*p) break;
20     args->push_back(p);
21     in_quote = false;
22
23     // Find the end of the chunk.
24     while (*p) {
25       if (*p == '"') {
26         in_quote = !in_quote;
27       } else if (isspace(*p) && !in_quote) {
28         break;
29       }
30       p++;
31     }
32     if (!*p) break;
33     *p = '\0';
34     p++;
35   } while(1);
36   args->push_back(NULL);
37 }
38
39 static volatile bool _child_ready = false;
40 static void SignalHandler(int signo) {
41   printf("SIGNAL!\n");
42   _child_ready = true;
43 }
44
45 void ChildProcess::Start() {
46   Stop();
47   vector<char *> args;
48   ParseCommandline(&args);
49
50   if (args.size() > 1) {
51     if (pipe(read_pipe_) != 0) {
52       perror("pipe");
53       exit(-1);
54     }
55     if (pipe(write_pipe_) != 0) {
56       perror("pipe");
57       exit(-1);
58     }
59
60     // Prepare a signal handler to tell us when the child process is
61     // ready.
62     _child_ready = false;
63     if (signal(SIGUSR1, SignalHandler) == SIG_ERR) {
64       perror("signal");
65       exit(-1);
66     }
67     sigset_t zeromask, mask, orig;
68     sigemptyset(&zeromask);
69     sigemptyset(&mask);
70     sigaddset(&mask, SIGUSR1);
71     if (sigprocmask(SIG_BLOCK, &mask, &orig) < 0) {
72       perror("sigprocmask");
73       exit(-1);
74     }
75
76     // Fork.
77     pid_ = fork();
78     if (pid_ < 0) {
79       perror("fork");
80       exit(-1);
81     }
82     if (pid_ == 0) {
83       printf("In the child process...\n");
84
85       // In the child process we do not need the pipes for parent
86       // reads and writes.
87       close(write_pipe_[1]);
88       close(read_pipe_[0]);
89       if (dup2(write_pipe_[0], fileno(stdin)) < 0) {
90         perror("dup2");
91         exit(-1);
92       }
93       close(write_pipe_[0]);
94       if (dup2(read_pipe_[1], fileno(stdout)) < 0) {
95         perror("dup2");
96         exit(-1);
97       }
98       if (dup2(fileno(stdout), fileno(stderr)) < 0) {
99         perror("dup2");
100         exit(-1);
101       }
102       close(read_pipe_[1]);
103
104       // Tell the parent that the pipes are set up then exec the child
105       // image.
106       kill(getppid(), SIGUSR1);
107       execvp(args[0], &(args[0]));
108       perror("exec");
109       exit(-1);
110     } else {
111       printf("In the parent process waiting...\n");
112       while(!_child_ready) {
113         sigsuspend(&zeromask);
114       }
115       printf("PARENT WAKEUP!\n");
116       _child_ready = false;
117       if (sigprocmask(SIG_SETMASK, &orig, NULL) < 0) {
118         perror("sigprocmask");
119         exit(-1);
120       }
121       close(write_pipe_[0]);
122       close(read_pipe_[1]);
123       from_ = fdopen(read_pipe_[0], "r");
124       to_ = fdopen(write_pipe_[1], "w");
125       setbuf(from_, NULL);
126       setbuf(to_, NULL);
127       running_ = true;
128       name_ = args[0];
129     }
130   }
131 }
132
133 void ChildProcess::Stop() {
134   if (!running_) {
135     return;
136   }
137   fclose(to_);
138   fclose(from_);
139   close(write_pipe_[1]);
140   close(read_pipe_[0]);
141   kill(pid_, SIGTERM);
142   int x;
143   waitpid(pid_, &x, 0);
144 }
145
146 void ChildProcess::Send(char *line) {
147   if (!running_) {
148     return;
149   }
150   fprintf(to_, "%s", line);
151   printf("%s sent: %s", name_, line);
152 }
153
154 void ChildProcess::Receive(char *line, int size) {
155   if (!size) return;
156   *line = '\0';
157   if (!running_) {
158     return;
159   }
160   if (!Poll()) {
161     return;
162   }
163   fgets(line, size, from_);
164   printf("%s recv: %s\n", name_, line);
165 }
166
167 bool ChildProcess::Poll() {
168   fd_set myset;
169   FD_ZERO(&myset);
170   FD_SET(read_pipe_[0], &myset);
171   timeval mytime;
172   mytime.tv_sec = 0;
173   mytime.tv_usec = 0;
174   return select(read_pipe_[0] + 1, &myset, NULL, NULL, &mytime) > 0;
175 }
176
177 void ChildProcess::Flush() {
178   static char line[1000];
179   while (Poll()) {
180     Receive(line, 1000);
181     printf("flush: %s", line);
182     if (!line[0]) {
183       break;
184     }
185   }
186 }
187
188 }  // namespace