diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index dc1598106..74423f2f4 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,65 @@ +2004-12-05 Christopher Faylor + + * sigproc.cc (mychild): Reimplement as list scan. + (proc_subproc): Don't mess with pinfo if it's myself. + +2004-12-05 Christopher Faylor + + * child_info.h (child_info_types): Label enum for _PROC constants. + (child_info::child_info): New constructor. + (child_info::~child_info): New destructor. + (child_info::sync): Declare new function. + (child_info_fork::child_info_fork): New constructor. + (child_info_spawn::child_info_spawn): Remove old constructor. + (child_info_spawn::child_info_spawn): New constructor. + * dcrt0.cc (dll_crt0_0): Use correct sizeof when doing sanity check on + passed in child_info. Signal readiness to parent when not forking (and + not spawning). + * fork.cc (sync_with_child): Delete. + (resume_child): Remove extra argument. + (sync_with_parent): Use child_info method to sync with parent. + (fork_child): Don't close fork_info->subproc_ready since that is now + handled by the destructor. + (fork_parent): Remove subproc_ready stuff. Use child_info sync method + for waiting.. Set start time here for child. Rename "forked" to + "child". + (fork): Check ch.subproc_ready for validity here. + * pinfo.h (_pinfo::exec_sendsig): Temp storage for exec stub which may + be staying around to handle non-cygwin captive process. + (_pinfo::exec_dwProcessId): Ditto. + (_pinfo::_lock): Renamed from lock. + (_pinfo::lock): New method. + (_pinfo::unlock): Ditto. + (_pinfo::initialize_lock): Ditto. + * pinfo.cc (set_myself): Use initialize_lock method to initialize + myself lock. Set "exec" fields in _pinfo to zero to indicate that + we've started successfully. Set start time here when appropriate. + (_pinfo::commune_send): Use pinfo lock/unlock methods. + (proc_waiter): Remove special case for non-cywin processes. + Reinstitute handling for PID_NOCLDSTOP. + * sigproc.cc (proc_subproc): Set proper EAGAIN errno when process table + is filled. + (sig_send): Use exec_* fields from _pinfo for sending signals if the + the _pinfo sendsig never materializes. + (child_info::child_info): New constructor, renamed from init_child_info. + Zeroes child_info structure and sets appropriate fields in structure + based on chtype. + (child_info::~child_info): New destructor. Closes subproc_ready if it + exists. + (child_info_fork::child_info_fork): New constructor. + (child_info_spawn::child_info_spawn): New constructor. + (child_info::ready): New function. Signals parent when child is ready. + (child_info::sync): New function. Wait for child to signal us or + process to die. + (remove_proc): Remove closing of hProcess since this should now be + handled shortly after process creation. + * spawn.cc (spawn_guts): Use child_info_spawn constructor rather than + init_child_info. Save exec_sendsig and exec_dwProcessId in execing + _pinfo. Rely on child_info constructor to properly set + parent_wr_proc_pipe in ciresrv. Revert to previous determination on + whether to start a process in suspended mode. Remove reparenting + stuff. Just keep a stub around if starting a non-cygwin process. + 2004-12-05 Bas van Gompel * fhandler.cc (fhandler_base::read): Remove superfluous check in diff --git a/winsup/cygwin/child_info.h b/winsup/cygwin/child_info.h index 48186e67c..601fdfd8b 100644 --- a/winsup/cygwin/child_info.h +++ b/winsup/cygwin/child_info.h @@ -10,7 +10,7 @@ details. */ #include -enum +enum child_info_types { _PROC_EXEC, _PROC_SPAWN, @@ -51,6 +51,10 @@ public: HANDLE cygheap_h; HANDLE parent_wr_proc_pipe; unsigned fhandler_union_cb; + child_info (unsigned, child_info_types); + ~child_info (); + void ready (bool); + bool sync (pinfo&, DWORD); }; class mount_info; @@ -64,6 +68,7 @@ public: jmp_buf jmp; // where child will jump to void *stacktop; // location of top of parent stack void *stackbottom; // location of bottom of parent stack + child_info_fork (); }; class fhandler_base; @@ -84,7 +89,6 @@ class child_info_spawn: public child_info public: cygheap_exec_info *moreinfo; - child_info_spawn (): moreinfo (NULL) {} ~child_info_spawn () { if (moreinfo) @@ -101,6 +105,7 @@ public: cfree (moreinfo); } } + child_info_spawn (child_info_types); }; void __stdcall init_child_info (DWORD, child_info *, HANDLE); diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 51b197e4c..bd9cf1cb1 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -618,13 +618,15 @@ dll_crt0_0 () case _PROC_SPAWN: case _PROC_EXEC: if (!should_be_cb) - should_be_cb = sizeof (child_info); + should_be_cb = sizeof (child_info_spawn); if (should_be_cb != child_proc_info->cb) multiple_cygwin_problem ("proc size", child_proc_info->cb, should_be_cb); else if (sizeof (fhandler_union) != child_proc_info->fhandler_union_cb) multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union)); else { + if (child_proc_info->type != _PROC_FORK) + child_proc_info->ready (true); cygwin_user_h = child_proc_info->user_h; break; } diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 4744202af..422b191a9 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -107,68 +107,8 @@ fork_copy (PROCESS_INFORMATION &pi, const char *what, ...) return 0; } -/* Wait for child to finish what it's doing and signal us. - We don't want to wait forever here.If there's a problem somewhere - it'll hang the entire system (since all forks are mutex'd). If we - time out, set errno = EAGAIN and hope the app tries again. */ static int -sync_with_child (PROCESS_INFORMATION &pi, HANDLE subproc_ready, - bool hang_child, const char *s) -{ - /* We also add the child process handle to the wait. If the child fails - to initialize (eg. because of a missing dll). Then this - handle will become signalled. This stops a *looong* timeout wait. - */ - HANDLE w4[2]; - - debug_printf ("waiting for child. reason: %s, hang_child %d", s, - hang_child); - w4[1] = pi.hProcess; - w4[0] = subproc_ready; - DWORD rc = WaitForMultipleObjects (2, w4, FALSE, FORK_WAIT_TIMEOUT); - - if (rc == WAIT_OBJECT_0 || - WaitForSingleObject (subproc_ready, 0) == WAIT_OBJECT_0) - /* That's ok */; - else if (rc == WAIT_FAILED || rc == WAIT_TIMEOUT) - { - if (rc != WAIT_FAILED) - system_printf ("WaitForMultipleObjects timed out"); - else - system_printf ("WaitForMultipleObjects failed, %E"); - set_errno (EAGAIN); - syscall_printf ("-1 = fork(), WaitForMultipleObjects failed"); - TerminateProcess (pi.hProcess, 1); - return 0; - } - else - { - /* Child died. Clean up and exit. */ - DWORD errcode; - GetExitCodeProcess (pi.hProcess, &errcode); - /* Fix me. This is not enough. The fork should not be considered - * to have failed if the process was essentially killed by a signal. - */ - if (errcode != STATUS_CONTROL_C_EXIT) - { - system_printf ("child %u(%p) died before initialization with status code %p", - cygwin_pid (pi.dwProcessId), pi.hProcess, errcode); - system_printf ("*** child state %s", s); -#ifdef DEBUGGING - try_to_debug (); -#endif - } - set_errno (EAGAIN); - syscall_printf ("Child died before subproc_ready signalled"); - return 0; - } - - debug_printf ("child signalled me"); - return 1; -} - -static int -resume_child (PROCESS_INFORMATION &pi, HANDLE forker_finished) +resume_child (HANDLE forker_finished) { SetEvent (forker_finished); debug_printf ("signalled child"); @@ -182,9 +122,7 @@ static void __stdcall sync_with_parent (const char *s, bool hang_self) { debug_printf ("signalling parent: %s", s); - /* Tell our parent we're waiting. */ - if (!SetEvent (fork_info->subproc_ready)) - api_fatal ("fork child - SetEvent for %s failed, %E", s); + fork_info->ready (false); if (hang_self) { HANDLE h = fork_info->forker_finished; @@ -281,7 +219,6 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls) } ForceCloseHandle (hParent); - (void) ForceCloseHandle1 (fork_info->subproc_ready, subproc_ready); (void) ForceCloseHandle1 (fork_info->forker_finished, forker_finished); _my_tls.fixup_after_fork (); @@ -308,7 +245,7 @@ slow_pid_reuse (HANDLE h) if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0]))) nfork_procs = 0; - /* Keep a list of handles to forked processes sitting around to prevent + /* Keep a list of handles to child processes sitting around to prevent Windows from reusing the same pid n times in a row. Having the same pids close in succesion confuses bash. Keeping a handle open will stop windows from reusing the same pid. */ @@ -330,7 +267,7 @@ static int __stdcall fork_parent (HANDLE& hParent, dll *&first_dll, bool& load_dlls, void *stack_here, child_info_fork &ch) { - HANDLE subproc_ready, forker_finished; + HANDLE forker_finished; DWORD rc; PROCESS_INFORMATION pi = {0, NULL, 0, 0}; @@ -379,35 +316,22 @@ fork_parent (HANDLE& hParent, dll *&first_dll, /* This will help some of the confusion. */ fflush (stdout); - subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL); - if (subproc_ready == NULL) - { - CloseHandle (hParent); - system_printf ("unable to allocate subproc_ready event, %E"); - return -1; - } forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL); if (forker_finished == NULL) { CloseHandle (hParent); - CloseHandle (subproc_ready); system_printf ("unable to allocate forker_finished event, %E"); return -1; } - ProtectHandleINH (subproc_ready); ProtectHandleINH (forker_finished); - init_child_info (PROC_FORK, &ch, subproc_ready); - ch.forker_finished = forker_finished; - ch.parent_wr_proc_pipe = myself->wr_proc_pipe == INVALID_HANDLE_VALUE - ? NULL : myself->wr_proc_pipe; stack_base (ch); si.cb = sizeof (STARTUPINFO); - si.lpReserved2 = (LPBYTE)&ch; + si.lpReserved2 = (LPBYTE) &ch; si.cbReserved2 = sizeof (ch); /* Remove impersonation */ @@ -437,7 +361,6 @@ fork_parent (HANDLE& hParent, dll *&first_dll, { __seterrno (); syscall_printf ("CreateProcessA failed, %E"); - ForceCloseHandle (subproc_ready); ForceCloseHandle (forker_finished); /* Restore impersonation */ cygheap->user.reimpersonate (); @@ -457,10 +380,11 @@ fork_parent (HANDLE& hParent, dll *&first_dll, ResumeThread (pi.hThread); } - int forked_pid = cygwin_pid (pi.dwProcessId); - pinfo forked (forked_pid, 1); + int child_pid = cygwin_pid (pi.dwProcessId); + pinfo child (child_pid, 1); + child->start_time = time (NULL); /* Register child's starting time. */ - if (!forked) + if (!child) { syscall_printf ("pinfo failed"); if (get_errno () != ENOMEM) @@ -470,7 +394,7 @@ fork_parent (HANDLE& hParent, dll *&first_dll, /* Initialize things that are done later in dll_crt0_1 that aren't done for the forkee. */ - strcpy (forked->progname, myself->progname); + strcpy (child->progname, myself->progname); /* Restore impersonation */ cygheap->user.reimpersonate (); @@ -478,18 +402,18 @@ fork_parent (HANDLE& hParent, dll *&first_dll, ProtectHandle (pi.hThread); /* Protect the handle but name it similarly to the way it will be called in subproc handling. */ - ProtectHandle1 (pi.hProcess, childhProc); + ProtectHandle (pi.hProcess); /* Fill in fields in the child's process table entry. */ - forked->dwProcessId = pi.dwProcessId; - forked.hProcess = pi.hProcess; + child->dwProcessId = pi.dwProcessId; + child.hProcess = pi.hProcess; /* Hopefully, this will succeed. The alternative to doing things this way is to reserve space prior to calling CreateProcess and then fill it in afterwards. This requires more bookkeeping than I like, though, so we'll just do it the easy way. So, terminate any child process if we can't actually record the pid in the internal table. */ - if (!forked.remember ()) + if (!child.remember ()) { TerminateProcess (pi.hProcess, 1); set_errno (EAGAIN); @@ -501,8 +425,11 @@ fork_parent (HANDLE& hParent, dll *&first_dll, #endif /* Wait for subproc to initialize itself. */ - if (!sync_with_child (pi, subproc_ready, true, "waiting for longjmp")) - goto cleanup; + if (!ch.sync (child, FORK_WAIT_TIMEOUT)) + { + system_printf ("child %d died waiting for longjmp before initialization", child_pid); + goto cleanup; + } /* CHILD IS STOPPED */ debug_printf ("child is alive (but stopped)"); @@ -547,9 +474,13 @@ fork_parent (HANDLE& hParent, dll *&first_dll, } /* Start thread, and wait for it to reload dlls. */ - if (!resume_child (pi, forker_finished) || - !sync_with_child (pi, subproc_ready, load_dlls, "child loading dlls")) + if (!resume_child (forker_finished)) goto cleanup; + else if (!ch.sync (child, FORK_WAIT_TIMEOUT)) + { + system_printf ("child %d died waiting for dll loading", child_pid); + goto cleanup; + } /* If DLLs were loaded in the parent, then the child has reloaded all of them and is now waiting to have all of the individual data and @@ -567,17 +498,17 @@ fork_parent (HANDLE& hParent, dll *&first_dll, goto cleanup; } /* Start the child up again. */ - (void) resume_child (pi, forker_finished); + (void) resume_child (forker_finished); } - ForceCloseHandle (subproc_ready); + ForceCloseHandle (pi.hProcess); ForceCloseHandle (pi.hThread); ForceCloseHandle (forker_finished); forker_finished = NULL; pi.hThread = NULL; pthread::atforkparent (); - return forked_pid; + return child_pid; /* Common cleanup code for failure cases */ cleanup: @@ -589,8 +520,6 @@ fork_parent (HANDLE& hParent, dll *&first_dll, ForceCloseHandle1 (pi.hProcess, childhProc); if (pi.hThread) ForceCloseHandle (pi.hThread); - if (subproc_ready) - ForceCloseHandle (subproc_ready); if (forker_finished) ForceCloseHandle (forker_finished); return -1; @@ -618,6 +547,11 @@ fork () myself->set_has_pgid_children (); child_info_fork ch; + if (ch.subproc_ready == NULL) + { + system_printf ("unable to allocate subproc_ready event, %E"); + return -1; + } sig_send (NULL, __SIGHOLD); int res = setjmp (ch.jmp); diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index b8c99738e..172de6e12 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -87,24 +87,24 @@ unsigned long cygwin_internal (cygwin_getinfo_types, ...); /* Flags associated with process_state */ enum { - PID_IN_USE = 0x0001, /* Entry in use. */ - PID_ZOMBIE = 0x0002, /* Child exited: no parent wait. */ - PID_STOPPED = 0x0004, /* Waiting for SIGCONT. */ - PID_TTYIN = 0x0008, /* Waiting for terminal input. */ - PID_TTYOU = 0x0010, /* Waiting for terminal output. */ - PID_ORPHANED = 0x0020, /* Member of an orphaned process group. */ - PID_ACTIVE = 0x0040, /* Pid accepts signals. */ - PID_CYGPARENT = 0x0080, /* Set if parent was a cygwin app. */ - PID_MAP_RW = 0x0100, /* Flag to open map rw. */ - PID_MYSELF = 0x0200, /* Flag that pid is me. */ - PID_NOCLDSTOP = 0x0400, /* Set if no SIGCHLD signal on stop. */ - PID_INITIALIZING = 0x0800, /* Set until ready to receive signals. */ - PID_USETTY = 0x1000, /* Setting this enables or disables cygwin's */ - /* tty support. This is inherited by */ - /* all execed or forked processes. */ - PID_ALLPIDS = 0x2000, /* child has execed */ - PID_EXECED = 0x4000, /* redirect to original pid info block */ - PID_NOREDIR = 0x8000, /* don't redirect if execed */ + PID_IN_USE = 0x00001, /* Entry in use. */ + PID_ZOMBIE = 0x00002, /* Child exited: no parent wait. */ + PID_STOPPED = 0x00004, /* Waiting for SIGCONT. */ + PID_TTYIN = 0x00008, /* Waiting for terminal input. */ + PID_TTYOU = 0x00010, /* Waiting for terminal output. */ + PID_ORPHANED = 0x00020, /* Member of an orphaned process group. */ + PID_ACTIVE = 0x00040, /* Pid accepts signals. */ + PID_CYGPARENT = 0x00080, /* Set if parent was a cygwin app. */ + PID_MAP_RW = 0x00100, /* Flag to open map rw. */ + PID_MYSELF = 0x00200, /* Flag that pid is me. */ + PID_NOCLDSTOP = 0x00400, /* Set if no SIGCHLD signal on stop. */ + PID_INITIALIZING = 0x00800, /* Set until ready to receive signals. */ + PID_USETTY = 0x01000, /* Setting this enables or disables cygwin's + tty support. This is inherited by + all execed or forked processes. */ + PID_ALLPIDS = 0x02000, /* used by pinfo scanner */ + PID_EXECED = 0x04000, /* redirect to original pid info block */ + PID_NOREDIR = 0x08000, /* don't redirect if execed */ PID_EXITED = 0x80000000 /* Free entry. */ }; diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 62dd63d5d..ebc6d07b9 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -50,21 +50,25 @@ set_myself (HANDLE h) cygheap->pid = cygwin_pid (GetCurrentProcessId ()); myself.init (cygheap->pid, PID_IN_USE | PID_MYSELF, h); myself->process_state |= PID_IN_USE; - myself->start_time = time (NULL); /* Register our starting time. */ + myself->dwProcessId = GetCurrentProcessId (); (void) GetModuleFileName (NULL, myself->progname, sizeof (myself->progname)); if (!strace.active) strace.hello (); debug_printf ("myself->dwProcessId %u", myself->dwProcessId); - InitializeCriticalSection (&myself.lock); - myself->dwProcessId = GetCurrentProcessId (); + myself.initialize_lock (); if (h) { /* here if execed */ static pinfo NO_COPY myself_identity; myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED); + myself->start_time = time (NULL); /* Register our starting time. */ + myself->exec_sendsig = NULL; + myself->exec_dwProcessId = 0; } - else if (myself->wr_proc_pipe) + else if (!myself->wr_proc_pipe) + myself->start_time = time (NULL); /* Register our starting time. */ + else { /* We've inherited the parent's wr_proc_pipe. We don't need it, so close it. */ @@ -522,7 +526,7 @@ _pinfo::commune_send (DWORD code, ...) __seterrno (); goto err; } - EnterCriticalSection (&myself.lock); + myself.lock (); myself->tothem = tome; myself->fromthem = fromme; myself->hello_pid = pid; @@ -626,7 +630,7 @@ err: out: myself->hello_pid = 0; - LeaveCriticalSection (&myself.lock); + myself.unlock (); return res; } @@ -710,13 +714,6 @@ proc_waiter (void *arg) /* Child exited. Do some cleanup and signal myself. */ CloseHandle (vchild.rd_proc_pipe); vchild.rd_proc_pipe = NULL; - - if (vchild->process_state != PID_EXITED && vchild.hProcess) - { - DWORD exit_code; - if (GetExitCodeProcess (vchild.hProcess, &exit_code)) - vchild->exitcode = (exit_code & 0xff) << 8; - } if (WIFEXITED (vchild->exitcode)) si.si_sigval.sival_int = CLD_EXITED; else if (WCOREDUMP (vchild->exitcode)) @@ -730,33 +727,13 @@ proc_waiter (void *arg) case SIGTTOU: case SIGTSTP: case SIGSTOP: + if (ISSTATE (myself, PID_NOCLDSTOP)) // FIXME: No need for this flag to be in _pinfo any longer + continue; /* Child stopped. Signal myself. */ si.si_sigval.sival_int = CLD_STOPPED; break; case SIGCONT: continue; - case __ALERT_REPARENT: /* sigh */ - /* spawn_guts has signalled us that it has just started a new - subprocess which will take over this cygwin pid. */ - - /* We need to keep a handle to the original windows process which - represents the cygwin process around to make sure that the - windows pid is not reused before we are through with it. - So, detect the first time that a subprocess calls exec - and save the current hprocess in the pid_handle field. - On subsequent execs just close the handle. */ - if (!vchild.hProcess) - /* something went wrong. oh well. */; - else if (vchild.pid_handle) - ForceCloseHandle1 (vchild.hProcess, childhProc); - else - vchild.pid_handle = vchild.hProcess; - vchild.hProcess = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, - vchild->dwProcessId); - vchild->cygstarted++; - if (vchild.hProcess) - ProtectHandle1 (vchild.hProcess, childhProc); - continue; default: system_printf ("unknown value %d on proc pipe", buf); continue; @@ -790,6 +767,40 @@ proc_waiter (void *arg) return 0; } +void +proc_pipe::set (bool closeem) +{ + myself.lock (); + if (!CreatePipe (&in, &out, &sec_none_nih, 16)) + { + system_printf ("couldn't create pipe, %E"); + return; + } + /* Duplicate the write end of the pipe into the subprocess. Make it inheritable + so that all of the execed children get it. */ + if (!DuplicateHandle (hMainProc, out, hMainProc, &out, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) + { + CloseHandle (in); + in = out = NULL; + system_printf ("couldn't make handle %p noninheritable, %E", out); + return; + } + _closeem = closeem; +} + +proc_pipe::~proc_pipe () +{ + if (_closeem) + { + if (in) + CloseHandle (in); + if (out) + CloseHandle (out); + } + myself.unlock (); +} + /* function to set up the process pipe and kick off proc_waiter */ int pinfo::wait () diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h index e1d5d35f6..c249fe174 100644 --- a/winsup/cygwin/pinfo.h +++ b/winsup/cygwin/pinfo.h @@ -111,6 +111,8 @@ public: /* signals */ HANDLE sendsig; + HANDLE exec_sendsig; + DWORD exec_dwProcessId; private: sigset_t sig_mask; public: @@ -132,14 +134,14 @@ class pinfo public: HANDLE rd_proc_pipe; HANDLE hProcess; - CRITICAL_SECTION lock; + CRITICAL_SECTION _lock; /* Handle associated with initial Windows pid which started it all. */ HANDLE pid_handle; void init (pid_t, DWORD, HANDLE = NULL) __attribute__ ((regparm(3))); pinfo () {} pinfo (_pinfo *x): procinfo (x), hProcess (NULL), pid_handle (NULL) {} pinfo (pid_t n) : rd_proc_pipe (NULL), hProcess (NULL), pid_handle (NULL) {init (n, 0);} - pinfo (pid_t n, DWORD flag) : rd_proc_pipe (NULL), hProcess (NULL), pid_handle (NULL) {init (n, flag);} + pinfo (pid_t n, DWORD flag) : rd_proc_pipe (NULL), hProcess (NULL), pid_handle (NULL) {init (n, flag);} void release (); int wait () __attribute__ ((regparm (1))); ~pinfo () @@ -147,7 +149,9 @@ public: if (destroy && procinfo) release (); } - + void initialize_lock () {InitializeCriticalSection (&_lock);} + void lock () {EnterCriticalSection (&_lock);} + void unlock () {LeaveCriticalSection (&_lock);} _pinfo *operator -> () const {return procinfo;} int operator == (pinfo *x) const {return x->procinfo == procinfo;} int operator == (pinfo &x) const {return x.procinfo == procinfo;} @@ -175,6 +179,20 @@ public: void set_acl(); }; +class proc_pipe +{ + bool _closeem; +public: + HANDLE in; + HANDLE out; + void set (bool); + proc_pipe (bool closeem) {set (closeem);} + proc_pipe () : _closeem (false), in (NULL), out (NULL) {}; + void close () {_closeem = true;} + ~proc_pipe (); + int operator == (int x) {return (int) in == x;} +}; + #define ISSTATE(p, f) (!!((p)->process_state & f)) #define NOTSTATE(p, f) (!((p)->process_state & f)) diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index b1fea5219..d7c57b1d2 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -200,19 +200,18 @@ proc_exists (_pinfo *p) return p && !(p->process_state & (PID_EXITED | PID_ZOMBIE)); } -/* Return 1 if this is one of our children, zero otherwise. - FIXME: This really should be integrated with the rest of the proc_subproc - testing. Scanning these lists twice is inefficient. */ -bool __stdcall +/* Return true if this is one of our children, false otherwise. */ +static inline bool __stdcall mychild (int pid) { - pinfo p (pid); - return p && p->ppid == myself->pid; + for (int i = 0; i < nprocs; i++) + if (procs[i]->pid == pid) + return true; + return false; } /* Handle all subprocess requests */ -#define vchild (*((pinfo *) val)) int __stdcall proc_subproc (DWORD what, DWORD val) { @@ -223,6 +222,7 @@ proc_subproc (DWORD what, DWORD val) waitq *w; #define wval ((waitq *) val) +#define vchild (*((pinfo *) val)) sigproc_printf ("args: %x, %d", what, val); @@ -244,18 +244,21 @@ proc_subproc (DWORD what, DWORD val) sigproc_printf ("proc table overflow: hit %d processes, pid %d\n", nprocs, vchild->pid); rc = 0; - set_errno (EMFILE); // FIXMENOW - what's the right errno? + set_errno (EAGAIN); break; } - vchild->ppid = myself->pid; - vchild->uid = myself->uid; - vchild->gid = myself->gid; - vchild->pgid = myself->pgid; - vchild->sid = myself->sid; - vchild->ctty = myself->ctty; - vchild->cygstarted = true; - vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); + if (vchild != myself) + { + vchild->ppid = myself->pid; + vchild->uid = myself->uid; + vchild->gid = myself->gid; + vchild->pgid = myself->pgid; + vchild->sid = myself->sid; + vchild->ctty = myself->ctty; + vchild->cygstarted = true; + vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); + } procs[nprocs] = vchild; rc = procs[nprocs].wait (); if (rc) @@ -353,6 +356,8 @@ out: out1: sigproc_printf ("returning %d", rc); return rc; +#undef wval +#undef vchild } // FIXME: This is inelegant @@ -566,9 +571,27 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) sendsig = myself->sendsig; else { - for (int i = 0; !p->dwProcessId && i < 10000; i++) + HANDLE dupsig; + DWORD dwProcessId; + for (int i = 0; !p->sendsig && i < 10000; i++) low_priority_sleep (0); - HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId); + if (p->sendsig) + { + dupsig = p->sendsig; + dwProcessId = p->dwProcessId; + } + else + { + dupsig = p->exec_sendsig; + dwProcessId = p->exec_dwProcessId; + } + if (!dupsig) + { + set_errno (EAGAIN); + sigproc_printf ("sendsig handle never materialized"); + goto out; + } + HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId); if (!hp) { sigproc_printf ("OpenProcess failed, %E"); @@ -576,14 +599,12 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) goto out; } VerifyHandle (hp); - for (int i = 0; !p->sendsig && i < 10000; i++) - low_priority_sleep (0); - if (!DuplicateHandle (hp, p->sendsig, hMainProc, &sendsig, false, 0, + if (!DuplicateHandle (hp, dupsig, hMainProc, &sendsig, false, 0, DUPLICATE_SAME_ACCESS) || !sendsig) { - CloseHandle (hp); - sigproc_printf ("DuplicateHandle failed, %E"); __seterrno (); + sigproc_printf ("DuplicateHandle failed, %E"); + CloseHandle (hp); goto out; } CloseHandle (hp); @@ -695,17 +716,98 @@ out: /* Initialize some of the memory block passed to child processes by fork/spawn/exec. */ -void __stdcall -init_child_info (DWORD chtype, child_info *ch, HANDLE subproc_ready) +child_info::child_info (unsigned in_cb, child_info_types chtype) { - memset (ch, 0, sizeof *ch); - ch->cb = chtype == PROC_FORK ? sizeof (child_info_fork) : sizeof (child_info); - ch->intro = PROC_MAGIC_GENERIC; - ch->magic = CHILD_INFO_MAGIC; - ch->type = chtype; - ch->subproc_ready = subproc_ready; - ch->fhandler_union_cb = sizeof (fhandler_union); - ch->user_h = cygwin_user_h; + memset (this, 0, in_cb); + cb = in_cb; + intro = PROC_MAGIC_GENERIC; + magic = CHILD_INFO_MAGIC; + type = chtype; + fhandler_union_cb = sizeof (fhandler_union); + user_h = cygwin_user_h; + if (chtype != PROC_SPAWN) + subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL); + sigproc_printf ("subproc_ready %p", subproc_ready); + if (chtype != PROC_EXEC && myself->wr_proc_pipe != INVALID_HANDLE_VALUE) + parent_wr_proc_pipe = myself->wr_proc_pipe; +} + +child_info::~child_info () +{ + if (subproc_ready) + CloseHandle (subproc_ready); +} + +child_info_fork::child_info_fork () : + child_info (sizeof *this, _PROC_FORK) +{ +} + +child_info_spawn::child_info_spawn (child_info_types chtype) : + child_info (sizeof *this, chtype) +{ +} + +void +child_info::ready (bool execed) +{ + if (!subproc_ready) + { + sigproc_printf ("subproc_ready not set"); + return; + } + + if (!SetEvent (subproc_ready)) + api_fatal ("SetEvent failed"); + else + sigproc_printf ("signalled %p that I was ready", subproc_ready); + + if (execed) + { + CloseHandle (subproc_ready); + subproc_ready = NULL; + } +} + +bool +child_info::sync (pinfo& vchild, DWORD howlong) +{ + if (!subproc_ready) + { + sigproc_printf ("not waiting. subproc_ready is NULL"); + return false; + } + + HANDLE w4[2]; + w4[0] = subproc_ready; + w4[1] = vchild.hProcess; + + bool res; + sigproc_printf ("waiting for subproc_ready(%p) and child process(%p)", w4[0], w4[1]); + switch (WaitForMultipleObjects (2, w4, FALSE, howlong)) + { + case WAIT_OBJECT_0: + sigproc_printf ("got subproc_ready for pid %d", vchild->pid); + res = true; + break; + case WAIT_OBJECT_0 + 1: + if (WaitForSingleObject (subproc_ready, 0) == WAIT_OBJECT_0) + sigproc_printf ("should never happen. noticed subproc_ready after process exit"); + else + { + DWORD exitcode = 0; + (void) GetExitCodeProcess (vchild.hProcess, &exitcode); + vchild->exitcode = (exitcode & 0xff) << 8; + sigproc_printf ("non-cygwin exit value is %p", exitcode); + } + res = false; + break; + default: + system_printf ("wait failed, pid %d, %E", vchild->pid); + res = false; + break; + } + return res; } /* Check the state of all of our children to see if any are stopped or diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 4eb231efd..b161b987a 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -372,17 +372,15 @@ spawn_guts (const char * prog_arg, const char *const *argv, STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}; - child_info_spawn ciresrv; - si.lpReserved2 = (LPBYTE) &ciresrv; - si.cbReserved2 = sizeof (ciresrv); - - DWORD chtype; + child_info_types chtype; if (mode != _P_OVERLAY) chtype = PROC_SPAWN; else chtype = PROC_EXEC; - init_child_info (chtype, &ciresrv, NULL); + child_info_spawn ciresrv (chtype); + si.lpReserved2 = (LPBYTE) &ciresrv; + si.cbReserved2 = sizeof (ciresrv); ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info)); ciresrv.moreinfo->old_title = NULL; @@ -616,16 +614,21 @@ spawn_guts (const char * prog_arg, const char *const *argv, if (mode == _P_DETACH || !set_console_state_for_spawn ()) flags |= DETACHED_PROCESS; - HANDLE saved_sendsig; + bool reset_sendsig = false; if (mode != _P_OVERLAY) - saved_sendsig = NULL; + myself->exec_sendsig = NULL; else { /* Reset sendsig so that any process which wants to send a signal to this pid will wait for the new process to become active. Save the old value in case the exec fails. */ - saved_sendsig = myself->sendsig; - myself->sendsig = INVALID_HANDLE_VALUE; + if (!myself->exec_sendsig) + { + myself->exec_sendsig = myself->sendsig; + myself->exec_dwProcessId = myself->dwProcessId; + myself->sendsig = NULL; + reset_sendsig = true; + } /* Save a copy of a handle to the current process around the first time we exec so that the pid will not be reused. Why did I stop cygwin from generating its own pids again? */ @@ -636,15 +639,13 @@ spawn_guts (const char * prog_arg, const char *const *argv, ProtectHandle (cygheap->pid_handle); else system_printf ("duplicate to pid_handle failed, %E"); - ciresrv.parent_wr_proc_pipe = myself->wr_proc_pipe; } - /* Start the process in a suspended state. Needed so that any potential parent will - be able to take notice of the new "execed" process. This is only really needed - to handle exec'ed windows processes since cygwin processes are smart enough that - the parent doesn't have to bother but what are you gonna do? Cygwin lives in - a windows world. */ - if (mode != _P_OVERLAY || !real_path.iscygexec ()) + /* Some file types (currently only sockets) need extra effort in the parent + after CreateProcess and before copying the datastructures to the child. + So we have to start the child in suspend state, unfortunately, to avoid + a race condition. */ + if (mode != _P_OVERLAY || cygheap->fdtab.need_fixup_before ()) flags |= CREATE_SUSPENDED; const char *runpath = null_app_name ? NULL : (const char *) real_path; @@ -739,8 +740,11 @@ spawn_guts (const char * prog_arg, const char *const *argv, __seterrno (); syscall_printf ("CreateProcess failed, %E"); /* If this was a failed exec, restore the saved sendsig. */ - if (saved_sendsig) - myself->sendsig = saved_sendsig; + if (reset_sendsig) + { + myself->sendsig = myself->exec_sendsig; + myself->exec_sendsig = NULL; + } cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0); return -1; } @@ -780,41 +784,32 @@ spawn_guts (const char * prog_arg, const char *const *argv, rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf); /* Name the handle similarly to proc_subproc. */ - ProtectHandle1 (pi.hProcess, childhProc); + ProtectHandle (pi.hProcess); - int wait_for_myself = false; - DWORD exec_cygstarted; + bool wait_for_myself = false; if (mode == _P_OVERLAY) { - if (!real_path.iscygexec ()) - { - /* Store the old exec_cygstarted since this is used as a crude semaphore for - detecting when the parent has noticed the change in windows pid for this - cygwin pid. */ - exec_cygstarted = myself->cygstarted; - myself->dwProcessId = dwExeced = pi.dwProcessId; /* Reparenting needs this */ - myself.alert_parent (__ALERT_REPARENT); - } - CloseHandle (saved_sendsig); + myself->dwProcessId = dwExeced = pi.dwProcessId; strace.execing = 1; - hExeced = pi.hProcess; + myself.hProcess = hExeced = pi.hProcess; strcpy (myself->progname, real_path); // FIXME: race? + sigproc_printf ("new process name %s", myself->progname); close_all_files (); - /* If wr_proc_pipe is NULL then this process was not started by a cygwin - process. So, we need to wait around until the process we've just "execed" - dies. Use our own wait facility to wait for our own pid to exit (there - is some minor special case code in proc_waiter and friends to accommodate - this). */ + /* If wr_proc_pipe doesn't exist then this process was not started by a cygwin + process. So, we need to wait around until the process we've just "execed" + dies. Use our own wait facility to wait for our own pid to exit (there + is some minor special case code in proc_waiter and friends to accommodeate + this). */ if (!myself->wr_proc_pipe) - { - myself.hProcess = pi.hProcess; - myself.remember (); - wait_for_myself = true; - } + { + myself.hProcess = pi.hProcess; + myself.remember (); + wait_for_myself = true; + myself->wr_proc_pipe = INVALID_HANDLE_VALUE; + } } else { - exec_cygstarted = 0; myself->set_has_pgid_children (); ProtectHandle (pi.hThread); pinfo child (cygpid, PID_IN_USE); @@ -830,8 +825,9 @@ spawn_guts (const char * prog_arg, const char *const *argv, child.hProcess = pi.hProcess; if (!child.remember ()) { - syscall_printf ("process table full"); - set_errno (EAGAIN); + /* FIXME: Child in strange state now. */ + CloseHandle (pi.hProcess); + CloseHandle (pi.hThread); res = -1; goto out; } @@ -844,222 +840,217 @@ spawn_guts (const char * prog_arg, const char *const *argv, However, we should try to find another way to do this eventually. */ (void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess, NULL, 0, 0, DUPLICATE_SAME_ACCESS); + child->start_time = time (NULL); /* Register child's starting time. */ } - /* Start the child running */ - if (flags & CREATE_SUSPENDED) - ResumeThread (pi.hThread); - ForceCloseHandle (pi.hThread); - // ForceCloseHandle (pi.hProcess); // handled by proc_subproc and friends +/* Start the child running */ +if (flags & CREATE_SUSPENDED) + ResumeThread (pi.hThread); +ForceCloseHandle (pi.hThread); - sigproc_printf ("spawned windows pid %d", pi.dwProcessId); +sigproc_printf ("spawned windows pid %d", pi.dwProcessId); - if (wait_for_myself) - waitpid (myself->pid, &res, 0); - else - { - /* Loop, waiting for parent to notice pid change, if exec_cygstarted. - In theory this wait should usually be a no-op. */ - if (exec_cygstarted) - while (myself->cygstarted == exec_cygstarted && myself.parent_alive ()) - low_priority_sleep (0); - res = 42; - } +if (wait_for_myself) + waitpid (myself->pid, &res, 0); +else + ciresrv.sync (myself, INFINITE); - switch (mode) - { - case _P_OVERLAY: - myself->exit (res, 1); - break; - case _P_WAIT: - case _P_SYSTEM: - if (waitpid (cygpid, (int *) &res, 0) != cygpid) - res = -1; - break; - case _P_DETACH: - res = 0; /* Lose all memory of this child. */ - break; - case _P_NOWAIT: - case _P_NOWAITO: - case _P_VFORK: - res = cygpid; - break; - default: - break; - } +ForceCloseHandle (pi.hProcess); + +switch (mode) + { + case _P_OVERLAY: + myself->exit (res, 1); + break; + case _P_WAIT: + case _P_SYSTEM: + if (waitpid (cygpid, (int *) &res, 0) != cygpid) + res = -1; + break; + case _P_DETACH: + res = 0; /* Lose all memory of this child. */ + break; + case _P_NOWAIT: + case _P_NOWAITO: + case _P_VFORK: + res = cygpid; + break; + default: + break; + } out: - pthread_cleanup_pop (1); - return (int) res; +pthread_cleanup_pop (1); +return (int) res; } extern "C" int cwait (int *result, int pid, int) { - return waitpid (pid, result, 0); +return waitpid (pid, result, 0); } /* - * Helper function for spawn runtime calls. - * Doesn't search the path. - */ +* Helper function for spawn runtime calls. +* Doesn't search the path. +*/ extern "C" int spawnve (int mode, const char *path, const char *const *argv, - const char *const *envp) + const char *const *envp) { - int ret; +int ret; #ifdef NEWVFORK - vfork_save *vf = vfork_storage.val (); +vfork_save *vf = vfork_storage.val (); - if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY) - mode = _P_NOWAIT; - else - vf = NULL; +if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY) + mode = _P_NOWAIT; +else + vf = NULL; #endif - syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp); +syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp); - switch (mode) - { - case _P_OVERLAY: - /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/ - /* Just act as an exec if _P_OVERLAY set. */ - spawn_guts (path, argv, envp, mode); - /* Errno should be set by spawn_guts. */ - ret = -1; - break; - case _P_VFORK: - case _P_NOWAIT: - case _P_NOWAITO: - case _P_WAIT: - case _P_DETACH: - case _P_SYSTEM: - ret = spawn_guts (path, argv, envp, mode); +switch (mode) + { + case _P_OVERLAY: + /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/ + /* Just act as an exec if _P_OVERLAY set. */ + spawn_guts (path, argv, envp, mode); + /* Errno should be set by spawn_guts. */ + ret = -1; + break; + case _P_VFORK: + case _P_NOWAIT: + case _P_NOWAITO: + case _P_WAIT: + case _P_DETACH: + case _P_SYSTEM: + ret = spawn_guts (path, argv, envp, mode); #ifdef NEWVFORK - if (vf) - { - if (ret > 0) - { - debug_printf ("longjmping due to vfork"); - vf->restore_pid (ret); - } - } + if (vf) + { + if (ret > 0) + { + debug_printf ("longjmping due to vfork"); + vf->restore_pid (ret); + } + } #endif - break; - default: - set_errno (EINVAL); - ret = -1; - break; - } - return ret; + break; + default: + set_errno (EINVAL); + ret = -1; + break; + } +return ret; } /* - * spawn functions as implemented in the MS runtime library. - * Most of these based on (and copied from) newlib/libc/posix/execXX.c - */ +* spawn functions as implemented in the MS runtime library. +* Most of these based on (and copied from) newlib/libc/posix/execXX.c +*/ extern "C" int spawnl (int mode, const char *path, const char *arg0, ...) { - int i; - va_list args; - const char *argv[256]; +int i; +va_list args; +const char *argv[256]; - va_start (args, arg0); - argv[0] = arg0; - i = 1; +va_start (args, arg0); +argv[0] = arg0; +i = 1; - do - argv[i] = va_arg (args, const char *); - while (argv[i++] != NULL); +do + argv[i] = va_arg (args, const char *); +while (argv[i++] != NULL); - va_end (args); +va_end (args); - return spawnve (mode, path, (char * const *) argv, cur_environ ()); +return spawnve (mode, path, (char * const *) argv, cur_environ ()); } extern "C" int spawnle (int mode, const char *path, const char *arg0, ...) { - int i; - va_list args; - const char * const *envp; - const char *argv[256]; +int i; +va_list args; +const char * const *envp; +const char *argv[256]; - va_start (args, arg0); - argv[0] = arg0; - i = 1; +va_start (args, arg0); +argv[0] = arg0; +i = 1; - do - argv[i] = va_arg (args, const char *); - while (argv[i++] != NULL); +do + argv[i] = va_arg (args, const char *); +while (argv[i++] != NULL); - envp = va_arg (args, const char * const *); - va_end (args); +envp = va_arg (args, const char * const *); +va_end (args); - return spawnve (mode, path, (char * const *) argv, (char * const *) envp); +return spawnve (mode, path, (char * const *) argv, (char * const *) envp); } extern "C" int spawnlp (int mode, const char *path, const char *arg0, ...) { - int i; - va_list args; - const char *argv[256]; +int i; +va_list args; +const char *argv[256]; - va_start (args, arg0); - argv[0] = arg0; - i = 1; +va_start (args, arg0); +argv[0] = arg0; +i = 1; - do - argv[i] = va_arg (args, const char *); - while (argv[i++] != NULL); +do + argv[i] = va_arg (args, const char *); +while (argv[i++] != NULL); - va_end (args); +va_end (args); - return spawnvpe (mode, path, (char * const *) argv, cur_environ ()); +return spawnvpe (mode, path, (char * const *) argv, cur_environ ()); } extern "C" int spawnlpe (int mode, const char *path, const char *arg0, ...) { - int i; - va_list args; - const char * const *envp; - const char *argv[256]; +int i; +va_list args; +const char * const *envp; +const char *argv[256]; - va_start (args, arg0); - argv[0] = arg0; - i = 1; +va_start (args, arg0); +argv[0] = arg0; +i = 1; - do - argv[i] = va_arg (args, const char *); - while (argv[i++] != NULL); +do + argv[i] = va_arg (args, const char *); +while (argv[i++] != NULL); - envp = va_arg (args, const char * const *); - va_end (args); +envp = va_arg (args, const char * const *); +va_end (args); - return spawnvpe (mode, path, (char * const *) argv, envp); +return spawnvpe (mode, path, (char * const *) argv, envp); } extern "C" int spawnv (int mode, const char *path, const char * const *argv) { - return spawnve (mode, path, argv, cur_environ ()); +return spawnve (mode, path, argv, cur_environ ()); } extern "C" int spawnvp (int mode, const char *path, const char * const *argv) { - return spawnvpe (mode, path, argv, cur_environ ()); +return spawnvpe (mode, path, argv, cur_environ ()); } extern "C" int spawnvpe (int mode, const char *file, const char * const *argv, - const char * const *envp) + const char * const *envp) { - path_conv buf; - return spawnve (mode, find_exec (file, buf), argv, envp); +path_conv buf; +return spawnve (mode, find_exec (file, buf), argv, envp); }