* 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.
This commit is contained in:
Christopher Faylor 2006-03-18 19:17:21 +00:00
parent 61337a0f45
commit a939686807
14 changed files with 170 additions and 86 deletions

View File

@ -1,3 +1,53 @@
2006-03-18 Christopher Faylor <cgf@timesys.com>
* 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 <cgf@timesys.com>
* cygheap.cc (init_cygheap::manage_console_count): Turn console control

View File

@ -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;

View File

@ -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 ();

View File

@ -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}}}
};

View File

@ -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;
}
}

View File

@ -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:

View File

@ -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));

View File

@ -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");

View File

@ -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?

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;