diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index cb12f151b..aaeac479b 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,53 @@ +2006-03-18 Christopher Faylor + + * child_info.h (CURR_CHILD_INFO_MAGIC): Regenerate. + (child_info::retry): Move here from fork subclass. + (child_info::exit_code): New field. + (child_info::retry_count): Max retry count for process start. + (child_info::proc_retry): Declare new function. + (child_info_fork::retry): Move to parent. + (child_info_fork::fork_retry): Ditto. + * dcrt0.cc (child_info::fork_retry): Rename and move. + (child_info_fork::handle_failure): Move. + (dll_crt0_0): Initialize console handler based on whether we have a + controlling tty or not. Avoid nonsensical check for fork where it can + never occur. + * environ.cc (set_proc_retry): Rename from set_fork_retry. Set + retry_count in child_info. + (parse_thing): Reflect above change. + * exceptions.cc (dummy_ctrl_c_handler): Remove unused variable name. + (ctrl_c_handler): Always return TRUE for the annoying + CTRL_LOGOFF_EVENT. + * fhandler_termios.cc (fhandler_termios::tcsetpgrp): Remove call to + init_console_handler. + * fhandler_tty.cc (fhandler_tty_slave::open): Just call + mange_console_count here and let it decide what to do with initializing + console control handling. + * fork.cc (fork_retry): Remove definition. + (frok::parent): Define static errbuf and use in error messages (not + thread safe yet). Close pi.hThread as soon as possible. Protect + pi.hProcess as soon as possible. Don't set retry_count. That happens + automatically in the constructor now. Accommodate name change from + fork_retry to proc_retry. + * init.cc (dll_entry): Turn off ctrl-c handling early until we know how + it is supposed to be handled. + * pinfo.cc (_pinfo::dup_proc_pipe): Remember original proc pipe value + for failure error message. Tweak debug message slightly. + * sigproc.cc (child_info::retry_count): Define. + (child_info::child_info): Initialize retry count. + (child_info::sync): Set exit code if process dies before + synchronization. + (child_info::proc_retry): Rename from child_info_fork::fork_retry. Use + previously derived exit code. Be more defensive about what is + classified as an error exit. + (child_info_fork::handle_failure): Move here from dcrt0.cc. + * spawn.cc (spawn_guts): Maintain error mode when starting new process + to avoid annoying pop ups. Move deimpersonate call within new loop. + Move envblock freeing to end. Loop if process dies prematurely with + bad exit code. + * syscalls.cc (init_console_handler): Remove hopefully unneeded call to + init_console_handler. + 2006-03-15 Christopher Faylor * cygheap.cc (init_cygheap::manage_console_count): Turn console control diff --git a/winsup/cygwin/child_info.h b/winsup/cygwin/child_info.h index 87da6e453..efca03465 100644 --- a/winsup/cygwin/child_info.h +++ b/winsup/cygwin/child_info.h @@ -29,7 +29,7 @@ enum child_info_types #define EXEC_MAGIC_SIZE sizeof(child_info) -#define CURR_CHILD_INFO_MAGIC 0x88e640f7U +#define CURR_CHILD_INFO_MAGIC 0x482b2eaU /* NOTE: Do not make gratuitous changes to the names or organization of the below class. The layout is checksummed to determine compatibility between @@ -50,11 +50,15 @@ public: DWORD cygheap_reserve_sz; unsigned char straced; unsigned fhandler_union_cb; + int retry; // number of times we've tried to start child process + DWORD exit_code; // process exit code + static int retry_count;// retry count; child_info (unsigned, child_info_types, bool); child_info (): subproc_ready (NULL), parent (NULL) {} ~child_info (); void ready (bool); bool sync (int, HANDLE&, DWORD) __attribute__ ((regparm (3))); + DWORD proc_retry (HANDLE); }; class mount_info; @@ -68,11 +72,9 @@ 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 - int retry; // number of times we've tried to fork child_info_fork (); void handle_fork (); bool handle_failure (DWORD); - DWORD fork_retry (HANDLE); }; class fhandler_base; diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index c309fb36c..52281c9f2 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -642,33 +642,6 @@ get_cygwin_startup_info () return res; } -DWORD -child_info_fork::fork_retry (HANDLE h) -{ - DWORD exit_code; - if (!GetExitCodeProcess (h, &exit_code)) - return STILL_ACTIVE; /* should never happen */ - switch (exit_code) - { - case STATUS_CONTROL_C_EXIT: - case STATUS_DLL_INIT_FAILED: - case STATUS_DLL_INIT_FAILED_LOGOFF: - case EXITCODE_RETRY: - if (retry-- > 0) - return 0; - break; - } - return exit_code; -} - -bool -child_info_fork::handle_failure (DWORD err) -{ - if (retry > 0) - ExitProcess (EXITCODE_RETRY); - return 0; -} - #define dll_data_start &_data_start__ #define dll_data_end &_data_end__ #define dll_bss_start &_bss_start__ @@ -736,7 +709,7 @@ dll_crt0_0 () if (!child_proc_info) { memory_init (); - init_console_handler (myself->ctty >= 0); + init_console_handler (!!GetConsoleCP ()); } else { @@ -757,8 +730,7 @@ dll_crt0_0 () DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) h = NULL; set_myself (h); - if (child_proc_info->type != _PROC_FORK) - child_proc_info->ready (true); + child_proc_info->ready (true); __argc = spawn_info->moreinfo->argc; __argv = spawn_info->moreinfo->argv; envp = spawn_info->moreinfo->envp; @@ -773,7 +745,6 @@ dll_crt0_0 () } break; } - init_console_handler (myself->ctty >= 0); } user_data->resourcelocks->Init (); diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 28bfe3959..1c934349f 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -39,7 +39,6 @@ static bool envcache = true; #ifdef USE_SERVER extern bool allow_server; #endif -extern int fork_retry; static char **lastenviron; @@ -520,9 +519,9 @@ set_chunksize (const char *buf) } static void -set_fork_retry (const char *buf) +set_proc_retry (const char *buf) { - fork_retry = strtoul (buf, NULL, 0); + child_info::retry_count = strtoul (buf, NULL, 0); } static void @@ -594,7 +593,7 @@ static struct parse_thing {"tty", {NULL}, set_process_state, NULL, {{0}, {PID_USETTY}}}, {"winsymlinks", {&allow_winsymlinks}, justset, NULL, {{false}, {true}}}, {"transparent_exe", {&transparent_exe}, justset, NULL, {{false}, {true}}}, - {"fork_retry", {func: set_fork_retry}, isfunc, NULL, {{0}, {5}}}, + {"proc_retry", {func: set_proc_retry}, isfunc, NULL, {{0}, {5}}}, {NULL, {0}, justset, 0, {{0}, {0}}} }; diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 53294cd63..dabfeab62 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -94,7 +94,7 @@ NO_COPY static struct /* Initialization code. */ BOOL WINAPI -dummy_ctrl_c_handler (DWORD dwCtrlType) +dummy_ctrl_c_handler (DWORD) { return TRUE; } @@ -103,6 +103,7 @@ void init_console_handler (bool install_handler) { BOOL res; + while (SetConsoleCtrlHandler (ctrl_c_handler, FALSE)) continue; if (install_handler) @@ -113,6 +114,7 @@ init_console_handler (bool install_handler) res = SetConsoleCtrlHandler (dummy_ctrl_c_handler, TRUE); if (!res) system_printf ("SetConsoleCtrlHandler failed, %E"); + return; } extern "C" void @@ -946,7 +948,7 @@ ctrl_c_handler (DWORD type) if (has_visible_window_station ()) sig_send (myself_nowait, SIGHUP); #endif - return FALSE; + return TRUE; } } diff --git a/winsup/cygwin/fhandler_termios.cc b/winsup/cygwin/fhandler_termios.cc index 9b1098fca..3475f5ed5 100644 --- a/winsup/cygwin/fhandler_termios.cc +++ b/winsup/cygwin/fhandler_termios.cc @@ -82,7 +82,6 @@ fhandler_termios::tcsetpgrp (const pid_t pgid) { case bg_ok: tc->setpgid (pgid); - init_console_handler (!!is_console ()); res = 0; break; case bg_signalled: diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 24ba4e347..2cf551a9f 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -576,9 +576,7 @@ fhandler_tty_slave::open (int flags, mode_t) set_output_handle (to_master_local); set_open_status (); - if (cygheap->manage_console_count ("fhandler_tty_slave::open", 1) == 1 - && !output_done_event && fhandler_console::need_invisible ()) - init_console_handler (TRUE); + cygheap->manage_console_count ("fhandler_tty_slave::open", 1); // FIXME: Do this better someday arch = (fhandler_tty_slave *) cmalloc (HEAP_ARCHETYPES, sizeof (*this)); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 260960895..0360d1a52 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -33,8 +33,6 @@ details. */ #define NPIDS_HELD 4 -int fork_retry = 5; - /* Timeout to wait for child to start, parent to init child, etc. */ /* FIXME: Once things stabilize, bump up to a few minutes. */ #define FORK_WAIT_TIMEOUT (300 * 1000) /* 300 seconds */ @@ -171,6 +169,7 @@ frok::child (void *) sync_with_parent ("loaded dlls", true); } + init_console_handler (myself->ctty >= 0); ForceCloseHandle1 (fork_info->forker_finished, forker_finished); pthread::atforkchild (); @@ -221,6 +220,7 @@ frok::parent (void *stack_here) this_errno = 0; bool fix_impersonation = false; pinfo child; + static char errbuf[256]; pthread::atforkprepare (); @@ -290,7 +290,6 @@ frok::parent (void *stack_here) myself->progname, myself->progname, c_flags, &si, &pi); bool locked = __malloc_lock (); time_t start_time; - ch.retry = fork_retry; while (1) { start_time = time (NULL); @@ -321,22 +320,25 @@ frok::parent (void *stack_here) ResumeThread (pi.hThread); } + CloseHandle (pi.hThread); + + /* Protect the handle but name it similarly to the way it will + be called in subproc handling. */ + ProtectHandle1 (pi.hProcess, childhProc); + strace.write_childpid (ch, pi.dwProcessId); /* Wait for subproc to initialize itself. */ if (!ch.sync (pi.dwProcessId, pi.hProcess, FORK_WAIT_TIMEOUT)) { - DWORD exit_code = ch.fork_retry (pi.hProcess); + DWORD exit_code = ch.proc_retry (pi.hProcess); if (!exit_code) continue; this_errno = EAGAIN; /* Not thread safe, but do we care? */ - static char buf[sizeof("died waiting for longjmp before " - "initialization, retry 4294967295, " - "exit code 0xfffffffff")]; - __small_sprintf (buf, "died waiting for longjmp before initialization, " + __small_sprintf (errbuf, "died waiting for longjmp before initialization, " "retry %d, exit code %p", ch.retry, exit_code); - error = buf; + error = errbuf; goto cleanup; } break; @@ -367,11 +369,6 @@ frok::parent (void *stack_here) cygheap->user.reimpersonate (); fix_impersonation = false; - ProtectHandle (pi.hThread); - /* Protect the handle but name it similarly to the way it will - be called in subproc handling. */ - ProtectHandle1 (pi.hProcess, childhProc); - /* Fill in fields in the child's process table entry. */ child->dwProcessId = pi.dwProcessId; child.hProcess = pi.hProcess; @@ -426,6 +423,11 @@ frok::parent (void *stack_here) if (!rc) { this_errno = get_errno (); + DWORD exit_code; + if (!GetExitCodeProcess (pi.hProcess, &exit_code)) + exit_code = 0xdeadbeef; + __small_sprintf (errbuf, "pid %u, exitval %p", pi.dwProcessId, exit_code); + error = errbuf; goto cleanup; } @@ -440,7 +442,11 @@ frok::parent (void *stack_here) { this_errno = get_errno (); #ifdef DEBUGGING - error = "fork_copy for linked dll data/bss failed"; + DWORD exit_code; + if (!GetExitCodeProcess (pi.hProcess, &exit_code)) + exit_code = 0xdeadbeef; + __small_sprintf (errbuf, "pid %u, exitval %p", pi.dwProcessId, exit_code); + error = errbuf; #endif goto cleanup; } @@ -481,10 +487,8 @@ frok::parent (void *stack_here) resume_child (forker_finished); } - ForceCloseHandle (pi.hThread); ForceCloseHandle (forker_finished); forker_finished = NULL; - pi.hThread = NULL; pthread::atforkparent (); return child_pid; @@ -499,8 +503,6 @@ cleanup: /* Remember to de-allocate the fd table. */ if (pi.hProcess && !child.hProcess) ForceCloseHandle1 (pi.hProcess, childhProc); - if (pi.hThread) - ForceCloseHandle (pi.hThread); if (forker_finished) ForceCloseHandle (forker_finished); debug_printf ("returning -1"); diff --git a/winsup/cygwin/init.cc b/winsup/cygwin/init.cc index 342bb1cfe..f8b6cd652 100644 --- a/winsup/cygwin/init.cc +++ b/winsup/cygwin/init.cc @@ -128,11 +128,12 @@ dll_entry (HANDLE h, DWORD reason, void *static_load) switch (reason) { case DLL_PROCESS_ATTACH: + wincap.init (); + init_console_handler (false); + cygwin_hmodule = (HMODULE) h; dynamically_loaded = (static_load == NULL); - wincap.init (); - /* Is the stack at an unusual address? This is, an address which is in the usual space occupied by the process image, but below the auto load address of DLLs? diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 146b43238..cacfc7913 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -927,7 +927,8 @@ bool _pinfo::dup_proc_pipe (HANDLE hProcess) { DWORD flags = DUPLICATE_SAME_ACCESS; - /* Grr. Can't set DUPLICATE_CLOSE_SOURCE for exec case because we could be + HANDLE orig_wr_proc_pipe = wr_proc_pipe; + /* Can't set DUPLICATE_CLOSE_SOURCE for exec case because we could be execing a non-cygwin process and we need to set the exit value before the parent sees it. */ if (this != myself || is_toplevel_proc) @@ -935,11 +936,12 @@ _pinfo::dup_proc_pipe (HANDLE hProcess) bool res = DuplicateHandle (hMainProc, wr_proc_pipe, hProcess, &wr_proc_pipe, 0, FALSE, flags); if (!res && WaitForSingleObject (hProcess, 0) != WAIT_OBJECT_0) - system_printf ("DuplicateHandle failed, pid %d, hProcess %p, %E", pid, hProcess); + system_printf ("DuplicateHandle failed, pid %d, hProcess %p, wr_proc_pipe %p, %E", + pid, hProcess, orig_wr_proc_pipe); else { wr_proc_pipe_owner = dwProcessId; - sigproc_printf ("closed wr_proc_pipe %p for pid %d(%u)", wr_proc_pipe, + sigproc_printf ("duped wr_proc_pipe %p for pid %d(%u)", wr_proc_pipe, pid, dwProcessId); res = true; } diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index 4cd538c32..a213a5607 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -523,7 +523,7 @@ sig_send (_pinfo *p, int sig) else { #ifdef DEBUGGING - system_printf ("signal %d sent to %p while signals are on hold", p, sig); + system_printf ("signal %d sent to %p while signals are on hold", sig, p); #endif return -1; } @@ -767,6 +767,7 @@ out: return rc; } +int child_info::retry_count = 10; /* Initialize some of the memory block passed to child processes by fork/spawn/exec. */ @@ -785,6 +786,7 @@ child_info::child_info (unsigned in_cb, child_info_types chtype, bool need_subpr cygheap = ::cygheap; cygheap_max = ::cygheap_max; straced = strace.attached (); + retry = child_info::retry_count; /* Create an inheritable handle to pass to the child process. This will allow the child to duplicate handles from the parent to itself. */ parent = NULL; @@ -862,21 +864,63 @@ child_info::sync (pid_t pid, HANDLE& hProcess, DWORD howlong) else { if (x != nsubproc_ready) - res = type != _PROC_FORK; + { + res = false; + GetExitCodeProcess (hProcess, &exit_code); + } else { + res = true; + exit_code = STILL_ACTIVE; if (type == _PROC_EXEC && myself->wr_proc_pipe) { ForceCloseHandle1 (hProcess, childhProc); hProcess = NULL; } - res = true; } sigproc_printf ("pid %u, WFMO returned %d, res %d", pid, x, res); } return res; } +DWORD +child_info::proc_retry (HANDLE h) +{ + switch (exit_code) + { + case STILL_ACTIVE: /* shouldn't happen */ + sigproc_printf ("STILL_ACTIVE? How'd we get here?"); + break; + case STATUS_CONTROL_C_EXIT: + case STATUS_DLL_INIT_FAILED: + case STATUS_DLL_INIT_FAILED_LOGOFF: + case EXITCODE_RETRY: + if (retry-- > 0) + exit_code = 0; + break; + /* Count down non-recognized exit codes more quickly since they aren't + due to known conditions. */ + default: + if ((exit_code & 0xc0000000) != 0xc0000000) + break; + if ((retry -= 2) < 0) + retry = 0; + else + exit_code = 0; + } + if (!exit_code) + ForceCloseHandle1 (h, childhProc); + return exit_code; +} + +bool +child_info_fork::handle_failure (DWORD err) +{ + if (retry > 0) + ExitProcess (EXITCODE_RETRY); + return 0; +} + /* Check the state of all of our children to see if any are stopped or * terminated. */ diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index cac02d988..16955bbb7 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -438,12 +438,13 @@ spawn_guts (const char * prog_arg, const char *const *argv, linebuf one_line; child_info_spawn ch; - char *envblock; + char *envblock = NULL; path_conv real_path; bool reset_sendsig = false; bool null_app_name = false; STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}; + int looped = 0; myfault efault; if (efault.faulted ()) @@ -600,7 +601,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, int flags = GetPriorityClass (hMainProc); sigproc_printf ("priority class %d", flags); - flags |= CREATE_DEFAULT_ERROR_MODE | CREATE_SEPARATE_WOW_VDM; + flags |= /* CREATE_DEFAULT_ERROR_MODE | */CREATE_SEPARATE_WOW_VDM; if (mode == _P_DETACH) flags |= DETACHED_PROCESS; @@ -649,7 +650,6 @@ spawn_guts (const char * prog_arg, const char *const *argv, cygbench ("spawn-guts"); cygheap->fdtab.set_file_pointers_for_exec (); - cygheap->user.deimpersonate (); moreinfo->envp = build_env (envp, envblock, moreinfo->envc, real_path.iscygexec ()); if (!moreinfo->envp || !envblock) @@ -669,6 +669,9 @@ spawn_guts (const char * prog_arg, const char *const *argv, effective vs. real ids. FIXME: If ruid != euid and ruid != saved_uid we currently give up on ruid. The new process will have ruid == euid. */ +loop: + cygheap->user.deimpersonate (); + if (!cygheap->user.issetuid () || (cygheap->user.saved_uid == cygheap->user.real_uid && cygheap->user.saved_gid == cygheap->user.real_gid @@ -726,9 +729,6 @@ spawn_guts (const char * prog_arg, const char *const *argv, if (mode != _P_OVERLAY || !rc) cygheap->user.reimpersonate (); - if (envblock) - free (envblock); - /* Set errno now so that debugging messages from it appear before our final debugging message [this is a general rule for debugging messages]. */ @@ -776,7 +776,8 @@ spawn_guts (const char * prog_arg, const char *const *argv, strace.execing = 1; myself.hProcess = hExeced = pi.hProcess; strcpy (myself->progname, real_path); // FIXME: race? - close_all_files (true); + if (!looped) + close_all_files (true); sigproc_printf ("new process name %s", myself->progname); /* 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" @@ -791,10 +792,13 @@ spawn_guts (const char * prog_arg, const char *const *argv, dup_proc_pipe essentially a no-op. */ if (!newargv.win16_exe && myself->wr_proc_pipe) { - myself->sync_proc_pipe (); /* Make sure that we own wr_proc_pipe - just in case we've been previously - execed. */ - myself.zap_cwd (); + if (!looped) + { + myself->sync_proc_pipe (); /* Make sure that we own wr_proc_pipe + just in case we've been previously + execed. */ + myself.zap_cwd (); + } myself->dup_proc_pipe (pi.hProcess); } pid = myself->pid; @@ -852,8 +856,16 @@ spawn_guts (const char * prog_arg, const char *const *argv, { case _P_OVERLAY: myself.hProcess = pi.hProcess; - if (synced && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT - && !myself->wr_proc_pipe) + if (!synced) + { + if (ch.proc_retry (pi.hProcess) == 0) + { + looped++; + goto loop; + } + } + else if (!myself->wr_proc_pipe + && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT) { extern bool is_toplevel_proc; is_toplevel_proc = true; @@ -880,6 +892,8 @@ spawn_guts (const char * prog_arg, const char *const *argv, } out: + if (envblock) + free (envblock); pthread_cleanup_pop (1); return (int) res; } diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 758949398..3b976c0d3 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1987,7 +1987,6 @@ setpgid (pid_t pid, pid_t pgid) if (p->pid != p->pgid) p->set_has_pgid_children (0); res = 0; - // init_console_handler (FALSE); } } diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index 3695f4917..73ef0a59f 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -302,9 +302,10 @@ bool mmap_is_attached_page (ULONG_PTR); int winprio_to_nice (DWORD) __attribute__ ((regparm (1))); DWORD nice_to_winprio (int &) __attribute__ ((regparm (1))); -bool __stdcall create_pipe (PHANDLE hr, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD) +bool __stdcall create_pipe (PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD) __attribute__ ((regparm (3))); #define CreatePipe create_pipe + inline bool flush_file_buffers (HANDLE h) { return (GetFileType (h) != FILE_TYPE_PIPE) ? FlushFileBuffers (h) : true;