921683c95952a169a9d96700cb0130ed378ebc71
[polyglot.git] / engine.cpp
1 #ifndef _WIN32\r
2 \r
3 // engine.cpp\r
4 \r
5 // includes\r
6 \r
7 #include <cerrno>\r
8 #include <cstdarg>\r
9 #include <cstdio>\r
10 #include <cstdlib>\r
11 #include <cstring>\r
12 \r
13 #include <sys/types.h>\r
14 #include <sys/resource.h>\r
15 #include <unistd.h>\r
16 \r
17 #include "engine.h"\r
18 #include "io.h"\r
19 #include "option.h"\r
20 #include "util.h"\r
21 \r
22 \r
23 // constants\r
24 \r
25 static const int StringSize = 4096;\r
26 \r
27 // variables\r
28 \r
29 engine_t Engine[1];\r
30 \r
31 // prototypes\r
32 \r
33 static void my_close (int fd);\r
34 static void my_dup2  (int old_fd, int new_fd);\r
35 \r
36 // functions\r
37 \r
38 // engine_is_ok()\r
39 \r
40 bool engine_is_ok(const engine_t * engine) {\r
41 \r
42    if (engine == NULL) return false;\r
43 \r
44    if (!io_is_ok(engine->io)) return false;\r
45 \r
46    return true;\r
47 }\r
48 \r
49 // engine_open()\r
50 \r
51 void engine_open(engine_t * engine) {\r
52 \r
53    const char * dir, * command;\r
54    char string[StringSize];\r
55    int argc;\r
56    char * ptr;\r
57    char * argv[256];\r
58    int from_engine[2], to_engine[2];\r
59    pid_t pid;\r
60 \r
61    ASSERT(engine!=NULL);\r
62 \r
63    // init\r
64 \r
65    dir = option_get_string("EngineDir");\r
66    my_log("POLYGLOT Dir \"%s\"\n",dir);\r
67 \r
68    command = option_get_string("EngineCommand");\r
69    my_log("POLYGLOT Command \"%s\"\n",command);\r
70 \r
71    // parse the command line and create the argument list\r
72 \r
73    if (strlen(command) >= StringSize) my_fatal("engine_open(): buffer overflow\n");\r
74    strcpy(string,command);\r
75 \r
76    argc = 0;\r
77 \r
78    for (ptr = strtok(string," "); ptr != NULL; ptr = strtok(NULL," ")) {\r
79       argv[argc++] = ptr;\r
80    }\r
81 \r
82    argv[argc] = NULL;\r
83 \r
84    // create the pipes\r
85 \r
86    if (pipe(from_engine) == -1) {\r
87       my_fatal("engine_open(): pipe(): %s\n",strerror(errno));\r
88    }\r
89 \r
90    if (pipe(to_engine) == -1) {\r
91       my_fatal("engine_open(): pipe(): %s\n",strerror(errno));\r
92    }\r
93 \r
94    // create the child process\r
95 \r
96    pid = fork();\r
97 \r
98    if (pid == -1) {\r
99 \r
100       my_fatal("engine_open(): fork(): %s\n",strerror(errno));\r
101 \r
102    } else if (pid == 0) {\r
103 \r
104       // child = engine\r
105 \r
106       // close unused pipe descriptors to avoid deadlocks\r
107 \r
108       my_close(from_engine[0]);\r
109       my_close(to_engine[1]);\r
110 \r
111       // attach the pipe to standard input\r
112 \r
113       my_dup2(to_engine[0],STDIN_FILENO);\r
114       my_close(to_engine[0]);\r
115 \r
116       // attach the pipe to standard output\r
117 \r
118       my_dup2(from_engine[1],STDOUT_FILENO);\r
119       my_close(from_engine[1]);\r
120 \r
121       // attach standard error to standard output\r
122 \r
123       my_dup2(STDOUT_FILENO,STDERR_FILENO);\r
124 \r
125       // set a low priority\r
126 \r
127       if (option_get_bool("UseNice")) {\r
128           my_log("POLYGLOT Adjust Engine Piority");\r
129           nice(option_get_int("NiceValue"));\r
130       }\r
131 \r
132       // change the current directory\r
133 \r
134       if (dir[0] != '\0' && chdir(dir) == -1) {\r
135          my_fatal("engine_open(): chdir(): %s\n",strerror(errno));\r
136       }\r
137 \r
138       // launch the new executable file\r
139 \r
140       execvp(argv[0],&argv[0]);\r
141 \r
142       // execvp() only returns when an error has occured\r
143 \r
144       my_fatal("engine_open(): execvp(): %s\n",strerror(errno));\r
145 \r
146    } else { // pid > 0\r
147 \r
148       ASSERT(pid>0);\r
149 \r
150       // parent = PolyGlot\r
151 \r
152       // close unused pipe descriptors to avoid deadlocks\r
153 \r
154       my_close(from_engine[1]);\r
155       my_close(to_engine[0]);\r
156 \r
157       // fill in the engine struct\r
158 \r
159       engine->io->in_fd = from_engine[0];\r
160       engine->io->out_fd = to_engine[1];\r
161       engine->io->name = "Engine";\r
162       engine->pid=pid;\r
163 \r
164       io_init(engine->io);\r
165    }\r
166 }\r
167 \r
168 // engine_set_nice_value()\r
169 \r
170 void engine_set_nice_value(engine_t * engine, int value){\r
171     setpriority(PRIO_PROCESS,engine->pid,value);\r
172 }\r
173 \r
174 \r
175 // engine_close()\r
176 \r
177 void engine_close(engine_t * engine) {\r
178 \r
179    ASSERT(engine_is_ok(engine));\r
180 \r
181    io_close(engine->io);\r
182 }\r
183 \r
184 // engine_get()\r
185 \r
186 void engine_get(engine_t * engine, char string[], int size) {\r
187 \r
188    ASSERT(engine_is_ok(engine));\r
189    ASSERT(string!=NULL);\r
190    ASSERT(size>=256);\r
191 \r
192    while (!io_line_ready(engine->io)) {\r
193       io_get_update(engine->io);\r
194    }\r
195 \r
196    if (!io_get_line(engine->io,string,size)) { // EOF\r
197        my_log("POLYGLOT *** EOF from Engine ***\n");\r
198       exit(EXIT_SUCCESS);\r
199    }\r
200 }\r
201 \r
202 // engine_send()\r
203 \r
204 void engine_send(engine_t * engine, const char format[], ...) {\r
205 \r
206    va_list arg_list;\r
207    char string[StringSize];\r
208 \r
209    ASSERT(engine_is_ok(engine));\r
210    ASSERT(format!=NULL);\r
211 \r
212    // format\r
213 \r
214    va_start(arg_list,format);\r
215    vsprintf(string,format,arg_list);\r
216    va_end(arg_list);\r
217 \r
218    // send\r
219 \r
220    io_send(engine->io,"%s",string);\r
221 }\r
222 \r
223 // engine_send_queue()\r
224 \r
225 void engine_send_queue(engine_t * engine, const char format[], ...) {\r
226 \r
227    va_list arg_list;\r
228    char string[StringSize];\r
229 \r
230    ASSERT(engine_is_ok(engine));\r
231    ASSERT(format!=NULL);\r
232 \r
233    // format\r
234 \r
235    va_start(arg_list,format);\r
236    vsprintf(string,format,arg_list);\r
237    va_end(arg_list);\r
238 \r
239    // send\r
240 \r
241    io_send_queue(engine->io,"%s",string);\r
242 }\r
243 \r
244 // my_close()\r
245 \r
246 static void my_close(int fd) {\r
247 \r
248    ASSERT(fd>=0);\r
249 \r
250    if (close(fd) == -1) my_fatal("my_close(): close(): %s\n",strerror(errno));\r
251 }\r
252 \r
253 // my_dup2()\r
254 \r
255 static void my_dup2(int old_fd, int new_fd) {\r
256 \r
257    ASSERT(old_fd>=0);\r
258    ASSERT(new_fd>=0);\r
259 \r
260    if (dup2(old_fd,new_fd) == -1) my_fatal("my_dup2(): dup2(): %s\n",strerror(errno));\r
261 }\r
262 \r
263 // end of posix part\r
264 #else\r
265 \r
266 // WIN32 part\r
267 \r
268 // includes\r
269 \r
270 #include <stdlib.h>\r
271 #include <string.h>\r
272 #include <windows.h>\r
273 #include <direct.h>\r
274 \r
275 \r
276 \r
277 #include "engine.h"\r
278 #include "option.h"\r
279 #include "pipe.h"\r
280 #include "posix.h"\r
281 \r
282 // constants\r
283 \r
284 static const int StringSize = 4096;\r
285 \r
286 // variables\r
287 \r
288 static int nQueuePtr = 0;\r
289 static char szQueueString[StringSize];\r
290 engine_t Engine[1];\r
291 \r
292 // functions\r
293 \r
294 DWORD GetWin32Priority(int nice)\r
295 {\r
296 /*\r
297 REALTIME_PRIORITY_CLASS     0x00000100\r
298 HIGH_PRIORITY_CLASS         0x00000080\r
299 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
300 NORMAL_PRIORITY_CLASS       0x00000020\r
301 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
302 IDLE_PRIORITY_CLASS         0x00000040\r
303 */\r
304         if (nice < -15) return 0x00000080;\r
305         if (nice < 0)   return 0x00008000;\r
306         if (nice == 0)  return 0x00000020;\r
307         if (nice < 15)  return 0x00004000;\r
308         return 0x00000040;\r
309 }\r
310 \r
311 \r
312 \r
313 void set_affinity(engine_t *engine, int affin){\r
314         if(affin==-1) return;\r
315     SetProcessAffinityMask((engine->pipeEngine).hProcess,affin);\r
316 }\r
317 \r
318 // Eric Mullins!\r
319 \r
320 void engine_set_nice_value(engine_t *engine, int value){\r
321     SetPriorityClass((engine->pipeEngine).hProcess,\r
322                      GetWin32Priority(value));\r
323 }\r
324 \r
325 \r
326 void engine_send_queue(engine_t * engine,const char *szFormat, ...) {\r
327   nQueuePtr += vsprintf(szQueueString + nQueuePtr, szFormat, (va_list) (&szFormat + 1));\r
328 }\r
329 \r
330 void engine_send(engine_t * engine, const char *szFormat, ...) {\r
331     vsprintf(szQueueString + nQueuePtr, szFormat, (va_list) (&szFormat + 1));\r
332     (engine->pipeEngine).LineOutput(szQueueString);\r
333     my_log("Adapter->Engine: %s\n",szQueueString);\r
334     nQueuePtr = 0;\r
335 }\r
336 \r
337 void engine_close(engine_t * engine){\r
338     (engine->pipeEngine).Close();\r
339 }\r
340 \r
341 \r
342 void engine_open(engine_t * engine){\r
343    int affinity;\r
344     char *my_dir;\r
345     if( (my_dir = _getcwd( NULL, 0 )) == NULL )\r
346         my_fatal("Can't build path: %s\n",strerror(errno));\r
347     SetCurrentDirectory(option_get_string("EngineDir"));\r
348     (engine->pipeEngine).Open(option_get_string("EngineCommand"));\r
349         //play with affinity (bad idea) \r
350     affinity=option_get_int("Affinity");\r
351     if(affinity!=-1) set_affinity(engine,affinity); //AAA\r
352         //lets go back\r
353     SetCurrentDirectory(my_dir);\r
354         // set a low priority\r
355     if (option_get_bool("UseNice")){\r
356           my_log("POLYGLOT Adjust Engine Piority\n");\r
357           engine_set_nice_value(engine, option_get_int("NiceValue"));\r
358     }\r
359     \r
360 }\r
361 \r
362 bool engine_get_non_blocking(engine_t * engine, char *szLineStr, int size){\r
363         if ((engine->pipeEngine).LineInput(szLineStr)) {\r
364         my_log("Engine->Adapter: %s\n",szLineStr);\r
365         return true;\r
366     } else {\r
367         szLineStr[0]='\0';\r
368         return false;\r
369     }\r
370 }\r
371 \r
372 void engine_get(engine_t * engine, char *szLineStr, int size){\r
373     bool data_available;\r
374     while(true){\r
375         data_available=engine_get_non_blocking(engine,szLineStr,size);\r
376         if(!data_available){\r
377             Idle();\r
378         }else{\r
379             break;\r
380         }\r
381     }\r
382 }\r
383 \r
384 \r
385 #endif\r