2d09a510aa6e9c70ba0c0f0bf4922812de7cae63
[progcomp2012.git] / judge / manager / program.cpp
1 #include <sstream>
2
3 #include <stdarg.h>
4
5 #include <cassert>
6
7 #include "thread_util.h"
8 #include "program.h"
9 #include <vector>
10 #include <string.h>
11
12 using namespace std;
13
14
15 /**
16  * Constructor
17  * @param executablePath - path to the program that will be run
18  *
19  * Creates two pipes - one for each direction between the parent process and the AI program
20  * Forks the process. 
21  *      The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program
22  *      The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience.
23  */
24 Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0), paused(false)
25 {
26         
27                 
28         
29         vector<char*> args;
30         if (executablePath[0] != '"')
31                 args.push_back((char*)executablePath);
32         else
33                 args.push_back((char*)(executablePath)+1);
34         char * token = NULL;
35         do
36         {
37                 token = strstr(args[args.size()-1], " ");
38                 if (token == NULL)
39                         break;
40
41                 *token = '\0';
42                 do
43                 {
44                         ++token;
45                         if (*token == '"')
46                                 *token = '\0';
47                 }
48                 while (*token != '\0' && iswspace(*token));
49
50                 if (*token != '\0' && !iswspace(*token))
51                 {
52                         args.push_back(token);
53                 }
54                 else
55                         break;
56         }
57         while (token != NULL);
58
59         char **  arguments = new char*[args.size()+2];
60         for (unsigned int i=0; i < args.size(); ++i)
61                 arguments[i] = args[i];
62
63         //See if file exists and is executable...
64         if (access(executablePath, X_OK) != 0)
65         {
66                 pid = -1;
67                 return;
68         }
69         
70         int readPipe[2]; int writePipe[2];
71         assert(pipe(readPipe) == 0);
72         assert(pipe(writePipe) == 0);
73
74         pid = fork();
75         if (pid == 0)
76         {
77                 close(readPipe[0]);  //close end that parent reads from
78                 close(writePipe[1]); //close end that parent writes to
79
80                 //TODO: Fix possible bug here if the process is already a daemon
81                 assert(writePipe[0] != 0 && readPipe[1] != 1);
82                 dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN
83                 dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT
84
85                 //TODO: Somehow force the exec'd process to be unbuffered
86                 setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect
87                 setbuf(stdout, NULL); //You should add them at the start of the wrapped program.
88                                         //If your wrapped program is not written in C/C++, you will probably have a problem
89                                 
90
91                 if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file
92                         execv(executablePath,arguments); ///Replace process with desired executable
93                 
94                 fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
95                 exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens
96         }
97         else
98         {
99                 close(writePipe[0]); //close end that child writes to
100                 close(readPipe[1]); //close end that child reads from
101
102                 input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w");
103                 setbuf(input, NULL);
104                 setbuf(output, NULL);
105         }
106         
107 }
108
109 /**
110  * Destructor
111  * Writes EOF to the wrapped program and then closes all streams
112  * Kills the wrapped program if it does not exit within 1 second.
113  */
114 Program::~Program()
115 {
116         if (Running()) //Check if the process created is still running...
117         {
118                 //fputc(EOF, output); //If it was, tell it to stop with EOF
119
120                 TimerThread timer(2); //Wait for 2 seconds
121                 timer.Start();          
122                 while (!timer.Finished())
123                 {
124                         if (!Running())
125                         {
126                                 timer.Stop();
127                                 break;
128                         }
129                 }
130                 timer.Stop();
131                 kill(pid, SIGKILL);
132         }
133         if (pid > 0)
134         {
135                 fclose(input);
136                 fclose(output);
137         }
138         
139 }
140
141 /**
142  * Forces the program to pause by sending SIGSTOP
143  * Program can be resumed by calling Continue() (which sends SIGCONT)
144  * @returns true if the program could be paused, false if it couldn't (probably because it wasn't running)
145  */
146 bool Program::Pause()
147 {
148         if (pid > 0 && kill(pid,SIGSTOP) == 0)
149         {
150                 paused = true;
151                 return true;
152         }
153         return false;
154 }
155
156 /**
157  * Causes a paused program to continue
158  * @returns true if the program could be continued, false if it couldn't (probably because it wasn't running)
159  */
160 bool Program::Continue()
161 {
162         if (pid > 0 && kill(pid,SIGCONT) == 0)
163         {
164                 paused = false;
165                 return true;
166         }
167         return false;
168 }
169
170 /**
171  * @returns true iff the program is paused
172  */
173 bool Program::Paused() const
174 {
175         return paused;
176 }
177
178
179 /**
180  * Sends a message to the wrapped AI program
181  * WARNING: Always prints a new line after the message (so don't include a new line)
182  *      This is because everything is always line buffered.
183  * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!)
184  */
185 bool Program::SendMessage(const char * print, ...)
186 {
187         if (!Running()) //Is the process running...
188                 return false; 
189
190         va_list ap;
191         va_start(ap, print);
192
193         if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
194         {
195                 va_end(ap);
196                 return false;
197         }
198         va_end(ap);
199         
200
201
202
203         return true;
204 }
205
206
207 /**
208  * Retrieves a message from the wrapped AI program, waiting a maximum amount of time
209  * @param buffer - C++ string to store the resultant message in
210  * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately.
211  * @returns true if the response was recieved within the specified time, false if it was not, or an EOF was recieved, or the process was not running.
212  */
213 bool Program::GetMessage(string & buffer, double timeout)
214 {
215         if (!Running() || timeout == 0)
216                 return false;
217
218         assert(&buffer != NULL);
219         GetterThread getterThread(input, buffer);
220         assert(&(getterThread.buffer) != NULL);
221
222         TimerThread timerThread(timeout*1000000);
223
224         getterThread.Start();
225         if (timeout > 0)
226                 timerThread.Start();
227
228         
229         while (!getterThread.Finished())
230         {
231                 if (timeout > 0 && timerThread.Finished())
232                 {
233                         getterThread.Stop();
234                         timerThread.Stop();
235                         return false;
236                 }
237         }
238
239         getterThread.Stop();
240         if (timeout > 0)
241                 timerThread.Stop();
242
243         
244
245         if (buffer.size() == 1 && buffer[0] == EOF)
246                 return false;
247         return true;
248
249
250 }
251
252 /**
253  * Returns true iff the process is running
254  * @returns what I just said, fool
255  */
256 bool Program::Running() const
257 {
258         return (pid > 0 && kill(pid,0) == 0);
259 }
260
261
262
263

UCC git Repository :: git.ucc.asn.au