7 #include "thread_util.h"
15 #include <sys/types.h>
24 * @param executablePath - path to the program that will be run
26 * Creates two pipes - one for each direction between the parent process and the AI program
28 * The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program
29 * The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience.
31 Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0), paused(false)
37 if (executablePath[0] != '"')
38 args.push_back((char*)executablePath);
40 args.push_back((char*)(executablePath)+1);
44 token = strstr(args[args.size()-1], " ");
55 while (*token != '\0' && iswspace(*token));
57 if (*token != '\0' && !iswspace(*token))
59 args.push_back(token);
64 while (token != NULL);
66 char ** arguments = NULL;
69 arguments = new char*[args.size()];
70 for (unsigned int i=0; i < args.size(); ++i)
71 arguments[i] = args[i];
74 //See if file exists and is executable...
75 if (access(executablePath, X_OK) != 0)
81 int readPipe[2]; int writePipe[2];
82 assert(pipe(readPipe) == 0);
83 assert(pipe(writePipe) == 0);
88 close(readPipe[0]); //close end that parent reads from
89 close(writePipe[1]); //close end that parent writes to
91 //TODO: Fix possible bug here if the process is already a daemon
92 assert(writePipe[0] != 0 && readPipe[1] != 1);
93 dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN
94 dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT
96 //TODO: Somehow force the exec'd process to be unbuffered
97 setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect
98 setbuf(stdout, NULL); //You should add them at the start of the wrapped program.
99 //If your wrapped program is not written in C/C++, you will probably have a problem
102 if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file
104 execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable
105 //execv(executablePath,arguments); ///Replace process with desired executable
107 perror("execv error:\n");
108 fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
109 exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens
113 close(writePipe[0]); //close end that child writes to
114 close(readPipe[1]); //close end that child reads from
116 input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w");
118 setbuf(output, NULL);
125 * Writes EOF to the wrapped program and then closes all streams
126 * Kills the wrapped program if it does not exit within 1 second.
130 if (Running()) //Check if the process created is still running...
132 //fputc(EOF, output); //If it was, tell it to stop with EOF
134 TimerThread timer(2); //Wait for 2 seconds
136 while (!timer.Finished())
156 * Forces the program to pause by sending SIGSTOP
157 * Program can be resumed by calling Continue() (which sends SIGCONT)
158 * @returns true if the program could be paused, false if it couldn't (probably because it wasn't running)
160 bool Program::Pause()
162 if (pid > 0 && kill(pid,SIGSTOP) == 0)
171 * Causes a paused program to continue
172 * @returns true if the program could be continued, false if it couldn't (probably because it wasn't running)
174 bool Program::Continue()
176 if (pid > 0 && kill(pid,SIGCONT) == 0)
185 * @returns true iff the program is paused
187 bool Program::Paused() const
194 * Sends a message to the wrapped AI program
195 * WARNING: Always prints a new line after the message (so don't include a new line)
196 * This is because everything is always line buffered.
197 * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!)
199 bool Program::SendMessage(const char * print, ...)
201 if (!Running()) //Is the process running...
207 if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
222 * Retrieves a message from the wrapped AI program, waiting a maximum amount of time
223 * @param buffer - C++ string to store the resultant message in
224 * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately.
225 * @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.
227 bool Program::GetMessage(string & buffer, double timeout)
229 if (!Running() || timeout == 0)
235 tv.tv_sec = (int)(timeout);
236 tv.tv_usec = (timeout - (double)((int)timeout)) * 1000000;
238 int fd = fileno(input);
241 FD_SET(fd, &readfds);
243 select(fd+1, &readfds, NULL, NULL, &tv);
245 if (!FD_ISSET(fd, &readfds))
246 return false; //Timed out
247 //fprintf(stderr, "Got message!\n");
248 for (char c = fgetc(input); c != '\n' && (int)(c) != EOF; c = fgetc(input))
250 //fprintf(stderr, "%c", c);
253 //fprintf(stderr, "%s\n", buffer.c_str());
256 /* Old way, using threads, which apparently is terrible
257 assert(&buffer != NULL);
258 GetterThread getterThread(input, buffer);
259 assert(&(getterThread.buffer) != NULL);
261 TimerThread timerThread(timeout*1000000);
263 getterThread.Start();
268 while (!getterThread.Finished())
270 if (timeout > 0 && timerThread.Finished())
284 if (buffer.size() == 1 && buffer[0] == EOF)
292 * Returns true iff the process is running
293 * @returns what I just said, fool
295 bool Program::Running() const
297 return (pid > 0 && kill(pid,0) == 0);