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