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

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