From dd11f11faee36b8999cd6a7d56318e3be289f897 Mon Sep 17 00:00:00 2001 From: Christopher Faylor Date: Mon, 6 Nov 2000 06:36:32 +0000 Subject: [PATCH] * child_info.h (child_info): Add pppid_handle for closing the parent's of the parent handle. * dcrt0.cc (_dll_crt0): Close parent's parent handle when spawned or forked. * debug.cc (add_handle): Correct erroneous reference to handle structure when printing warning. * exceptions.cc (interrupt_now): Always return 1. (interrupt_on_return): Accept a sigthread argument. Check to see if this argument has been trashed prior to setting up the stack return. (call_handler): Add a loop around attempts to dispatch signals to detect case where interrupt_on_return fails. (_sigdelayed): Set up a temporary frame pointer prior to calling stuff that could trigger an interrupt or the stack walking code will be very confused. * fork.cc (fork_parent): Move a lot of the setup of the child process into proc_subproc. * spawn.cc (spawn_guts): Ditto. Use ppid_handle to contact logical parent when reparenting. * pinfo.h (_pinfo): Remember the logical handle of the parent process. * sigproc.cc (proc_subproc): Record most stuff necessary for the _pinfo structure that is inferrable from myself when adding children. (wait_sig): Always set 'pending_signals' flag when about to kick off the signal scanning loop. Reset it only if there are no pending signals. --- winsup/cygwin/ChangeLog | 27 ++++++ winsup/cygwin/child_info.h | 3 +- winsup/cygwin/dcrt0.cc | 3 +- winsup/cygwin/debug.cc | 3 +- winsup/cygwin/exceptions.cc | 187 ++++++++++++++++++++---------------- winsup/cygwin/fork.cc | 27 +----- winsup/cygwin/pinfo.h | 3 + winsup/cygwin/sigproc.cc | 55 +++++++++-- winsup/cygwin/spawn.cc | 23 +---- 9 files changed, 189 insertions(+), 142 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index ea147443b..a7e48ed45 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,30 @@ +Mon Nov 6 01:04:35 2000 Christopher Faylor + + * child_info.h (child_info): Add pppid_handle for closing the parent's + of the parent handle. + * dcrt0.cc (_dll_crt0): Close parent's parent handle when spawned or + forked. + * debug.cc (add_handle): Correct erroneous reference to handle + structure when printing warning. + * exceptions.cc (interrupt_now): Always return 1. + (interrupt_on_return): Accept a sigthread argument. Check to see if + this argument has been trashed prior to setting up the stack return. + (call_handler): Add a loop around attempts to dispatch signals to + detect case where interrupt_on_return fails. + (_sigdelayed): Set up a temporary frame pointer prior to calling stuff + that could trigger an interrupt or the stack walking code will be very + confused. + * fork.cc (fork_parent): Move a lot of the setup of the child process + into proc_subproc. + * spawn.cc (spawn_guts): Ditto. Use ppid_handle to contact logical + parent when reparenting. + * pinfo.h (_pinfo): Remember the logical handle of the parent process. + * sigproc.cc (proc_subproc): Record most stuff necessary for the _pinfo + structure that is inferrable from myself when adding children. + (wait_sig): Always set 'pending_signals' flag when about to kick off + the signal scanning loop. Reset it only if there are no pending + signals. + Sun Nov 5 13:46:23 2000 Christopher Faylor * pinfo (wait_subproc): Son of neverending debug tweaking. diff --git a/winsup/cygwin/child_info.h b/winsup/cygwin/child_info.h index 725833050..92492476b 100644 --- a/winsup/cygwin/child_info.h +++ b/winsup/cygwin/child_info.h @@ -12,7 +12,7 @@ details. */ enum { - PROC_MAGIC = 0xaf09f000, + PROC_MAGIC = 0xaf0af000, PROC_FORK = PROC_MAGIC + 1, PROC_EXEC = PROC_MAGIC + 2, PROC_SPAWN = PROC_MAGIC + 3, @@ -38,6 +38,7 @@ public: HANDLE console_h; HANDLE parent_alive; // handle of thread used to track children HANDLE parent; + HANDLE pppid_handle; void *cygheap; void *cygheap_max; }; diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 56873b165..faa7cfa6e 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -898,8 +898,9 @@ _dll_crt0 () case PROC_FORK: case PROC_FORK1: user_data->forkee = fork_info->cygpid; - case PROC_EXEC: case PROC_SPAWN: + CloseHandle (fork_info->pppid_handle); + case PROC_EXEC: { child_proc_info = fork_info; mypid = child_proc_info->cygpid; diff --git a/winsup/cygwin/debug.cc b/winsup/cygwin/debug.cc index e25dc1ca8..ebe207ce6 100644 --- a/winsup/cygwin/debug.cc +++ b/winsup/cygwin/debug.cc @@ -228,7 +228,7 @@ newh () goto out; /* All used up??? */ - if ((hl = (handle_list *)malloc (sizeof *hl)) != NULL) + if ((hl = (handle_list *) malloc (sizeof *hl)) != NULL) { memset (hl, 0, sizeof (*hl)); hl->allocated = TRUE; @@ -248,6 +248,7 @@ add_handle (const char *func, int ln, HANDLE h, const char *name) if ((hl = find_handle (h))) { + hl = hl->next; system_printf ("%s:%d - multiple attempts to add handle %s<%p>", func, ln, name, h); system_printf (" previously allocated by %s:%d(%s<%p>)", diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 5fa58890b..4358e6a75 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -30,6 +30,7 @@ static int handle_exceptions (EXCEPTION_RECORD *, void *, CONTEXT *, void *); extern void sigreturn (); extern void sigdelayed (); extern void siglast (); +extern DWORD __no_sig_start, __no_sig_end; }; extern DWORD sigtid; @@ -627,12 +628,13 @@ interrupt_setup (int sig, struct sigaction& siga, void *handler, sigsave.saved_errno = -1; // Flag: no errno to save } -static void +static bool interrupt_now (CONTEXT *ctx, int sig, struct sigaction& siga, void *handler) { interrupt_setup (sig, siga, handler, ctx->Eip, 0); ctx->Eip = (DWORD) sigdelayed; SetThreadContext (myself->getthread2signal (), ctx); /* Restart the thread */ + return 1; } void __stdcall @@ -662,12 +664,13 @@ signal_fixup_after_exec (bool isspawn) } static int -interrupt_on_return (DWORD ebp, int sig, struct sigaction& siga, void *handler) +interrupt_on_return (sigthread *th, int sig, struct sigaction& siga, void *handler) { int i; + DWORD ebp = th->frame; - if (sigsave.sig) - return 0; /* Already have a signal stacked up */ + if (!ebp) + return 0; thestack.init (ebp); /* Initialize from the input CONTEXT */ for (i = 0; i < 32 && thestack++ ; i++) @@ -677,12 +680,16 @@ interrupt_on_return (DWORD ebp, int sig, struct sigaction& siga, void *handler) if (*addr_retaddr == thestack.sf.AddrReturn.Offset) { interrupt_setup (sig, siga, handler, *addr_retaddr, addr_retaddr); + if (ebp != th->frame) + { + sigsave.sig = 0; + break; + } *addr_retaddr = (DWORD) sigdelayed; } return 1; } - api_fatal ("couldn't send signal %d", sig); return 0; } @@ -700,11 +707,10 @@ static int call_handler (int sig, struct sigaction& siga, void *handler) { CONTEXT cx; - int interrupted = 1; + bool interrupted = 0; HANDLE hth = NULL; - DWORD ebp; int res; - int using_mainthread_frame; + sigthread *th; #if 0 mainthread.lock->acquire (); @@ -713,98 +719,101 @@ call_handler (int sig, struct sigaction& siga, void *handler) if (sigsave.sig) goto set_pending; - if (mainthread.frame) + for (int i = 0; !interrupted && i < 10; i++) { - ebp = mainthread.frame; - using_mainthread_frame = 1; - } - else - { - int i; - using_mainthread_frame = 0; -#if 0 - mainthread.lock->release (); -#endif - - hth = myself->getthread2signal (); - /* Suspend the thread which will receive the signal. But first ensure that - this thread doesn't have any mutos. (FIXME: Someday we should just grab - all of the mutos rather than checking for them) - For Windows 95, we also have to ensure that the addresses returned by GetThreadContext - are valid. - If one of these conditions is not true we loop for a fixed number of times - since we don't want to stall the signal handler. FIXME: Will this result in - noticeable delays? - If the thread is already suspended (which can occur when a program is stopped) then - just queue the signal. */ - for (i = 0; i < SUSPEND_TRIES; i++) + if (mainthread.frame) + th = &mainthread; + else { - sigproc_printf ("suspending mainthread"); - res = SuspendThread (hth); - - muto *m; - /* FIXME: Make multi-thread aware */ - for (m = muto_start.next; m != NULL; m = m->next) - if (m->unstable () || m->owner () == mainthread.id) - goto owns_muto; - -#if 0 - mainthread.lock->acquire (); -#endif - if (mainthread.frame) - { - ebp = mainthread.frame; /* try to avoid a race */ - using_mainthread_frame = 1; - goto next; - } -#if 0 + int i; + th = NULL; + #if 0 mainthread.lock->release (); -#endif + #endif - cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - if (!GetThreadContext (hth, &cx)) + hth = myself->getthread2signal (); + /* Suspend the thread which will receive the signal. But first ensure that + this thread doesn't have any mutos. (FIXME: Someday we should just grab + all of the mutos rather than checking for them) + For Windows 95, we also have to ensure that the addresses returned by GetThreadContext + are valid. + If one of these conditions is not true we loop for a fixed number of times + since we don't want to stall the signal handler. FIXME: Will this result in + noticeable delays? + If the thread is already suspended (which can occur when a program is stopped) then + just queue the signal. */ + for (i = 0; i < SUSPEND_TRIES; i++) { - system_printf ("couldn't get context of main thread, %E"); - goto out; + sigproc_printf ("suspending mainthread"); + res = SuspendThread (hth); + + /* Just set pending if thread is already suspended */ + if (res) + goto set_pending; + + muto *m; + /* FIXME: Make multi-thread aware */ + for (m = muto_start.next; m != NULL; m = m->next) + if (m->unstable () || m->owner () == mainthread.id) + goto owns_muto; + + #if 0 + mainthread.lock->acquire (); + #endif + if (mainthread.frame) + { + th = &mainthread; + goto next; + } + #if 0 + mainthread.lock->release (); + #endif + + cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + if (!GetThreadContext (hth, &cx)) + { + system_printf ("couldn't get context of main thread, %E"); + goto out; + } + + if (interruptible (cx.Eip, 1)) + break; + + sigproc_printf ("suspended thread in a strange state pc %p, sp %p", + cx.Eip, cx.Esp); + goto resume_thread; + + owns_muto: + sigproc_printf ("suspended thread owns a muto (%s)", m->name); + + resume_thread: + ResumeThread (hth); + Sleep (0); } - if (interruptible (cx.Eip, 1)) - break; - - sigproc_printf ("suspended thread in a strange state pc %p, sp %p", - cx.Eip, cx.Esp); - goto resume_thread; - - owns_muto: - sigproc_printf ("suspended thread owns a muto (%s)", m->name); - - if (res) + if (i >= SUSPEND_TRIES) goto set_pending; - resume_thread: - ResumeThread (hth); - Sleep (0); + sigproc_printf ("SuspendThread returned %d", res); } - if (i >= SUSPEND_TRIES) - goto set_pending; - - sigproc_printf ("SuspendThread returned %d", res); - ebp = cx.Ebp; + next: + if (th) + interrupted = interrupt_on_return (th, sig, siga, handler); + else if (interruptible (cx.Eip)) + interrupted = interrupt_now (&cx, sig, siga, handler); + else + break; } -next: - if (!using_mainthread_frame && interruptible (cx.Eip)) - interrupt_now (&cx, sig, siga, handler); - else if (!interrupt_on_return (ebp, sig, siga, handler)) +set_pending: + if (!interrupted) { - set_pending: pending_signals = 1; /* FIXME: Probably need to be more tricky here */ sig_set_pending (sig); - interrupted = 0; + sigproc_printf ("couldn't send signal %d", sig); } - - if (interrupted) + else { res = SetEvent (signal_arrived); // For an EINTR case sigproc_printf ("armed signal_arrived %p, res %d", signal_arrived, res); @@ -882,6 +891,9 @@ set_process_mask (sigset_t newmask) mask_sync->release (); if (oldmask != newmask && GetCurrentThreadId () != sigtid) sig_dispatch_pending (); + else + sigproc_printf ("not calling sig_dispatch_pending. sigtid %p current %p", + sigtid, GetCurrentThreadId ()); return; } @@ -1099,7 +1111,7 @@ __asm__ volatile (" .text _sigreturn: - addl $4,%%esp + addl $4,%%esp # Remove argument movl %%esp,%%ebp addl $36,%%ebp call _set_process_mask@4 @@ -1118,6 +1130,7 @@ _sigreturn: popf ret +__no_sig_start: _sigdelayed: pushl %2 # original return address pushf @@ -1132,6 +1145,8 @@ _sigdelayed: pushl %3 # oldmask pushl %4 # signal argument pushl $_sigreturn + pushl %%ebp + movl %%esp,%%esp call _reset_signal_arrived@0 movl $0,%0 @@ -1141,7 +1156,9 @@ _sigdelayed: pushl $0 call _sig_dispatch_pending@4 -2: jmp *%5 +2: popl %%ebp + jmp *%5 +__no_sig_end: " : "=m" (sigsave.sig) : "m" (&_impure_ptr->_errno), "g" (sigsave.retaddr), "g" (sigsave.oldmask), "g" (sigsave.sig), diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 87315a4c2..c5536e2b7 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -471,35 +471,12 @@ fork_parent (void *stack_here, HANDLE& hParent, dll *&first_dll, bool& load_dlls system_printf ("couldn't create last_fork_proc, %E"); /* Fill in fields in the child's process table entry. */ - forked->ppid = myself->pid; forked->hProcess = pi.hProcess; forked->dwProcessId = pi.dwProcessId; - forked->uid = myself->uid; - forked->gid = myself->gid; - forked->pgid = myself->pgid; - forked->sid = myself->sid; - forked->ctty = myself->ctty; - forked->umask = myself->umask; forked->copysigs(myself); - forked->process_state |= PID_INITIALIZING | - (myself->process_state & PID_USETTY); memcpy (forked->username, myself->username, MAX_USER_NAME); - if (myself->use_psid) - { - memcpy (forked->psid, myself->psid, MAX_SID_LEN); - forked->use_psid = 1; - } - memcpy (forked->logsrv, myself->logsrv, MAX_HOST_NAME); - memcpy (forked->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1); - forked->token = myself->token; - forked->impersonated = myself->impersonated; - forked->orig_uid = myself->orig_uid; - forked->orig_gid = myself->orig_gid; - forked->real_uid = myself->real_uid; - forked->real_gid = myself->real_gid; - strcpy (forked->root, myself->root); - forked->rootlen = myself->rootlen; set_child_mmap_ptr (forked); + forked.remember (); /* Wait for subproc to initialize itself. */ if (!sync_with_child(pi, subproc_ready, TRUE, "waiting for longjmp")) @@ -536,8 +513,6 @@ fork_parent (void *stack_here, HANDLE& hParent, dll *&first_dll, bool& load_dlls goto cleanup; } - forked.remember (); - /* 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")) diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h index b078c9bb1..2ff4ba589 100644 --- a/winsup/cygwin/pinfo.h +++ b/winsup/cygwin/pinfo.h @@ -44,6 +44,9 @@ public: /* Handle associated with initial Windows pid which started it all. */ HANDLE pid_handle; + /* Handle to the logical parent of this pid. */ + HANDLE ppid_handle; + /* Parent process id. */ pid_t ppid; diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index 24fe1d0e2..81ba58b4c 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -235,7 +235,7 @@ proc_subproc (DWORD what, DWORD val) if (!get_proc_lock (what, val)) // Serialize access to this function { - sigproc_printf ("I am not ready"); + system_printf ("couldn't get proc lock. Something is wrong."); goto out1; } @@ -253,6 +253,33 @@ proc_subproc (DWORD what, DWORD val) 0, 0, DUPLICATE_SAME_ACCESS)) system_printf ("Couldn't duplicate child handle for pid %d, %E", vchild->pid); ProtectHandle1 (vchild->pid_handle, pid_handle); + + if (!DuplicateHandle (hMainProc, hMainProc, vchild->hProcess, &vchild->ppid_handle, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + system_printf ("Couldn't duplicate my handle<%p> for pid %d, %E", hMainProc, vchild->pid); + vchild->ppid = myself->pid; + vchild->gid = myself->gid; + vchild->pgid = myself->pgid; + vchild->sid = myself->sid; + vchild->ctty = myself->ctty; + vchild->umask = myself->umask; + vchild->orig_uid = myself->orig_uid; + vchild->orig_gid = myself->orig_gid; + vchild->real_uid = myself->real_uid; + vchild->real_gid = myself->real_gid; + vchild->impersonated = myself->impersonated; + if (myself->use_psid) + { + vchild->use_psid = 1; + memcpy (vchild->psid, myself->psid, MAX_SID_LEN); + } + memcpy (vchild->logsrv, myself->logsrv, MAX_HOST_NAME); + memcpy (vchild->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1); + memcpy (vchild->root, myself->root, MAX_PATH+1); + vchild->token = myself->token; + vchild->rootlen = myself->rootlen; + vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); + sigproc_printf ("added pid %d to wait list, slot %d, winpid %p, handle %p", vchild->pid, nchildren, vchild->dwProcessId, vchild->hProcess); @@ -810,6 +837,7 @@ init_child_info (DWORD chtype, child_info *ch, pid_t pid, HANDLE subproc_ready) ch->shared_h = cygwin_shared_h; ch->console_h = console_shared_h; ch->subproc_ready = subproc_ready; + ch->pppid_handle = myself->ppid_handle; if (chtype != PROC_EXEC || !parent_alive) ch->parent_alive = hwait_subproc; else @@ -1100,7 +1128,7 @@ wait_sig (VOID *) HANDLE catchem[] = {sigcatch_main, sigcatch_nonmain, sigcatch_nosync}; sigproc_printf ("Ready. dwProcessid %d", myself->dwProcessId); - for (;;) + for (int i = 0; ; i++) { DWORD rc = WaitForMultipleObjects (3, catchem, FALSE, sig_loop_wait); @@ -1128,7 +1156,8 @@ wait_sig (VOID *) /* A sigcatch semaphore has been signaled. Scan the sigtodo * array looking for any unprocessed signals. */ - pending_signals = 0; + pending_signals = -1; + int saw_pending_signals = 0; int saw_sigchld = 0; int dispatched_sigchld = 0; for (int sig = -__SIGOFFSET; sig < NSIG; sig++) @@ -1179,11 +1208,26 @@ wait_sig (VOID *) } /* Decremented too far. */ if (InterlockedIncrement (myself->getsigtodo(sig)) > 0) - pending_signals = 1; + saw_pending_signals = 1; nextsig: continue; } + /* FIXME: The dispatched stuff probably isn't needed anymore. */ + if (dispatched >= 0 && pending_signals < 0 && !saw_pending_signals) + { + pending_signals = 0; + /* FIXME FIXME FIXME FIXME FIXME + This is a real kludge designed to handle runaway processes who + missed a signal and never processed a signal handler. We have + to reset signal_arrived or stuff goes crazy. */ + if (i >= 20) + { + i = 0; + ResetEvent (signal_arrived); + } + } + if (nzombies && saw_sigchld && !dispatched_sigchld) proc_subproc (PROC_CLEARWAIT, 0); /* Signal completion of signal handling depending on which semaphore @@ -1202,8 +1246,6 @@ wait_sig (VOID *) break; } - if (dispatched < 0) - pending_signals = 1; sigproc_printf ("looping"); } @@ -1258,7 +1300,6 @@ wait_subproc (VOID *) system_printf ("pid %d, dwProcessId %u, hProcess %p, progname '%s'", pchildren[i - 1]->pid, pchildren[i - 1]->dwProcessId, pchildren[i - 1]->hProcess, pchildren[i - 1]->progname); - Sleep (10000); } break; } diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index a430cf954..8e2e490be 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -707,22 +707,6 @@ skip_arg_parsing: } child->username[0] = '\0'; child->progname[0] = '\0'; - child->ppid = myself->pid; - child->gid = myself->gid; - child->pgid = myself->pgid; - child->sid = myself->sid; - child->ctty = myself->ctty; - child->umask = myself->umask; - child->process_state |= PID_INITIALIZING; - if (myself->use_psid) - { - child->use_psid = 1; - memcpy (child->psid, myself->psid, MAX_SID_LEN); - } - memcpy (child->logsrv, myself->logsrv, MAX_HOST_NAME); - memcpy (child->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1); - memcpy (child->root, myself->root, MAX_PATH+1); - child->rootlen = myself->rootlen; child->dwProcessId = pi.dwProcessId; child->hProcess = pi.hProcess; child.remember (); @@ -808,20 +792,17 @@ skip_arg_parsing: { int rc = 0; HANDLE oldh = myself->hProcess; - HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, - parent->dwProcessId); + HANDLE h = myself->ppid_handle; sigproc_printf ("parent handle %p, pid %d", h, parent->dwProcessId); if (h == NULL && GetLastError () == ERROR_INVALID_PARAMETER) - rc = 1; + rc = 0; else if (h) { - ProtectHandle (h); rc = DuplicateHandle (hMainProc, pi.hProcess, h, &myself->hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS); sigproc_printf ("%d = DuplicateHandle, oldh %p, newh %p", rc, oldh, myself->hProcess); - ForceCloseHandle (h); } if (!rc) {