OSDN Git Service

Throughout, replace hMainProc with GetCurrentProcess/NtCurrentProcess
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / fork.cc
1 /* fork.cc
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
4    2007, 2008, 2009 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include "winsup.h"
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include "cygerrno.h"
17 #include "path.h"
18 #include "fhandler.h"
19 #include "dtable.h"
20 #include "sigproc.h"
21 #include "pinfo.h"
22 #include "cygheap.h"
23 #include "child_info.h"
24 #include "cygtls.h"
25 #include "tls_pbuf.h"
26 #include "dll_init.h"
27 #include "cygmalloc.h"
28
29 #define NPIDS_HELD 4
30
31 /* Timeout to wait for child to start, parent to init child, etc.  */
32 /* FIXME: Once things stabilize, bump up to a few minutes.  */
33 #define FORK_WAIT_TIMEOUT (300 * 1000)     /* 300 seconds */
34
35 class frok
36 {
37   bool load_dlls;
38   child_info_fork ch;
39   const char *error;
40   int child_pid;
41   int this_errno;
42   int __stdcall parent (volatile char * volatile here);
43   int __stdcall child (volatile char * volatile here);
44   friend int fork ();
45 };
46
47 class lock_signals
48 {
49   bool worked;
50 public:
51   lock_signals ()
52   {
53     worked = sig_send (NULL, __SIGHOLD) == 0;
54   }
55   operator int () const
56   {
57     return worked;
58   }
59   void dont_bother ()
60   {
61     worked = false;
62   }
63   ~lock_signals ()
64   {
65     if (worked)
66       sig_send (NULL, __SIGNOHOLD);
67   }
68 };
69
70 class lock_pthread
71 {
72   bool bother;
73 public:
74   lock_pthread (): bother (1)
75   {
76     pthread::atforkprepare ();
77   }
78   void dont_bother ()
79   {
80     bother = false;
81   }
82   ~lock_pthread ()
83   {
84     if (bother)
85       pthread::atforkparent ();
86   }
87 };
88
89 class hold_everything
90 {
91 public: /* DELETEME*/
92   bool& ischild;
93   /* Note the order of the locks below.  It is important,
94      to avoid races, that the lock order be preserved.
95
96      pthread is first because it serves as a master lock
97      against other forks being attempted while this one is active.
98
99      signals is next to stop signal processing for the duration
100      of the fork.
101
102      process is last.  If it is put before signals, then a deadlock
103      could be introduced if the process attempts to exit due to a signal. */
104   lock_pthread pthread;
105   lock_signals signals;
106   lock_process process;
107
108 public:
109   hold_everything (bool& x): ischild (x) {}
110   operator int () const {return signals;}
111
112   ~hold_everything()
113   {
114     if (ischild)
115       {
116         pthread.dont_bother ();
117         process.dont_bother ();
118         signals.dont_bother ();
119       }
120   }
121
122 };
123
124 static void
125 resume_child (HANDLE forker_finished)
126 {
127   SetEvent (forker_finished);
128   debug_printf ("signalled child");
129   return;
130 }
131
132 /* Notify parent that it is time for the next step. */
133 static void __stdcall
134 sync_with_parent (const char *s, bool hang_self)
135 {
136   debug_printf ("signalling parent: %s", s);
137   fork_info->ready (false);
138   if (hang_self)
139     {
140       HANDLE h = fork_info->forker_finished;
141       /* Wait for the parent to fill in our stack and heap.
142          Don't wait forever here.  If our parent dies we don't want to clog
143          the system.  If the wait fails, we really can't continue so exit.  */
144       DWORD psync_rc = WaitForSingleObject (h, FORK_WAIT_TIMEOUT);
145       debug_printf ("awake");
146       switch (psync_rc)
147         {
148         case WAIT_TIMEOUT:
149           api_fatal ("WFSO timed out %s", s);
150           break;
151         case WAIT_FAILED:
152           if (GetLastError () == ERROR_INVALID_HANDLE &&
153               WaitForSingleObject (fork_info->forker_finished, 1) != WAIT_FAILED)
154             break;
155           api_fatal ("WFSO failed %s, fork_finished %p, %E", s,
156                      fork_info->forker_finished);
157           break;
158         default:
159           debug_printf ("no problems");
160           break;
161         }
162     }
163 }
164
165 int __stdcall
166 frok::child (volatile char * volatile here)
167 {
168   HANDLE& hParent = ch.parent;
169   extern void fixup_lockf_after_fork ();
170   extern void fixup_hooks_after_fork ();
171   extern void fixup_timers_after_fork ();
172   debug_printf ("child is running.  pid %d, ppid %d, stack here %p",
173                 myself->pid, myself->ppid, __builtin_frame_address (0));
174
175   sync_with_parent ("after longjmp", true);
176   sigproc_printf ("hParent %p, load_dlls %d", hParent, load_dlls);
177
178   /* If we've played with the stack, stacksize != 0.  That means that
179      fork() was invoked from other than the main thread.  Make sure that
180      the threadinfo information is properly set up.  */
181   if (fork_info->stacksize)
182     {
183       _main_tls = &_my_tls;
184       _main_tls->init_thread (NULL, NULL);
185       _main_tls->local_clib = *_impure_ptr;
186       _impure_ptr = &_main_tls->local_clib;
187     }
188
189   set_cygwin_privileges (hProcToken);
190   clear_procimptoken ();
191   cygheap->user.reimpersonate ();
192
193 #ifdef DEBUGGING
194   if (GetEnvironmentVariableA ("FORKDEBUG", NULL, 0))
195     try_to_debug ();
196   char buf[80];
197   /* This is useful for debugging fork problems.  Use gdb to attach to
198      the pid reported here. */
199   if (GetEnvironmentVariableA ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
200     {
201       small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
202       Sleep (atoi (buf));
203     }
204 #endif
205
206   MALLOC_CHECK;
207
208   /* Incredible but true:  If we use sockets and SYSV IPC shared memory,
209      there's a good chance that a duplicated socket in the child occupies
210      memory which is needed to duplicate shared memory from the parent
211      process, if the shared memory hasn't been duplicated already.
212      The same goes very likely for "normal" mmap shared memory, too, but
213      with SYSV IPC it was the first time observed.  So, *never* fixup
214      fdtab before fixing up shared memory. */
215   if (fixup_shms_after_fork ())
216     api_fatal ("recreate_shm areas after fork failed");
217
218   MALLOC_CHECK;
219
220   /* If we haven't dynamically loaded any dlls, just signal
221      the parent.  Otherwise, load all the dlls, tell the parent
222       that we're done, and wait for the parent to fill in the.
223       loaded dlls' data/bss. */
224   if (!load_dlls)
225     {
226       cygheap->fdtab.fixup_after_fork (hParent);
227       sync_with_parent ("performed fork fixup", false);
228     }
229   else
230     {
231       dlls.load_after_fork (hParent);
232       cygheap->fdtab.fixup_after_fork (hParent);
233       sync_with_parent ("loaded dlls", true);
234     }
235
236   init_console_handler (myself->ctty >= 0);
237   ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
238
239   pthread::atforkchild ();
240   fixup_timers_after_fork ();
241   cygbench ("fork-child");
242   ld_preload ();
243   fixup_hooks_after_fork ();
244   _my_tls.fixup_after_fork ();
245   wait_for_sigthread ();
246   cygwin_finished_initializing = true;
247   return 0;
248 }
249
250 #define NO_SLOW_PID_REUSE
251 #ifndef NO_SLOW_PID_REUSE
252 static void
253 slow_pid_reuse (HANDLE h)
254 {
255   static NO_COPY HANDLE last_fork_procs[NPIDS_HELD];
256   static NO_COPY unsigned nfork_procs;
257
258   if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0])))
259     nfork_procs = 0;
260   /* Keep a list of handles to child processes sitting around to prevent
261      Windows from reusing the same pid n times in a row.  Having the same pids
262      close in succesion confuses bash.  Keeping a handle open will stop
263      windows from reusing the same pid.  */
264   if (last_fork_procs[nfork_procs])
265     ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
266   if (DuplicateHandle (GetCurrentProcess (), h,
267                        GetCurrentProcess (), &last_fork_procs[nfork_procs],
268                        0, FALSE, DUPLICATE_SAME_ACCESS))
269     ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
270   else
271     {
272       last_fork_procs[nfork_procs] = NULL;
273       system_printf ("couldn't create last_fork_proc, %E");
274     }
275   nfork_procs++;
276 }
277 #endif
278
279 int __stdcall
280 frok::parent (volatile char * volatile stack_here)
281 {
282   HANDLE forker_finished;
283   DWORD rc;
284   child_pid = -1;
285   error = NULL;
286   this_errno = 0;
287   bool fix_impersonation = false;
288   pinfo child;
289   static char errbuf[256];
290
291   int c_flags = GetPriorityClass (GetCurrentProcess ());
292   debug_printf ("priority class %d", c_flags);
293
294   /* If we don't have a console, then don't create a console for the
295      child either.  */
296   HANDLE console_handle = CreateFile ("CONOUT$", GENERIC_WRITE,
297                                       FILE_SHARE_WRITE, &sec_none_nih,
298                                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
299                                       NULL);
300
301   if (console_handle != INVALID_HANDLE_VALUE)
302     CloseHandle (console_handle);
303   else
304     c_flags |= DETACHED_PROCESS;
305
306   /* Some file types (currently only sockets) need extra effort in the
307      parent after CreateProcess and before copying the datastructures
308      to the child. So we have to start the child in suspend state,
309      unfortunately, to avoid a race condition. */
310   if (cygheap->fdtab.need_fixup_before ())
311     c_flags |= CREATE_SUSPENDED;
312
313   /* Remember if we need to load dynamically linked dlls.
314      We do this here so that this information will be available
315      in the parent and, when the stack is copied, in the child. */
316   load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
317
318   forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
319   if (forker_finished == NULL)
320     {
321       this_errno = geterrno_from_win_error ();
322       error = "unable to allocate forker_finished event";
323       return -1;
324     }
325
326   ProtectHandleINH (forker_finished);
327
328   ch.forker_finished = forker_finished;
329
330   ch.stackbottom = _tlsbase;
331   ch.stacktop = (void *) stack_here;
332   ch.stacksize = (char *) ch.stackbottom - (char *) stack_here;
333   debug_printf ("stack - bottom %p, top %p, size %d",
334                 ch.stackbottom, ch.stacktop, ch.stacksize);
335
336   PROCESS_INFORMATION pi;
337   STARTUPINFOW si;
338
339   memset (&si, 0, sizeof (si));
340   si.cb = sizeof si;
341
342   si.lpReserved2 = (LPBYTE) &ch;
343   si.cbReserved2 = sizeof (ch);
344
345   /* FIXME: myself->progname should be converted to WCHAR. */
346   tmp_pathbuf tp;
347   PWCHAR progname = tp.w_get ();
348   sys_mbstowcs (progname, NT_MAX_PATH, myself->progname);
349
350   syscall_printf ("CreateProcess (%W, %W, 0, 0, 1, %p, 0, 0, %p, %p)",
351                   progname, progname, c_flags, &si, &pi);
352   bool locked = __malloc_lock ();
353   time_t start_time = time (NULL);
354
355   /* Remove impersonation */
356   cygheap->user.deimpersonate ();
357   fix_impersonation = true;
358
359   while (1)
360     {
361       rc = CreateProcessW (progname, /* image to run */
362                            progname, /* what we send in arg0 */
363                            &sec_none_nih,
364                            &sec_none_nih,
365                            TRUE,          /* inherit handles from parent */
366                            c_flags,
367                            NULL,          /* environment filled in later */
368                            0,     /* use current drive/directory */
369                            &si,
370                            &pi);
371
372       if (!rc)
373         {
374           this_errno = geterrno_from_win_error ();
375           error = "CreateProcessW failed";
376           memset (&pi, 0, sizeof (pi));
377           goto cleanup;
378         }
379
380       if (cygheap->fdtab.need_fixup_before ())
381         {
382           cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
383           ResumeThread (pi.hThread);
384         }
385
386       CloseHandle (pi.hThread);
387
388       /* Protect the handle but name it similarly to the way it will
389          be called in subproc handling. */
390       ProtectHandle1 (pi.hProcess, childhProc);
391
392       strace.write_childpid (ch, pi.dwProcessId);
393
394       /* Wait for subproc to initialize itself. */
395       if (!ch.sync (pi.dwProcessId, pi.hProcess, FORK_WAIT_TIMEOUT))
396         {
397           DWORD exit_code = ch.proc_retry (pi.hProcess);
398           if (!exit_code)
399             continue;
400           this_errno = EAGAIN;
401           /* Not thread safe, but do we care? */
402           __small_sprintf (errbuf, "died waiting for longjmp before initialization, "
403                            "retry %d, exit code %p", ch.retry, exit_code);
404           error = errbuf;
405           goto cleanup;
406         }
407       break;
408     }
409
410   /* Restore impersonation */
411   cygheap->user.reimpersonate ();
412   fix_impersonation = false;
413
414   child_pid = cygwin_pid (pi.dwProcessId);
415   child.init (child_pid, 1, NULL);
416
417   if (!child)
418     {
419       this_errno = get_errno () == ENOMEM ? ENOMEM : EAGAIN;
420 #ifdef DEBUGGING
421       error = "pinfo failed";
422 #else
423       syscall_printf ("pinfo failed");
424 #endif
425       goto cleanup;
426     }
427
428   child->start_time = start_time; /* Register child's starting time. */
429   child->nice = myself->nice;
430
431   /* Initialize things that are done later in dll_crt0_1 that aren't done
432      for the forkee.  */
433   strcpy (child->progname, myself->progname);
434
435   /* Fill in fields in the child's process table entry.  */
436   child->dwProcessId = pi.dwProcessId;
437   child.hProcess = pi.hProcess;
438
439   /* Hopefully, this will succeed.  The alternative to doing things this
440      way is to reserve space prior to calling CreateProcess and then fill
441      it in afterwards.  This requires more bookkeeping than I like, though,
442      so we'll just do it the easy way.  So, terminate any child process if
443      we can't actually record the pid in the internal table. */
444   if (!child.remember (false))
445     {
446       TerminateProcess (pi.hProcess, 1);
447       this_errno = EAGAIN;
448 #ifdef DEBUGGING0
449       error = "child.remember failed";
450 #endif
451       goto cleanup;
452     }
453
454 #ifndef NO_SLOW_PID_REUSE
455   slow_pid_reuse (pi.hProcess);
456 #endif
457
458   /* CHILD IS STOPPED */
459   debug_printf ("child is alive (but stopped)");
460
461   /* Initialize, in order: stack, dll data, dll bss.
462      data, bss, heap were done earlier (in dcrt0.cc)
463      Note: variables marked as NO_COPY will not be copied since they are
464      placed in a protected segment.  */
465
466   MALLOC_CHECK;
467   const void *impure_beg;
468   const void *impure_end;
469   const char *impure;
470   if (&_my_tls == _main_tls)
471     impure_beg = impure_end = impure = NULL;
472   else
473     {
474       impure = "impure";
475       impure_beg = _impure_ptr;
476       impure_end = _impure_ptr + 1;
477     }
478   rc = child_copy (pi.hProcess, true,
479                    "stack", stack_here, ch.stackbottom,
480                    impure, impure_beg, impure_end,
481                    NULL);
482
483   __malloc_unlock ();
484   locked = false;
485   MALLOC_CHECK;
486   if (!rc)
487     {
488       this_errno = get_errno ();
489       DWORD exit_code;
490       if (!GetExitCodeProcess (pi.hProcess, &exit_code))
491         exit_code = 0xdeadbeef;
492       __small_sprintf (errbuf, "pid %u, exitval %p", pi.dwProcessId, exit_code);
493       error = errbuf;
494       goto cleanup;
495     }
496
497   /* Now fill data/bss of any DLLs that were linked into the program. */
498   for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ())
499     {
500       debug_printf ("copying data/bss of a linked dll");
501       if (!child_copy (pi.hProcess, true,
502                        "linked dll data", d->p.data_start, d->p.data_end,
503                        "linked dll bss", d->p.bss_start, d->p.bss_end,
504                        NULL))
505         {
506           this_errno = get_errno ();
507 #ifdef DEBUGGING
508           DWORD exit_code;
509           if (!GetExitCodeProcess (pi.hProcess, &exit_code))
510             exit_code = 0xdeadbeef;
511           __small_sprintf (errbuf, "pid %u, exitval %p", pi.dwProcessId, exit_code);
512           error = errbuf;
513 #endif
514           goto cleanup;
515         }
516     }
517
518   /* Start thread, and then wait for it to reload dlls.  */
519   resume_child (forker_finished);
520   if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT))
521     {
522       this_errno = EAGAIN;
523       error = "died waiting for dll loading";
524       goto cleanup;
525     }
526
527   /* If DLLs were loaded in the parent, then the child has reloaded all
528      of them and is now waiting to have all of the individual data and
529      bss sections filled in. */
530   if (load_dlls)
531     {
532       /* CHILD IS STOPPED */
533       /* write memory of reloaded dlls */
534       for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
535         {
536           debug_printf ("copying data/bss for a loaded dll");
537           if (!child_copy (pi.hProcess, true,
538                            "loaded dll data", d->p.data_start, d->p.data_end,
539                            "loaded dll bss", d->p.bss_start, d->p.bss_end,
540                            NULL))
541             {
542               this_errno = get_errno ();
543 #ifdef DEBUGGING
544               error = "copying data/bss for a loaded dll";
545 #endif
546               goto cleanup;
547             }
548         }
549       /* Start the child up again. */
550       resume_child (forker_finished);
551     }
552
553   ForceCloseHandle (forker_finished);
554   forker_finished = NULL;
555
556   return child_pid;
557
558 /* Common cleanup code for failure cases */
559 cleanup:
560   if (fix_impersonation)
561     cygheap->user.reimpersonate ();
562   if (locked)
563     __malloc_unlock ();
564
565   /* Remember to de-allocate the fd table. */
566   if (pi.hProcess && !child.hProcess)
567     ForceCloseHandle1 (pi.hProcess, childhProc);
568   if (forker_finished)
569     ForceCloseHandle (forker_finished);
570   debug_printf ("returning -1");
571   return -1;
572 }
573
574 extern "C" int
575 fork ()
576 {
577   frok grouped;
578
579   debug_printf ("entering");
580   grouped.load_dlls = 0;
581
582   int res;
583   bool ischild = false;
584
585   myself->set_has_pgid_children ();
586
587   if (grouped.ch.parent == NULL)
588     return -1;
589   if (grouped.ch.subproc_ready == NULL)
590     {
591       system_printf ("unable to allocate subproc_ready event, %E");
592       return -1;
593     }
594
595   {
596     hold_everything held_everything (ischild);
597     /* This tmp_pathbuf constructor is required here because the below setjmp
598        magic will otherwise not restore the original buffer count values in
599        the thread-local storage.  A process forking too deeply will run into
600        the problem to be out of temporary TLS path buffers. */
601     tmp_pathbuf tp;
602
603     if (!held_everything)
604       {
605         if (exit_state)
606           Sleep (INFINITE);
607         set_errno (EAGAIN);
608         return -1;
609       }
610
611     ischild = !!setjmp (grouped.ch.jmp);
612
613     volatile char * volatile esp;
614     __asm__ volatile ("movl %%esp,%0": "=r" (esp));
615
616     if (!ischild)
617       res = grouped.parent (esp);
618     else
619       {
620         res = grouped.child (esp);
621         ischild = true; /* might have been reset by fork mem copy */
622       }
623   }
624
625   MALLOC_CHECK;
626   if (ischild || res > 0)
627     /* everything is ok */;
628   else
629     {
630       if (!grouped.error)
631         syscall_printf ("fork failed - child pid %d, errno %d", grouped.child_pid, grouped.this_errno);
632       else
633         {
634           char buf[strlen (grouped.error) + sizeof ("child %d - , errno 4294967295  ")];
635           strcpy (buf, "child %d - ");
636           strcat (buf, grouped.error);
637           strcat (buf, ", errno %d");
638           system_printf (buf, grouped.child_pid, grouped.this_errno);
639         }
640
641       set_errno (grouped.this_errno);
642     }
643   syscall_printf ("%d = fork()", res);
644   return res;
645 }
646 #ifdef DEBUGGING
647 void
648 fork_init ()
649 {
650 }
651 #endif /*DEBUGGING*/
652
653 #ifdef NEWVFORK
654 /* Dummy function to force second assignment below to actually be
655    carried out */
656 static vfork_save *
657 get_vfork_val ()
658 {
659   return vfork_storage.val ();
660 }
661 #endif
662
663 extern "C" int
664 vfork ()
665 {
666 #ifndef NEWVFORK
667   debug_printf ("stub called");
668   return fork ();
669 #else
670   vfork_save *vf = get_vfork_val ();
671   char **esp, **pp;
672
673   if (vf == NULL)
674     vf = vfork_storage.create ();
675   else if (vf->pid)
676     return fork ();
677
678   // FIXME the tls stuff could introduce a signal race if a child process
679   // exits quickly.
680   if (!setjmp (vf->j))
681     {
682       vf->pid = -1;
683       __asm__ volatile ("movl %%esp,%0": "=r" (vf->vfork_esp):);
684       __asm__ volatile ("movl %%ebp,%0": "=r" (vf->vfork_ebp):);
685       for (pp = (char **) vf->frame, esp = vf->vfork_esp;
686            esp <= vf->vfork_ebp + 2; pp++, esp++)
687         *pp = *esp;
688       vf->ctty = myself->ctty;
689       vf->sid = myself->sid;
690       vf->pgid = myself->pgid;
691       cygheap->ctty_on_hold = cygheap->ctty;
692       vf->console_count = cygheap->console_count;
693       debug_printf ("cygheap->ctty_on_hold %p, cygheap->console_count %d", cygheap->ctty_on_hold, cygheap->console_count);
694       int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
695       debug_printf ("%d = vfork()", res);
696       _my_tls.call_signal_handler ();   // FIXME: racy
697       vf->tls = _my_tls;
698       return res;
699     }
700
701   vf = get_vfork_val ();
702
703   for (pp = (char **) vf->frame, esp = vf->vfork_esp;
704        esp <= vf->vfork_ebp + 2; pp++, esp++)
705     *esp = *pp;
706
707   cygheap->fdtab.vfork_parent_restore ();
708
709   myself->ctty = vf->ctty;
710   myself->sid = vf->sid;
711   myself->pgid = vf->pgid;
712   termios_printf ("cygheap->ctty %p, cygheap->ctty_on_hold %p", cygheap->ctty, cygheap->ctty_on_hold);
713   cygheap->console_count = vf->console_count;
714
715   if (vf->pid < 0)
716     {
717       int exitval = vf->exitval;
718       vf->pid = 0;
719       if ((vf->pid = fork ()) == 0)
720         exit (exitval);
721     }
722
723   int pid = vf->pid;
724   vf->pid = 0;
725   debug_printf ("exiting vfork, pid %d", pid);
726   sig_dispatch_pending ();
727
728   _my_tls.call_signal_handler ();       // FIXME: racy
729   _my_tls = vf->tls;
730   return pid;
731 #endif
732 }
733
734 /* Copy memory from one process to another. */
735
736 bool
737 child_copy (HANDLE hp, bool write, ...)
738 {
739   va_list args;
740   va_start (args, write);
741   static const char *huh[] = {"read", "write"};
742
743   char *what;
744   while ((what = va_arg (args, char *)))
745     {
746       char *low = va_arg (args, char *);
747       char *high = va_arg (args, char *);
748       DWORD todo = wincap.chunksize () ?: high - low;
749       char *here;
750
751       for (here = low; here < high; here += todo)
752         {
753           DWORD done = 0;
754           if (here + todo > high)
755             todo = high - here;
756           int res;
757           if (write)
758             res = WriteProcessMemory (hp, here, here, todo, &done);
759           else
760             res = ReadProcessMemory (hp, here, here, todo, &done);
761           debug_printf ("%s - hp %p low %p, high %p, res %d", what, hp, low, high, res);
762           if (!res || todo != done)
763             {
764               if (!res)
765                 __seterrno ();
766               /* If this happens then there is a bug in our fork
767                  implementation somewhere. */
768               system_printf ("%s %s copy failed, %p..%p, done %d, windows pid %u, %E",
769                             what, huh[write], low, high, done, myself->dwProcessId);
770               goto err;
771             }
772         }
773     }
774
775   va_end (args);
776   debug_printf ("done");
777   return true;
778
779  err:
780   va_end (args);
781   TerminateProcess (hp, 1);
782   set_errno (EAGAIN);
783   return false;
784 }