1 // crm_expr_syscall.c - system call expression handling
2
3 // Copyright 2001-2009 William S. Yerazunis.
4 // This file is under GPLv3, as described in COPYING.
5
6 // include some standard files
7 #include "crm114_sysincludes.h"
8
9 // include any local crm114 configuration file
10 #include "crm114_config.h"
11
12 // include the crm114 data structures file
13 #include "crm114_structs.h"
14
15 // and include the routine declarations file
16 #include "crm114.h"
17
18 // the globals used when we need a big buffer - allocated once, used
19 // wherever needed. These are sized to the same size as the data window.
20 extern char *inbuf;
21 extern char *outbuf;
22
23 #ifndef CRM_WINDOWS
24 // Normal options for UNIX/Linux
25
26 #else // CRM_WINDOWS
27 typedef struct
28 {
29 HANDLE to_minion;
30 char *inbuf;
31 long inlen;
32 long internal_trace;
33 long keep_proc;
34 } pusherparams;
35
36 typedef struct
37 {
38 HANDLE from_minion;
39 int timeout;
40 } suckerparams;
41
pusher_proc(LPVOID lpParameter)42 DWORD WINAPI pusher_proc(LPVOID lpParameter)
43 {
44 DWORD bytesWritten;
45 pusherparams *p = (pusherparams *)lpParameter;
46 WriteFile(p->to_minion, p->inbuf, p->inlen, &bytesWritten, NULL);
47 free(p->inbuf);
48 if (p->internal_trace)
49 fprintf (stderr, "pusher: input sent to minion.\n");
50
51 // if we don't want to keep this proc, we close it's input, and
52 // wait for it to exit.
53 if (! p->keep_proc)
54 {
55 CloseHandle (p->to_minion);
56 if (internal_trace)
57 fprintf (stderr, "minion input pipe closed\n");
58 }
59 if (p->internal_trace)
60 fprintf (stderr, "pusher: exiting pusher\n");
61 return 0;
62 }
63
sucker_proc(LPVOID lpParameter)64 DWORD WINAPI sucker_proc(LPVOID lpParameter)
65 {
66 DWORD bytesRead;
67 suckerparams *p = (suckerparams *)lpParameter;
68 char *outbuf = malloc(sizeof(char) * 8192);
69
70 // we're in the sucker process here- just throw away
71 // everything till we get EOF, then exit.
72 while (1)
73 {
74 Sleep (p->timeout);
75 ReadFile(p->from_minion, outbuf,
76 8192, &bytesRead, NULL);
77 if (bytesRead == 0) break;
78 };
79 return 0;
80 }
81 #endif // CRM_WINDOWS
82
crm_expr_syscall(CSL_CELL * csl,ARGPARSE_BLOCK * apb)83 int crm_expr_syscall ( CSL_CELL *csl, ARGPARSE_BLOCK *apb)
84 {
85 // Go off and fork a process, sending that process
86 // one pattern evaluated as input, and then accepting
87 // all the returns from that process as the new value
88 // for a variable.
89 //
90 // syntax is:
91 // exec (:to:) (:from:) (:ctl:) /commandline/
92 long inlen;
93 long outlen;
94 char from_var [MAX_VARNAME];
95 char sys_cmd [MAX_PATTERN];
96 long cmd_len;
97 char keep_buf [MAX_PATTERN];
98 long keep_len;
99 char exp_keep_buf[MAX_PATTERN];
100 long exp_keep_len;
101 long vstart;
102 long vlen;
103 long done, charsread;
104 int keep_proc;
105 int async_mode;
106 int to_minion[2];
107 int from_minion[2];
108 pid_t minion;
109 int minion_exit_status;
110 pid_t pusher;
111 pid_t sucker;
112 pid_t random_child;
113 int status;
114 long timeout;
115
116 #ifndef CRM_WINDOWS
117 if (user_trace)
118 fprintf (stderr, "executing an SYSCALL statement");
119 timeout = MINION_SLEEP_USEC;
120
121 // clean up any prior processes - note that
122 // we don't keep track of these. For that matter, we have
123 // no context to keep track of 'em.
124 //
125 while ( (random_child = waitpid ( 0, &status, WNOHANG)) > 0 );
126
127 #else // CRM_WINDOWS
128 SECURITY_ATTRIBUTES pipeSecAttr;
129 HANDLE hminion;
130 timeout = MINION_SLEEP_USEC / 1000; // need milliseconds for Sleep()
131
132 if (MINION_SLEEP_USEC > 0 && timeout < 1)
133 {
134 timeout = 1;
135 }
136
137 #endif // CRM_WINDOWS
138
139 // get the flags
140 //
141 keep_proc = 0;
142 if (apb->sflags & CRM_KEEP)
143 {
144 if (user_trace)
145 fprintf (stderr, "Keeping the process around if possible\n");
146 keep_proc = 1;
147 };
148 async_mode = 0;
149 if (apb->sflags & CRM_ASYNC)
150 {
151 if (user_trace)
152 fprintf (stderr, "Letting the process go off on it's own");
153 async_mode = 1;
154 };
155
156 // Sanity check - <async> is incompatible with <keep>
157 //
158 if (keep_proc && async_mode)
159 {
160 nonfatalerror5 ("This syscall uses both async and keep, but async is "
161 "incompatible with keep. Since keep is safer"
162 "we will use that.\n",
163 "You need to fix this program.", CRM_ENGINE_HERE);
164 async_mode = 0;
165 };
166
167 // get the input variable(s)
168 //
169 crm_get_pgm_arg (inbuf, data_window_size, apb->p1start, apb->p1len);
170 inlen = crm_nexpandvar (inbuf, apb->p1len, data_window_size);
171 if (user_trace)
172 fprintf (stderr, " command's input wil be: ***%s***\n", inbuf);
173
174 // now get the name of the variable where the return will be
175 // placed... this is a crock and should be fixed someday.
176 // the output goes only into a single var (the first one)
177 // so we extract that
178 //
179 crm_get_pgm_arg (from_var, MAX_PATTERN, apb->p2start, apb->p2len);
180 outlen = crm_nexpandvar (from_var, apb->p2len, MAX_PATTERN);
181 done = 0;
182 vstart = 0;
183 while (from_var[vstart] < 0x021 && from_var[vstart] > 0x0 )
184 vstart++;
185 vlen = 0;
186 while (from_var[vstart+vlen] >= 0x021)
187 vlen++;
188 memmove (from_var, &from_var[vstart], vlen);
189 from_var[vlen] = '\000';
190 if (user_trace)
191 fprintf (stderr, " command output will overwrite var ***%s***\n",
192 from_var);
193
194
195 // now get the name of the variable (if it exists) where
196 // the kept-around minion process's pipes and pid are stored.
197 crm_get_pgm_arg (keep_buf, MAX_PATTERN, apb->p3start, apb->p3len);
198 keep_len = crm_nexpandvar (keep_buf, apb->p3len, MAX_PATTERN);
199 if (user_trace)
200 fprintf (stderr, " command status kept in var ***%s***\n",
201 keep_buf);
202
203 // Get the command to execute
204 //
205 // GROT GROT GROT
206 // In retrospect, putting the command to execute in /slashes/
207 // was a design error. It's not a pattern to match, it's a
208 // source to operate on (in the meta sense, at least). And,
209 // from a practical point of view, it means that pathnames with
210 // embedded slashes are a pain in the neck to write. So- we'll
211 // allow the boxed [string] syntax as well as the slash /string/
212 // syntax for now.
213 // GROT GROT GROT
214 if (apb->s1len > 0)
215 {
216 crm_get_pgm_arg (sys_cmd, MAX_PATTERN, apb->s1start, apb->s1len);
217 cmd_len = crm_nexpandvar (sys_cmd, apb->s1len, MAX_PATTERN);
218 };
219 if (apb->b1len > 0)
220 {
221 crm_get_pgm_arg (sys_cmd, MAX_PATTERN, apb->b1start, apb->b1len);
222 cmd_len = crm_nexpandvar (sys_cmd, apb->b1len, MAX_PATTERN);
223 };
224
225 if (user_trace)
226 fprintf (stderr, " command will be ***%s***\n", sys_cmd);
227
228 // Do we reuse an already-existing process? Check to see if the
229 // keeper variable has it... note that we have to :* prefix it
230 // and expand it again.
231 minion = 0;
232 to_minion[0] = 0;
233 from_minion[1] = 0;
234 exp_keep_buf [0] = '\000';
235 // this is 8-bit-safe because vars are never wchars.
236 strcat (exp_keep_buf, ":*");
237 strncat (exp_keep_buf, keep_buf, keep_len);
238 exp_keep_len = crm_nexpandvar (exp_keep_buf, keep_len+2, MAX_PATTERN);
239 sscanf (exp_keep_buf, "MINION PROC PID: %d from-pipe: %d to-pipe: %d",
240 &minion,
241 &from_minion[0],
242 &to_minion[1]);
243
244 #ifndef CRM_WINDOWS
245 // if, no minion already existing, we create
246 // communications pipes and launch the subprocess. This
247 // code borrows concepts from both liblaunch and from
248 // netcat (thanks, *Hobbit*!)
249 //
250 if (minion == 0)
251 {
252 long status1, status2;
253 if (user_trace)
254 fprintf (stderr, " Must start a new minion.\n");
255 status1 = pipe (to_minion);
256 status2 = pipe (from_minion);
257 if (status1 > 0 || status2 > 0)
258 {
259 nonfatalerror5 ("Problem setting up the to/from pipes to a minion. ",
260 "Perhaps the system file descriptor table is full?",
261 CRM_ENGINE_HERE);
262 return (1);
263 };
264 minion = fork();
265
266 if (minion < 0)
267 {
268 nonfatalerror5 ("Tried to fork your minion, but it failed.",
269 "Your system may have run out of process slots",
270 CRM_ENGINE_HERE);
271 return (1);
272 };
273
274 if (minion == 0)
275 { // START OF IN THE MINION
276 //
277 // if minion == 0, then We're in the minion here
278 int retcode;
279 long vstart, vlen;
280 long varline;
281 // close the ends of the pipes we don't need.
282 //
283 // NOTE: if this gets messed up, you end up with a race
284 // condition, because both master and minion processes
285 // can both read and write both pipes (effectively a
286 // process could write something out, then read it again
287 // right back out of the pipe)! So, it's REALLY REALLY
288 // IMPORTANT that you use two pipe structures, (one for
289 // each direction) and you keep track of which process
290 // should write to which pipe!!!
291 //
292 close (to_minion[1]);
293 close (from_minion[0]);
294 dup2 (to_minion[0], fileno(stdin));
295 dup2 (from_minion[1], fileno(stdout));
296
297 // Are we a syscall to a :label:, or should we invoke the
298 // shell on an external command?
299 //
300 crm_nextword (sys_cmd, strlen (sys_cmd), 0, &vstart, &vlen);
301 varline = crm_lookupvarline (vht, sys_cmd, vstart, vlen);
302 if (varline > 0)
303 {
304 // sys_cmd[vstart+vlen] = '\0';
305 if (user_trace)
306 fprintf (stderr, "FORK transferring control to line %s\n",
307 &sys_cmd[vstart]);
308
309 // set the current pid and parent pid.
310 {
311 char pidstr [32];
312 long pid;
313 pid = (long) getpid();
314 sprintf (pidstr, "%ld", pid);
315 crm_set_temp_var (":_pid:", pidstr);
316 if (user_trace)
317 fprintf (stderr, "My new PID is %s\n", pidstr);
318 pid = (long) getppid();
319 sprintf (pidstr, "%ld", pid);
320 crm_set_temp_var (":_ppid:", pidstr);
321 }
322 // See if we have redirection of stdin and stdout
323 while (crm_nextword (sys_cmd, strlen (sys_cmd), vstart+vlen,
324 &vstart, &vlen))
325 {
326 char filename[MAX_PATTERN];
327 if (sys_cmd[vstart] == '<')
328 {
329 strncpy (filename, &sys_cmd[vstart+1], vlen);
330 filename[vlen-1] = '\0';
331 if (user_trace)
332 fprintf (stderr, "Redirecting minion stdin to %s\n",
333 filename);
334 (void )freopen (filename, "rb", stdin);
335 };
336 if (sys_cmd[vstart] == '>')
337 {
338 if (sys_cmd[vstart+1] != '>')
339 {
340 strncpy (filename, &sys_cmd[vstart+1], vlen);
341 filename[vlen-1] = '\0';
342 if (user_trace)
343 fprintf (stderr,
344 "Redirecting minion stdout to %s\n",
345 filename);
346 (void )freopen (filename, "wb", stdout);
347 }
348 else
349 {
350 strncpy (filename, &sys_cmd[vstart+2], vlen);
351 filename[vlen-2] = '\0';
352 if (user_trace)
353 fprintf (stderr,
354 "Appending minion stdout to %s\n",
355 filename);
356 (void )freopen (filename, "a+", stdout);
357 }
358 };
359 }
360 csl->cstmt = varline;
361 // and note that this isn't a failure.
362 csl->aliusstk [ csl->mct[csl->cstmt]->nest_level ] = 1;
363 // The minion's real work should now start; get out of
364 // the syscall code and go run something real. :)
365 return (0);
366 }
367 else
368 {
369 if (user_trace)
370 fprintf (stderr, "Systemcalling on shell command %s\n",
371 sys_cmd);
372 retcode = system (sys_cmd);
373 //
374 // This code only ever happens if an error occurs...
375 //
376 if (retcode == -1 )
377 {
378 char errstr [4096];
379 sprintf (errstr,
380 "The command was >%s< and returned exit code %d .",
381 sys_cmd, WEXITSTATUS (retcode));
382 nonfatalerror5 ("This program tried a shell command that "
383 "didn't run correctly. ",
384 errstr, CRM_ENGINE_HERE);
385 if (engine_exit_base != 0)
386 {
387 exit (engine_exit_base + 11);
388 }
389 else
390 exit (WEXITSTATUS (retcode ));
391 };
392 exit ( WEXITSTATUS (retcode) );
393 };
394 }; // END OF IN THE MINION
395 }
396 else
397 {
398 if (user_trace)
399 fprintf (stderr, " reusing old minion PID: %d\n", minion);
400 };
401 // Now, we're out of the minion for sure.
402 // so we close the pipe ends we know we won't be using.
403 if (to_minion[0] != 0)
404 {
405 close (to_minion[0]);
406 close (from_minion[1]);
407 };
408 //
409 // launch "pusher" process to send the buffer to the minion
410 // (this hint from Dave Soderberg). This avoids the deadly
411 // embrace situation where both processes are waiting to read
412 // (or, equally, both processes have written and filled up
413 // their buffers, and are now held up waiting for the other
414 // process to empty some space in the output buffer)
415 //
416 if (strlen (inbuf) > 0)
417 {
418 pusher = fork ();
419 // we're in the "input pusher" process if we got here.
420 // shove the input buffer out to the minion
421 if (pusher == 0)
422 {
423 dontcare = write (to_minion[1], inbuf, inlen );
424 if (internal_trace)
425 fprintf (stderr, "pusher: input sent to minion.\n");
426 close (to_minion[1]);
427 if (internal_trace)
428 fprintf (stderr, "pusher: minion input pipe closed\n");
429 if (internal_trace)
430 fprintf (stderr, "pusher: exiting pusher\n");
431 // The pusher always exits with success, so do NOT
432 // do not use the engine_exit_base value
433 exit ( EXIT_SUCCESS );
434 };
435 };
436 // now we're out of the pusher process.
437 // if we don't want to keep this proc, we close it's input, and
438 // wait for it to exit.
439 if (! keep_proc)
440 {
441 close (to_minion[1]);
442 if (internal_trace)
443 fprintf (stderr, "minion input pipe closed\n");
444 }
445
446 // and see what is in the pipe for us.
447 outbuf[0] = '\000';
448 done = 0;
449 outlen = 0;
450 // grot grot grot this only works if varnames are not widechars
451 if (strlen (from_var) > 0)
452 {
453 if (async_mode == 0 && keep_proc == 0)
454 {
455 usleep (timeout);
456 // synchronous read- read till we hit EOF, which is read
457 // returning a char count of zero.
458 readloop:
459 if (internal_trace) fprintf (stderr, "SYNCH READ ");
460 usleep (timeout);
461 charsread =
462 read (from_minion[0],
463 &outbuf[done],
464 (data_window_size >> SYSCALL_WINDOW_RATIO) - done - 2);
465 done = done + charsread;
466 if ( charsread > 0
467 && done + 2 < (data_window_size >> SYSCALL_WINDOW_RATIO))
468 goto readloop;
469 if (done < 0) done = 0;
470 outbuf [done] = '\000';
471 outlen = done ;
472 };
473 if (keep_proc == 1 || async_mode == 1)
474 {
475 // we're in either 'keep' 'async' mode. Set nonblocking mode, then
476 // read it once; then put it back in regular mode.
477 //fcntl (from_minion[0], F_SETFL, O_NONBLOCK);
478 // usleep (timeout);
479 charsread = read (from_minion[0],
480 &outbuf[done],
481 (data_window_size >> SYSCALL_WINDOW_RATIO));
482 done = charsread;
483 if (done < 0) done = 0;
484 outbuf [done] = '\000';
485 outlen = done ;
486 //fcntl (from_minion[0], F_SETFL, 0);
487 };
488
489 // If the minion process managed to fill our buffer, and we
490 // aren't "keep"ing it around, OR if the process is "async",
491 // then we should also launch a sucker process to
492 // asynchronously eat all of the stuff we couldn't get into
493 // the buffer. The sucker proc just reads stuff and throws it
494 // away asynchronously... and exits when it gets EOF.
495 //
496 if ( async_mode ||
497 (outlen >= ((data_window_size >> SYSCALL_WINDOW_RATIO) - 2 )
498 && keep_proc == 0))
499 {
500 sucker = fork ();
501 if (sucker == 0)
502 {
503 // we're in the sucker process here- just throw away
504 // everything till we get EOF, then exit.
505 while (1)
506 {
507 usleep (timeout);
508 charsread = read (from_minion[0],
509 &outbuf[0],
510 data_window_size >> SYSCALL_WINDOW_RATIO );
511 // in the sucker here, don't use engine_exit_base exit
512 if (charsread == 0) exit (EXIT_SUCCESS);
513 };
514 };
515 };
516
517 // and set the returned value into from_var.
518 if (user_trace)
519 fprintf (stderr, "SYSCALL output: %ld chars ---%s---.\n ",
520 outlen, outbuf);
521 if (internal_trace)
522 fprintf (stderr, " storing return str in var %s\n", from_var);
523
524 crm_destructive_alter_nvariable ( from_var, vlen, outbuf, outlen);
525 };
526
527 // Record useful minion data, if possible.
528 if (strlen (keep_buf) > 0)
529 {
530 sprintf (exp_keep_buf,
531 "MINION PROC PID: %d from-pipe: %d to-pipe: %d",
532 minion,
533 from_minion[0],
534 to_minion[1]);
535 if (internal_trace)
536 fprintf (stderr, " saving minion state: %s \n",
537 exp_keep_buf);
538 crm_destructive_alter_nvariable (keep_buf, keep_len,
539 exp_keep_buf,
540 strlen (exp_keep_buf));
541 };
542 // If we're keeping this minion process around, record the useful
543 // information, like pid, in and out pipes, etc.
544 if (keep_proc || async_mode)
545 {
546 }
547 else
548 {
549 if (internal_trace)
550 fprintf (stderr, "No keep, no async, so not keeping minion, closing everything.\n");
551
552 // de-zombify any dead minions;
553 waitpid ( minion, &minion_exit_status, 0);
554
555 // we're not keeping it around, so close the pipe.
556 //
557 close (from_minion [0]);
558
559 if ( crm_vht_lookup (vht, keep_buf, strlen (keep_buf)))
560 {
561 char exit_value_string[MAX_VARNAME];
562 if (internal_trace)
563 fprintf (stderr, "minion waitpid result :%d; whacking %s\n",
564 minion_exit_status,
565 keep_buf);
566 sprintf (exit_value_string, "DEAD MINION, EXIT CODE: %d",
567 WEXITSTATUS (minion_exit_status));
568 if (keep_len > 0)
569 crm_destructive_alter_nvariable (keep_buf, keep_len,
570 exit_value_string,
571 strlen (exit_value_string));
572 };
573 };
574 #else // CRM_WINDOWS
575 // if, no minion already existing, we create
576 // communications pipes and launch the subprocess. This
577 // code borrows concepts from both liblaunch and from
578 // netcat (thanks, *Hobbit*!)
579 //
580 if (minion == 0)
581 {
582 int retcode;
583 long vstart, vlen;
584 long varline;
585
586 if (user_trace)
587 fprintf (stderr, " Must start a new minion.\n");
588
589 pipeSecAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
590 pipeSecAttr.bInheritHandle = TRUE;
591 pipeSecAttr.lpSecurityDescriptor = NULL;
592
593 status = CreatePipe(&to_minion[0], &to_minion[1], &pipeSecAttr, 2^10 * 32);
594 status = CreatePipe(&from_minion[0], &from_minion[1], &pipeSecAttr, 2^10 * 32);
595
596 crm_nextword (sys_cmd, strlen (sys_cmd), 0, &vstart, &vlen);
597 varline = crm_lookupvarline (vht, sys_cmd, vstart, vlen);
598 if (varline > 0)
599 {
600 fatalerror5 (" Sorry, syscall to a label isn't implemented in this version", "", CRM_ENGINE_HERE);
601 }
602 else
603 {
604 STARTUPINFO si;
605 PROCESS_INFORMATION pi;
606 HANDLE stdout_save, stdin_save;
607 HANDLE to_minion_write, from_minion_read;
608
609 stdout_save = GetStdHandle(STD_OUTPUT_HANDLE);
610 SetStdHandle(STD_OUTPUT_HANDLE, from_minion[1]);
611
612 stdin_save = GetStdHandle(STD_INPUT_HANDLE);
613 SetStdHandle(STD_INPUT_HANDLE, to_minion[0]);
614
615 DuplicateHandle(GetCurrentProcess(), from_minion[0], GetCurrentProcess(), &from_minion_read , 0, FALSE, DUPLICATE_SAME_ACCESS);
616 CloseHandle(from_minion[0]);
617 from_minion[0] = from_minion_read;
618
619 DuplicateHandle(GetCurrentProcess(), to_minion[1], GetCurrentProcess(), &to_minion_write , 0, FALSE, DUPLICATE_SAME_ACCESS);
620 CloseHandle(to_minion[1]);
621 to_minion[1] = to_minion_write;
622
623 if (user_trace)
624 fprintf (stderr, "systemcalling on shell command %s\n",
625 sys_cmd);
626
627
628 ZeroMemory( &si, sizeof(si) );
629 si.cb = sizeof(si);
630
631 ZeroMemory( &pi, sizeof(pi) );
632
633 retcode = CreateProcess(NULL, sys_cmd, NULL, NULL, TRUE , NULL, NULL, NULL, &si, &pi);
634
635 if (!retcode)
636 {
637 char errstr [4096];
638 sprintf (errstr, "The command was >>%s<< and returned exit code %d .",
639 sys_cmd, retcode);
640 fatalerror5 ("This program tried a shell command that "
641 "didn't run correctly. ",
642 errstr, CRM_ENGINE_HERE);
643 { if (engine_exit_base != 0)
644 {
645 exit (engine_exit_base + 13);
646 }
647 else
648 exit ( EXIT_FAILURE );
649 }
650 }
651 else
652 {
653 minion = pi.dwProcessId;
654 hminion = pi.hProcess;
655 SetStdHandle(STD_OUTPUT_HANDLE, stdout_save);
656 SetStdHandle(STD_INPUT_HANDLE, stdin_save);
657 CloseHandle(pi.hThread);
658 }
659 };
660 }
661 else
662 {
663 if (user_trace)
664 fprintf (stderr, " reusing old minion PID: %d\n", minion);
665 hminion = OpenProcess(PROCESS_ALL_ACCESS, 0, minion);
666 if (hminion == NULL)
667 fatalerror5 ("Couldn't open the existing minion process",
668 "", CRM_ENGINE_HERE);
669 };
670 // Now, we're out of the minion for sure.
671 // so we close the pipe ends we know we won't be using.
672 if (to_minion[0] != 0)
673 {
674 CloseHandle (to_minion[0]);
675 CloseHandle (from_minion[1]);
676 };
677 //
678 // launch "pusher" process to send the buffer to the minion
679 // (this hint from Dave Soderberg). This avoids the deadly
680 // embrace situation where both processes are waiting to read
681 // (or, equally, both processes have written and filled up
682 // their buffers, and are now held up waiting for the other
683 // process to empty some space in the output buffer)
684 //
685 if (strlen (inbuf) > 0)
686 {
687 HANDLE hThread;
688 pusherparams pp;
689 char *inbuf_copy = malloc(sizeof(char) * inlen+1);
690 int i;
691 //Since the pusher thread may continue executing after the
692 //syscall statement has finished, we need to make a copy of
693 //inbuf for the pusher thread to use. The pusher process will
694 //free the memory.
695 for (i=0; i<inlen; i++)
696 inbuf_copy[i] = inbuf[i];
697 inbuf_copy[inlen] = 0;
698 pp.inbuf = inbuf_copy;
699 pp.inlen = inlen;
700 pp.internal_trace = internal_trace;
701 pp.keep_proc = keep_proc;
702 pp.to_minion = to_minion[1];
703 CreateThread(NULL, 0, pusher_proc , &pp , 0, &hThread);
704 }
705
706 // and see what is in the pipe for us.
707 outbuf[0] = '\000';
708 done = 0;
709 outlen = 0;
710 // grot grot grot this only works if varnames are not widechars
711 if (strlen (from_var) > 0)
712 {
713 if (async_mode == 0)
714 {
715 Sleep (timeout);
716 // synchronous read- read till we hit EOF, which is read
717 // returning a char count of zero.
718 readloop:
719 if (internal_trace) fprintf (stderr, "SYNCH READ ");
720 Sleep (timeout);
721 charsread = 0;
722
723 ReadFile(from_minion[0],
724 outbuf + done,
725 (data_window_size >> SYSCALL_WINDOW_RATIO) - done - 2,
726 &charsread, NULL);
727
728 done = done + charsread;
729 if (charsread > 0 && done + 2 < (data_window_size >> SYSCALL_WINDOW_RATIO))
730 goto readloop;
731 if (done < 0) done = 0;
732 outbuf [done] = '\000';
733 outlen = done ;
734 }
735 else
736 {
737 // we're in 'async' mode. Just grab what we can
738 ReadFile(from_minion[0],
739 &outbuf[done],
740 (data_window_size >> SYSCALL_WINDOW_RATIO), &charsread, NULL);
741 done = charsread;
742 if (done < 0) done = 0;
743 outbuf [done] = '\000';
744 outlen = done ;
745
746 }
747
748 // If the minion process managed to fill our buffer, and we
749 // aren't "keep"ing it around, OR if the process is "async",
750 // then we should also launch a sucker process to
751 // asynchronously eat all of the stuff we couldn't get into
752 // the buffer. The sucker proc just reads stuff and throws it
753 // away asynchronously... and exits when it gets EOF.
754 //
755 if ( async_mode || (outlen >= ((data_window_size >> SYSCALL_WINDOW_RATIO) - 2 )
756 && keep_proc == 0))
757 {
758 HANDLE hThread;
759 suckerparams sp;
760 sp.from_minion = from_minion[0];
761 sp.timeout = timeout;
762 CreateThread(NULL, 0, sucker_proc , &sp , NULL, &hThread);
763 }
764
765 // and set the returned value into from_var.
766 if (user_trace)
767 fprintf (stderr, "SYSCALL output: %ld chars ---%s---.\n ",
768 outlen, outbuf);
769 if (internal_trace)
770 fprintf (stderr, " storing return str in var %s\n", from_var);
771
772 crm_destructive_alter_nvariable ( from_var, vlen, outbuf, outlen);
773 }
774
775 // Record useful minion data, if possible.
776 if (strlen (keep_buf) > 0)
777 {
778 sprintf (exp_keep_buf,
779 "MINION PROC PID: %d from-pipe: %d to-pipe: %d",
780 minion,
781 from_minion[0],
782 to_minion[1]);
783 if (internal_trace)
784 fprintf (stderr, " saving minion state: %s \n", exp_keep_buf);
785 crm_destructive_alter_nvariable (keep_buf, keep_len,
786 exp_keep_buf,
787 strlen (exp_keep_buf));
788 };
789
790 // If we're keeping this minion process around, record the useful
791 // information, like pid, in and out pipes, etc.
792 if (!keep_proc && !async_mode)
793 {
794 DWORD exit_code;
795 if (internal_trace)
796 fprintf (stderr, "No keep, no async, so not keeping minion, closing everything.\n");
797
798 // no, we're not keeping it around, so close the pipe.
799 //
800 CloseHandle(from_minion [0]);
801
802 WaitForSingleObject(hminion, INFINITE);
803 if (!GetExitCodeProcess(hminion, &exit_code))
804 {
805 DWORD error = GetLastError();
806 }
807 if ( crm_vht_lookup (vht, keep_buf, strlen (keep_buf)))
808 {
809 char exit_value_string[MAX_VARNAME];
810 if (internal_trace)
811 fprintf (stderr, "minion exit code :%d; whacking %s\n",
812 exit_code,
813 keep_buf);
814 sprintf (exit_value_string, "DEAD MINION, EXIT CODE: %d",
815 exit_code);
816 if (keep_len > 0)
817 crm_destructive_alter_nvariable (keep_buf, keep_len,
818 exit_value_string,
819 strlen (exit_value_string));
820 };
821 CloseHandle(hminion);
822 };
823 #endif // CRM_WINDOWS
824
825 return (0);
826 };
827