8b4e4afcdf6072bcc2c9825d83198fd3f7ddbd24
[progcomp2012.git] / 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
10
11 using namespace std;
12
13
14 /**
15  * Constructor
16  * @param executablePath - path to the program that will be run
17  *
18  * Creates two pipes - one for each direction between the parent process and the AI program
19  * Forks the process. 
20  *      The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program
21  *      The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience.
22  */
23 Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0)
24 {
25         
26
27         int readPipe[2]; int writePipe[2];
28         assert(pipe(readPipe) == 0);
29         assert(pipe(writePipe) == 0);
30
31         pid = fork();
32         if (pid == 0)
33         {
34                 close(readPipe[0]);  //close end that parent reads from
35                 close(writePipe[1]); //close end that parent writes to
36
37                 //TODO: Fix possible bug here if the process is already a daemon
38                 assert(writePipe[0] != 0 && readPipe[1] != 1);
39                 dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN
40                 dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT
41
42                 //TODO: Somehow force the exec'd process to be unbuffered
43                 setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect
44                 setbuf(stdout, NULL); //You should add them at the start of the wrapped program.
45                                         //If your wrapped program is not written in C/C++, you will probably have a problem
46                                 
47
48
49                 execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable
50                 fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
51                 exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens
52         }
53         else
54         {
55                 close(writePipe[0]); //close end that child writes to
56                 close(readPipe[1]); //close end that child reads from
57
58                 input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w");
59                 setbuf(input, NULL);
60                 setbuf(output, NULL);
61         }
62         
63 }
64
65 /**
66  * Destructor
67  * Writes EOF to the wrapped program and then closes all streams
68  * Kills the wrapped program if it does not exit within 1 second.
69  */
70 Program::~Program()
71 {
72         if (kill(pid, 0) == 0) //Check if the process created is still running...
73         {
74                 fputc(EOF, output); //If it was, tell it to stop with EOF
75                 sleep(1); //Give it 1 second to respond...
76                 if (kill(pid, 0) == 0) //Check if its still running
77                 {
78                         kill(pid, 9); //Slay the infidel mercilessly!
79                 }
80         }
81         fclose(input);
82         fclose(output);
83         
84 }
85
86
87
88
89
90 /**
91  * Sends a message to the wrapped AI program
92  * WARNING: Always prints a new line after the message (so don't include a new line)
93  *      This is because everything is always line buffered.
94  * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!)
95  */
96 bool Program::SendMessage(const char * print, ...)
97 {
98         if (kill(pid, 0) != 0) //Is the process running...
99                 return false; 
100
101         va_list ap;
102         va_start(ap, print);
103
104         if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
105         {
106                 va_end(ap);
107                 return false;
108         }
109         va_end(ap);
110         
111
112
113
114         return true;
115 }
116
117
118 /**
119  * Retrieves a message from the wrapped AI program, waiting a maximum amount of time
120  * @param buffer - C++ string to store the resultant message in
121  * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately.
122  * @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.
123  */
124 bool Program::GetMessage(string & buffer, double timeout)
125 {
126         if (kill(pid, 0) != 0)
127                 return false;
128
129         assert(&buffer != NULL);
130         GetterThread getterThread(input, buffer);
131         assert(&(getterThread.buffer) != NULL);
132         TimerThread timerThread(timeout*1000000);
133
134         getterThread.Start();
135         if (timeout > 0)
136                 timerThread.Start();
137
138         
139         while (!getterThread.Finished())
140         {
141                 if (timeout > 0 && timerThread.Finished())
142                 {
143                         getterThread.Stop();
144                         timerThread.Stop();
145                         return false;
146                 }
147         }
148
149         getterThread.Stop();
150         timerThread.Stop();
151
152         
153
154         if (buffer.size() == 1 && buffer[0] == EOF)
155                 return false;
156         return true;
157
158
159 }
160
161 /**
162  * Returns true iff the process is running
163  * @returns what I just said, fool
164  */
165 bool Program::Running() const
166 {
167         return (kill(pid,0) == 0);
168 }
169
170
171
172

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