summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <cgf@redhat.com>2004-11-16 06:02:05 +0000
committerChristopher Faylor <cgf@redhat.com>2004-11-16 06:02:05 +0000
commit41431ad5531eff99c85ea598a2fe1c3e8c5c8329 (patch)
treeca84c6b0eee3da6750399b4bd60cdf206c19653a
parent69a7b5f79888513741e65a54216d7756474b76c2 (diff)
downloadgdb-41431ad5531eff99c85ea598a2fe1c3e8c5c8329.tar.gz
experimental branch which removes cygwin's reparenting code, in favor of a pipe.
-rw-r--r--winsup/cygwin/child_info.h110
-rw-r--r--winsup/cygwin/cygthread.cc313
-rw-r--r--winsup/cygwin/dcrt0.cc1177
-rw-r--r--winsup/cygwin/dtable.cc910
-rw-r--r--winsup/cygwin/exceptions.cc1206
-rw-r--r--winsup/cygwin/fork.cc740
-rw-r--r--winsup/cygwin/include/sys/cygwin.h265
-rw-r--r--winsup/cygwin/include/sys/wait.h74
-rw-r--r--winsup/cygwin/pinfo.cc981
-rw-r--r--winsup/cygwin/pinfo.h226
-rw-r--r--winsup/cygwin/signal.cc551
-rw-r--r--winsup/cygwin/sigproc.cc1045
-rw-r--r--winsup/cygwin/sigproc.h97
-rw-r--r--winsup/cygwin/spawn.cc1088
-rw-r--r--winsup/cygwin/tty.cc494
15 files changed, 9277 insertions, 0 deletions
diff --git a/winsup/cygwin/child_info.h b/winsup/cygwin/child_info.h
new file mode 100644
index 00000000000..e3d643a4f96
--- /dev/null
+++ b/winsup/cygwin/child_info.h
@@ -0,0 +1,110 @@
+/* child_info.h: shared child info for cygwin
+
+ Copyright 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include <setjmp.h>
+
+enum
+{
+ _PROC_EXEC,
+ _PROC_SPAWN,
+ _PROC_FORK,
+ _PROC_WHOOPS
+};
+
+#define OPROC_MAGIC_MASK 0xff00ff00
+#define OPROC_MAGIC_GENERIC 0xaf00f000
+
+#define PROC_MAGIC_GENERIC 0xaf00fa00
+
+#define PROC_EXEC (_PROC_EXEC)
+#define PROC_SPAWN (_PROC_SPAWN)
+#define PROC_FORK (_PROC_FORK)
+
+#define EXEC_MAGIC_SIZE sizeof(child_info)
+
+#define CURR_CHILD_INFO_MAGIC 0x568a5527U
+
+/* NOTE: Do not make gratuitous changes to the names or organization of the
+ below class. The layout is checksummed to determine compatibility between
+ different cygwin versions. */
+class child_info
+{
+public:
+ DWORD zero[4]; // must be zeroed
+ DWORD cb; // size of this record
+ DWORD intro; // improbable string
+ unsigned long magic; // magic number unique to child_info
+ unsigned short type; // type of record, exec, spawn, fork
+ HANDLE subproc_ready; // used for synchronization with parent
+ HANDLE user_h;
+ HANDLE parent;
+ init_cygheap *cygheap;
+ void *cygheap_max;
+ DWORD cygheap_reserve_sz;
+ HANDLE cygheap_h;
+ unsigned fhandler_union_cb;
+};
+
+class mount_info;
+class _pinfo;
+
+class child_info_fork: public child_info
+{
+public:
+ HANDLE forker_finished;// for synchronization with child
+ DWORD stacksize; // size of parent stack
+ jmp_buf jmp; // where child will jump to
+ void *stacktop; // location of top of parent stack
+ void *stackbottom; // location of bottom of parent stack
+};
+
+class fhandler_base;
+
+class cygheap_exec_info
+{
+public:
+ char *old_title;
+ int argc;
+ char **argv;
+ int envc;
+ char **envp;
+ HANDLE myself_pinfo;
+};
+
+class child_info_spawn: public child_info
+{
+public:
+ cygheap_exec_info *moreinfo;
+ HANDLE hexec_proc;
+
+ child_info_spawn (): moreinfo (NULL) {}
+ ~child_info_spawn ()
+ {
+ if (moreinfo)
+ {
+ if (moreinfo->old_title)
+ cfree (moreinfo->old_title);
+ if (moreinfo->envp)
+ {
+ for (char **e = moreinfo->envp; *e; e++)
+ cfree (*e);
+ cfree (moreinfo->envp);
+ }
+ CloseHandle (moreinfo->myself_pinfo);
+ cfree (moreinfo);
+ }
+ }
+};
+
+void __stdcall init_child_info (DWORD, child_info *, HANDLE);
+
+extern child_info *child_proc_info;
+extern child_info_spawn *spawn_info __attribute__ ((alias ("child_proc_info")));
+extern child_info_fork *fork_info __attribute__ ((alias ("child_proc_info")));
diff --git a/winsup/cygwin/cygthread.cc b/winsup/cygwin/cygthread.cc
new file mode 100644
index 00000000000..e3f3fdc4ccf
--- /dev/null
+++ b/winsup/cygwin/cygthread.cc
@@ -0,0 +1,313 @@
+/* cygthread.cc
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <windows.h>
+#include <stdlib.h>
+#include "exceptions.h"
+#include "security.h"
+#include "cygthread.h"
+#include "sync.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "thread.h"
+#include "cygtls.h"
+
+#undef CloseHandle
+
+static cygthread NO_COPY threads[128];
+#define NTHREADS (sizeof (threads) / sizeof (threads[0]))
+
+DWORD NO_COPY cygthread::main_thread_id;
+bool NO_COPY cygthread::exiting;
+
+/* Initial stub called by cygthread constructor. Performs initial
+ per-thread initialization and loops waiting for new thread functions
+ to execute. */
+DWORD WINAPI
+cygthread::stub (VOID *arg)
+{
+ cygthread *info = (cygthread *) arg;
+ if (info->arg == cygself)
+ {
+ if (info->ev)
+ {
+ CloseHandle (info->ev);
+ CloseHandle (info->thread_sync);
+ }
+ info->ev = info->thread_sync = info->stack_ptr = NULL;
+ }
+ else
+ {
+ info->stack_ptr = &arg;
+ if (!info->ev)
+ {
+ info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ }
+ }
+
+ while (1)
+ {
+ if (!info->__name)
+ system_printf ("erroneous thread activation");
+ else
+ {
+ if (!info->func || exiting)
+ return 0;
+
+ /* Cygwin threads should not call ExitThread directly */
+ info->func (info->arg == cygself ? info : info->arg);
+ /* ...so the above should always return */
+
+#ifdef DEBUGGING
+ info->func = NULL; // catch erroneous activation
+#endif
+ info->__name = NULL;
+ SetEvent (info->ev);
+ }
+ switch (WaitForSingleObject (info->thread_sync, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ continue;
+ default:
+ api_fatal ("WFSO failed, %E");
+ break;
+ }
+ }
+}
+
+/* Overflow stub called by cygthread constructor. Calls specified function
+ and then exits the thread. */
+DWORD WINAPI
+cygthread::simplestub (VOID *arg)
+{
+ cygthread *info = (cygthread *) arg;
+ info->stack_ptr = &arg;
+ info->ev = info->h;
+ info->func (info->arg == cygself ? info : info->arg);
+ return 0;
+}
+
+/* Start things going. Called from dll_crt0_1. */
+void
+cygthread::init ()
+{
+ main_thread_id = GetCurrentThreadId ();
+}
+
+cygthread *
+cygthread::freerange ()
+{
+ cygthread *self = (cygthread *) calloc (1, sizeof (*self));
+ self->is_freerange = true;
+ self->inuse = 1;
+ return self;
+}
+
+void * cygthread::operator
+new (size_t)
+{
+ cygthread *info;
+
+ /* Search the threads array for an empty slot to use */
+ for (info = threads; info < threads + NTHREADS; info++)
+ if (!InterlockedExchange (&info->inuse, 1))
+ {
+ /* available */
+#ifdef DEBUGGING
+ if (info->__name)
+ api_fatal ("name not NULL? id %p, i %d", info->id, info - threads);
+#endif
+ goto out;
+ }
+
+#ifdef DEBUGGING
+ char buf[1024];
+ if (!GetEnvironmentVariable ("CYGWIN_FREERANGE_NOCHECK", buf, sizeof (buf)))
+ api_fatal ("Overflowed cygwin thread pool");
+ else
+ thread_printf ("Overflowed cygwin thread pool");
+#endif
+
+ info = freerange (); /* exhausted thread pool */
+
+out:
+ return info;
+}
+
+cygthread::cygthread (LPTHREAD_START_ROUTINE start, LPVOID param,
+ const char *name): __name (name),
+ func (start), arg (param)
+{
+ thread_printf ("name %s, id %p", name, id);
+ if (h)
+ {
+ while (!thread_sync)
+ low_priority_sleep (0);
+ SetEvent (thread_sync);
+ thread_printf ("activated thread_sync %p", thread_sync);
+ }
+ else
+ {
+ stack_ptr = NULL;
+ h = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
+ this, 0, &id);
+ if (!h)
+ api_fatal ("thread handle not set - %p<%p>, %E", h, id);
+ thread_printf ("created thread %p", h);
+ }
+}
+
+/* Return the symbolic name of the current thread for debugging.
+ */
+const char *
+cygthread::name (DWORD tid)
+{
+ const char *res = NULL;
+ if (!tid)
+ tid = GetCurrentThreadId ();
+
+ if (tid == main_thread_id)
+ return "main";
+
+ for (DWORD i = 0; i < NTHREADS; i++)
+ if (threads[i].id == tid)
+ {
+ res = threads[i].__name ?: "exiting thread";
+ break;
+ }
+
+ if (!res)
+ {
+ static char buf[30] NO_COPY = {0};
+ __small_sprintf (buf, "unknown (%p)", tid);
+ res = buf;
+ }
+
+ return res;
+}
+
+cygthread::operator
+HANDLE ()
+{
+ while (!ev)
+ low_priority_sleep (0);
+ return ev;
+}
+
+/* Should only be called when the process is exiting since it
+ leaves an open thread slot. */
+void
+cygthread::exit_thread ()
+{
+ if (!is_freerange)
+ SetEvent (*this);
+ ExitThread (0);
+}
+
+/* Forcibly terminate a thread. */
+void
+cygthread::terminate_thread ()
+{
+ if (!is_freerange)
+ {
+ ResetEvent (*this);
+ ResetEvent (thread_sync);
+ }
+ (void) TerminateThread (h, 0);
+ (void) WaitForSingleObject (h, INFINITE);
+ CloseHandle (h);
+
+ while (!stack_ptr)
+ low_priority_sleep (0);
+
+ MEMORY_BASIC_INFORMATION m;
+ memset (&m, 0, sizeof (m));
+ (void) VirtualQuery (stack_ptr, &m, sizeof m);
+
+ if (!m.RegionSize)
+ system_printf ("m.RegionSize 0? stack_ptr %p", stack_ptr);
+ else if (!VirtualFree (m.AllocationBase, 0, MEM_RELEASE))
+ debug_printf ("VirtualFree of allocation base %p<%p> failed, %E",
+ stack_ptr, m.AllocationBase);
+
+ if (is_freerange)
+ free (this);
+ else
+ {
+ h = NULL;
+ __name = NULL;
+ stack_ptr = NULL;
+ (void) InterlockedExchange (&inuse, 0); /* No longer in use */
+ }
+}
+
+/* Detach the cygthread from the current thread. Note that the
+ theory is that cygthreads are only associated with one thread.
+ So, there should be never be multiple threads doing waits
+ on the same cygthread. */
+bool
+cygthread::detach (HANDLE sigwait)
+{
+ bool signalled = false;
+ if (!inuse)
+ system_printf ("called detach but inuse %d, thread %p?", inuse, id);
+ else
+ {
+ DWORD res;
+
+ if (!sigwait)
+ res = WaitForSingleObject (*this, INFINITE);
+ else
+ {
+ HANDLE w4[2];
+ w4[0] = *this;
+ w4[1] = signal_arrived;
+ res = WaitForSingleObject (sigwait, INFINITE);
+ if (res != WAIT_OBJECT_0)
+ system_printf ("WFSO sigwait %p failed, res %u, %E", sigwait, res);
+ res = WaitForMultipleObjects (2, w4, FALSE, INFINITE);
+ if (res == WAIT_OBJECT_0)
+ /* nothing */;
+ else if (WaitForSingleObject (sigwait, 0) == WAIT_OBJECT_0)
+ res = WaitForSingleObject (*this, INFINITE);
+ else if ((res = WaitForSingleObject (*this, 0)) != WAIT_OBJECT_0)
+ {
+ signalled = true;
+ terminate_thread ();
+ set_sig_errno (EINTR); /* caller should be dealing with return
+ values. */
+ }
+ }
+
+ thread_printf ("%s returns %d, id %p", sigwait ? "WFMO" : "WFSO",
+ res, id);
+
+ if (signalled)
+ /* already handled */;
+ else if (is_freerange)
+ {
+ CloseHandle (h);
+ free (this);
+ }
+ else
+ {
+ ResetEvent (*this);
+ /* Mark the thread as available by setting inuse to zero */
+ (void) InterlockedExchange (&inuse, 0);
+ }
+ }
+ return signalled;
+}
+
+void
+cygthread::terminate ()
+{
+ exiting = 1;
+}
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
new file mode 100644
index 00000000000..eabda1c8968
--- /dev/null
+++ b/winsup/cygwin/dcrt0.cc
@@ -0,0 +1,1177 @@
+/* dcrt0.cc -- essentially the main() for the Cygwin dll
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "glob.h"
+#include "exceptions.h"
+#include <ctype.h>
+#include <limits.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygerrno.h"
+#define NEED_VFORK
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info_magic.h"
+#include "perthread.h"
+#include "shared_info.h"
+#include "cygwin_version.h"
+#include "dll_init.h"
+#include "cygthread.h"
+#include "sync.h"
+#include "heap.h"
+
+#define MAX_AT_FILE_LEVEL 10
+
+#define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0]))
+
+HANDLE NO_COPY hMainProc = (HANDLE) -1;
+HANDLE NO_COPY hMainThread;
+
+#ifdef NEWVFORK
+per_thread_vfork NO_COPY vfork_storage;
+#endif
+
+per_thread NO_COPY *threadstuff[] = {
+#ifdef NEWVFORK
+ &vfork_storage,
+#endif
+ NULL};
+
+bool display_title;
+bool strip_title_path;
+bool allow_glob = true;
+codepage_type current_codepage = ansi_cp;
+
+int __argc_safe;
+int _declspec(dllexport) __argc;
+char _declspec(dllexport) **__argv;
+#ifdef NEWVFORK
+vfork_save NO_COPY *main_vfork;
+#endif
+
+static int NO_COPY envc;
+char NO_COPY **envp;
+
+extern "C" void __sinit (_reent *);
+
+_cygtls NO_COPY *_main_tls;
+
+bool NO_COPY cygwin_finished_initializing;
+
+/* Used in SIGTOMASK for generating a bit for insertion into a sigset_t.
+ This is subtracted from the signal number prior to shifting the bit.
+ In older versions of cygwin, the signal was used as-is to shift the
+ bit for masking. So, we'll temporarily detect this and set it to zero
+ for programs that are linked using older cygwins. This is just a stopgap
+ measure to allow an orderly transfer to the new, correct sigmask method. */
+unsigned NO_COPY int signal_shift_subtract = 1;
+
+ResourceLocks _reslock NO_COPY;
+MTinterface _mtinterf;
+
+bool NO_COPY _cygwin_testing;
+
+char NO_COPY almost_null[1];
+
+extern "C"
+{
+ /* This is an exported copy of environ which can be used by DLLs
+ which use cygwin.dll. */
+ char **__cygwin_environ;
+ char ***main_environ;
+ /* __progname used in getopt error message */
+ char *__progname;
+ struct per_process __cygwin_user_data =
+ {/* initial_sp */ 0, /* magic_biscuit */ 0,
+ /* dll_major */ CYGWIN_VERSION_DLL_MAJOR,
+ /* dll_major */ CYGWIN_VERSION_DLL_MINOR,
+ /* impure_ptr_ptr */ NULL, /* envptr */ NULL,
+ /* malloc */ malloc, /* free */ free,
+ /* realloc */ realloc,
+ /* fmode_ptr */ NULL, /* main */ NULL, /* ctors */ NULL,
+ /* dtors */ NULL, /* data_start */ NULL, /* data_end */ NULL,
+ /* bss_start */ NULL, /* bss_end */ NULL,
+ /* calloc */ calloc,
+ /* premain */ {NULL, NULL, NULL, NULL},
+ /* run_ctors_p */ 0,
+ /* unused */ {0, 0, 0, 0, 0, 0, 0},
+ /* forkee */ 0,
+ /* hmodule */ NULL,
+ /* api_major */ CYGWIN_VERSION_API_MAJOR,
+ /* api_minor */ CYGWIN_VERSION_API_MINOR,
+ /* unused2 */ {0, 0, 0, 0, 0},
+ /* resourcelocks */ &_reslock, /* threadinterface */ &_mtinterf,
+ /* impure_ptr */ _GLOBAL_REENT,
+ };
+ bool ignore_case_with_glob;
+ int __declspec (dllexport) _check_for_executable = true;
+#ifdef DEBUGGING
+ int pinger;
+#endif
+};
+
+char *old_title;
+char title_buf[TITLESIZE + 1];
+
+static void
+do_global_dtors (void)
+{
+ if (user_data->dtors)
+ {
+ void (**pfunc)() = user_data->dtors;
+ while (*++pfunc)
+ (*pfunc) ();
+ }
+}
+
+static void __stdcall
+do_global_ctors (void (**in_pfunc)(), int force)
+{
+ if (!force && user_data->forkee)
+ return; // inherit constructed stuff from parent pid
+
+ /* Run ctors backwards, so skip the first entry and find how many
+ there are, then run them. */
+
+ void (**pfunc) () = in_pfunc;
+
+ while (*++pfunc)
+ ;
+ while (--pfunc > in_pfunc)
+ (*pfunc) ();
+}
+
+/*
+ * Replaces @file in the command line with the contents of the file.
+ * There may be multiple @file's in a single command line
+ * A \@file is replaced with @file so that echo \@foo would print
+ * @foo and not the contents of foo.
+ */
+static bool __stdcall
+insert_file (char *name, char *&cmd)
+{
+ HANDLE f;
+ DWORD size;
+
+ f = CreateFile (name + 1,
+ GENERIC_READ, /* open for reading */
+ FILE_SHARE_READ, /* share for reading */
+ &sec_none_nih, /* no security */
+ OPEN_EXISTING, /* existing file only */
+ FILE_ATTRIBUTE_NORMAL, /* normal file */
+ NULL); /* no attr. template */
+
+ if (f == INVALID_HANDLE_VALUE)
+ {
+ debug_printf ("couldn't open file '%s', %E", name);
+ return false;
+ }
+
+ /* This only supports files up to about 4 billion bytes in
+ size. I am making the bold assumption that this is big
+ enough for this feature */
+ size = GetFileSize (f, NULL);
+ if (size == 0xFFFFFFFF)
+ {
+ debug_printf ("couldn't get file size for '%s', %E", name);
+ return false;
+ }
+
+ int new_size = strlen (cmd) + size + 2;
+ char *tmp = (char *) malloc (new_size);
+ if (!tmp)
+ {
+ debug_printf ("malloc failed, %E");
+ return false;
+ }
+
+ /* realloc passed as it should */
+ DWORD rf_read;
+ BOOL rf_result;
+ rf_result = ReadFile (f, tmp, size, &rf_read, NULL);
+ CloseHandle (f);
+ if (!rf_result || (rf_read != size))
+ {
+ debug_printf ("ReadFile failed, %E");
+ return false;
+ }
+
+ tmp[size++] = ' ';
+ strcpy (tmp + size, cmd);
+ cmd = tmp;
+ return true;
+}
+
+static inline int
+isquote (char c)
+{
+ char ch = c;
+ return ch == '"' || ch == '\'';
+}
+
+/* Step over a run of characters delimited by quotes */
+static /*__inline*/ char *
+quoted (char *cmd, int winshell)
+{
+ char *p;
+ char quote = *cmd;
+
+ if (!winshell)
+ {
+ char *p;
+ strcpy (cmd, cmd + 1);
+ if (*(p = strechr (cmd, quote)))
+ strcpy (p, p + 1);
+ return p;
+ }
+
+ const char *s = quote == '\'' ? "'" : "\\\"";
+ /* This must have been run from a Windows shell, so preserve
+ quotes for globify to play with later. */
+ while (*cmd && *++cmd)
+ if ((p = strpbrk (cmd, s)) == NULL)
+ {
+ cmd = strchr (cmd, '\0'); // no closing quote
+ break;
+ }
+ else if (*p == '\\')
+ cmd = ++p;
+ else if (quote == '"' && p[1] == '"')
+ {
+ *p = '\\';
+ cmd = ++p; // a quoted quote
+ }
+ else
+ {
+ cmd = p + 1; // point to after end
+ break;
+ }
+ return cmd;
+}
+
+/* Perform a glob on word if it contains wildcard characters.
+ Also quote every character between quotes to force glob to
+ treat the characters literally. */
+static int __stdcall
+globify (char *word, char **&argv, int &argc, int &argvlen)
+{
+ if (*word != '~' && strpbrk (word, "?*[\"\'(){}") == NULL)
+ return 0;
+
+ int n = 0;
+ char *p, *s;
+ int dos_spec = isdrive (word);
+ if (!dos_spec && isquote (*word) && word[1] && word[2])
+ dos_spec = isdrive (word + 1);
+
+ /* We'll need more space if there are quoting characters in
+ word. If that is the case, doubling the size of the
+ string should provide more than enough space. */
+ if (strpbrk (word, "'\""))
+ n = strlen (word);
+ char pattern[strlen (word) + ((dos_spec + 1) * n) + 1];
+
+ /* Fill pattern with characters from word, quoting any
+ characters found within quotes. */
+ for (p = pattern, s = word; *s != '\000'; s++, p++)
+ if (!isquote (*s))
+ {
+ if (dos_spec && *s == '\\')
+ *p++ = '\\';
+ *p = *s;
+ }
+ else
+ {
+ char quote = *s;
+ while (*++s && *s != quote)
+ {
+ if (dos_spec || *s != '\\')
+ /* nothing */;
+ else if (s[1] == quote || s[1] == '\\')
+ s++;
+ *p++ = '\\';
+ *p++ = *s;
+ }
+ if (*s == quote)
+ p--;
+ if (*s == '\0')
+ break;
+ }
+
+ *p = '\0';
+
+ glob_t gl;
+ gl.gl_offs = 0;
+
+ /* Attempt to match the argument. Return just word (minus quoting) if no match. */
+ if (glob (pattern, GLOB_TILDE | GLOB_NOCHECK | GLOB_BRACE | GLOB_QUOTE, NULL, &gl) || !gl.gl_pathc)
+ return 0;
+
+ /* Allocate enough space in argv for the matched filenames. */
+ n = argc;
+ if ((argc += gl.gl_pathc) > argvlen)
+ {
+ argvlen = argc + 10;
+ argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0]));
+ }
+
+ /* Copy the matched filenames to argv. */
+ char **gv = gl.gl_pathv;
+ char **av = argv + n;
+ while (*gv)
+ {
+ debug_printf ("argv[%d] = '%s'", n++, *gv);
+ *av++ = *gv++;
+ }
+
+ /* Clean up after glob. */
+ free (gl.gl_pathv);
+ return 1;
+}
+
+/* Build argv, argc from string passed from Windows. */
+
+static void __stdcall
+build_argv (char *cmd, char **&argv, int &argc, int winshell)
+{
+ int argvlen = 0;
+ int nesting = 0; // monitor "nesting" from insert_file
+
+ argc = 0;
+ argvlen = 0;
+ argv = NULL;
+
+ /* Scan command line until there is nothing left. */
+ while (*cmd)
+ {
+ /* Ignore spaces */
+ if (issep (*cmd))
+ {
+ cmd++;
+ continue;
+ }
+
+ /* Found the beginning of an argument. */
+ char *word = cmd;
+ char *sawquote = NULL;
+ while (*cmd)
+ {
+ if (*cmd != '"' && (!winshell || *cmd != '\''))
+ cmd++; // Skip over this character
+ else
+ /* Skip over characters until the closing quote */
+ {
+ sawquote = cmd;
+ cmd = quoted (cmd, winshell && argc > 0);
+ }
+ if (issep (*cmd)) // End of argument if space
+ break;
+ }
+ if (*cmd)
+ *cmd++ = '\0'; // Terminate `word'
+
+ /* Possibly look for @file construction assuming that this isn't
+ the very first argument and the @ wasn't quoted */
+ if (argc && sawquote != word && *word == '@')
+ {
+ if (++nesting > MAX_AT_FILE_LEVEL)
+ api_fatal ("Too many levels of nesting for %s", word);
+ if (insert_file (word, cmd))
+ continue; // There's new stuff in cmd now
+ }
+
+ /* See if we need to allocate more space for argv */
+ if (argc >= argvlen)
+ {
+ argvlen = argc + 10;
+ argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0]));
+ }
+
+ /* Add word to argv file after (optional) wildcard expansion. */
+ if (!winshell || !argc || !globify (word, argv, argc, argvlen))
+ {
+ debug_printf ("argv[%d] = '%s'", argc, word);
+ argv[argc++] = word;
+ }
+ }
+
+ argv[argc] = NULL;
+
+ debug_printf ("argc %d", argc);
+}
+
+/* sanity and sync check */
+void __stdcall
+check_sanity_and_sync (per_process *p)
+{
+ /* Sanity check to make sure developers didn't change the per_process */
+ /* struct without updating SIZEOF_PER_PROCESS [it makes them think twice */
+ /* about changing it]. */
+ if (sizeof (per_process) != SIZEOF_PER_PROCESS)
+ {
+ api_fatal ("per_process sanity check failed");
+ }
+
+ /* Make sure that the app and the dll are in sync. */
+
+ /* Complain if older than last incompatible change */
+ if (p->dll_major < CYGWIN_VERSION_DLL_EPOCH)
+ api_fatal ("cygwin DLL and APP are out of sync -- DLL version mismatch %d < %d",
+ p->dll_major, CYGWIN_VERSION_DLL_EPOCH);
+
+ /* magic_biscuit != 0 if using the old style version numbering scheme. */
+ if (p->magic_biscuit != SIZEOF_PER_PROCESS)
+ api_fatal ("Incompatible cygwin .dll -- incompatible per_process info %d != %d",
+ p->magic_biscuit, SIZEOF_PER_PROCESS);
+
+ /* Complain if incompatible API changes made */
+ if (p->api_major > cygwin_version.api_major)
+ api_fatal ("cygwin DLL and APP are out of sync -- API version mismatch %d > %d",
+ p->api_major, cygwin_version.api_major);
+
+ if (CYGWIN_VERSION_DLL_MAKE_COMBINED (p->dll_major, p->dll_minor) <=
+ CYGWIN_VERSION_DLL_BAD_SIGNAL_MASK)
+ signal_shift_subtract = 0;
+}
+
+child_info NO_COPY *child_proc_info = NULL;
+static MEMORY_BASIC_INFORMATION NO_COPY sm;
+
+#define CYGWIN_GUARD ((wincap.has_page_guard ()) ? \
+ PAGE_EXECUTE_READWRITE|PAGE_GUARD : PAGE_NOACCESS)
+
+static void
+alloc_stack_hard_way (child_info_fork *ci, volatile char *b)
+{
+ void *new_stack_pointer;
+ MEMORY_BASIC_INFORMATION m;
+ void *newbase;
+ int newlen;
+ LPBYTE curbot = (LPBYTE) sm.BaseAddress + sm.RegionSize;
+ bool noguard;
+
+ if (ci->stacktop > (LPBYTE) sm.AllocationBase && ci->stacktop < curbot)
+ {
+ newbase = curbot;
+ newlen = (LPBYTE) ci->stackbottom - (LPBYTE) curbot;
+ noguard = 1;
+ }
+ else
+ {
+ newbase = ci->stacktop;
+ newlen = (DWORD) ci->stackbottom - (DWORD) ci->stacktop;
+ noguard = 0;
+ }
+ if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS))
+ api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
+ ci->stacktop, ci->stackbottom);
+
+ new_stack_pointer = (void *) ((LPBYTE) ci->stackbottom - ci->stacksize);
+
+ if (!VirtualAlloc (new_stack_pointer, ci->stacksize, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE))
+ api_fatal ("fork: can't commit memory for stack %p(%d), %E",
+ new_stack_pointer, ci->stacksize);
+ if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m))
+ api_fatal ("fork: couldn't get new stack info, %E");
+ if (!noguard)
+ {
+ m.BaseAddress = (LPVOID) ((DWORD) m.BaseAddress - 1);
+ if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT,
+ CYGWIN_GUARD))
+ api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
+ m.BaseAddress);
+ }
+ if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m))
+ api_fatal ("fork: couldn't get new stack info, %E");
+ ci->stacktop = m.BaseAddress;
+ b[0] = '\0';
+}
+
+/* extend the stack prior to fork longjmp */
+
+static void
+alloc_stack (child_info_fork *ci)
+{
+ /* FIXME: adding 16384 seems to avoid a stack copy problem during
+ fork on Win95, but I don't know exactly why yet. DJ */
+ volatile char b[ci->stacksize + 16384];
+
+ if (!VirtualQuery ((LPCVOID) &b, &sm, sizeof sm))
+ api_fatal ("fork: couldn't get stack info, %E");
+
+ if (sm.AllocationBase == ci->stacktop)
+ ci->stacksize = 0;
+ else
+ alloc_stack_hard_way (ci, b + sizeof (b) - 1);
+
+ return;
+}
+
+#ifdef DEBUGGING
+void
+break_here ()
+{
+ debug_printf ("break here");
+}
+#endif
+
+static void
+initial_env ()
+{
+ char buf[CYG_MAX_PATH + 1];
+ if (GetEnvironmentVariable ("CYGWIN_TESTING", buf, sizeof (buf) - 1))
+ _cygwin_testing = 1;
+
+#ifdef DEBUGGING
+ DWORD len;
+
+ if (GetEnvironmentVariable ("CYGWIN_SLEEP", buf, sizeof (buf) - 1))
+ {
+ DWORD ms = atoi (buf);
+ buf[0] = '\0';
+ len = GetModuleFileName (NULL, buf, CYG_MAX_PATH);
+ console_printf ("Sleeping %d, pid %u %s\n", ms, GetCurrentProcessId (), buf);
+ Sleep (ms);
+ if (!strace.active)
+ {
+ strace.inited = 0;
+ strace.hello ();
+ }
+ }
+ if (GetEnvironmentVariable ("CYGWIN_DEBUG", buf, sizeof (buf) - 1))
+ {
+ char buf1[CYG_MAX_PATH + 1];
+ len = GetModuleFileName (NULL, buf1, CYG_MAX_PATH);
+ strlwr (buf1);
+ strlwr (buf);
+ char *p = strchr (buf, ':');
+ if (!p)
+ p = (char *) "gdb.exe -nw";
+ else
+ *p++ = '\0';
+ if (strstr (buf1, buf))
+ {
+ error_start_init (p);
+ try_to_debug ();
+ console_printf ("*** Sending Break. gdb may issue spurious SIGTRAP message.\n");
+ DebugBreak ();
+ break_here ();
+ }
+ }
+#endif
+
+}
+
+void __stdcall
+dll_crt0_0 ()
+{
+ wincap.init ();
+ initial_env ();
+
+ char zeros[sizeof (child_proc_info->zero)] = {0};
+
+ init_console_handler ();
+ init_global_security ();
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (),
+ GetCurrentProcess (), &hMainProc, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ hMainProc = GetCurrentProcess ();
+
+ DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &hMainThread, 0, false, DUPLICATE_SAME_ACCESS);
+
+ (void) SetErrorMode (SEM_FAILCRITICALERRORS);
+
+ STARTUPINFO si;
+ GetStartupInfo (&si);
+ child_proc_info = (child_info *) si.lpReserved2;
+
+ if (si.cbReserved2 < EXEC_MAGIC_SIZE || !child_proc_info
+ || memcmp (child_proc_info->zero, zeros,
+ sizeof (child_proc_info->zero)) != 0)
+ child_proc_info = NULL;
+ else
+ {
+ if ((child_proc_info->intro & OPROC_MAGIC_MASK) == OPROC_MAGIC_GENERIC)
+ multiple_cygwin_problem ("proc", child_proc_info->intro, 0);
+ else if (child_proc_info->intro == PROC_MAGIC_GENERIC
+ && child_proc_info->magic != CHILD_INFO_MAGIC)
+ multiple_cygwin_problem ("proc", child_proc_info->magic,
+ CHILD_INFO_MAGIC);
+ else if (child_proc_info->cygheap != (void *) &_cygheap_start)
+ multiple_cygwin_problem ("cygheap", (DWORD) child_proc_info->cygheap,
+ (DWORD) &_cygheap_start);
+ unsigned should_be_cb = 0;
+ switch (child_proc_info->type)
+ {
+ case _PROC_FORK:
+ user_data->forkee = true;
+ should_be_cb = sizeof (child_info_fork);
+ /* fall through */;
+ case _PROC_SPAWN:
+ case _PROC_EXEC:
+ if (!should_be_cb)
+ should_be_cb = sizeof (child_info);
+ 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
+ {
+ cygwin_user_h = child_proc_info->user_h;
+ break;
+ }
+ default:
+ system_printf ("unknown exec type %d", child_proc_info->type);
+ /* intentionally fall through */
+ case _PROC_WHOOPS:
+ child_proc_info = NULL;
+ break;
+ }
+ }
+
+ device::init ();
+ do_global_ctors (&__CTOR_LIST__, 1);
+ cygthread::init ();
+
+ if (!child_proc_info)
+ memory_init ();
+ else
+ {
+ bool close_hexec_proc = false;
+ switch (child_proc_info->type)
+ {
+ case _PROC_FORK:
+ alloc_stack (fork_info);
+ cygheap_fixup_in_child (false);
+ memory_init ();
+ set_myself (NULL);
+ break;
+ case _PROC_SPAWN:
+ /* Have to delay closes until after cygheap is setup */
+ close_hexec_proc = !!spawn_info->hexec_proc;
+ goto around;
+ case _PROC_EXEC:
+ hexec_proc = spawn_info->hexec_proc;
+ around:
+ HANDLE h;
+ cygheap_fixup_in_child (true);
+ memory_init ();
+ if (!spawn_info->moreinfo->myself_pinfo ||
+ !DuplicateHandle (hMainProc, spawn_info->moreinfo->myself_pinfo,
+ hMainProc, &h, 0, FALSE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ h = NULL;
+ set_myself (h);
+ __argc = spawn_info->moreinfo->argc;
+ __argv = spawn_info->moreinfo->argv;
+ envp = spawn_info->moreinfo->envp;
+ envc = spawn_info->moreinfo->envc;
+ cygheap->fdtab.fixup_after_exec ();
+ signal_fixup_after_exec ();
+ if (spawn_info->moreinfo->old_title)
+ {
+ old_title = strcpy (title_buf, spawn_info->moreinfo->old_title);
+ cfree (spawn_info->moreinfo->old_title);
+ }
+ break;
+ }
+ if (close_hexec_proc)
+ CloseHandle (spawn_info->hexec_proc);
+ }
+
+ _cygtls::init ();
+
+ /* Initialize events */
+ events_init ();
+
+ cygheap->cwd.init ();
+}
+
+/* Take over from libc's crt0.o and start the application. Note the
+ various special cases when Cygwin DLL is being runtime loaded (as
+ opposed to being link-time loaded by Cygwin apps) from a non
+ cygwin app via LoadLibrary. */
+static void
+dll_crt0_1 (char *)
+{
+ /* According to onno@stack.urc.tue.nl, the exception handler record must
+ be on the stack. */
+ /* FIXME: Verify forked children get their exception handler set up ok. */
+ exception_list cygwin_except_entry;
+
+ check_sanity_and_sync (user_data);
+ malloc_init ();
+
+ /* Initialize SIGSEGV handling, etc. */
+ init_exceptions (&cygwin_except_entry);
+
+ user_data->resourcelocks->Init ();
+ user_data->threadinterface->Init ();
+ ProtectHandle (hMainProc);
+ ProtectHandle (hMainThread);
+
+ /* Initialize pthread mainthread when not forked and it is safe to call new,
+ otherwise it is reinitalized in fixup_after_fork */
+ if (!user_data->forkee)
+ pthread::init_mainthread ();
+
+#ifdef DEBUGGING
+ strace.microseconds ();
+#endif
+
+ /* Initialize debug muto, if DLL is built with --enable-debugging.
+ Need to do this before any helper threads start. */
+ debug_init ();
+
+#ifdef NEWVFORK
+ cygheap->fdtab.vfork_child_fixup ();
+ main_vfork = vfork_storage.create ();
+#endif
+
+ cygbench ("pre-forkee");
+ if (user_data->forkee)
+ {
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ frame pointer is referencing the new stack so that the OS knows what
+ to do when it needs to increase the size of the stack.
+
+ NOTE: Don't do anything that involves the stack until you've completed
+ this step. */
+ if (fork_info->stacksize)
+ {
+ _tlsbase = (char *) fork_info->stackbottom;
+ _tlstop = (char *) fork_info->stacktop;
+ }
+ longjmp (fork_info->jmp, true);
+ }
+
+#ifdef DEBUGGING
+ {
+ extern void fork_init ();
+ fork_init ();
+ }
+#endif
+
+ /* Initialize our process table entry. */
+ pinfo_init (envp, envc);
+
+ if (!old_title && GetConsoleTitle (title_buf, TITLESIZE))
+ old_title = title_buf;
+
+ /* Allocate cygheap->fdtab */
+ dtable_init ();
+
+ /* Initialize user info. */
+ uinfo_init ();
+
+ /* Initialize signal/subprocess handling. */
+ sigproc_init ();
+
+ /* Connect to tty. */
+ tty_init ();
+
+ if (!__argc)
+ {
+ char *line = GetCommandLineA ();
+ line = strcpy ((char *) alloca (strlen (line) + 1), line);
+
+ if (current_codepage == oem_cp)
+ CharToOemA (line, line);
+
+ /* Scan the command line and build argv. Expand wildcards if not
+ called from another cygwin process. */
+ build_argv (line, __argv, __argc,
+ NOTSTATE (myself, PID_CYGPARENT) && allow_glob);
+
+ /* Convert argv[0] to posix rules if it's currently blatantly
+ win32 style. */
+ if ((strchr (__argv[0], ':')) || (strchr (__argv[0], '\\')))
+ {
+ char *new_argv0 = (char *) malloc (CYG_MAX_PATH);
+ cygwin_conv_to_posix_path (__argv[0], new_argv0);
+ __argv[0] = (char *) realloc (new_argv0, strlen (new_argv0) + 1);
+ }
+ }
+
+ __argc_safe = __argc;
+ if (user_data->premain[0])
+ for (unsigned int i = 0; i < PREMAIN_LEN / 2; i++)
+ user_data->premain[i] (__argc, __argv, user_data);
+
+ /* Set up standard fds in file descriptor table. */
+ cygheap->fdtab.stdio_init ();
+
+ /* Set up __progname for getopt error call. */
+ if (__argv[0] && (__progname = strrchr (__argv[0], '/')))
+ ++__progname;
+ else
+ __progname = __argv[0];
+ if (__progname)
+ {
+ char *cp = strchr (__progname, '\0') - 4;
+ if (cp > __progname && strcasematch (cp, ".exe"))
+ *cp = '\0';
+ }
+
+ /* Set new console title if appropriate. */
+
+ if (display_title && !dynamically_loaded)
+ {
+ char *cp = __progname;
+ if (strip_title_path)
+ for (char *ptr = cp; *ptr && *ptr != ' '; ptr++)
+ if (isdirsep (*ptr))
+ cp = ptr + 1;
+ set_console_title (cp);
+ }
+
+ cygwin_finished_initializing = true;
+ /* Call init of loaded dlls. */
+ dlls.init ();
+
+ /* Execute any specified "premain" functions */
+ if (user_data->premain[PREMAIN_LEN / 2])
+ for (unsigned int i = PREMAIN_LEN / 2; i < PREMAIN_LEN; i++)
+ user_data->premain[i] (__argc, __argv, user_data);
+
+ debug_printf ("user_data->main %p", user_data->main);
+
+ if (dynamically_loaded)
+ {
+ set_errno (0);
+ return;
+ }
+
+ /* Disable case-insensitive globbing */
+ ignore_case_with_glob = false;
+
+
+ set_errno (0);
+
+ MALLOC_CHECK;
+ cygbench (__progname);
+
+ /* Flush signals and ensure that signal thread is up and running. Can't
+ do this for noncygwin case since the signal thread is blocked due to
+ LoadLibrary serialization. */
+ wait_for_sigthread ();
+ if (user_data->main)
+ exit (user_data->main (__argc, __argv, *user_data->envptr));
+}
+
+struct _reent *
+initialize_main_tls (char *padding)
+{
+ if (!_main_tls)
+ {
+ _main_tls = &_my_tls;
+ _main_tls->init_thread (padding, NULL);
+ }
+ return &_main_tls->local_clib;
+}
+
+/* Wrap the real one, otherwise gdb gets confused about
+ two symbols with the same name, but different addresses.
+
+ UPTR is a pointer to global data that lives on the libc side of the
+ line [if one distinguishes the application from the dll]. */
+
+extern "C" void __stdcall
+_dll_crt0 ()
+{
+ extern HANDLE sync_startup;
+ extern unsigned threadfunc_ix;
+ if (threadfunc_ix)
+ /* nothing to do */;
+ else if (!sync_startup)
+ system_printf ("internal error: sync_startup not called at start. Expect signal problems.");
+ else
+ {
+ (void) WaitForSingleObject (sync_startup, INFINITE);
+ CloseHandle (sync_startup);
+ }
+
+ if (!threadfunc_ix)
+ system_printf ("internal error: couldn't determine location of thread function on stack. Expect signal problems.");
+
+ main_environ = user_data->envptr;
+ *main_environ = NULL;
+
+ char padding[CYGTLS_PADSIZE];
+ _impure_ptr = _GLOBAL_REENT;
+ _impure_ptr->_stdin = &_impure_ptr->__sf[0];
+ _impure_ptr->_stdout = &_impure_ptr->__sf[1];
+ _impure_ptr->_stderr = &_impure_ptr->__sf[2];
+ _impure_ptr->_current_locale = "C";
+
+ if (child_proc_info && child_proc_info->type == _PROC_FORK)
+ user_data->forkee = true;
+ else
+ __sinit (_impure_ptr);
+
+ initialize_main_tls (padding);
+ dll_crt0_1 (padding);
+}
+
+void
+dll_crt0 (per_process *uptr)
+{
+ /* Set the local copy of the pointer into the user space. */
+ if (uptr && uptr != user_data)
+ {
+ memcpy (user_data, uptr, per_process_overwrite);
+ *(user_data->impure_ptr_ptr) = _GLOBAL_REENT;
+ }
+ _dll_crt0 ();
+}
+
+/* This must be called by anyone who uses LoadLibrary to load cygwin1.dll */
+extern "C" void
+cygwin_dll_init ()
+{
+ static char **envp;
+ static int _fmode;
+
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (),
+ GetCurrentProcess (), &hMainProc, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ hMainProc = GetCurrentProcess ();
+
+ DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &hMainThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ user_data->magic_biscuit = sizeof (per_process);
+
+ user_data->envptr = &envp;
+ user_data->fmode_ptr = &_fmode;
+
+ dll_crt0_1 (NULL);
+}
+
+extern "C" void
+__main (void)
+{
+ do_global_ctors (user_data->ctors, false);
+ atexit (do_global_dtors);
+}
+
+exit_states NO_COPY exit_state;
+extern CRITICAL_SECTION exit_lock;
+
+void __stdcall
+do_exit (int status)
+{
+ syscall_printf ("do_exit (%d), exit_state %d", status, exit_state);
+
+#ifdef NEWVFORK
+ vfork_save *vf = vfork_storage.val ();
+ if (vf != NULL && vf->pid < 0)
+ {
+ exit_state = ES_NOT_EXITING;
+ vf->restore_exit (status);
+ }
+#endif
+
+ EnterCriticalSection (&exit_lock);
+ muto::set_exiting_thread ();
+ if (exit_state < ES_EVENTS_TERMINATE)
+ {
+ exit_state = ES_EVENTS_TERMINATE;
+ events_terminate ();
+ }
+
+ UINT n = (UINT) status;
+ if (exit_state < ES_THREADTERM)
+ {
+ exit_state = ES_THREADTERM;
+ cygthread::terminate ();
+ }
+
+ if (exit_state < ES_SIGNAL)
+ {
+ exit_state = ES_SIGNAL;
+ signal (SIGCHLD, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGINT, SIG_IGN);
+ signal (SIGQUIT, SIG_IGN);
+ }
+
+ if (exit_state < ES_CLOSEALL)
+ {
+ exit_state = ES_CLOSEALL;
+ close_all_files ();
+ }
+
+ if (exit_state < ES_SIGPROCTERMINATE)
+ {
+ exit_state = ES_SIGPROCTERMINATE;
+ sigproc_terminate ();
+ }
+
+ myself->stopsig = 0;
+ if (exit_state < ES_TITLE)
+ {
+ exit_state = ES_TITLE;
+ /* restore console title */
+ if (old_title && display_title)
+ set_console_title (old_title);
+ }
+
+ if (exit_state < ES_HUP_PGRP)
+ {
+ exit_state = ES_HUP_PGRP;
+ /* Kill orphaned children on group leader exit */
+ if (myself->has_pgid_children && myself->pid == myself->pgid)
+ {
+ siginfo_t si;
+ si.si_signo = -SIGHUP;
+ si.si_code = SI_KERNEL;
+ si.si_pid = si.si_uid = si.si_errno = 0;
+ sigproc_printf ("%d == pgrp %d, send SIG{HUP,CONT} to stopped children",
+ myself->pid, myself->pgid);
+ kill_pgrp (myself->pgid, si);
+ }
+ }
+
+ if (exit_state < ES_HUP_SID)
+ {
+ exit_state = ES_HUP_SID;
+ /* Kill the foreground process group on session leader exit */
+ if (getpgrp () > 0 && myself->pid == myself->sid && real_tty_attached (myself))
+ {
+ tty *tp = cygwin_shared->tty[myself->ctty];
+ sigproc_printf ("%d == sid %d, send SIGHUP to children",
+ myself->pid, myself->sid);
+
+ /* CGF FIXME: This can't be right. */
+ if (tp->getsid () == myself->sid)
+ tp->kill_pgrp (SIGHUP);
+ }
+
+ }
+
+ if (exit_state < ES_TTY_TERMINATE)
+ {
+ exit_state = ES_TTY_TERMINATE;
+ tty_terminate ();
+ }
+
+ minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n);
+ myself->exit (n);
+}
+
+static muto *atexit_lock;
+
+extern "C" int
+cygwin_atexit (void (*function)(void))
+{
+ int res;
+ if (!atexit_lock)
+ new_muto (atexit_lock);
+ atexit_lock->acquire ();
+ res = atexit (function);
+ atexit_lock->release ();
+ return res;
+}
+
+extern "C" void
+cygwin_exit (int n)
+{
+ if (atexit_lock)
+ atexit_lock->acquire ();
+ exit (n);
+}
+
+extern "C" void
+_exit (int n)
+{
+ do_exit (((DWORD) n & 0xff) << 8);
+}
+
+extern "C" void
+__api_fatal (const char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+
+ va_start (ap, fmt);
+ int n = __small_sprintf (buf, "%P (%u): *** ", cygwin_pid (GetCurrentProcessId ()));
+ __small_vsprintf (buf + n, fmt, ap);
+ va_end (ap);
+ strcat (buf, "\n");
+ int len = strlen (buf);
+ DWORD done;
+ (void) WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, len, &done, 0);
+
+ /* Make sure that the message shows up on the screen, too, since this is
+ a serious error. */
+ if (GetFileType (GetStdHandle (STD_ERROR_HANDLE)) != FILE_TYPE_CHAR)
+ {
+ HANDLE h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_WRITE,
+ &sec_none, OPEN_EXISTING, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ (void) WriteFile (h, buf, len, &done, 0);
+ }
+
+ /* We are going down without mercy. Make sure we reset
+ our process_state. */
+ sigproc_terminate ();
+#ifdef DEBUGGING
+ (void) try_to_debug ();
+#endif
+ myself->exit (1);
+}
+
+void
+multiple_cygwin_problem (const char *what, unsigned magic_version, unsigned version)
+{
+ if (_cygwin_testing && (strstr (what, "proc") || strstr (what, "cygheap")))
+ {
+ child_proc_info->type = _PROC_WHOOPS;
+ return;
+ }
+
+ char buf[1024];
+ if (GetEnvironmentVariable ("CYGWIN_MISMATCH_OK", buf, sizeof (buf)))
+ return;
+
+ if (CYGWIN_VERSION_MAGIC_VERSION (magic_version) == version)
+ system_printf ("%s magic number mismatch detected - %p/%p", what, magic_version, version);
+ else
+ api_fatal ("%s version mismatch detected - %p/%p.\n\
+You have multiple copies of cygwin1.dll on your system.\n\
+Search for cygwin1.dll using the Windows Start->Find/Search facility\n\
+and delete all but the most recent version. The most recent version *should*\n\
+reside in x:\\cygwin\\bin, where 'x' is the drive on which you have\n\
+installed the cygwin distribution.", what, magic_version, version);
+}
+
+#ifdef DEBUGGING
+void __stdcall
+cygbench (const char *s)
+{
+ char buf[1024];
+ if (GetEnvironmentVariable ("CYGWIN_BENCH", buf, sizeof (buf)))
+ small_printf ("%05d ***** %s : %10d\n", GetCurrentProcessId (), s, strace.microseconds ());
+}
+#endif
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
new file mode 100644
index 00000000000..81c84a99e2e
--- /dev/null
+++ b/winsup/cygwin/dtable.cc
@@ -0,0 +1,910 @@
+/* dtable.cc: file descriptor support.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <ntdef.h>
+#include <winnls.h>
+
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "pinfo.h"
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+#include "tty.h"
+
+static const char NO_COPY unknown_file[] = "some disk file";
+
+static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
+ STD_ERROR_HANDLE};
+
+static const char *handle_to_fn (HANDLE, char *);
+
+/* Set aside space for the table of fds */
+void
+dtable_init ()
+{
+ if (!cygheap->fdtab.size)
+ cygheap->fdtab.extend (NOFILE_INCR);
+ cygheap->fdtab.init_lock ();
+
+}
+
+void __stdcall
+set_std_handle (int fd)
+{
+ if (fd == 0)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_handle ());
+ else if (fd <= 2)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_output_handle ());
+}
+
+void
+dtable::init_lock ()
+{
+ new_muto (lock_cs);
+}
+
+int
+dtable::extend (int howmuch)
+{
+ int new_size = size + howmuch;
+ fhandler_base **newfds;
+
+ if (howmuch <= 0)
+ return 0;
+
+ if (new_size > (100 * NOFILE_INCR))
+ {
+ set_errno (EMFILE);
+ return 0;
+ }
+
+ /* Try to allocate more space for fd table. We can't call realloc ()
+ here to preserve old table if memory allocation fails */
+
+ if (!(newfds = (fhandler_base **) ccalloc (HEAP_ARGV, new_size, sizeof newfds[0])))
+ {
+ debug_printf ("calloc failed");
+ set_errno (ENOMEM);
+ return 0;
+ }
+ if (fds)
+ {
+ memcpy (newfds, fds, size * sizeof (fds[0]));
+ cfree (fds);
+ }
+
+ size = new_size;
+ fds = newfds;
+ debug_printf ("size %d, fds %p", size, fds);
+ return 1;
+}
+
+void
+dtable::get_debugger_info ()
+{
+ if (being_debugged ())
+ {
+ char std[3][sizeof ("/dev/ttyNNNN")];
+ std[0][0] = std[1][0] = std [2][0] = '\0';
+ char buf[sizeof ("cYgstd %x") + 32];
+ sprintf (buf, "cYgstd %x %x %x", (unsigned) &std, sizeof (std[0]), 3);
+ OutputDebugString (buf);
+ for (int i = 0; i < 3; i++)
+ if (std[i][0])
+ {
+ HANDLE h = GetStdHandle (std_consts[i]);
+ fhandler_base *fh = build_fh_name (std[i]);
+ if (!fh)
+ continue;
+ fds[i] = fh;
+ if (!fh->open ((i ? (i == 2 ? O_RDWR : O_WRONLY) : O_RDONLY)
+ | O_BINARY, 0777))
+ release (i);
+ else
+ CloseHandle (h);
+ }
+ }
+}
+
+/* Initialize the file descriptor/handle mapping table.
+ This function should only be called when a cygwin function is invoked
+ by a non-cygwin function, i.e., it should only happen very rarely. */
+
+void
+dtable::stdio_init ()
+{
+ extern void set_console_ctty ();
+ /* Set these before trying to output anything from strace.
+ Also, always set them even if we're to pick up our parent's fds
+ in case they're missed. */
+
+ if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
+ return;
+
+ HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
+ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
+ HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
+
+ init_std_file_from_handle (0, in);
+
+ /* STD_ERROR_HANDLE has been observed to be the same as
+ STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes
+ to pass data from child to parent). */
+ if (out == err)
+ {
+ /* Since this code is not invoked for forked tasks, we don't have
+ to worry about the close-on-exec flag here. */
+ if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0,
+ 1, DUPLICATE_SAME_ACCESS))
+ {
+ /* If that fails, do this as a fall back. */
+ err = out;
+ system_printf ("couldn't make stderr distinct from stdout");
+ }
+ }
+
+ init_std_file_from_handle (1, out);
+ init_std_file_from_handle (2, err);
+ /* Assign the console as the controlling tty for this process if we actually
+ have a console and no other controlling tty has been assigned. */
+ if (myself->ctty < 0 && GetConsoleCP () > 0)
+ set_console_ctty ();
+}
+
+const int dtable::initial_archetype_size;
+
+fhandler_base *
+dtable::find_archetype (device& dev)
+{
+ for (unsigned i = 0; i < farchetype; i++)
+ if (archetypes[i]->get_device () == (unsigned) dev)
+ return archetypes[i];
+ return NULL;
+}
+
+fhandler_base **
+dtable::add_archetype ()
+{
+ if (farchetype++ >= narchetypes)
+ archetypes = (fhandler_base **) crealloc (archetypes, (narchetypes += initial_archetype_size) * sizeof archetypes[0]);
+ return archetypes + farchetype - 1;
+}
+
+void
+dtable::delete_archetype (fhandler_base *fh)
+{
+ for (unsigned i = 0; i < farchetype; i++)
+ if (fh == archetypes[i])
+ {
+ debug_printf ("deleting element %d for %s", i, fh->get_name ());
+ if (i < --farchetype)
+ archetypes[i] = archetypes[farchetype];
+ break;
+ }
+
+ delete fh;
+}
+
+int
+dtable::find_unused_handle (int start)
+{
+ do
+ {
+ for (size_t i = start; i < size; i++)
+ /* See if open -- no need for overhead of not_open */
+ if (fds[i] == NULL)
+ return i;
+ }
+ while (extend (NOFILE_INCR));
+ return -1;
+}
+
+void
+dtable::release (int fd)
+{
+ if (!not_open (fd))
+ {
+ if (fds[fd]->need_fixup_before ())
+ dec_need_fixup_before ();
+ fhandler_base *arch = fds[fd]->archetype;
+ delete fds[fd];
+ if (arch && !arch->usecount)
+ cygheap->fdtab.delete_archetype (arch);
+ fds[fd] = NULL;
+ }
+}
+
+extern "C" int
+cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin,
+ DWORD myaccess)
+{
+ if (fd == -1)
+ fd = cygheap->fdtab.find_unused_handle ();
+ fhandler_base *fh = build_fh_name (name);
+ cygheap->fdtab[fd] = fh;
+ fh->init (handle, myaccess, bin ?: fh->pc_binmode ());
+ return fd;
+}
+
+void
+dtable::init_std_file_from_handle (int fd, HANDLE handle)
+{
+ const char *name = NULL;
+ CONSOLE_SCREEN_BUFFER_INFO buf;
+ struct sockaddr sa;
+ int sal = sizeof (sa);
+ DCB dcb;
+ unsigned bin = O_BINARY;
+ device dev;
+
+ dev.devn = 0; /* FIXME: device */
+ first_fd_for_open = 0;
+
+ if (!not_open (fd))
+ return;
+
+ SetLastError (0);
+ DWORD ft = GetFileType (handle);
+ if (ft != FILE_TYPE_UNKNOWN || GetLastError () != ERROR_INVALID_HANDLE)
+ {
+ /* See if we can consoleify it */
+ if (GetConsoleScreenBufferInfo (handle, &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (ft == FILE_TYPE_PIPE)
+ {
+ if (fd == 0)
+ dev = *piper_dev;
+ else
+ dev = *pipew_dev;
+ }
+ else if (wsock_started && getpeername ((SOCKET) handle, &sa, &sal) == 0)
+ dev = *tcp_dev;
+ else if (GetCommState (handle, &dcb))
+ dev.parse ("/dev/ttyS0");
+ else
+ {
+ name = handle_to_fn (handle, (char *) alloca (CYG_MAX_PATH + 100));
+ bin = 0;
+ }
+ }
+
+ if (!name && !dev)
+ fds[fd] = NULL;
+ else
+ {
+ fhandler_base *fh;
+
+ if (dev)
+ fh = build_fh_dev (dev);
+ else
+ fh = build_fh_name (name);
+
+ if (fh)
+ cygheap->fdtab[fd] = fh;
+
+ if (!bin)
+ {
+ bin = fh->get_default_fmode (O_RDWR);
+ if (bin)
+ /* nothing */;
+ else if (dev)
+ bin = O_BINARY;
+ else if (name != unknown_file)
+ bin = fh->pc_binmode ();
+ }
+
+ fh->init (handle, GENERIC_READ | GENERIC_WRITE, bin);
+ set_std_handle (fd);
+ paranoid_printf ("fd %d, handle %p", fd, handle);
+ }
+}
+
+#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name
+fhandler_base *
+build_fh_name (const char *name, HANDLE h, unsigned opt, suffix_info *si)
+{
+ path_conv pc (name, opt | PC_NULLEMPTY | PC_FULL | PC_POSIX, si);
+ if (pc.error)
+ {
+ fhandler_base *fh = cnew (fhandler_nodevice) ();
+ fh->set_error (pc.error);
+ set_errno (pc.error);
+ return fh;
+ }
+
+ if (!pc.exists () && h)
+ pc.fillin (h);
+
+ return build_fh_pc (pc);
+}
+
+fhandler_base *
+build_fh_dev (const device& dev, const char *unix_name)
+{
+ path_conv pc (dev);
+ if (unix_name)
+ pc.set_normalized_path (unix_name);
+ else
+ pc.set_normalized_path (dev.name);
+ return build_fh_pc (pc);
+}
+
+fhandler_base *
+build_fh_pc (path_conv& pc)
+{
+ fhandler_base *fh = NULL;
+
+ switch (pc.dev.major)
+ {
+ case DEV_TTYS_MAJOR:
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ case DEV_TTYM_MAJOR:
+ fh = cnew (fhandler_tty_master) ();
+ break;
+ case DEV_CYGDRIVE_MAJOR:
+ fh = cnew (fhandler_cygdrive) ();
+ break;
+ case DEV_FLOPPY_MAJOR:
+ case DEV_CDROM_MAJOR:
+ case DEV_SD_MAJOR:
+ fh = cnew (fhandler_dev_floppy) ();
+ break;
+ case DEV_TAPE_MAJOR:
+ fh = cnew (fhandler_dev_tape) ();
+ break;
+ case DEV_SERIAL_MAJOR:
+ fh = cnew (fhandler_serial) ();
+ break;
+ default:
+ switch (pc.dev)
+ {
+ case FH_CONSOLE:
+ case FH_CONIN:
+ case FH_CONOUT:
+ fh = cnew (fhandler_console) ();
+ break;
+ case FH_PTYM:
+ fh = cnew (fhandler_pty_master) ();
+ break;
+ case FH_WINDOWS:
+ fh = cnew (fhandler_windows) ();
+ break;
+ case FH_FIFO:
+ fh = cnew (fhandler_fifo) ();
+ break;
+ case FH_PIPE:
+ case FH_PIPER:
+ case FH_PIPEW:
+ fh = cnew (fhandler_pipe) ();
+ break;
+ case FH_TCP:
+ case FH_UDP:
+ case FH_ICMP:
+ case FH_UNIX:
+ case FH_STREAM:
+ case FH_DGRAM:
+ fh = cnew (fhandler_socket) ();
+ break;
+ case FH_FS:
+ fh = cnew (fhandler_disk_file) ();
+ break;
+ case FH_NULL:
+ fh = cnew (fhandler_dev_null) ();
+ break;
+ case FH_ZERO:
+ fh = cnew (fhandler_dev_zero) ();
+ break;
+ case FH_RANDOM:
+ case FH_URANDOM:
+ fh = cnew (fhandler_dev_random) ();
+ break;
+ case FH_MEM:
+ case FH_PORT:
+ fh = cnew (fhandler_dev_mem) ();
+ break;
+ case FH_CLIPBOARD:
+ fh = cnew (fhandler_dev_clipboard) ();
+ break;
+ case FH_OSS_DSP:
+ fh = cnew (fhandler_dev_dsp) ();
+ break;
+ case FH_PROC:
+ fh = cnew (fhandler_proc) ();
+ break;
+ case FH_REGISTRY:
+ fh = cnew (fhandler_registry) ();
+ break;
+ case FH_PROCESS:
+ fh = cnew (fhandler_process) ();
+ break;
+ case FH_TTY:
+ {
+ if (myself->ctty == TTY_CONSOLE)
+ fh = cnew (fhandler_console) ();
+ else if (myself->ctty >= 0)
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ }
+ }
+ }
+
+ if (!fh)
+ fh = cnew (fhandler_nodevice) ();
+
+ fh->set_name (pc);
+
+ debug_printf ("fh %p", fh);
+ return fh;
+}
+
+fhandler_base *
+dtable::dup_worker (fhandler_base *oldfh)
+{
+ fhandler_base *newfh = build_fh_pc (oldfh->pc);
+ *newfh = *oldfh;
+ newfh->set_io_handle (NULL);
+ if (oldfh->dup (newfh))
+ {
+ cfree (newfh);
+ newfh = NULL;
+ return NULL;
+ }
+
+ newfh->close_on_exec (false);
+ MALLOC_CHECK;
+ debug_printf ("duped '%s' old %p, new %p", oldfh->get_name (), oldfh->get_io_handle (), newfh->get_io_handle ());
+ return newfh;
+}
+
+int
+dtable::dup2 (int oldfd, int newfd)
+{
+ int res = -1;
+ fhandler_base *newfh = NULL; // = NULL to avoid an incorrect warning
+
+ MALLOC_CHECK;
+ debug_printf ("dup2 (%d, %d)", oldfd, newfd);
+ lock ();
+
+ if (not_open (oldfd))
+ {
+ syscall_printf ("fd %d not open", oldfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd < 0)
+ {
+ syscall_printf ("new fd out of bounds: %d", newfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd == oldfd)
+ {
+ res = 0;
+ goto done;
+ }
+
+ if ((newfh = dup_worker (fds[oldfd])) == NULL)
+ {
+ res = -1;
+ goto done;
+ }
+
+ debug_printf ("newfh->io_handle %p, oldfh->io_handle %p",
+ newfh->get_io_handle (), fds[oldfd]->get_io_handle ());
+
+ if (!not_open (newfd))
+ close (newfd);
+ else if ((size_t) newfd < size)
+ /* nothing to do */;
+ else if (find_unused_handle (newfd) < 0)
+ {
+ newfh->close ();
+ res = -1;
+ goto done;
+ }
+
+ fds[newfd] = newfh;
+
+ if ((res = newfd) <= 2)
+ set_std_handle (res);
+
+done:
+ MALLOC_CHECK;
+ unlock ();
+ syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd);
+
+ return res;
+}
+
+fhandler_fifo *
+dtable::find_fifo (const char *path)
+{
+ lock ();
+ fhandler_fifo *fh_res = NULL;
+ for (unsigned i = 0; i < size; i++)
+ {
+ fhandler_base *fh = fds[i];
+ if (fh && fh->isfifo () && strcmp (path, fh->get_win32_name ()) == 0)
+ {
+ fh_res = (fhandler_fifo *) fh;
+ break;
+ }
+ }
+ unlock ();
+ return fh_res;
+}
+
+select_record *
+dtable::select_read (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_read (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_write (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_write (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_except (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_except (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+/* Function to walk the fd table after an exec and perform
+ per-fhandler type fixups. */
+void
+dtable::fixup_before_fork (DWORD target_proc_id)
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ unlock ();
+}
+
+void
+dtable::fixup_before_exec (DWORD target_proc_id)
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && !fh->close_on_exec ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ unlock ();
+}
+
+void
+dtable::set_file_pointers_for_exec ()
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && fh->get_flags () & O_APPEND)
+ SetFilePointer (fh->get_handle (), 0, 0, FILE_END);
+ unlock ();
+}
+
+void
+dtable::fixup_after_exec ()
+{
+ first_fd_for_open = 0;
+ fhandler_base *fh;
+ cygheap->fdtab.init_lock ();
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (fh->close_on_exec ())
+ {
+ if (fh->archetype)
+ fh->close ();
+ release (i);
+ }
+ else
+ {
+ fh->fixup_after_exec ();
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+ }
+}
+
+void
+dtable::fixup_after_fork (HANDLE parent)
+{
+ fhandler_base *fh;
+ cygheap->fdtab.init_lock ();
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ if (fh->close_on_exec () || fh->need_fork_fixup ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_after_fork (parent);
+ }
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+}
+
+#ifdef NEWVFORK
+int
+dtable::vfork_child_dup ()
+{
+ fhandler_base **newtable;
+ lock ();
+ newtable = (fhandler_base **) ccalloc (HEAP_ARGV, size, sizeof (fds[0]));
+ int res = 1;
+
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+ if (cygheap->ctty)
+ {
+ cygheap->ctty->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (cygheap->ctty, "vfork dup", "incremented ", "");
+ }
+
+ for (size_t i = 0; i < size; i++)
+ if (not_open (i))
+ continue;
+ else if ((newtable[i] = dup_worker (fds[i])) != NULL)
+ newtable[i]->set_close_on_exec (fds[i]->close_on_exec ());
+ else
+ {
+ res = 0;
+ set_errno (EBADF);
+ goto out;
+ }
+
+ fds_on_hold = fds;
+ fds = newtable;
+
+out:
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+
+ unlock ();
+ return 1;
+}
+
+void
+dtable::vfork_parent_restore ()
+{
+ lock ();
+
+ fhandler_tty_slave *ctty_on_hold = cygheap->ctty_on_hold;
+ close_all_files ();
+ fhandler_base **deleteme = fds;
+ fds = fds_on_hold;
+ fds_on_hold = NULL;
+ cfree (deleteme);
+ unlock ();
+
+ if (cygheap->ctty != ctty_on_hold)
+ {
+ cygheap->ctty = ctty_on_hold; // revert
+ cygheap->ctty->close (); // Undo previous bump of this archetype
+ }
+ cygheap->ctty_on_hold = NULL;
+
+ return;
+}
+
+void
+dtable::vfork_child_fixup ()
+{
+ if (!fds_on_hold)
+ return;
+ debug_printf ("here");
+ fhandler_base **saveme = fds;
+ fds = fds_on_hold;
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (!fh->archetype && fh->close_on_exec ())
+ release (i);
+ else
+ {
+ fh->close ();
+ release (i);
+ }
+ }
+
+ fds = saveme;
+ cfree (fds_on_hold);
+ fds_on_hold = NULL;
+
+ if (cygheap->ctty_on_hold)
+ {
+ cygheap->ctty_on_hold->close ();
+ cygheap->ctty_on_hold = NULL;
+ }
+
+ return;
+}
+#endif /*NEWVFORK*/
+
+#define DEVICE_PREFIX "\\device\\"
+#define DEVICE_PREFIX_LEN sizeof (DEVICE_PREFIX) - 1
+#define REMOTE "\\Device\\LanmanRedirector\\"
+#define REMOTE_LEN sizeof (REMOTE) - 1
+
+static const char *
+handle_to_fn (HANDLE h, char *posix_fn)
+{
+ OBJECT_NAME_INFORMATION *ntfn;
+ char fnbuf[32768];
+
+ memset (fnbuf, 0, sizeof (fnbuf));
+ ntfn = (OBJECT_NAME_INFORMATION *) fnbuf;
+ ntfn->Name.MaximumLength = sizeof (fnbuf) - sizeof (*ntfn);
+ ntfn->Name.Buffer = (WCHAR *) (ntfn + 1);
+
+ DWORD res = NtQueryObject (h, ObjectNameInformation, ntfn, sizeof (fnbuf), NULL);
+
+ if (res)
+ {
+ strcpy (posix_fn, unknown_file);
+ debug_printf ("NtQueryObject failed");
+ return unknown_file;
+ }
+
+ // NT seems to do this on an unopened file
+ if (!ntfn->Name.Buffer)
+ {
+ debug_printf ("nt->Name.Buffer == NULL");
+ return NULL;
+ }
+
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = 0;
+
+ char win32_fn[CYG_MAX_PATH + 100];
+ sys_wcstombs (win32_fn, ntfn->Name.Buffer, ntfn->Name.Length);
+ debug_printf ("nt name '%s'", win32_fn);
+ if (!strncasematch (win32_fn, DEVICE_PREFIX, DEVICE_PREFIX_LEN)
+ || !QueryDosDevice (NULL, fnbuf, sizeof (fnbuf)))
+ return strcpy (posix_fn, win32_fn);
+
+ char *p = strechr (win32_fn + DEVICE_PREFIX_LEN, '\\');
+
+ int n = p - win32_fn;
+ int maxmatchlen = 0;
+ char *maxmatchdos = NULL;
+ for (char *s = fnbuf; *s; s = strchr (s, '\0') + 1)
+ {
+ char device[CYG_MAX_PATH + 10];
+ device[CYG_MAX_PATH + 9] = '\0';
+ if (strchr (s, ':') == NULL)
+ continue;
+ if (!QueryDosDevice (s, device, sizeof (device) - 1))
+ continue;
+ char *q = strrchr (device, ';');
+ if (q)
+ {
+ char *r = strchr (q, '\\');
+ if (r)
+ strcpy (q, r + 1);
+ }
+ int devlen = strlen (device);
+ if (device[devlen - 1] == '\\')
+ device[--devlen] = '\0';
+ if (devlen < maxmatchlen)
+ continue;
+ if (!strncasematch (device, win32_fn, devlen) ||
+ (win32_fn[devlen] != '\0' && win32_fn[devlen] != '\\'))
+ continue;
+ maxmatchlen = devlen;
+ maxmatchdos = s;
+ debug_printf ("current match '%s'", device);
+ }
+
+ char *w32 = win32_fn;
+ if (maxmatchlen)
+ {
+ n = strlen (maxmatchdos);
+ if (maxmatchdos[n - 1] == '\\')
+ n--;
+ w32 += maxmatchlen - n;
+ memcpy (w32, maxmatchdos, n);
+ w32[n] = '\\';
+ }
+ else if (strncasematch (w32, REMOTE, REMOTE_LEN))
+ {
+ w32 += REMOTE_LEN - 2;
+ *w32 = '\\';
+ debug_printf ("remote drive");
+ }
+
+
+ debug_printf ("derived path '%s'", w32);
+ cygwin_conv_to_full_posix_path (w32, posix_fn);
+ return posix_fn;
+}
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
new file mode 100644
index 00000000000..2ac13588aec
--- /dev/null
+++ b/winsup/cygwin/exceptions.cc
@@ -0,0 +1,1206 @@
+/* exceptions.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <imagehlp.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <assert.h>
+
+#include "exceptions.h"
+#include "sync.h"
+#include "pinfo.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "cygerrno.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "shared_info.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygthread.h"
+
+#define CALL_HANDLER_RETRY 20
+
+char debugger_command[2 * CYG_MAX_PATH + 20];
+
+extern "C" {
+static int handle_exceptions (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
+extern void sigdelayed ();
+};
+
+extern DWORD sigtid;
+
+extern HANDLE hExeced;
+extern DWORD dwExeced;
+
+static BOOL WINAPI ctrl_c_handler (DWORD);
+static void signal_exit (int) __attribute__ ((noreturn));
+static char windows_system_directory[1024];
+static size_t windows_system_directory_length;
+
+/* This is set to indicate that we have already exited. */
+
+static NO_COPY int exit_already = 0;
+static NO_COPY muto *mask_sync = NULL;
+
+NO_COPY static struct
+{
+ unsigned int code;
+ const char *name;
+} status_info[] =
+{
+#define X(s) s, #s
+ { X (STATUS_ABANDONED_WAIT_0) },
+ { X (STATUS_ACCESS_VIOLATION) },
+ { X (STATUS_ARRAY_BOUNDS_EXCEEDED) },
+ { X (STATUS_BREAKPOINT) },
+ { X (STATUS_CONTROL_C_EXIT) },
+ { X (STATUS_DATATYPE_MISALIGNMENT) },
+ { X (STATUS_FLOAT_DENORMAL_OPERAND) },
+ { X (STATUS_FLOAT_DIVIDE_BY_ZERO) },
+ { X (STATUS_FLOAT_INEXACT_RESULT) },
+ { X (STATUS_FLOAT_INVALID_OPERATION) },
+ { X (STATUS_FLOAT_OVERFLOW) },
+ { X (STATUS_FLOAT_STACK_CHECK) },
+ { X (STATUS_FLOAT_UNDERFLOW) },
+ { X (STATUS_GUARD_PAGE_VIOLATION) },
+ { X (STATUS_ILLEGAL_INSTRUCTION) },
+ { X (STATUS_INTEGER_DIVIDE_BY_ZERO) },
+ { X (STATUS_INTEGER_OVERFLOW) },
+ { X (STATUS_INVALID_DISPOSITION) },
+ { X (STATUS_IN_PAGE_ERROR) },
+ { X (STATUS_NONCONTINUABLE_EXCEPTION) },
+ { X (STATUS_NO_MEMORY) },
+ { X (STATUS_PENDING) },
+ { X (STATUS_PRIVILEGED_INSTRUCTION) },
+ { X (STATUS_SINGLE_STEP) },
+ { X (STATUS_STACK_OVERFLOW) },
+ { X (STATUS_TIMEOUT) },
+ { X (STATUS_USER_APC) },
+ { X (STATUS_WAIT_0) },
+ { 0, 0 }
+#undef X
+};
+
+/* Initialization code. */
+
+// Set up the exception handler for the current thread. The PowerPC & Mips
+// use compiler generated tables to set up the exception handlers for each
+// region of code, and the kernel walks the call list until it finds a region
+// of code that handles exceptions. The x86 on the other hand uses segment
+// register fs, offset 0 to point to the current exception handler.
+
+extern exception_list *_except_list asm ("%fs:0");
+
+void
+init_exception_handler (exception_list *el, exception_handler *eh)
+{
+ el->handler = eh;
+ el->prev = _except_list;
+ _except_list = el;
+}
+
+extern "C" void
+init_exceptions (exception_list *el)
+{
+ init_exception_handler (el, handle_exceptions);
+}
+
+void
+init_console_handler ()
+{
+ (void) SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
+ if (!SetConsoleCtrlHandler (ctrl_c_handler, TRUE))
+ system_printf ("SetConsoleCtrlHandler failed, %E");
+}
+
+extern "C" void
+error_start_init (const char *buf)
+{
+ if (!buf || !*buf)
+ {
+ debugger_command[0] = '\0';
+ return;
+ }
+
+ char pgm[CYG_MAX_PATH + 1];
+ if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH))
+ strcpy (pgm, "cygwin1.dll");
+ for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\'))
+ *p = '/';
+
+ __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm);
+}
+
+static void
+open_stackdumpfile ()
+{
+ if (myself->progname[0])
+ {
+ const char *p;
+ /* write to progname.stackdump if possible */
+ if (!myself->progname[0])
+ p = "unknown";
+ else if ((p = strrchr (myself->progname, '\\')))
+ p++;
+ else
+ p = myself->progname;
+ char corefile[strlen (p) + sizeof (".stackdump")];
+ __small_sprintf (corefile, "%s.stackdump", p);
+ HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
+ CREATE_ALWAYS, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (!myself->cygstarted)
+ system_printf ("Dumping stack trace to %s", corefile);
+ else
+ debug_printf ("Dumping stack trace to %s", corefile);
+ SetStdHandle (STD_ERROR_HANDLE, h);
+ }
+ }
+}
+
+/* Utilities for dumping the stack, etc. */
+
+static void
+exception (EXCEPTION_RECORD *e, CONTEXT *in)
+{
+ const char *exception_name = NULL;
+
+ if (e)
+ {
+ for (int i = 0; status_info[i].name; i++)
+ {
+ if (status_info[i].code == e->ExceptionCode)
+ {
+ exception_name = status_info[i].name;
+ break;
+ }
+ }
+ }
+
+ if (exception_name)
+ small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip);
+ else
+ small_printf ("Exception %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
+ small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
+ in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
+ small_printf ("ebp=%08x esp=%08x program=%s, pid %u, thread %s\r\n",
+ in->Ebp, in->Esp, myself->progname, myself->pid, cygthread::name ());
+ small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
+ in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs);
+}
+
+/* A class for manipulating the stack. */
+class stack_info
+{
+ int walk (); /* Uses the "old" method */
+ char *next_offset () {return *((char **) sf.AddrFrame.Offset);}
+ bool needargs;
+ DWORD dummy_frame;
+public:
+ STACKFRAME sf; /* For storing the stack information */
+ void init (DWORD, bool, bool); /* Called the first time that stack info is needed */
+
+ /* Postfix ++ iterates over the stack, returning zero when nothing is left. */
+ int operator ++(int) { return walk (); }
+};
+
+/* The number of parameters used in STACKFRAME */
+#define NPARAMS (sizeof (thestack.sf.Params) / sizeof (thestack.sf.Params[0]))
+
+/* This is the main stack frame info for this process. */
+static NO_COPY stack_info thestack;
+
+/* Initialize everything needed to start iterating. */
+void
+stack_info::init (DWORD ebp, bool wantargs, bool goodframe)
+{
+# define debp ((DWORD *) ebp)
+ memset (&sf, 0, sizeof (sf));
+ if (!goodframe)
+ sf.AddrFrame.Offset = ebp;
+ else
+ {
+ dummy_frame = ebp;
+ sf.AddrFrame.Offset = (DWORD) &dummy_frame;
+ }
+ sf.AddrReturn.Offset = debp[1];
+ sf.AddrFrame.Mode = AddrModeFlat;
+ needargs = wantargs;
+# undef debp
+}
+
+/* Walk the stack by looking at successive stored 'bp' frames.
+ This is not foolproof. */
+int
+stack_info::walk ()
+{
+ char **ebp;
+ if ((ebp = (char **) next_offset ()) == NULL)
+ return 0;
+
+ sf.AddrFrame.Offset = (DWORD) ebp;
+ sf.AddrPC.Offset = sf.AddrReturn.Offset;
+
+ if (!sf.AddrPC.Offset)
+ return 0; /* stack frames are exhausted */
+
+ /* The return address always follows the stack pointer */
+ sf.AddrReturn.Offset = (DWORD) *++ebp;
+
+ if (needargs)
+ /* The arguments follow the return address */
+ for (unsigned i = 0; i < NPARAMS; i++)
+ sf.Params[i] = (DWORD) *++ebp;
+
+ return 1;
+}
+
+static void
+stackdump (DWORD ebp, int open_file, bool isexception)
+{
+ extern unsigned long rlim_core;
+
+ if (rlim_core == 0UL)
+ return;
+
+ if (open_file)
+ open_stackdumpfile ();
+
+ int i;
+
+ thestack.init (ebp, 1, !isexception); /* Initialize from the input CONTEXT */
+ small_printf ("Stack trace:\r\nFrame Function Args\r\n");
+ for (i = 0; i < 16 && thestack++; i++)
+ {
+ small_printf ("%08x %08x ", thestack.sf.AddrFrame.Offset,
+ thestack.sf.AddrPC.Offset);
+ for (unsigned j = 0; j < NPARAMS; j++)
+ small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]);
+ small_printf (")\r\n");
+ }
+ small_printf ("End of stack trace%s\n",
+ i == 16 ? " (more stack frames may be present)" : "");
+}
+
+/* Temporary (?) function for external callers to get a stack dump */
+extern "C" void
+cygwin_stackdump ()
+{
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (GetCurrentThread (), &c);
+ stackdump (c.Ebp, 0, 0);
+}
+
+#define TIME_TO_WAIT_FOR_DEBUGGER 10000
+
+extern "C" int
+try_to_debug (bool waitloop)
+{
+ debug_printf ("debugger_command '%s'", debugger_command);
+ if (*debugger_command == '\0' || being_debugged ())
+ return 0;
+
+ __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ());
+
+ LONG prio = GetThreadPriority (GetCurrentThread ());
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
+ PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
+
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = 0;
+ si.cb = sizeof (si);
+
+ /* FIXME: need to know handles of all running threads to
+ suspend_all_threads_except (current_thread_id);
+ */
+
+ /* if any of these mutexes is owned, we will fail to start any cygwin app
+ until trapped app exits */
+
+ ReleaseMutex (tty_mutex);
+
+ /* prevent recursive exception handling */
+ char* rawenv = GetEnvironmentStrings () ;
+ for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1)
+ {
+ if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0)
+ {
+ char* q = strstr (p, "error_start") ;
+ /* replace 'error_start=...' with '_rror_start=...' */
+ if (q)
+ {
+ *q = '_' ;
+ SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ;
+ }
+ break ;
+ }
+ }
+
+ console_printf ("*** starting debugger for pid %u, tid %u\n",
+ cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
+ BOOL dbg;
+ dbg = CreateProcess (NULL,
+ debugger_command,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL,
+ NULL,
+ &si,
+ &pi);
+
+ if (!dbg)
+ system_printf ("Failed to start debugger, %E");
+ else
+ {
+ if (!waitloop)
+ return dbg;
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
+ while (!being_debugged ())
+ Sleep (0);
+ Sleep (2000);
+ }
+
+ console_printf ("*** continuing pid %u from debugger call (%d)\n",
+ cygwin_pid (GetCurrentProcessId ()), dbg);
+
+ SetThreadPriority (GetCurrentThread (), prio);
+ return dbg;
+}
+
+/* Main exception handler. */
+
+extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
+static int
+handle_exceptions (EXCEPTION_RECORD *e0, void *frame, CONTEXT *in0, void *)
+{
+ static bool NO_COPY debugging = false;
+ static int NO_COPY recursed = 0;
+
+ if (debugging && ++debugging < 500000)
+ {
+ SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
+ return 0;
+ }
+
+ /* If we've already exited, don't do anything here. Returning 1
+ tells Windows to keep looking for an exception handler. */
+ if (exit_already)
+ return 1;
+
+ EXCEPTION_RECORD e = *e0;
+ CONTEXT in = *in0;
+
+ siginfo_t si;
+ /* Coerce win32 value to posix value. */
+ switch (e.ExceptionCode)
+ {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_STACK_CHECK:
+ si.si_signo = SIGFPE;
+ si.si_sigval.sival_int = FPE_FLTSUB;
+ break;
+ case STATUS_FLOAT_INEXACT_RESULT:
+ si.si_signo = SIGFPE;
+ si.si_sigval.sival_int = FPE_FLTRES;
+ break;
+ case STATUS_FLOAT_OVERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_sigval.sival_int = FPE_FLTOVF;
+ break;
+ case STATUS_FLOAT_UNDERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_sigval.sival_int = FPE_FLTUND;
+ break;
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ si.si_signo = SIGFPE;
+ si.si_sigval.sival_int = FPE_INTDIV;
+ break;
+ case STATUS_INTEGER_OVERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_sigval.sival_int = FPE_INTOVF;
+ break;
+
+ case STATUS_ILLEGAL_INSTRUCTION:
+ si.si_signo = SIGILL;
+ si.si_sigval.sival_int = ILL_ILLOPC;
+ break;
+
+ case STATUS_PRIVILEGED_INSTRUCTION:
+ si.si_signo = SIGILL;
+ si.si_sigval.sival_int = ILL_PRVOPC;
+ break;
+
+ case STATUS_NONCONTINUABLE_EXCEPTION:
+ si.si_signo = SIGILL;
+ si.si_sigval.sival_int = ILL_ILLADR;
+ break;
+
+ case STATUS_TIMEOUT:
+ si.si_signo = SIGALRM;
+ si.si_sigval.sival_int = 0;
+ break;
+
+ case STATUS_ACCESS_VIOLATION:
+ case STATUS_DATATYPE_MISALIGNMENT:
+ case STATUS_ARRAY_BOUNDS_EXCEEDED:
+ case STATUS_GUARD_PAGE_VIOLATION:
+ case STATUS_IN_PAGE_ERROR:
+ case STATUS_NO_MEMORY:
+ case STATUS_INVALID_DISPOSITION:
+ case STATUS_STACK_OVERFLOW:
+ si.si_signo = SIGSEGV;
+ si.si_sigval.sival_int = SEGV_MAPERR;
+ break;
+
+ case STATUS_CONTROL_C_EXIT:
+ si.si_signo = SIGINT;
+ si.si_sigval.sival_int = 0;
+ break;
+
+ case STATUS_INVALID_HANDLE:
+ /* CloseHandle will throw this exception if it is given an
+ invalid handle. We don't care about the exception; we just
+ want CloseHandle to return an error. This can be revisited
+ if gcc ever supports Windows style structured exception
+ handling. */
+ return 0;
+
+ default:
+ /* If we don't recognize the exception, we have to assume that
+ we are doing structured exception handling, and we let
+ something else handle it. */
+ return 1;
+ }
+
+ debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e.ExceptionCode, in.Eip, in.Esp);
+ debug_printf ("In cygwin_except_handler sig %d at %p", si.si_signo, in.Eip);
+
+ if (global_sigs[si.si_signo].sa_mask & SIGTOMASK (si.si_signo))
+ syscall_printf ("signal %d, masked %p", si.si_signo,
+ global_sigs[si.si_signo].sa_mask);
+
+ debug_printf ("In cygwin_except_handler calling %p",
+ global_sigs[si.si_signo].sa_handler);
+
+ DWORD *ebp = (DWORD *)in.Esp;
+ for (DWORD *bpend = (DWORD *) __builtin_frame_address (0); ebp > bpend; ebp--)
+ if (*ebp == in.SegCs && ebp[-1] == in.Eip)
+ {
+ ebp -= 2;
+ break;
+ }
+
+ if (!cygwin_finished_initializing
+ || GetCurrentThreadId () == sigtid
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_DFL
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_IGN
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_ERR)
+ {
+ /* Print the exception to the console */
+ if (!myself->cygstarted)
+ for (int i = 0; status_info[i].name; i++)
+ if (status_info[i].code == e.ExceptionCode)
+ {
+ system_printf ("Exception: %s", status_info[i].name);
+ break;
+ }
+
+ /* Another exception could happen while tracing or while exiting.
+ Only do this once. */
+ if (recursed++)
+ system_printf ("Error while dumping state (probably corrupted stack)");
+ else
+ {
+ if (try_to_debug (0))
+ {
+ debugging = true;
+ return 0;
+ }
+
+ open_stackdumpfile ();
+ exception (&e, &in);
+ stackdump ((DWORD) ebp, 0, 1);
+ }
+
+ signal_exit (0x80 | si.si_signo); // Flag signal + core dump
+ }
+
+ extern DWORD ret_here[];
+ RtlUnwind (frame, ret_here, e0, 0);
+ __asm__ volatile (".equ _ret_here,.");
+
+ si.si_addr = ebp;
+ si.si_code = SI_KERNEL;
+ si.si_errno = si.si_pid = si.si_uid = 0;
+ _my_tls.push ((__stack_t) ebp, true);
+ sig_send (NULL, si, &_my_tls); // Signal myself
+ return 1;
+}
+
+/* Utilities to call a user supplied exception handler. */
+
+#define SIG_NONMASKABLE (SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP))
+
+/* Non-raceable sigsuspend
+ * Note: This implementation is based on the Single UNIX Specification
+ * man page. This indicates that sigsuspend always returns -1 and that
+ * attempts to block unblockable signals will be silently ignored.
+ * This is counter to what appears to be documented in some UNIX
+ * man pages, e.g. Linux.
+ */
+int __stdcall
+handle_sigsuspend (sigset_t tempmask)
+{
+ sigset_t oldmask = myself->getsigmask (); // Remember for restoration
+
+ set_signal_mask (tempmask, oldmask);
+ sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
+
+ pthread_testcancel ();
+ pthread::cancelable_wait (signal_arrived, INFINITE);
+
+ set_sig_errno (EINTR); // Per POSIX
+
+ /* A signal dispatch function will have been added to our stack and will
+ be hit eventually. Set the old mask to be restored when the signal
+ handler returns and indicate its presence by modifying deltamask. */
+
+ _my_tls.deltamask |= SIG_NONMASKABLE;
+ _my_tls.oldmask = oldmask; // Will be restored by signal handler
+ return -1;
+}
+
+extern DWORD exec_exit; // Possible exit value for exec
+
+extern "C" {
+static void
+sig_handle_tty_stop (int sig)
+{
+ _my_tls.incyg = 1;
+ /* Silently ignore attempts to suspend if there is no accommodating
+ cygwin parent to deal with this behavior. */
+ if (!myself->cygstarted)
+ {
+ myself->process_state &= ~PID_STOPPED;
+ return;
+ }
+
+ myself->stopsig = sig;
+ char pipesig;
+ DWORD nb;
+ /* See if we have a living parent. If so, send it a special signal.
+ It will figure out exactly which pid has stopped by scanning
+ its list of subprocesses. */
+ if (my_parent_is_alive ())
+ {
+ pinfo parent (myself->ppid);
+ if (NOTSTATE (parent, PID_NOCLDSTOP))
+ {
+ pipesig = sig;
+ if (!WriteFile (myself->wr_proc_pipe, &pipesig, 1, &nb, NULL))
+ debug_printf ("sending stop notification to parent failed, %E");
+ }
+ }
+ sigproc_printf ("process %d stopped by signal %d", myself->pid, sig);
+ HANDLE w4[2];
+ w4[0] = sigCONT;
+ w4[1] = signal_arrived;
+ switch (WaitForMultipleObjects (2, w4, TRUE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ case WAIT_OBJECT_0 + 1:
+ reset_signal_arrived ();
+ if (my_parent_is_alive ())
+ {
+ pinfo parent (myself->ppid);
+ if (parent)
+ {
+ sig = SIGCONT;
+ if (!WriteFile (myself->wr_proc_pipe, &sig, 1, &nb, NULL))
+ debug_printf ("sending stop notification to parent failed, %E");
+ }
+ }
+ break;
+ default:
+ api_fatal ("WaitSingleObject failed, %E");
+ break;
+ }
+ _my_tls.incyg = 0;
+ return;
+}
+}
+
+bool
+interruptible (DWORD pc)
+{
+ int res;
+ MEMORY_BASIC_INFORMATION m;
+
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) pc, &m, sizeof m))
+ sigproc_printf ("couldn't get memory info, pc %p, %E", pc);
+
+ char *checkdir = (char *) alloca (windows_system_directory_length + 4);
+ memset (checkdir, 0, sizeof (checkdir));
+
+# define h ((HMODULE) m.AllocationBase)
+ /* Apparently Windows 95 can sometimes return bogus addresses from
+ GetThreadContext. These resolve to a strange allocation base.
+ These should *never* be treated as interruptible. */
+ if (!h || m.State != MEM_COMMIT)
+ res = false;
+ else if (h == user_data->hmodule)
+ res = true;
+ else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2))
+ res = false;
+ else
+ res = !strncasematch (windows_system_directory, checkdir,
+ windows_system_directory_length);
+ sigproc_printf ("pc %p, h %p, interruptible %d", pc, h, res);
+# undef h
+ return res;
+}
+void __stdcall
+_cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga)
+{
+ push ((__stack_t) sigdelayed, false);
+ deltamask = (siga.sa_mask | SIGTOMASK (sig)) & ~SIG_NONMASKABLE;
+ sa_flags = siga.sa_flags;
+ func = (void (*) (int)) handler;
+ saved_errno = -1; // Flag: no errno to save
+ if (handler == sig_handle_tty_stop)
+ {
+ myself->stopsig = 0;
+ myself->process_state |= PID_STOPPED;
+ }
+
+ this->sig = sig; // Should always be last thing set to avoid a race
+
+ /* Clear any waiting threads prior to dispatching to handler function */
+ int res = SetEvent (signal_arrived); // For an EINTR case
+ proc_subproc (PROC_CLEARWAIT, 1);
+ sigproc_printf ("armed signal_arrived %p, sig %d, res %d", signal_arrived,
+ sig, res);
+}
+
+bool
+_cygtls::interrupt_now (CONTEXT *ctx, int sig, void *handler,
+ struct sigaction& siga)
+{
+ push ((__stack_t) ctx->Eip, false);
+ interrupt_setup (sig, handler, siga);
+ ctx->Eip = pop ();
+ SetThreadContext (*this, ctx); /* Restart the thread in a new location */
+ return 1;
+}
+
+extern "C" void __stdcall
+set_sig_errno (int e)
+{
+ *_my_tls.errno_addr = e;
+ _my_tls.saved_errno = e;
+ // sigproc_printf ("errno %d", e);
+}
+
+static int setup_handler (int, void *, struct sigaction&, _cygtls *tls)
+ __attribute__((regparm(3)));
+static int
+setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls)
+{
+ CONTEXT cx;
+ bool interrupted = false;
+
+ if (tls->sig)
+ {
+ sigproc_printf ("trying to send sig %d but signal %d already armed",
+ sig, tls->sig);
+ goto out;
+ }
+
+ for (int i = 0; i < CALL_HANDLER_RETRY; i++)
+ {
+ tls->lock ();
+ if (tls->incyg || tls->in_exception ())
+ {
+ sigproc_printf ("controlled interrupt. incyg %d, exception %d, stackptr %p, stack %p, stackptr[-1] %p",
+ tls->incyg, tls->in_exception (), tls->stackptr, tls->stack, tls->stackptr[-1]);
+ tls->reset_exception ();
+ tls->interrupt_setup (sig, handler, siga);
+ interrupted = true;
+ tls->unlock ();
+ break;
+ }
+
+ tls->unlock ();
+ DWORD res;
+ HANDLE hth = (HANDLE) *tls;
+
+ /* Suspend the thread which will receive the signal.
+ 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 has called
+ SuspendThread on itself) then just queue the signal. */
+
+#ifndef DEBUGGING
+ sigproc_printf ("suspending mainthread");
+#else
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ memset (&cx, 0, sizeof cx);
+ sigproc_printf ("suspending mainthread PC %p", cx.Eip);
+#endif
+ res = SuspendThread (hth);
+ /* Just set pending if thread is already suspended */
+ if (res)
+ {
+ (void) ResumeThread (hth);
+ break;
+ }
+ if (tls->incyg || tls->in_exception () || tls->spinning || tls->locked ())
+ sigproc_printf ("incyg %d, in_exception %d, spinning %d, locked %d\n",
+ tls->incyg, tls->in_exception (), tls->spinning, tls->locked ());
+ else
+ {
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ system_printf ("couldn't get context of main thread, %E");
+ else if (interruptible (cx.Eip))
+ interrupted = tls->interrupt_now (&cx, sig, handler, siga);
+ }
+
+ res = ResumeThread (hth);
+ if (interrupted)
+ break;
+
+ sigproc_printf ("couldn't interrupt. trying again.");
+ low_priority_sleep (0);
+ }
+
+out:
+ if (interrupted && tls->event)
+ {
+ HANDLE h = tls->event;
+ tls->event = NULL;
+ SetEvent (h);
+ }
+ sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not ");
+ return interrupted;
+}
+
+/* Keyboard interrupt handler. */
+static BOOL WINAPI
+ctrl_c_handler (DWORD type)
+{
+ static bool saw_close;
+
+ if (!cygwin_finished_initializing)
+ {
+ if (myself->cygstarted) /* Was this process created by a cygwin process? */
+ return TRUE; /* Yes. Let the parent eventually handle CTRL-C issues. */
+ debug_printf ("exiting with status %p", STATUS_CONTROL_C_EXIT);
+ ExitProcess (STATUS_CONTROL_C_EXIT);
+ }
+
+ _my_tls.remove (INFINITE);
+
+ /* Return FALSE to prevent an "End task" dialog box from appearing
+ for each Cygwin process window that's open when the computer
+ is shut down or console window is closed. */
+
+ if (type == CTRL_SHUTDOWN_EVENT)
+ {
+#if 0
+ /* Don't send a signal. Only NT service applications and their child
+ processes will receive this event and the services typically already
+ handle the shutdown action when getting the SERVICE_CONTROL_SHUTDOWN
+ control message. */
+ sig_send (NULL, SIGTERM);
+#endif
+ return FALSE;
+ }
+
+ if (myself->ctty != -1)
+ {
+ if (type == CTRL_CLOSE_EVENT)
+ {
+ sig_send (NULL, SIGHUP);
+ saw_close = true;
+ return FALSE;
+ }
+ if (!saw_close && type == CTRL_LOGOFF_EVENT)
+ {
+ /* Check if the process is actually associated with a visible
+ window station, one which actually represents a visible desktop.
+ If not, the CTRL_LOGOFF_EVENT doesn't concern this process. */
+ if (has_visible_window_station ())
+ sig_send (myself_nowait, SIGHUP);
+ return FALSE;
+ }
+ }
+
+ /* If we are a stub and the new process has a pinfo structure, let it
+ handle this signal. */
+ if (dwExeced && pinfo (dwExeced))
+ return TRUE;
+
+ /* We're only the process group leader when we have a valid pinfo structure.
+ If we don't have one, then the parent "stub" will handle the signal. */
+ if (!pinfo (cygwin_pid (GetCurrentProcessId ())))
+ return TRUE;
+
+ tty_min *t = cygwin_shared->tty.get_tty (myself->ctty);
+ /* Ignore this if we're not the process group leader since it should be handled
+ *by* the process group leader. */
+ if (myself->ctty != -1 && t->getpgid () == myself->pid &&
+ (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP)
+ /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
+ that we have handled the signal). At this point, type should be
+ a CTRL_C_EVENT or CTRL_BREAK_EVENT. */
+ {
+ t->last_ctrl_c = GetTickCount ();
+ killsys (-myself->pid, SIGINT);
+ t->last_ctrl_c = GetTickCount ();
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+/* Function used by low level sig wrappers. */
+extern "C" void __stdcall
+set_process_mask (sigset_t newmask)
+{
+ set_signal_mask (newmask);
+}
+
+extern "C" int
+sighold (int sig)
+{
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", sig);
+ return -1;
+ }
+ mask_sync->acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ sigaddset (&mask, sig);
+ set_signal_mask (mask);
+ mask_sync->release ();
+ return 0;
+}
+
+/* Update the signal mask for this process
+ and return the old mask.
+ Called from sigdelayed */
+extern "C" sigset_t
+set_process_mask_delta ()
+{
+ mask_sync->acquire (INFINITE);
+ sigset_t newmask, oldmask;
+
+ if (_my_tls.deltamask & SIG_NONMASKABLE)
+ oldmask = _my_tls.oldmask; /* from handle_sigsuspend */
+ else
+ oldmask = myself->getsigmask ();
+ newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
+ sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask,
+ _my_tls.deltamask);
+ myself->setsigmask (newmask);
+ mask_sync->release ();
+ return oldmask;
+}
+
+/* Set the signal mask for this process.
+ Note that some signals are unmaskable, as in UNIX. */
+extern "C" void __stdcall
+set_signal_mask (sigset_t newmask, sigset_t oldmask)
+{
+ mask_sync->acquire (INFINITE);
+ newmask &= ~SIG_NONMASKABLE;
+ sigset_t mask_bits = oldmask & ~newmask;
+ sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask,
+ mask_bits);
+ myself->setsigmask (newmask); // Set a new mask
+ if (mask_bits)
+ sig_dispatch_pending (true);
+ else
+ sigproc_printf ("not calling sig_dispatch_pending");
+ mask_sync->release ();
+ return;
+}
+
+int __stdcall
+sigpacket::process ()
+{
+ DWORD continue_now;
+ if (si.si_signo != SIGCONT)
+ continue_now = false;
+ else
+ {
+ continue_now = myself->process_state & PID_STOPPED;
+ myself->stopsig = 0;
+ myself->process_state &= ~PID_STOPPED;
+ /* Clear pending stop signals */
+ sig_clear (SIGSTOP);
+ sig_clear (SIGTSTP);
+ sig_clear (SIGTTIN);
+ sig_clear (SIGTTOU);
+ }
+
+ int rc = 1;
+
+ sigproc_printf ("signal %d processing", si.si_signo);
+ struct sigaction thissig = global_sigs[si.si_signo];
+
+ myself->rusage_self.ru_nsignals++;
+
+ if (si.si_signo == SIGKILL)
+ goto exit_sig;
+ if ( si.si_signo == SIGSTOP)
+ {
+ sig_clear (SIGCONT);
+ if (!tls)
+ tls = _main_tls;
+ goto stop;
+ }
+
+ bool masked;
+ bool special_case;
+ bool insigwait_mask;
+ insigwait_mask = masked = false;
+ if (special_case = (VFORKPID || ISSTATE (myself, PID_STOPPED)))
+ /* nothing to do */;
+ else if (tls && sigismember (&tls->sigwait_mask, si.si_signo))
+ insigwait_mask = true;
+ else if (!tls && (tls = _cygtls::find_tls (si.si_signo)))
+ insigwait_mask = true;
+ else if (!(masked = sigismember (mask, si.si_signo)) && tls)
+ masked = sigismember (&tls->sigmask, si.si_signo);
+
+ if (insigwait_mask)
+ goto thread_specific;
+
+ if (!tls)
+ tls = _main_tls;
+
+ if (special_case || masked)
+ {
+ sigproc_printf ("signal %d blocked", si.si_signo);
+ rc = -1;
+ goto done;
+ }
+
+ void *handler;
+ handler = (void *) thissig.sa_handler;
+
+ /* Clear pending SIGCONT on stop signals */
+ if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
+ sig_clear (SIGCONT);
+
+#if 0
+ char sigmsg[24];
+ __small_sprintf (sigmsg, "cygwin: signal %d\n", si.si_signo);
+ OutputDebugString (sigmsg);
+#endif
+
+ if (handler == (void *) SIG_DFL)
+ {
+ if (insigwait_mask)
+ goto thread_specific;
+ if (si.si_signo == SIGCHLD || si.si_signo == SIGIO || si.si_signo == SIGCONT || si.si_signo == SIGWINCH
+ || si.si_signo == SIGURG)
+ {
+ sigproc_printf ("default signal %d ignored", si.si_signo);
+ if (continue_now)
+ SetEvent (signal_arrived);
+ goto done;
+ }
+
+ if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
+ goto stop;
+
+ goto exit_sig;
+ }
+
+ if (handler == (void *) SIG_IGN)
+ {
+ sigproc_printf ("signal %d ignored", si.si_signo);
+ goto done;
+ }
+
+ if (handler == (void *) SIG_ERR)
+ goto exit_sig;
+
+ tls->set_siginfo (this);
+ goto dosig;
+
+stop:
+ /* Eat multiple attempts to STOP */
+ if (ISSTATE (myself, PID_STOPPED))
+ goto done;
+ handler = (void *) sig_handle_tty_stop;
+ thissig = global_sigs[SIGSTOP];
+
+dosig:
+ /* Dispatch to the appropriate function. */
+ sigproc_printf ("signal %d, about to call %p", si.si_signo, handler);
+ rc = setup_handler (si.si_signo, handler, thissig, tls);
+
+done:
+ if (continue_now)
+ SetEvent (sigCONT);
+ sigproc_printf ("returning %d", rc);
+ return rc;
+
+thread_specific:
+ tls->sig = si.si_signo;
+ tls->set_siginfo (this);
+ sigproc_printf ("releasing sigwait for thread");
+ SetEvent (tls->event);
+ goto done;
+
+exit_sig:
+ if (si.si_signo == SIGQUIT || si.si_signo == SIGABRT)
+ {
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (hMainThread, &c);
+ if (!try_to_debug ())
+ stackdump (c.Ebp, 1, 1);
+ si.si_signo |= 0x80;
+ }
+ sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
+ signal_exit (si.si_signo);
+ /* Never returns */
+}
+
+CRITICAL_SECTION NO_COPY exit_lock;
+
+/* Cover function to `do_exit' to handle exiting even in presence of more
+ exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit
+ routines to run. */
+static void
+signal_exit (int rc)
+{
+ EnterCriticalSection (&exit_lock);
+ if (exit_already++)
+ myself->exit (rc);
+
+ /* We'd like to stop the main thread from executing but when we do that it
+ causes random, inexplicable hangs. So, instead, we set up the priority
+ of this thread really high so that it should do its thing and then exit. */
+ (void) SetThreadPriority (hMainThread, THREAD_PRIORITY_IDLE);
+ (void) SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+
+ user_data->resourcelocks->Delete ();
+ user_data->resourcelocks->Init ();
+
+ if (hExeced)
+ {
+ sigproc_printf ("terminating captive process");
+ TerminateProcess (hExeced, rc);
+ }
+
+ sigproc_printf ("about to call do_exit (%x)", rc);
+ (void) SetEvent (signal_arrived);
+ do_exit (rc);
+}
+
+HANDLE NO_COPY tty_mutex = NULL;
+
+void
+events_init (void)
+{
+ char *name;
+ char mutex_name[CYG_MAX_PATH];
+ /* tty_mutex is on while searching for a tty slot. It's necessary
+ while finding console window handle */
+
+ if (!(tty_mutex = CreateMutex (&sec_all_nih, FALSE,
+ name = shared_name (mutex_name,
+ "tty_mutex", 0))))
+ api_fatal ("can't create title mutex '%s', %E", name);
+
+ ProtectHandle (tty_mutex);
+ new_muto (mask_sync);
+ windows_system_directory[0] = '\0';
+ (void) GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2);
+ char *end = strchr (windows_system_directory, '\0');
+ if (end == windows_system_directory)
+ api_fatal ("can't find windows system directory");
+ if (end[-1] != '\\')
+ {
+ *end++ = '\\';
+ *end = '\0';
+ }
+ windows_system_directory_length = end - windows_system_directory;
+ debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
+ windows_system_directory, windows_system_directory_length);
+ InitializeCriticalSection (&exit_lock);
+}
+
+void
+events_terminate (void)
+{
+ exit_already = 1;
+}
+
+int
+_cygtls::call_signal_handler ()
+{
+ int this_sa_flags = 0;
+ /* Call signal handler. */
+ while (sig)
+ {
+ lock (); unlock (); // make sure synchronized
+ this_sa_flags = sa_flags;
+ int thissig = sig;
+ void (*sigfunc) (int) = func;
+
+ (void) pop ();
+ reset_signal_arrived ();
+ sigset_t this_oldmask = set_process_mask_delta ();
+ int this_errno = saved_errno;
+ incyg--;
+ sig = 0;
+ sigfunc (thissig);
+ incyg++;
+ set_signal_mask (this_oldmask);
+ if (this_errno >= 0)
+ set_errno (this_errno);
+ }
+
+ return this_sa_flags & SA_RESTART;
+}
+
+extern "C" void __stdcall
+reset_signal_arrived ()
+{
+ // NEEDED? WaitForSingleObject (signal_arrived, 10);
+ (void) ResetEvent (signal_arrived);
+ sigproc_printf ("reset signal_arrived");
+ if (_my_tls.stackptr > _my_tls.stack)
+ debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]);
+}
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
new file mode 100644
index 00000000000..52a5b5ea521
--- /dev/null
+++ b/winsup/cygwin/fork.cc
@@ -0,0 +1,740 @@
+/* fork.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "child_info.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "perprocess.h"
+#include "dll_init.h"
+#include "sync.h"
+#include "shared_info.h"
+#include "cygmalloc.h"
+#include "cygthread.h"
+
+#define NPIDS_HELD 4
+
+/* 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 */
+
+#define dll_data_start &_data_start__
+#define dll_data_end &_data_end__
+#define dll_bss_start &_bss_start__
+#define dll_bss_end &_bss_end__
+
+void
+per_thread::set (void *s)
+{
+ if (s == PER_THREAD_FORK_CLEAR)
+ {
+ tls = TlsAlloc ();
+ s = NULL;
+ }
+ TlsSetValue (get_tls (), s);
+}
+
+static void
+stack_base (child_info_fork &ch)
+{
+ MEMORY_BASIC_INFORMATION m;
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) &m, &m, sizeof m))
+ system_printf ("couldn't get memory info, %E");
+
+ ch.stacktop = m.AllocationBase;
+ ch.stackbottom = (LPBYTE) m.BaseAddress + m.RegionSize;
+ ch.stacksize = (DWORD) ch.stackbottom - (DWORD) &m;
+ debug_printf ("bottom %p, top %p, stack %p, size %d, reserve %d",
+ ch.stackbottom, ch.stacktop, &m, ch.stacksize,
+ (DWORD) ch.stackbottom - (DWORD) ch.stacktop);
+}
+
+/* Copy memory from parent to child.
+ The result is a boolean indicating success. */
+
+static int
+fork_copy (PROCESS_INFORMATION &pi, const char *what, ...)
+{
+ va_list args;
+ char *low;
+ int pass = 0;
+
+ va_start (args, what);
+
+ while ((low = va_arg (args, char *)))
+ {
+ char *high = va_arg (args, char *);
+ DWORD todo = wincap.chunksize () ?: high - low;
+ char *here;
+
+ for (here = low; here < high; here += todo)
+ {
+ DWORD done = 0;
+ if (here + todo > high)
+ todo = high - here;
+ int res = WriteProcessMemory (pi.hProcess, here, here, todo, &done);
+ debug_printf ("child handle %p, low %p, high %p, res %d", pi.hProcess,
+ low, high, res);
+ if (!res || todo != done)
+ {
+ if (!res)
+ __seterrno ();
+ /* If this happens then there is a bug in our fork
+ implementation somewhere. */
+ system_printf ("%s pass %d failed, %p..%p, done %d, windows pid %u, %E",
+ what, pass, low, high, done, pi.dwProcessId);
+ goto err;
+ }
+ }
+
+ pass++;
+ }
+
+ debug_printf ("done");
+ return 1;
+
+ err:
+ TerminateProcess (pi.hProcess, 1);
+ set_errno (EAGAIN);
+ 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)
+{
+ SetEvent (forker_finished);
+ debug_printf ("signalled child");
+ return 1;
+}
+
+/* Notify parent that it is time for the next step.
+ Note that this has to be a macro since the parent may be messing with
+ our stack. */
+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);
+ if (hang_self)
+ {
+ HANDLE h = fork_info->forker_finished;
+ /* Wait for the parent to fill in our stack and heap.
+ Don't wait forever here. If our parent dies we don't want to clog
+ the system. If the wait fails, we really can't continue so exit. */
+ DWORD psync_rc = WaitForSingleObject (h, FORK_WAIT_TIMEOUT);
+ debug_printf ("awake");
+ switch (psync_rc)
+ {
+ case WAIT_TIMEOUT:
+ api_fatal ("WFSO timed out %s", s);
+ break;
+ case WAIT_FAILED:
+ if (GetLastError () == ERROR_INVALID_HANDLE &&
+ WaitForSingleObject (fork_info->forker_finished, 1) != WAIT_FAILED)
+ break;
+ api_fatal ("WFSO failed %s, fork_finished %p, %E", s,
+ fork_info->forker_finished);
+ break;
+ default:
+ debug_printf ("no problems");
+ break;
+ }
+ }
+}
+
+static int __stdcall
+fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
+{
+ extern void fixup_timers_after_fork ();
+ debug_printf ("child is running. pid %d, ppid %d, stack here %p",
+ myself->pid, myself->ppid, __builtin_frame_address (0));
+
+ /* Restore the inheritance state as in parent
+ Don't call setuid here! The flags are already set. */
+ cygheap->user.reimpersonate ();
+
+ sync_with_parent ("after longjmp", true);
+ sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent,
+ first_dll, load_dlls);
+
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ the threadinfo information is properly set up. */
+ if (fork_info->stacksize)
+ {
+ _main_tls = &_my_tls;
+ _main_tls->init_thread (NULL, NULL);
+ _main_tls->local_clib = *_impure_ptr;
+ _impure_ptr = &_main_tls->local_clib;
+ }
+
+#ifdef DEBUGGING
+ char c;
+ if (GetEnvironmentVariable ("FORKDEBUG", &c, 1))
+ try_to_debug ();
+ char buf[80];
+ /* This is useful for debugging fork problems. Use gdb to attach to
+ the pid reported here. */
+ if (GetEnvironmentVariable ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
+ {
+ small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
+ Sleep (atoi (buf));
+ }
+#endif
+
+ set_file_api_mode (current_codepage);
+
+ MALLOC_CHECK;
+
+ if (fixup_mmaps_after_fork (hParent))
+ api_fatal ("recreate_mmaps_after_fork_failed");
+
+
+ MALLOC_CHECK;
+
+ /* If we haven't dynamically loaded any dlls, just signal
+ the parent. Otherwise, load all the dlls, tell the parent
+ that we're done, and wait for the parent to fill in the.
+ loaded dlls' data/bss. */
+ if (!load_dlls)
+ {
+ cygheap->fdtab.fixup_after_fork (hParent);
+ ProtectHandleINH (hParent);
+ sync_with_parent ("performed fork fixup", false);
+ }
+ else
+ {
+ dlls.load_after_fork (hParent, first_dll);
+ cygheap->fdtab.fixup_after_fork (hParent);
+ ProtectHandleINH (hParent);
+ sync_with_parent ("loaded dlls", true);
+ }
+
+ ForceCloseHandle (hParent);
+ (void) ForceCloseHandle1 (fork_info->subproc_ready, subproc_ready);
+ (void) ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
+
+ pinfo_fixup_after_fork ();
+ _my_tls.fixup_after_fork ();
+ sigproc_init ();
+
+#ifdef USE_SERVER
+ if (fixup_shms_after_fork ())
+ api_fatal ("recreate_shm areas after fork failed");
+#endif
+
+ /* Set thread local stuff to zero. Under Windows 95/98 this is sometimes
+ non-zero, for some reason.
+ FIXME: There is a memory leak here after a fork. */
+ for (per_thread **t = threadstuff; *t; t++)
+ if ((*t)->clear_on_fork ())
+ (*t)->set ();
+
+ pthread::atforkchild ();
+ fixup_timers_after_fork ();
+ cygbench ("fork-child");
+ cygwin_finished_initializing = true;
+ return 0;
+}
+
+#ifndef NO_SLOW_PID_REUSE
+static void
+slow_pid_reuse (HANDLE h)
+{
+ static NO_COPY HANDLE last_fork_procs[NPIDS_HELD] = {0};
+ static NO_COPY unsigned nfork_procs = 0;
+
+ 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
+ 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. */
+ if (last_fork_procs[nfork_procs])
+ ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
+ if (DuplicateHandle (hMainProc, h, hMainProc, &last_fork_procs[nfork_procs],
+ 0, FALSE, DUPLICATE_SAME_ACCESS))
+ ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
+ else
+ {
+ last_fork_procs[nfork_procs] = NULL;
+ system_printf ("couldn't create last_fork_proc, %E");
+ }
+ nfork_procs++;
+}
+#endif
+
+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;
+ DWORD rc;
+ PROCESS_INFORMATION pi = {0, NULL, 0, 0};
+
+ pthread::atforkprepare ();
+
+ int c_flags = GetPriorityClass (hMainProc) /*|
+ CREATE_NEW_PROCESS_GROUP*/;
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+
+ /* If we don't have a console, then don't create a console for the
+ child either. */
+ HANDLE console_handle = CreateFile ("CONOUT$", GENERIC_WRITE,
+ FILE_SHARE_WRITE, &sec_none_nih,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (console_handle != INVALID_HANDLE_VALUE)
+ CloseHandle (console_handle);
+ else
+ c_flags |= DETACHED_PROCESS;
+
+ /* 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 (cygheap->fdtab.need_fixup_before ())
+ c_flags |= CREATE_SUSPENDED;
+
+ /* Create an inheritable handle to pass to the child process. This will
+ allow the child to duplicate handles from the parent to itself. */
+ hParent = NULL;
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hParent, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("couldn't create handle to myself for child, %E");
+ return -1;
+ }
+
+ /* Remember the address of the first loaded dll and decide
+ if we need to load dlls. We do this here so that this
+ information will be available in the parent and, when
+ the stack is copied, in the child. */
+ first_dll = dlls.start.next;
+ load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
+
+ /* 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;
+
+ stack_base (ch);
+
+ si.cb = sizeof (STARTUPINFO);
+ si.lpReserved2 = (LPBYTE)&ch;
+ si.cbReserved2 = sizeof (ch);
+
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+
+ ch.parent = hParent;
+
+ syscall_printf ("CreateProcess (%s, %s, 0, 0, 1, %x, 0, 0, %p, %p)",
+ myself->progname, myself->progname, c_flags, &si, &pi);
+ bool locked = __malloc_lock ();
+ void *newheap;
+ newheap = cygheap_setup_for_child (&ch, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcess (myself->progname, /* image to run */
+ myself->progname, /* what we send in arg0 */
+ &sec_none_nih,
+ &sec_none_nih,
+ TRUE, /* inherit handles from parent */
+ c_flags,
+ NULL, /* environment filled in later */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+
+ CloseHandle (hParent);
+
+ if (!rc)
+ {
+ __seterrno ();
+ syscall_printf ("CreateProcessA failed, %E");
+ ForceCloseHandle (subproc_ready);
+ ForceCloseHandle (forker_finished);
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+ cygheap_setup_for_child_cleanup (newheap, &ch, 0);
+ __malloc_unlock ();
+ return -1;
+ }
+
+ /* Fixup the parent datastructure if needed and resume the child's
+ main thread. */
+ if (!cygheap->fdtab.need_fixup_before ())
+ cygheap_setup_for_child_cleanup (newheap, &ch, 0);
+ else
+ {
+ cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
+ cygheap_setup_for_child_cleanup (newheap, &ch, 1);
+ ResumeThread (pi.hThread);
+ }
+
+ int forked_pid = cygwin_pid (pi.dwProcessId);
+ pinfo forked (forked_pid, 1);
+
+ if (!forked)
+ {
+ syscall_printf ("pinfo failed");
+ if (get_errno () != ENOMEM)
+ set_errno (EAGAIN);
+ goto cleanup;
+ }
+
+ /* Initialize things that are done later in dll_crt0_1 that aren't done
+ for the forkee. */
+ strcpy (forked->progname, myself->progname);
+
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+
+ 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. */
+ forked->dwProcessId = pi.dwProcessId;
+ forked.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 ())
+ {
+ TerminateProcess (pi.hProcess, 1);
+ set_errno (EAGAIN);
+ goto cleanup;
+ }
+
+#ifndef NO_SLOW_PID_REUSE
+ slow_pid_reuse (pi.hProcess);
+#endif
+
+ /* Wait for subproc to initialize itself. */
+ if (!sync_with_child (pi, subproc_ready, true, "waiting for longjmp"))
+ goto cleanup;
+
+ /* CHILD IS STOPPED */
+ debug_printf ("child is alive (but stopped)");
+
+ /* Initialize, in order: data, bss, heap, stack, dll data, dll bss
+ Note: variables marked as NO_COPY will not be copied
+ since they are placed in a protected segment. */
+
+
+ MALLOC_CHECK;
+ void *impure_beg;
+ void *impure_end;
+ if (&_my_tls == _main_tls)
+ impure_beg = impure_end = NULL;
+ else
+ {
+ impure_beg = _impure_ptr;
+ impure_end = _impure_ptr + 1;
+ }
+ rc = fork_copy (pi, "user/cygwin data",
+ user_data->data_start, user_data->data_end,
+ user_data->bss_start, user_data->bss_end,
+ cygheap->user_heap.base, cygheap->user_heap.ptr,
+ stack_here, ch.stackbottom,
+ dll_data_start, dll_data_end,
+ dll_bss_start, dll_bss_end, impure_beg, impure_end, NULL);
+
+ __malloc_unlock ();
+ locked = false;
+ MALLOC_CHECK;
+ if (!rc)
+ goto cleanup;
+
+ /* Now fill data/bss of any DLLs that were linked into the program. */
+ for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ())
+ {
+ debug_printf ("copying data/bss of a linked dll");
+ if (!fork_copy (pi, "linked dll data/bss", d->p.data_start, d->p.data_end,
+ d->p.bss_start, d->p.bss_end,
+ NULL))
+ goto cleanup;
+ }
+
+ /* 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"))
+ 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
+ bss sections filled in. */
+ if (load_dlls)
+ {
+ /* CHILD IS STOPPED */
+ /* write memory of reloaded dlls */
+ for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
+ {
+ debug_printf ("copying data/bss for a loaded dll");
+ if (!fork_copy (pi, "loaded dll data/bss", d->p.data_start, d->p.data_end,
+ d->p.bss_start, d->p.bss_end,
+ NULL))
+ goto cleanup;
+ }
+ /* Start the child up again. */
+ (void) resume_child (pi, forker_finished);
+ }
+
+ if (pi.hProcess)
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+ ForceCloseHandle (subproc_ready);
+ ForceCloseHandle (pi.hThread);
+ ForceCloseHandle (forker_finished);
+ forker_finished = NULL;
+ pi.hThread = NULL;
+ pthread::atforkparent ();
+
+ return forked_pid;
+
+/* Common cleanup code for failure cases */
+ cleanup:
+ if (locked)
+ __malloc_unlock ();
+
+ /* Remember to de-allocate the fd table. */
+ if (pi.hProcess)
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+ if (pi.hThread)
+ ForceCloseHandle (pi.hThread);
+ if (subproc_ready)
+ ForceCloseHandle (subproc_ready);
+ if (forker_finished)
+ ForceCloseHandle (forker_finished);
+ return -1;
+}
+
+extern "C" int
+fork ()
+{
+ struct
+ {
+ HANDLE hParent;
+ dll *first_dll;
+ bool load_dlls;
+ } grouped;
+
+ MALLOC_CHECK;
+
+ debug_printf ("entering");
+ grouped.hParent = grouped.first_dll = NULL;
+ grouped.load_dlls = 0;
+
+ void *esp;
+ __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+
+ myself->set_has_pgid_children ();
+
+ child_info_fork ch;
+
+ sig_send (NULL, __SIGHOLD);
+ int res = setjmp (ch.jmp);
+ if (res)
+ res = fork_child (grouped.hParent, grouped.first_dll, grouped.load_dlls);
+ else
+ res = fork_parent (grouped.hParent, grouped.first_dll, grouped.load_dlls, esp, ch);
+ sig_send (NULL, __SIGNOHOLD);
+
+ MALLOC_CHECK;
+ syscall_printf ("%d = fork()", res);
+ return res;
+}
+#ifdef DEBUGGING
+void
+fork_init ()
+{
+}
+#endif /*DEBUGGING*/
+
+#ifdef NEWVFORK
+/* Dummy function to force second assignment below to actually be
+ carried out */
+static vfork_save *
+get_vfork_val ()
+{
+ return vfork_storage.val ();
+}
+#endif
+
+extern "C" int
+vfork ()
+{
+#ifndef NEWVFORK
+ debug_printf ("stub called");
+ return fork ();
+#else
+ vfork_save *vf = get_vfork_val ();
+ char **esp, **pp;
+
+ if (vf == NULL)
+ vf = vfork_storage.create ();
+ else if (vf->pid)
+ return fork ();
+
+ // FIXME the tls stuff could introduce a signal race if a child process
+ // exits quickly.
+ if (!setjmp (vf->j))
+ {
+ vf->pid = -1;
+ __asm__ volatile ("movl %%esp,%0": "=r" (vf->vfork_esp):);
+ __asm__ volatile ("movl %%ebp,%0": "=r" (vf->vfork_ebp):);
+ for (pp = (char **) vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *pp = *esp;
+ vf->ctty = myself->ctty;
+ vf->sid = myself->sid;
+ vf->pgid = myself->pgid;
+ cygheap->ctty_on_hold = cygheap->ctty;
+ vf->open_fhs = cygheap->open_fhs;
+ debug_printf ("cygheap->ctty_on_hold %p, cygheap->open_fhs %d", cygheap->ctty_on_hold, cygheap->open_fhs);
+ int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
+ debug_printf ("%d = vfork()", res);
+ _my_tls.call_signal_handler (); // FIXME: racy
+ vf->tls = _my_tls;
+ return res;
+ }
+
+ vf = get_vfork_val ();
+
+ for (pp = (char **) vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *esp = *pp;
+
+ cygheap->fdtab.vfork_parent_restore ();
+
+ myself->ctty = vf->ctty;
+ myself->sid = vf->sid;
+ myself->pgid = vf->pgid;
+ termios_printf ("cygheap->ctty %p, cygheap->ctty_on_hold %p", cygheap->ctty, cygheap->ctty_on_hold);
+ cygheap->open_fhs = vf->open_fhs;
+
+ if (vf->pid < 0)
+ {
+ int exitval = vf->exitval;
+ vf->pid = 0;
+ if ((vf->pid = fork ()) == 0)
+ exit (exitval);
+ }
+
+ int pid = vf->pid;
+ vf->pid = 0;
+ debug_printf ("exiting vfork, pid %d", pid);
+ sig_dispatch_pending ();
+
+ _my_tls.call_signal_handler (); // FIXME: racy
+ _my_tls = vf->tls;
+ return pid;
+#endif
+}
diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h
new file mode 100644
index 00000000000..b8c99738e48
--- /dev/null
+++ b/winsup/cygwin/include/sys/cygwin.h
@@ -0,0 +1,265 @@
+/* sys/cygwin.h
+
+ Copyright 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _SYS_CYGWIN_H
+#define _SYS_CYGWIN_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern pid_t cygwin32_winpid_to_pid (int);
+extern void cygwin32_win32_to_posix_path_list (const char *, char *);
+extern int cygwin32_win32_to_posix_path_list_buf_size (const char *);
+extern void cygwin32_posix_to_win32_path_list (const char *, char *);
+extern int cygwin32_posix_to_win32_path_list_buf_size (const char *);
+extern int cygwin32_conv_to_win32_path (const char *, char *);
+extern int cygwin32_conv_to_full_win32_path (const char *, char *);
+extern void cygwin32_conv_to_posix_path (const char *, char *);
+extern void cygwin32_conv_to_full_posix_path (const char *, char *);
+extern int cygwin32_posix_path_list_p (const char *);
+extern void cygwin32_split_path (const char *, char *, char *);
+
+extern pid_t cygwin_winpid_to_pid (int);
+extern int cygwin_win32_to_posix_path_list (const char *, char *);
+extern int cygwin_win32_to_posix_path_list_buf_size (const char *);
+extern int cygwin_posix_to_win32_path_list (const char *, char *);
+extern int cygwin_posix_to_win32_path_list_buf_size (const char *);
+extern int cygwin_conv_to_win32_path (const char *, char *);
+extern int cygwin_conv_to_full_win32_path (const char *, char *);
+extern int cygwin_conv_to_posix_path (const char *, char *);
+extern int cygwin_conv_to_full_posix_path (const char *, char *);
+extern int cygwin_posix_path_list_p (const char *);
+extern void cygwin_split_path (const char *, char *, char *);
+
+struct __cygwin_perfile
+{
+ const char *name;
+ unsigned flags;
+};
+
+/* External interface stuff */
+
+typedef enum
+ {
+ CW_LOCK_PINFO,
+ CW_UNLOCK_PINFO,
+ CW_GETTHREADNAME,
+ CW_GETPINFO,
+ CW_SETPINFO,
+ CW_SETTHREADNAME,
+ CW_GETVERSIONINFO,
+ CW_READ_V1_MOUNT_TABLES,
+ CW_USER_DATA,
+ CW_PERFILE,
+ CW_GET_CYGDRIVE_PREFIXES,
+ CW_GETPINFO_FULL,
+ CW_INIT_EXCEPTIONS,
+ CW_GET_CYGDRIVE_INFO,
+ CW_SET_CYGWIN_REGISTRY_NAME,
+ CW_GET_CYGWIN_REGISTRY_NAME,
+ CW_STRACE_TOGGLE,
+ CW_STRACE_ACTIVE,
+ CW_CYGWIN_PID_TO_WINPID,
+ CW_EXTRACT_DOMAIN_AND_USER,
+ CW_CMDLINE,
+ CW_CHECK_NTSEC,
+ CW_GET_ERRNO_FROM_WINERROR,
+ CW_GET_POSIX_SECURITY_ATTRIBUTE,
+ CW_GET_SHMLBA,
+ CW_GET_UID_FROM_SID,
+ CW_GET_GID_FROM_SID,
+ CW_GET_BINMODE
+ } cygwin_getinfo_types;
+
+#define CW_NEXTPID 0x80000000 /* or with pid to get next one */
+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_EXITED = 0x80000000 /* Free entry. */
+};
+
+#ifdef WINVER
+#ifdef _PATH_PASSWD
+extern HANDLE cygwin_logon_user (const struct passwd *, const char *);
+#endif
+
+/* This lives in the app and is initialized before jumping into the DLL.
+ It should only contain stuff which the user's process needs to see, or
+ which is needed before the user pointer is initialized, or is needed to
+ carry inheritance information from parent to child. Note that it cannot
+ be used to carry inheritance information across exec!
+
+ Remember, this structure is linked into the application's executable.
+ Changes to this can invalidate existing executables, so we go to extra
+ lengths to avoid having to do it.
+
+ When adding/deleting members, remember to adjust {public,internal}_reserved.
+ The size of the class shouldn't change [unless you really are prepared to
+ invalidate all existing executables]. The program does a check (using
+ SIZEOF_PER_PROCESS) to make sure you remember to make the adjustment.
+*/
+
+#ifdef __cplusplus
+class ResourceLocks;
+class MTinterface;
+#endif
+
+struct per_process
+{
+ char *initial_sp;
+
+ /* The offset of these 3 values can never change. */
+ /* magic_biscuit is the size of this class and should never change. */
+ unsigned long magic_biscuit;
+ unsigned long dll_major;
+ unsigned long dll_minor;
+
+ struct _reent **impure_ptr_ptr;
+ char ***envptr;
+
+ /* Used to point to the memory machine we should use. Usually these
+ point back into the dll, but they can be overridden by the user. */
+ void *(*malloc)(size_t);
+ void (*free)(void *);
+ void *(*realloc)(void *, size_t);
+
+ int *fmode_ptr;
+
+ int (*main)(int, char **, char **);
+ void (**ctors)(void);
+ void (**dtors)(void);
+
+ /* For fork */
+ void *data_start;
+ void *data_end;
+ void *bss_start;
+ void *bss_end;
+
+ void *(*calloc)(size_t, size_t);
+ /* For future expansion of values set by the app. */
+ void (*premain[4]) (int, char **, struct per_process *);
+
+ /* The rest are *internal* to cygwin.dll.
+ Those that are here because we want the child to inherit the value from
+ the parent (which happens when bss is copied) are marked as such. */
+
+ /* non-zero of ctors have been run. Inherited from parent. */
+ int run_ctors_p;
+
+ DWORD unused[7];
+
+ /* Non-zero means the task was forked. The value is the pid.
+ Inherited from parent. */
+ int forkee;
+
+ HMODULE hmodule;
+
+ DWORD api_major; /* API version that this program was */
+ DWORD api_minor; /* linked with */
+ /* For future expansion, so apps won't have to be relinked if we
+ add an item. */
+ DWORD unused2[5];
+
+#if defined (__INSIDE_CYGWIN__) && defined (__cplusplus)
+ ResourceLocks *resourcelocks;
+ MTinterface *threadinterface;
+#else
+ void *resourcelocks;
+ void *threadinterface;
+#endif
+ struct _reent *impure_ptr;
+};
+#define per_process_overwrite ((unsigned) &(((struct per_process *) NULL)->resourcelocks))
+
+extern void cygwin_premain0 (int argc, char **argv, struct per_process *);
+extern void cygwin_premain1 (int argc, char **argv, struct per_process *);
+extern void cygwin_premain2 (int argc, char **argv, struct per_process *);
+extern void cygwin_premain3 (int argc, char **argv, struct per_process *);
+
+extern void cygwin_set_impersonation_token (const HANDLE);
+
+/* included if <windows.h> is included */
+extern int cygwin32_attach_handle_to_fd (char *, int, HANDLE, mode_t, DWORD);
+extern int cygwin_attach_handle_to_fd (char *, int, HANDLE, mode_t, DWORD);
+
+#ifdef __CYGWIN__
+#include <sys/resource.h>
+
+#define TTY_CONSOLE 0x40000000
+
+#define EXTERNAL_PINFO_VERSION_16_BIT 0
+#define EXTERNAL_PINFO_VERSION_32_BIT 1
+#define EXTERNAL_PINFO_VERSION EXTERNAL_PINFO_VERSION_32_BIT
+
+#ifndef _SYS_TYPES_H
+typedef unsigned short __uid16_t;
+typedef unsigned short __gid16_t;
+typedef unsigned long __uid32_t;
+typedef unsigned long __gid32_t;
+#endif
+
+struct external_pinfo
+ {
+ pid_t pid;
+ pid_t ppid;
+ DWORD exitcode;
+ DWORD dwProcessId, dwSpawnedProcessId;
+ __uid16_t uid;
+ __gid16_t gid;
+ pid_t pgid;
+ pid_t sid;
+ int ctty;
+ mode_t umask;
+
+ long start_time;
+ struct rusage rusage_self;
+ struct rusage rusage_children;
+
+ char progname[MAX_PATH];
+
+ DWORD strace_mask;
+ DWORD version;
+
+ DWORD process_state;
+
+ /* Only available if version >= EXTERNAL_PINFO_VERSION_32_BIT */
+ __uid32_t uid32;
+ __gid32_t gid32;
+};
+#endif /*__CYGWIN__*/
+#endif /*WINVER*/
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* _SYS_CYGWIN_H */
diff --git a/winsup/cygwin/include/sys/wait.h b/winsup/cygwin/include/sys/wait.h
new file mode 100644
index 00000000000..d0708383f7d
--- /dev/null
+++ b/winsup/cygwin/include/sys/wait.h
@@ -0,0 +1,74 @@
+/* sys/wait.h
+
+ Copyright 1997, 1998, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _SYS_WAIT_H
+#define _SYS_WAIT_H
+
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WNOHANG 1
+#define WUNTRACED 2
+
+/* A status looks like:
+ <2 bytes info> <2 bytes code>
+
+ <code> == 0, child has exited, info is the exit value
+ <code> == 1..7e, child has exited, info is the signal number.
+ <code> == 7f, child has stopped, info was the signal number.
+ <code> == 80, there was a core dump.
+*/
+
+#define WIFEXITED(w) (((w) & 0xff) == 0)
+#define WIFSIGNALED(w) (((w) & 0x7f) > 0 && (((w) & 0x7f) < 0x7f))
+#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
+#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
+#define WTERMSIG(w) ((w) & 0x7f)
+#define WSTOPSIG WEXITSTATUS
+#define WCOREDUMP(w) (WIFSIGNALED(w) && (w & 0x80))
+
+pid_t wait (int *);
+pid_t waitpid (pid_t, int *, int);
+pid_t wait3 (int *__status, int __options, struct rusage *__rusage);
+pid_t wait4 (pid_t __pid, int *__status, int __options, struct rusage *__rusage);
+
+union wait
+ {
+ int w_status;
+ struct
+ {
+ unsigned int __w_termsig:7; /* Terminating signal. */
+ unsigned int __w_coredump:1; /* Set if dumped core. */
+ unsigned int __w_retcode:8; /* Return code if exited normally. */
+ unsigned int:16;
+ } __wait_terminated;
+ struct
+ {
+ unsigned int __w_stopval:8; /* W_STOPPED if stopped. */
+ unsigned int __w_stopsig:8; /* Stopping signal. */
+ unsigned int:16;
+ } __wait_stopped;
+ };
+
+#define w_termsig __wait_terminated.__w_termsig
+#define w_coredump __wait_terminated.__w_coredump
+#define w_retcode __wait_terminated.__w_retcode
+#define w_stopsig __wait_stopped.__w_stopsig
+#define w_stopval __wait_stopped.__w_stopval
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc
new file mode 100644
index 00000000000..f11e1c5558f
--- /dev/null
+++ b/winsup/cygwin/pinfo.cc
@@ -0,0 +1,981 @@
+/* pinfo.cc: process table support
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include <time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygwin_version.h"
+#include "perprocess.h"
+#include "environ.h"
+#include <assert.h>
+#include <sys/wait.h>
+#include <ntdef.h>
+#include "ntdll.h"
+#include "cygthread.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#include "fhandler.h"
+#include "cygmalloc.h"
+#include "cygtls.h"
+
+static char NO_COPY pinfo_dummy[sizeof (_pinfo)] = {0};
+
+pinfo NO_COPY myself ((_pinfo *)&pinfo_dummy); // Avoid myself != NULL checks
+
+HANDLE hexec_proc;
+
+void __stdcall
+pinfo_fixup_after_fork ()
+{
+ if (hexec_proc)
+ CloseHandle (hexec_proc);
+ /* Keeps the cygpid from being reused. No rights required */
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0,
+ TRUE, 0))
+ {
+ system_printf ("couldn't save current process handle %p, %E", hMainProc);
+ hexec_proc = NULL;
+ }
+ VerifyHandle (hexec_proc);
+}
+
+/* Initialize the process table.
+ This is done once when the dll is first loaded. */
+
+void __stdcall
+set_myself (HANDLE h)
+{
+ if (!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. */
+
+ (void) GetModuleFileName (NULL, myself->progname, sizeof (myself->progname));
+ if (!strace.active)
+ strace.hello ();
+ debug_printf ("myself->dwProcessId %u", myself->dwProcessId);
+ InitializeCriticalSection (&myself.lock);
+ if (!h && myself->ppid)
+ {
+ pinfo parent (myself->ppid);
+ if (parent && parent->wr_proc_pipe)
+ CloseHandle (parent->wr_proc_pipe);
+ }
+ return;
+}
+
+/* Initialize the process table entry for the current task.
+ This is not called for forked tasks, only execed ones. */
+void __stdcall
+pinfo_init (char **envp, int envc)
+{
+ if (envp)
+ {
+ environ_init (envp, envc);
+ /* spawn has already set up a pid structure for us so we'll use that */
+ myself->process_state |= PID_CYGPARENT;
+ }
+ else
+ {
+ /* Invent our own pid. */
+
+ set_myself (NULL);
+ myself->ppid = 1;
+ myself->pgid = myself->sid = myself->pid;
+ myself->ctty = -1;
+ myself->uid = ILLEGAL_UID;
+ myself->gid = UNKNOWN_GID;
+ environ_init (NULL, 0); /* call after myself has been set up */
+ }
+
+ debug_printf ("pid %d, pgid %d", myself->pid, myself->pgid);
+}
+
+void
+_pinfo::exit (UINT n, bool norecord)
+{
+ exit_state = ES_FINAL;
+ cygthread::terminate ();
+ if (norecord)
+ sigproc_terminate ();
+ else
+ exitcode = n;
+ if (this)
+ {
+ if (!norecord)
+ process_state = PID_EXITED;
+
+ /* FIXME: There is a potential race between an execed process and its
+ parent here. I hated to add a mutex just for this, though. */
+ struct rusage r;
+ fill_rusage (&r, hMainProc);
+ add_rusage (&rusage_self, &r);
+ }
+
+ sigproc_printf ("Calling ExitProcess %d", n);
+ _my_tls.stacklock = 0;
+ _my_tls.stackptr = _my_tls.stack;
+ ExitProcess (n);
+}
+
+void
+pinfo::init (pid_t n, DWORD flag, HANDLE in_h)
+{
+ if (myself && n == myself->pid)
+ {
+ procinfo = myself;
+ destroy = 0;
+ h = NULL;
+ return;
+ }
+
+ void *mapaddr;
+ if (!(flag & PID_MYSELF))
+ mapaddr = NULL;
+ else
+ {
+ flag &= ~PID_MYSELF;
+ HANDLE hdummy;
+ mapaddr = open_shared (NULL, 0, hdummy, 0, SH_MYSELF);
+ }
+
+ int createit = flag & (PID_IN_USE | PID_EXECED);
+ DWORD access = FILE_MAP_READ
+ | (flag & (PID_IN_USE | PID_EXECED | PID_MAP_RW) ? FILE_MAP_WRITE : 0);
+ for (int i = 0; i < 10; i++)
+ {
+ int created;
+ char mapname[CYG_MAX_PATH]; /* XXX Not a path */
+ shared_name (mapname, "cygpid", n);
+
+ int mapsize;
+ if (flag & PID_EXECED)
+ mapsize = PINFO_REDIR_SIZE;
+ else
+ mapsize = sizeof (_pinfo);
+
+ if (in_h)
+ {
+ h = in_h;
+ created = 0;
+ }
+ else if (!createit)
+ {
+ h = OpenFileMapping (access, FALSE, mapname);
+ created = 0;
+ }
+ else
+ {
+ char sa_buf[1024];
+ PSECURITY_ATTRIBUTES sec_attribs =
+ sec_user_nih (sa_buf, cygheap->user.sid(), well_known_world_sid,
+ FILE_MAP_READ);
+ h = CreateFileMapping (INVALID_HANDLE_VALUE, sec_attribs,
+ PAGE_READWRITE, 0, mapsize, mapname);
+ created = h && GetLastError () != ERROR_ALREADY_EXISTS;
+ }
+
+ if (!h)
+ {
+ if (createit)
+ __seterrno ();
+ procinfo = NULL;
+ return;
+ }
+
+ procinfo = (_pinfo *) MapViewOfFileEx (h, access, 0, 0, 0, mapaddr);
+ if (procinfo)
+ /* it worked */;
+ else if (exit_state)
+ return; /* exiting */
+ else
+ {
+ if (GetLastError () == ERROR_INVALID_HANDLE)
+ api_fatal ("MapViewOfFileEx(%p, in_h %p) failed, %E", h, in_h);
+ else
+ {
+ debug_printf ("MapViewOfFileEx(%p, in_h %p) failed, %E", h, in_h);
+ CloseHandle (h);
+ }
+ if (i < 9)
+ continue;
+ else
+ return;
+ }
+
+ ProtectHandle1 (h, pinfo_shared_handle);
+
+ if ((procinfo->process_state & PID_INITIALIZING) && (flag & PID_NOREDIR)
+ && cygwin_pid (procinfo->dwProcessId) != procinfo->pid)
+ {
+ release ();
+ set_errno (ENOENT);
+ return;
+ }
+
+ if (procinfo->process_state & PID_EXECED)
+ {
+ assert (!i);
+ pid_t realpid = procinfo->pid;
+ debug_printf ("execed process windows pid %d, cygwin pid %d", n, realpid);
+ if (realpid == n)
+ api_fatal ("retrieval of execed process info for pid %d failed due to recursion.", n);
+ n = realpid;
+ release ();
+ if (flag & PID_ALLPIDS)
+ {
+ set_errno (ENOENT);
+ break;
+ }
+ continue;
+ }
+
+ /* In certain rare, pathological cases, it is possible for the shared
+ memory region to exist for a while after a process has exited. This
+ should only be a brief occurrence, so rather than introduce some kind
+ of locking mechanism, just loop. FIXME: I'm sure I'll regret doing it
+ this way at some point. */
+ if (i < 9 && !created && createit && (procinfo->process_state & PID_EXITED))
+ {
+ low_priority_sleep (5);
+ release ();
+ continue;
+ }
+
+ if (!created)
+ /* nothing */;
+ else if (!(flag & PID_EXECED))
+ procinfo->pid = n;
+ else
+ {
+ procinfo->process_state |= PID_IN_USE | PID_EXECED;
+ procinfo->pid = myself->pid;
+ }
+
+ break;
+ }
+ destroy = 1;
+}
+
+void
+pinfo::set_acl()
+{
+ char sa_buf[1024];
+ SECURITY_DESCRIPTOR sd;
+
+ sec_acl ((PACL) sa_buf, true, true, cygheap->user.sid (),
+ well_known_world_sid, FILE_MAP_READ);
+ if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
+ debug_printf ("InitializeSecurityDescriptor %E");
+ else if (!SetSecurityDescriptorDacl (&sd, TRUE, (PACL) sa_buf, FALSE))
+ debug_printf ("SetSecurityDescriptorDacl %E");
+ else if (!SetKernelObjectSecurity (h, DACL_SECURITY_INFORMATION, &sd))
+ debug_printf ("SetKernelObjectSecurity %E");
+}
+
+void
+_pinfo::set_ctty (tty_min *tc, int flags, fhandler_tty_slave *arch)
+{
+ debug_printf ("checking if /dev/tty%d changed", ctty);
+ if ((ctty < 0 || ctty == tc->ntty) && !(flags & O_NOCTTY))
+ {
+ ctty = tc->ntty;
+ syscall_printf ("attached tty%d sid %d, pid %d, tty->pgid %d, tty->sid %d",
+ tc->ntty, sid, pid, pgid, tc->getsid ());
+
+ pinfo p (tc->getsid ());
+ if (sid == pid && (!p || p->pid == pid || !proc_exists (p)))
+ {
+ paranoid_printf ("resetting tty%d sid. Was %d, now %d. pgid was %d, now %d.",
+ tc->ntty, tc->getsid (), sid, tc->getpgid (), pgid);
+ /* We are the session leader */
+ tc->setsid (sid);
+ tc->setpgid (pgid);
+ }
+ else
+ sid = tc->getsid ();
+ if (tc->getpgid () == 0)
+ tc->setpgid (pgid);
+ if (cygheap->ctty != arch)
+ {
+ debug_printf ("cygheap->ctty %p, arch %p", cygheap->ctty, arch);
+ if (!cygheap->ctty)
+ syscall_printf ("ctty NULL");
+ else
+ {
+ syscall_printf ("ctty %p, usecount %d", cygheap->ctty,
+ cygheap->ctty->usecount);
+ cygheap->ctty->close ();
+ }
+ cygheap->ctty = arch;
+ if (arch)
+ {
+ arch->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (cygheap->ctty, "ctty", "incremented ", "");
+ }
+ }
+ }
+}
+
+bool
+_pinfo::alive ()
+{
+ HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, false, dwProcessId);
+ if (h)
+ CloseHandle (h);
+ return !!h;
+}
+
+extern char **__argv;
+
+void
+_pinfo::commune_recv ()
+{
+ DWORD nr;
+ DWORD code;
+ HANDLE hp;
+ HANDLE __fromthem = NULL;
+ HANDLE __tothem = NULL;
+
+ hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId);
+ if (!hp)
+ {
+ sigproc_printf ("couldn't open handle for pid %d(%u)", pid, dwProcessId);
+ hello_pid = -1;
+ return;
+ }
+ if (!DuplicateHandle (hp, fromthem, hMainProc, &__fromthem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate fromthem, %E");
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ if (!DuplicateHandle (hp, tothem, hMainProc, &__tothem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate tothem, %E");
+ CloseHandle (__fromthem);
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ hello_pid = 0;
+
+ if (!ReadFile (__fromthem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ CloseHandle (hp);
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ {
+ unsigned n = 1;
+ CloseHandle (__fromthem); __fromthem = NULL;
+ extern int __argc_safe;
+ const char *argv[__argc_safe + 1];
+
+ CloseHandle (hp);
+ for (int i = 0; i < __argc_safe; i++)
+ {
+ if (IsBadStringPtr (__argv[i], INT32_MAX))
+ argv[i] = "";
+ else
+ argv[i] = __argv[i];
+ n += strlen (argv[i]) + 1;
+ }
+ argv[__argc_safe] = NULL;
+ if (!WriteFile (__tothem, &n, sizeof n, &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile sizeof argv failed, %E");
+ }
+ else
+ for (const char **a = argv; *a; a++)
+ if (!WriteFile (__tothem, *a, strlen (*a) + 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile arg %d failed, %E", a - argv);
+ break;
+ }
+ if (!WriteFile (__tothem, "", 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile null failed, %E");
+ break;
+ }
+ break;
+ }
+ case PICOM_FIFO:
+ {
+ char path[CYG_MAX_PATH + 1];
+ unsigned len;
+ if (!ReadFile (__fromthem, &len, sizeof len, &nr, NULL)
+ || nr != sizeof len)
+ {
+ CloseHandle (hp);
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+ /* Get null-terminated path */
+ if (!ReadFile (__fromthem, path, len, &nr, NULL)
+ || nr != len)
+ {
+ CloseHandle (hp);
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ fhandler_fifo *fh = cygheap->fdtab.find_fifo (path);
+ HANDLE it[2];
+ if (fh == NULL)
+ it[0] = it[1] = NULL;
+ else
+ {
+ it[0] = fh->get_handle ();
+ it[1] = fh->get_output_handle ();
+ for (int i = 0; i < 2; i++)
+ if (!DuplicateHandle (hMainProc, it[i], hp, &it[i], 0, false,
+ DUPLICATE_SAME_ACCESS))
+ {
+ it[0] = it[1] = NULL; /* FIXME: possibly left a handle open in child? */
+ break;
+ }
+ }
+
+ CloseHandle (hp);
+ if (!WriteFile (__tothem, it, sizeof (it), &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile read handle failed, %E");
+ }
+
+ (void) ReadFile (__fromthem, &nr, sizeof (nr), &nr, NULL);
+ break;
+ }
+ }
+
+out:
+ if (__fromthem)
+ CloseHandle (__fromthem);
+ if (__tothem)
+ CloseHandle (__tothem);
+}
+
+#define PIPEBUFSIZE (4096 * sizeof (DWORD))
+
+commune_result
+_pinfo::commune_send (DWORD code, ...)
+{
+ HANDLE fromthem = NULL, tome = NULL;
+ HANDLE fromme = NULL, tothem = NULL;
+ DWORD nr;
+ commune_result res;
+ va_list args;
+
+ va_start (args, code);
+
+ res.s = NULL;
+ res.n = 0;
+
+ if (!this || !pid)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+ if (!CreatePipe (&fromthem, &tome, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ if (!CreatePipe (&fromme, &tothem, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("second CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ EnterCriticalSection (&myself.lock);
+ myself->tothem = tome;
+ myself->fromthem = fromme;
+ myself->hello_pid = pid;
+ if (!WriteFile (tothem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ __seterrno ();
+ goto err;
+ }
+
+ if (sig_send (this, __SIGCOMMUNE))
+ goto err;
+
+ /* FIXME: Need something better than an busy loop here */
+ bool isalive;
+ for (int i = 0; (isalive = alive ()) && (i < 10000); i++)
+ if (myself->hello_pid <= 0)
+ break;
+ else
+ low_priority_sleep (0);
+
+ CloseHandle (tome);
+ tome = NULL;
+ CloseHandle (fromme);
+ fromme = NULL;
+
+ if (!isalive)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+
+ if (myself->hello_pid < 0)
+ {
+ set_errno (ENOSYS);
+ goto err;
+ }
+
+ size_t n;
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ if (!ReadFile (fromthem, &n, sizeof n, &nr, NULL) || nr != sizeof n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.s = (char *) malloc (n);
+ char *p;
+ for (p = res.s; ReadFile (fromthem, p, n, &nr, NULL); p += nr)
+ continue;
+ if ((unsigned) (p - res.s) != n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.n = n;
+ break;
+ case PICOM_FIFO:
+ {
+ char *path = va_arg (args, char *);
+ size_t len = strlen (path) + 1;
+ if (!WriteFile (tothem, &len, sizeof (len), &nr, NULL)
+ || nr != sizeof (len))
+ {
+ __seterrno ();
+ goto err;
+ }
+ if (!WriteFile (tothem, path, len, &nr, NULL) || nr != len)
+ {
+ __seterrno ();
+ goto err;
+ }
+
+ DWORD x = ReadFile (fromthem, res.handles, sizeof (res.handles), &nr, NULL);
+ (void) WriteFile (tothem, &x, sizeof (x), &x, NULL);
+ if (!x)
+ goto err;
+
+ if (nr != sizeof (res.handles))
+ {
+ set_errno (EPIPE);
+ goto err;
+ }
+ break;
+ }
+ }
+ CloseHandle (tothem);
+ CloseHandle (fromthem);
+ goto out;
+
+err:
+ if (tome)
+ CloseHandle (tome);
+ if (fromthem)
+ CloseHandle (fromthem);
+ if (tothem)
+ CloseHandle (tothem);
+ if (fromme)
+ CloseHandle (fromme);
+ memset (&res, 0, sizeof (res));
+
+out:
+ myself->hello_pid = 0;
+ LeaveCriticalSection (&myself.lock);
+ return res;
+}
+
+char *
+_pinfo::cmdline (size_t& n)
+{
+ char *s;
+ if (!this || !pid)
+ return NULL;
+ if (pid != myself->pid)
+ {
+ commune_result cr = commune_send (PICOM_CMDLINE);
+ s = cr.s;
+ n = cr.n;
+ }
+ else
+ {
+ n = 1;
+ for (char **a = __argv; *a; a++)
+ n += strlen (*a) + 1;
+ char *p;
+ p = s = (char *) malloc (n);
+ for (char **a = __argv; *a; a++)
+ {
+ strcpy (p, *a);
+ p = strchr (p, '\0') + 1;
+ }
+ *p = '\0';
+ }
+ return s;
+}
+
+static DWORD WINAPI
+proc_waiter (void *arg)
+{
+ pinfo vchild = *(pinfo *) arg;
+ vchild.preserve ();
+
+ siginfo_t si;
+ si.si_signo = SIGCHLD;
+ si.si_code = SI_KERNEL;
+ si.si_pid = vchild->pid;
+ si.si_errno = 0;
+#if 0 // FIXME: This is tricky to get right
+ si.si_utime = pchildren[rc]->rusage_self.ru_utime;
+ si.si_stime = pchildren[rc].rusage_self.ru_stime;
+#else
+ si.si_utime = 0;
+ si.si_stime = 0;
+#endif
+ pid_t pid = vchild->pid;
+
+ for (;;)
+ {
+ DWORD nb;
+ char buf = '\0';
+ if (!ReadFile (vchild.rd_proc_pipe, &buf, 1, &nb, NULL)
+ && GetLastError () != ERROR_BROKEN_PIPE)
+ {
+ system_printf ("error on read of child wait pipe %p, %E", vchild.rd_proc_pipe);
+ break;
+ }
+
+ si.si_uid = vchild->uid;
+
+ int proc_todo;
+ switch (buf)
+ {
+ case 0:
+ if (WIFEXITED (vchild->exitcode))
+ si.si_sigval.sival_int = CLD_STOPPED;
+ else if (WCOREDUMP (vchild->exitcode))
+ si.si_sigval.sival_int = CLD_DUMPED;
+ else
+ si.si_sigval.sival_int = CLD_KILLED;
+ CloseHandle (vchild.rd_proc_pipe);
+ vchild.rd_proc_pipe = NULL;
+ si.si_status = vchild->exitcode;
+ // proc_todo = PROC_CHILDTERMINATED;
+ vchild->process_state = PID_ZOMBIE;
+ break;
+ case SIGTTIN:
+ case SIGTTOU:
+ case SIGTSTP:
+ case SIGSTOP:
+ si.si_sigval.sival_int = CLD_STOPPED;
+ // proc_todo = PROC_CHILDSTOPPED;
+ break;
+ case SIGCONT:
+ // proc_todo = PROC_CHILDCONTINUED;
+ continue;
+ default:
+ system_printf ("unknown value %d on proc pipe", buf);
+ continue;
+ }
+
+ /* Send a SIGCHLD to myself. We do this here, rather than in proc_subproc
+ to avoid the proc_subproc lock since the signal thread will eventually
+ be calling proc_subproc and could unnecessarily block. */
+ sig_send (myself_nowait, si);
+
+ /* If we're just stopped or got a continue signal, keep looping.
+ Otherwise, return this thread to the pool. */
+ if (buf != '\0')
+ sigproc_printf ("looping");
+ else
+ break;
+ }
+ sigproc_printf ("exiting wait thread for pid %d", pid);
+ return 0;
+}
+
+int
+pinfo::wait ()
+{
+ HANDLE out;
+ /* FIXME: execed processes should be able to wait for pids that were started
+ by the process which execed them. */
+ if (!CreatePipe (&rd_proc_pipe, &out, &sec_none_nih, 16))
+ {
+ system_printf ("Couldn't create pipe tracker for pid %d, %E",
+ (*this)->pid);
+ return 0;
+ }
+ if (!DuplicateHandle (hMainProc, out, hProcess, &((*this)->wr_proc_pipe), 0,
+ TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("Couldn't duplicate pipe topid %d(%p), %E", (*this)->pid,
+ hProcess);
+ return 0;
+ }
+ CloseHandle (out);
+
+
+#if 1
+ DWORD tid;
+ HANDLE h = CreateThread (&sec_none_nih, 0, proc_waiter, this, 0, &tid);
+ if (!h)
+ sigproc_printf ("tracking thread creation failed for pid %d", (*this)->pid);
+ else
+ CloseHandle (h);
+#else
+ cygthread *h = new cygthread (proc_waiter, this, "sig");
+ if (!h)
+ sigproc_printf ("tracking thread creation failed for pid %d", (*this)->pid);
+ else
+ {
+ h->zap_h ();
+ sigproc_printf ("created tracking thread for pid %d, winpid %p, rd_pipe %p",
+ (*this)->pid, (*this)->dwProcessId, rd_proc_pipe);
+ }
+#endif
+ return 1;
+}
+
+void
+pinfo::release ()
+{
+ if (h)
+ {
+#ifdef DEBUGGING
+ if (((DWORD) procinfo & 0x77000000) == 0x61000000)
+ try_to_debug ();
+#endif
+ UnmapViewOfFile (procinfo);
+ procinfo = NULL;
+ ForceCloseHandle1 (h, pinfo_shared_handle);
+ h = NULL;
+ }
+}
+
+/* DOCTOOL-START
+
+<sect1 id="func-cygwin-winpid-to-pid">
+ <title>cygwin_winpid_to_pid</title>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>extern "C" pid_t
+ <function>cygwin_winpid_to_pid</function>
+ </funcdef>
+ <paramdef>int <parameter>winpid</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+
+ <para>Given a windows pid, converts to the corresponding Cygwin
+pid, if any. Returns -1 if windows pid does not correspond to
+a cygwin pid.</para>
+ <example>
+ <title>Example use of cygwin_winpid_to_pid</title>
+ <programlisting>
+ extern "C" cygwin_winpid_to_pid (int winpid);
+ pid_t mypid;
+ mypid = cygwin_winpid_to_pid (windows_pid);
+ </programlisting>
+ </example>
+</sect1>
+
+ DOCTOOL-END */
+
+extern "C" pid_t
+cygwin_winpid_to_pid (int winpid)
+{
+ pinfo p (cygwin_pid (winpid));
+ if (p)
+ return p->pid;
+
+ set_errno (ESRCH);
+ return (pid_t) -1;
+}
+
+#include <tlhelp32.h>
+
+#define slop_pidlist 200
+#define size_pidlist(i) (sizeof (pidlist[0]) * ((i) + 1))
+#define size_pinfolist(i) (sizeof (pinfolist[0]) * ((i) + 1))
+
+inline void
+winpids::add (DWORD& nelem, bool winpid, DWORD pid)
+{
+ pid_t cygpid = cygwin_pid (pid);
+ if (nelem >= npidlist)
+ {
+ npidlist += slop_pidlist;
+ pidlist = (DWORD *) realloc (pidlist, size_pidlist (npidlist + 1));
+ pinfolist = (pinfo *) realloc (pinfolist, size_pinfolist (npidlist + 1));
+ }
+
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0)
+ | pinfo_access);
+ if (winpid)
+ goto out;
+
+ if (!pinfolist[nelem])
+ {
+ if (!pinfo_access)
+ return;
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0));
+ if (!pinfolist[nelem])
+ return;
+ }
+
+ /* Scan list of previously recorded pids to make sure that this pid hasn't
+ shown up before. This can happen when a process execs. */
+ for (unsigned i = 0; i < nelem; i++)
+ if (pinfolist[i]->pid == pinfolist[nelem]->pid)
+ {
+ if ((_pinfo *) pinfolist[nelem] != (_pinfo *) myself)
+ pinfolist[nelem].release ();
+ return;
+ }
+
+out:
+ pidlist[nelem++] = pid;
+}
+
+DWORD
+winpids::enumNT (bool winpid)
+{
+ static DWORD szprocs;
+ static SYSTEM_PROCESSES *procs;
+
+ DWORD nelem = 0;
+ if (!szprocs)
+ procs = (SYSTEM_PROCESSES *) malloc (sizeof (*procs) + (szprocs = 200 * sizeof (*procs)));
+
+ NTSTATUS res;
+ for (;;)
+ {
+ res = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ procs, szprocs, NULL);
+ if (res == 0)
+ break;
+
+ if (res == STATUS_INFO_LENGTH_MISMATCH)
+ procs = (SYSTEM_PROCESSES *) realloc (procs, szprocs += 200 * sizeof (*procs));
+ else
+ {
+ system_printf ("error %p reading system process information", res);
+ return 0;
+ }
+ }
+
+ SYSTEM_PROCESSES *px = procs;
+ for (;;)
+ {
+ if (px->ProcessId)
+ add (nelem, winpid, px->ProcessId);
+ if (!px->NextEntryDelta)
+ break;
+ px = (SYSTEM_PROCESSES *) ((char *) px + px->NextEntryDelta);
+ }
+
+ return nelem;
+}
+
+DWORD
+winpids::enum9x (bool winpid)
+{
+ DWORD nelem = 0;
+
+ HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
+ if (!h)
+ {
+ system_printf ("Couldn't create process snapshot, %E");
+ return 0;
+ }
+
+ PROCESSENTRY32 proc;
+ proc.dwSize = sizeof (proc);
+
+ if (Process32First (h, &proc))
+ do
+ {
+ if (proc.th32ProcessID)
+ add (nelem, winpid, proc.th32ProcessID);
+ }
+ while (Process32Next (h, &proc));
+
+ CloseHandle (h);
+ return nelem;
+}
+
+void
+winpids::set (bool winpid)
+{
+ __malloc_lock ();
+ npids = (this->*enum_processes) (winpid);
+ if (pidlist)
+ pidlist[npids] = 0;
+ __malloc_unlock ();
+}
+
+DWORD
+winpids::enum_init (bool winpid)
+{
+ if (wincap.is_winnt ())
+ enum_processes = &winpids::enumNT;
+ else
+ enum_processes = &winpids::enum9x;
+
+ return (this->*enum_processes) (winpid);
+}
+
+void
+winpids::release ()
+{
+ for (unsigned i = 0; i < npids; i++)
+ if (pinfolist[i] && (_pinfo *) pinfolist[i] != (_pinfo *) myself)
+ pinfolist[i].release ();
+}
+
+winpids::~winpids ()
+{
+ if (npidlist)
+ {
+ release ();
+ free (pidlist);
+ free (pinfolist);
+ }
+}
diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h
new file mode 100644
index 00000000000..89e4a6c9fe9
--- /dev/null
+++ b/winsup/cygwin/pinfo.h
@@ -0,0 +1,226 @@
+/* pinfo.h: process table info
+
+ Copyright 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _PINFO_H
+#define _PINFO_H
+#include <sys/resource.h>
+#include "thread.h"
+
+struct commune_result
+{
+ char *s;
+ int n;
+ HANDLE handles[2];
+};
+
+enum picom
+{
+ PICOM_CMDLINE = 1,
+ PICOM_FIFO = 2
+};
+
+class _pinfo
+{
+public:
+ /* Cygwin pid */
+ pid_t pid;
+
+ /* Various flags indicating the state of the process. See PID_
+ constants below. */
+ DWORD process_state;
+
+ DWORD exitcode; /* set when process exits */
+
+#define PINFO_REDIR_SIZE ((char *) &myself.procinfo->exitcode - (char *) myself.procinfo)
+
+ /* Handle associated with initial Windows pid which started it all. */
+ HANDLE pid_handle;
+
+ /* True if started by a cygwin process (DWORD for hysterical reasons) */
+ DWORD cygstarted;
+
+ /* Parent process id. */
+ pid_t ppid;
+
+ /* dwProcessId contains the processid used for sending signals. It
+ * will be reset in a child process when it is capable of receiving
+ * signals.
+ */
+ DWORD dwProcessId;
+
+ /* Used to spawn a child for fork(), among other things. */
+ char progname[CYG_MAX_PATH];
+
+ /* User information.
+ The information is derived from the GetUserName system call,
+ with the name looked up in /etc/passwd and assigned a default value
+ if not found. This data resides in the shared data area (allowing
+ tasks to store whatever they want here) so it's for informational
+ purposes only. */
+ __uid32_t uid; /* User ID */
+ __gid32_t gid; /* Group ID */
+ pid_t pgid; /* Process group ID */
+ pid_t sid; /* Session ID */
+ int ctty; /* Control tty */
+ bool has_pgid_children;/* True if we've forked or spawned children with our GID. */
+
+ /* Resources used by process. */
+ long start_time;
+ struct rusage rusage_self;
+ struct rusage rusage_children;
+
+ /* Non-zero if process was stopped by a signal. */
+ char stopsig;
+
+ /* commune */
+ pid_t hello_pid;
+ HANDLE tothem;
+ HANDLE fromthem;
+
+ void exit (UINT n, bool norecord = 0) __attribute__ ((noreturn, regparm(2)));
+
+ inline void set_has_pgid_children ()
+ {
+ if (pgid == pid)
+ has_pgid_children = 1;
+ }
+
+ inline void set_has_pgid_children (bool val) {has_pgid_children = val;}
+
+ inline sigset_t& getsigmask ()
+ {
+ return sig_mask;
+ }
+
+ inline void setsigmask (sigset_t mask)
+ {
+ sig_mask = mask;
+ }
+
+ void commune_recv ();
+ commune_result commune_send (DWORD, ...);
+ bool alive ();
+ char *cmdline (size_t &);
+ void set_ctty (class tty_min *, int, class fhandler_tty_slave *);
+
+ friend void __stdcall set_myself (HANDLE);
+
+ /* signals */
+ HANDLE sendsig;
+private:
+ sigset_t sig_mask;
+public:
+ HANDLE wr_proc_pipe;
+ friend class pinfo;
+};
+
+class pinfo
+{
+ HANDLE h;
+ _pinfo *procinfo;
+ bool destroy;
+public:
+ HANDLE rd_proc_pipe;
+ HANDLE hProcess;
+ CRITICAL_SECTION lock;
+ void init (pid_t, DWORD, HANDLE = NULL) __attribute__ ((regparm(3)));
+ pinfo () {}
+ pinfo (_pinfo *x): procinfo (x) {}
+ pinfo (pid_t n) {init (n, 0);}
+ pinfo (pid_t n, DWORD flag) {init (n, flag);}
+ void release ();
+ int wait ();
+ ~pinfo ()
+ {
+ if (destroy && procinfo)
+ release ();
+ }
+
+ _pinfo *operator -> () const {return procinfo;}
+ int operator == (pinfo *x) const {return x->procinfo == procinfo;}
+ int operator == (pinfo &x) const {return x.procinfo == procinfo;}
+ int operator == (_pinfo *x) const {return x == procinfo;}
+ int operator == (void *x) const {return procinfo == x;}
+ int operator == (int x) const {return (int) procinfo == (int) x;}
+ int operator == (char *x) const {return (char *) procinfo == x;}
+ _pinfo *operator * () const {return procinfo;}
+ operator _pinfo * () const {return procinfo;}
+ // operator bool () const {return (int) h;}
+ void preserve() { destroy = false; }
+#ifndef _SIGPROC_H
+ int remember () {system_printf ("remember is not here"); return 0;}
+#else
+ int remember ()
+ {
+ int res = proc_subproc (PROC_ADDCHILD, (DWORD) this);
+ destroy = res ? false : true;
+ return res;
+ }
+#endif
+ HANDLE shared_handle () {return h;}
+ void set_acl();
+};
+
+#define ISSTATE(p, f) (!!((p)->process_state & f))
+#define NOTSTATE(p, f) (!((p)->process_state & f))
+
+class winpids
+{
+ DWORD *pidlist;
+ DWORD npidlist;
+ pinfo *pinfolist;
+ DWORD pinfo_access; // access type for pinfo open
+ DWORD (winpids::* enum_processes) (bool winpid);
+ DWORD enum_init (bool winpid);
+ DWORD enumNT (bool winpid);
+ DWORD enum9x (bool winpid);
+ void add (DWORD& nelem, bool, DWORD pid);
+public:
+ DWORD npids;
+ inline void reset () { npids = 0; release (); }
+ void set (bool winpid);
+ winpids (int): pinfo_access (0), enum_processes (&winpids::enum_init)
+ { reset (); }
+ winpids (DWORD acc = 0): pidlist (NULL), npidlist (0), pinfolist (NULL),
+ enum_processes (&winpids::enum_init), npids (0)
+ {
+ pinfo_access = acc;
+ set (0);
+ }
+ inline DWORD& winpid (int i) const {return pidlist[i];}
+ inline _pinfo *operator [] (int i) const {return (_pinfo *) pinfolist[i];}
+ ~winpids ();
+ void release ();
+};
+
+extern __inline pid_t
+cygwin_pid (pid_t pid)
+{
+ return (pid_t) (wincap.has_negative_pids ()) ? -(int) pid : pid;
+}
+
+void __stdcall pinfo_init (char **, int);
+void __stdcall set_myself (HANDLE h);
+extern pinfo myself;
+
+#define _P_VFORK 0
+#define _P_SYSTEM 512
+
+extern void __stdcall pinfo_fixup_after_fork ();
+extern HANDLE hexec_proc;
+
+/* For mmaps across fork(). */
+int __stdcall fixup_mmaps_after_fork (HANDLE parent);
+/* for shm areas across fork (). */
+int __stdcall fixup_shms_after_fork ();
+
+void __stdcall fill_rusage (struct rusage *, HANDLE);
+void __stdcall add_rusage (struct rusage *, struct rusage *);
+#endif /*_PINFO_H*/
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
new file mode 100644
index 00000000000..f57e0d09da0
--- /dev/null
+++ b/winsup/cygwin/signal.cc
@@ -0,0 +1,551 @@
+/* signal.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+ Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com
+ Significant changes by Sergey Okhapkin <sos@prospect.com.ru>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include "cygerrno.h"
+#include <sys/cygwin.h>
+#include "pinfo.h"
+#include "sigproc.h"
+#include "hires.h"
+#include "security.h"
+#include "cygtls.h"
+
+int sigcatchers; /* FIXME: Not thread safe. */
+
+#define sigtrapped(func) ((func) != SIG_IGN && (func) != SIG_DFL)
+
+static inline void
+set_sigcatchers (void (*oldsig) (int), void (*cursig) (int))
+{
+#ifdef DEBUGGING
+ int last_sigcatchers = sigcatchers;
+#endif
+ if (!sigtrapped (oldsig) && sigtrapped (cursig))
+ sigcatchers++;
+ else if (sigtrapped (oldsig) && !sigtrapped (cursig))
+ sigcatchers--;
+#ifdef DEBUGGING
+ if (last_sigcatchers != sigcatchers)
+ sigproc_printf ("last %d, old %d, cur %p, cur %p", last_sigcatchers,
+ sigcatchers, oldsig, cursig);
+#endif
+}
+
+extern "C" _sig_func_ptr
+signal (int sig, _sig_func_ptr func)
+{
+ sig_dispatch_pending ();
+ _sig_func_ptr prev;
+
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = signal (%d, %p)", sig, func);
+ return (_sig_func_ptr) SIG_ERR;
+ }
+
+ prev = global_sigs[sig].sa_handler;
+ global_sigs[sig].sa_handler = func;
+ global_sigs[sig].sa_mask = 0;
+ /* SA_RESTART is set to maintain BSD compatible signal behaviour by default.
+ This is also compatible with the behaviour of signal(2) in Linux. */
+ global_sigs[sig].sa_flags |= SA_RESTART;
+ set_sigcatchers (prev, func);
+
+ syscall_printf ("%p = signal (%d, %p)", prev, sig, func);
+ return prev;
+}
+
+extern "C" int
+nanosleep (const struct timespec *rqtp, struct timespec *rmtp)
+{
+ int res = 0;
+ sig_dispatch_pending ();
+ pthread_testcancel ();
+
+ if ((unsigned int) rqtp->tv_sec > (HIRES_DELAY_MAX / 1000 - 1)
+ || (unsigned int) rqtp->tv_nsec > 999999999)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ DWORD resolution = gtod.resolution ();
+ DWORD req = ((rqtp->tv_sec * 1000 + (rqtp->tv_nsec + 999999) / 1000000
+ + resolution - 1) / resolution) * resolution;
+ DWORD end_time = gtod.dmsecs () + req;
+ syscall_printf ("nanosleep (%ld)", req);
+
+ int rc = pthread::cancelable_wait (signal_arrived, req);
+ DWORD rem;
+ if ((rem = end_time - gtod.dmsecs ()) > HIRES_DELAY_MAX)
+ rem = 0;
+ if (rc == WAIT_OBJECT_0)
+ {
+ (void) _my_tls.call_signal_handler ();
+ set_errno (EINTR);
+ res = -1;
+ }
+
+ if (rmtp)
+ {
+ rmtp->tv_sec = rem / 1000;
+ rmtp->tv_nsec = (rem % 1000) * 1000000;
+ }
+
+ syscall_printf ("%d = nanosleep (%ld, %ld)", res, req, rem);
+ return res;
+}
+
+extern "C" unsigned int
+sleep (unsigned int seconds)
+{
+ struct timespec req, rem;
+ req.tv_sec = seconds;
+ req.tv_nsec = 0;
+ nanosleep (&req, &rem);
+ return rem.tv_sec + (rem.tv_nsec > 0);
+}
+
+extern "C" unsigned int
+usleep (unsigned int useconds)
+{
+ struct timespec req;
+ req.tv_sec = useconds / 1000000;
+ req.tv_nsec = (useconds % 1000000) * 1000;
+ int res = nanosleep (&req, 0);
+ return res;
+}
+
+extern "C" int
+sigprocmask (int sig, const sigset_t *set, sigset_t *oldset)
+{
+ return handle_sigprocmask (sig, set, oldset, myself->getsigmask ());
+}
+
+int __stdcall
+handle_sigprocmask (int sig, const sigset_t *set, sigset_t *oldset, sigset_t& opmask)
+{
+ sig_dispatch_pending ();
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", sig);
+ return -1;
+ }
+
+ if (oldset)
+ {
+ if (check_null_invalid_struct_errno (oldset))
+ return -1;
+ *oldset = opmask;
+ }
+
+ if (set)
+ {
+ if (check_invalid_read_struct_errno (set))
+ return -1;
+ sigset_t newmask = opmask;
+ switch (sig)
+ {
+ case SIG_BLOCK:
+ /* add set to current mask */
+ newmask |= *set;
+ break;
+ case SIG_UNBLOCK:
+ /* remove set from current mask */
+ newmask &= ~*set;
+ break;
+ case SIG_SETMASK:
+ /* just set it */
+ newmask = *set;
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+ (void) set_signal_mask (newmask, opmask);
+ }
+ return 0;
+}
+
+static int
+kill_worker (pid_t pid, siginfo_t& si)
+{
+ sig_dispatch_pending ();
+
+ int res = 0;
+ pinfo dest (pid);
+ bool sendSIGCONT;
+
+ if (!dest)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+
+ if ((sendSIGCONT = (si.si_signo < 0)))
+ si.si_signo = -si.si_signo;
+
+ DWORD process_state = dest->process_state;
+ if (si.si_signo == 0)
+ {
+ res = proc_exists (dest) ? 0 : -1;
+ if (res < 0)
+ set_errno (ESRCH);
+ }
+ else if ((res = sig_send (dest, si)))
+ {
+ sigproc_printf ("%d = sig_send, %E ", res);
+ res = -1;
+ }
+ else if (sendSIGCONT)
+ {
+ siginfo_t si2;
+ si2.si_signo = SIGCONT;
+ si2.si_code = SI_KERNEL;
+ si2.si_pid = si2.si_uid = si2.si_errno = 0;
+ (void) sig_send (dest, si2);
+ }
+
+ syscall_printf ("%d = kill_worker (%d, %d), process_state %p", res, pid,
+ si.si_signo, process_state);
+ return res;
+}
+
+int
+raise (int sig)
+{
+ return kill (myself->pid, sig);
+}
+
+static int
+kill0 (pid_t pid, siginfo_t& si)
+{
+ syscall_printf ("kill (%d, %d)", pid, si.si_signo);
+ /* check that sig is in right range */
+ if (si.si_signo < 0 || si.si_signo >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", si.si_signo);
+ return -1;
+ }
+
+ /* Silently ignore stop signals from a member of orphaned process group.
+ FIXME: Why??? */
+ if (ISSTATE (myself, PID_ORPHANED) &&
+ (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU))
+ si.si_signo = 0;
+
+ return (pid > 0) ? kill_worker (pid, si) : kill_pgrp (-pid, si);
+}
+
+int
+killsys (pid_t pid, int sig)
+{
+ siginfo_t si;
+ si.si_signo = sig;
+ si.si_code = SI_KERNEL;
+ si.si_pid = si.si_uid = si.si_errno = 0;
+ return kill0 (pid, si);
+}
+int
+kill (pid_t pid, int sig)
+{
+ siginfo_t si;
+ si.si_signo = sig;
+ si.si_code = SI_USER;
+ si.si_pid = si.si_uid = si.si_errno = 0;
+ return kill0 (pid, si);
+}
+
+int
+kill_pgrp (pid_t pid, siginfo_t& si)
+{
+ int res = 0;
+ int found = 0;
+ int killself = 0;
+
+ sigproc_printf ("pid %d, signal %d", pid, si.si_signo);
+
+ winpids pids ((DWORD) PID_MAP_RW);
+ for (unsigned i = 0; i < pids.npids; i++)
+ {
+ _pinfo *p = pids[i];
+
+ if (!proc_exists (p))
+ continue;
+
+ /* Is it a process we want to kill? */
+ if ((pid == 0 && (p->pgid != myself->pgid || p->ctty != myself->ctty)) ||
+ (pid > 1 && p->pgid != pid) ||
+ (si.si_signo < 0 && NOTSTATE (p, PID_STOPPED)))
+ continue;
+ sigproc_printf ("killing pid %d, pgrp %d, p->ctty %d, myself->ctty %d",
+ p->pid, p->pgid, p->ctty, myself->ctty);
+ if (p == myself)
+ killself++;
+ else if (kill_worker (p->pid, si))
+ res = -1;
+ found++;
+ }
+
+ if (killself && !exit_state && kill_worker (myself->pid, si))
+ res = -1;
+
+ if (!found)
+ {
+ set_errno (ESRCH);
+ res = -1;
+ }
+ syscall_printf ("%d = kill (%d, %d)", res, pid, si.si_signo);
+ return res;
+}
+
+extern "C" int
+killpg (pid_t pgrp, int sig)
+{
+ return kill (-pgrp, sig);
+}
+
+extern "C" void
+abort (void)
+{
+ sig_dispatch_pending ();
+ /* Flush all streams as per SUSv2.
+ From my reading of this document, this isn't strictly correct.
+ The streams are supposed to be flushed prior to exit. However,
+ if there is I/O in any signal handler that will not necessarily
+ be flushed.
+ However this is the way FreeBSD does it, and it is much easier to
+ do things this way, so... */
+ if (_REENT->__cleanup)
+ _REENT->__cleanup (_REENT);
+
+ /* Ensure that SIGABRT can be caught regardless of blockage. */
+ sigset_t sig_mask;
+ sigfillset (&sig_mask);
+ sigdelset (&sig_mask, SIGABRT);
+ set_signal_mask (sig_mask);
+
+ raise (SIGABRT);
+ (void) _my_tls.call_signal_handler (); /* Call any signal handler */
+ do_exit (SIGABRT); /* signal handler didn't exit. Goodbye. */
+}
+
+extern "C" int
+sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
+{
+ sig_dispatch_pending ();
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ sigproc_printf ("signal %d, newact %p, oldact %p", sig, newact, oldact);
+ syscall_printf ("SIG_ERR = sigaction signal %d out of range", sig);
+ return -1;
+ }
+
+ struct sigaction oa = global_sigs[sig];
+
+ if (newact)
+ sigproc_printf ("signal %d, newact %p (handler %p), oa %p", sig, newact, newact->sa_handler, oa, oa.sa_handler);
+ else
+ sigproc_printf ("signal %d, newact %p, oa %p", sig, newact, oa, oa.sa_handler);
+
+ if (newact)
+ {
+ if (sig == SIGKILL || sig == SIGSTOP)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ global_sigs[sig] = *newact;
+ if (newact->sa_handler == SIG_IGN)
+ sig_clear (sig);
+ if (newact->sa_handler == SIG_DFL && sig == SIGCHLD)
+ sig_clear (sig);
+ set_sigcatchers (oa.sa_handler, newact->sa_handler);
+ if (sig == SIGCHLD)
+ {
+ myself->process_state &= ~PID_NOCLDSTOP;
+ if (newact->sa_flags & SA_NOCLDSTOP)
+ myself->process_state |= PID_NOCLDSTOP;
+ }
+ }
+
+ if (oldact)
+ *oldact = oa;
+
+ return 0;
+}
+
+extern "C" int
+sigaddset (sigset_t *set, const int sig)
+{
+ /* check that sig is in right range */
+ if (sig <= 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigaddset signal %d out of range", sig);
+ return -1;
+ }
+
+ *set |= SIGTOMASK (sig);
+ return 0;
+}
+
+extern "C" int
+sigdelset (sigset_t *set, const int sig)
+{
+ /* check that sig is in right range */
+ if (sig <= 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
+ return -1;
+ }
+
+ *set &= ~SIGTOMASK (sig);
+ return 0;
+}
+
+extern "C" int
+sigismember (const sigset_t *set, int sig)
+{
+ /* check that sig is in right range */
+ if (sig <= 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
+ return -1;
+ }
+
+ if (*set & SIGTOMASK (sig))
+ return 1;
+ else
+ return 0;
+}
+
+extern "C" int
+sigemptyset (sigset_t *set)
+{
+ *set = (sigset_t) 0;
+ return 0;
+}
+
+extern "C" int
+sigfillset (sigset_t *set)
+{
+ *set = ~((sigset_t) 0);
+ return 0;
+}
+
+extern "C" int
+sigsuspend (const sigset_t *set)
+{
+ return handle_sigsuspend (*set);
+}
+
+extern "C" int
+sigpause (int signal_mask)
+{
+ return handle_sigsuspend ((sigset_t) signal_mask);
+}
+
+extern "C" int
+pause (void)
+{
+ return handle_sigsuspend (myself->getsigmask ());
+}
+
+extern "C" int
+siginterrupt (int sig, int flag)
+{
+ struct sigaction act;
+ (void) sigaction(sig, NULL, &act);
+ if (flag)
+ act.sa_flags &= ~SA_RESTART;
+ else
+ act.sa_flags |= SA_RESTART;
+ return sigaction (sig, &act, NULL);
+}
+
+extern "C" int
+sigwait (const sigset_t *set, int *sig_ptr)
+{
+ int sig = sigwaitinfo (set, NULL);
+ if (sig > 0)
+ *sig_ptr = sig;
+ return sig > 0 ? 0 : -1;
+}
+
+extern "C" int
+sigwaitinfo (const sigset_t *set, siginfo_t *info)
+{
+ pthread_testcancel ();
+ HANDLE h;
+ h = _my_tls.event = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ if (!h)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ _my_tls.sigwait_mask = *set;
+ sig_dispatch_pending (true);
+
+ int res;
+ switch (WaitForSingleObject (h, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ if (!sigismember (set, _my_tls.infodata.si_signo))
+ {
+ set_errno (EINTR);
+ res = -1;
+ }
+ else
+ {
+ if (info)
+ *info = _my_tls.infodata;
+ res = _my_tls.infodata.si_signo;
+ InterlockedExchange ((LONG *) &_my_tls.sig, (LONG) 0);
+ }
+ break;
+ default:
+ __seterrno ();
+ res = -1;
+ }
+ CloseHandle (h);
+ sigproc_printf ("returning sig %d", res);
+ return res;
+}
+
+extern "C" int
+sigqueue (pid_t pid, int sig, const union sigval value)
+{
+ siginfo_t si;
+ pinfo dest (pid);
+ if (!dest)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+ si.si_signo = sig;
+ si.si_code = SI_USER;
+ si.si_pid = si.si_uid = si.si_errno = 0;
+ si.si_value = value;
+ return sig_send (dest, si);
+}
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
new file mode 100644
index 00000000000..1ddb74e4ef1
--- /dev/null
+++ b/winsup/cygwin/sigproc.cc
@@ -0,0 +1,1045 @@
+/* sigproc.cc: inter/intra signal and sub process handler
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+ Written by Christopher Faylor
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <sys/signal.h>
+#include "cygerrno.h"
+#include "sync.h"
+#include "pinfo.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info_magic.h"
+#include "shared_info.h"
+#include "cygthread.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "perthread.h"
+#include "exceptions.h"
+
+/*
+ * Convenience defines
+ */
+#define WSSC 60000 // Wait for signal completion
+#define WPSP 40000 // Wait for proc_subproc mutex
+
+#define PSIZE 63 // Number of processes
+
+#define no_signals_available() (!hwait_sig || (myself->sendsig == INVALID_HANDLE_VALUE) || exit_state)
+
+#define NPROCS 256
+
+/*
+ * Global variables
+ */
+struct sigaction *global_sigs;
+
+const char *__sp_fn ;
+int __sp_ln;
+
+char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to
+ // current process but no wait is required
+HANDLE NO_COPY signal_arrived; // Event signaled when a signal has
+ // resulted in a user-specified
+ // function call
+
+#define Static static NO_COPY
+
+HANDLE NO_COPY sigCONT; // Used to "STOP" a process
+Static cygthread *hwait_sig; // Handle of wait_sig thread
+
+Static HANDLE wait_sig_inited; // Control synchronization of
+ // message queue startup
+
+Static int nprocs; // Number of deceased children
+Static char cprocs[(NPROCS + 1) * sizeof (pinfo)]; // All my deceased children info
+#define procs ((pinfo *) cprocs)
+Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads
+
+muto NO_COPY *sync_proc_subproc = NULL; // Control access to subproc stuff
+
+DWORD NO_COPY sigtid = 0; // ID of the signal thread
+
+/* Function declarations */
+static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1)));
+static __inline__ bool get_proc_lock (DWORD, DWORD);
+static void __stdcall remove_proc (int);
+static bool __stdcall stopped_or_terminated (waitq *, _pinfo *);
+static DWORD WINAPI wait_sig (VOID *arg);
+
+/* wait_sig bookkeeping */
+
+class pending_signals
+{
+ sigpacket sigs[NSIG + 1];
+ sigpacket start;
+ sigpacket *end;
+ sigpacket *prev;
+ sigpacket *curr;
+public:
+ void reset () {curr = &start; prev = &start;}
+ void add (sigpacket&);
+ void del ();
+ sigpacket *next ();
+ sigpacket *save () const {return curr;}
+ void restore (sigpacket *saved) {curr = saved;}
+ friend void __stdcall sig_dispatch_pending (bool);
+ friend DWORD WINAPI wait_sig (VOID *arg);
+};
+
+static pending_signals sigq;
+
+/* Functions */
+void __stdcall
+sigalloc ()
+{
+ cygheap->sigs = global_sigs =
+ (struct sigaction *) ccalloc (HEAP_SIGS, NSIG, sizeof (struct sigaction));
+}
+
+void __stdcall
+signal_fixup_after_exec ()
+{
+ global_sigs = cygheap->sigs;
+ /* Set up child's signal handlers */
+ for (int i = 0; i < NSIG; i++)
+ {
+ global_sigs[i].sa_mask = 0;
+ if (global_sigs[i].sa_handler != SIG_IGN)
+ global_sigs[i].sa_handler = SIG_DFL;
+ }
+}
+
+/* Determine if the parent process is alive.
+ */
+
+bool __stdcall
+my_parent_is_alive ()
+{
+ bool res;
+ if (myself->cygstarted)
+ res = pid_exists (myself->ppid);
+ else
+ {
+ debug_printf ("Not started by cygwin app");
+ res = false;
+ }
+ return res;
+}
+
+void __stdcall
+wait_for_sigthread ()
+{
+ sigproc_printf ("wait_sig_inited %p", wait_sig_inited);
+ HANDLE hsig_inited = wait_sig_inited;
+ (void) WaitForSingleObject (hsig_inited, INFINITE);
+ wait_sig_inited = NULL;
+ (void) ForceCloseHandle1 (hsig_inited, wait_sig_inited);
+}
+
+/* Get the sync_proc_subproc muto to control access to
+ * children, proc arrays.
+ * Attempt to handle case where process is exiting as we try to grab
+ * the mutex.
+ */
+static bool
+get_proc_lock (DWORD what, DWORD val)
+{
+ Static int lastwhat = -1;
+ if (!sync_proc_subproc)
+ {
+ sigproc_printf ("sync_proc_subproc is NULL (1)");
+ return false;
+ }
+ if (sync_proc_subproc->acquire (WPSP))
+ {
+ lastwhat = what;
+ return true;
+ }
+ if (!sync_proc_subproc)
+ {
+ sigproc_printf ("sync_proc_subproc is NULL (2)");
+ return false;
+ }
+ system_printf ("Couldn't aquire sync_proc_subproc for(%d,%d), last %d, %E",
+ what, val, lastwhat);
+ return true;
+}
+
+static bool __stdcall
+proc_can_be_signalled (_pinfo *p)
+{
+ if (p->sendsig == INVALID_HANDLE_VALUE)
+ {
+ set_errno (EPERM);
+ return false;
+ }
+
+ if (p == myself_nowait || p == myself)
+ return hwait_sig;
+
+ if (ISSTATE (p, PID_INITIALIZING) ||
+ (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) ==
+ (PID_ACTIVE | PID_IN_USE)))
+ return true;
+
+ set_errno (ESRCH);
+ return false;
+}
+
+bool __stdcall
+pid_exists (pid_t pid)
+{
+ pinfo p (pid);
+ return proc_exists (p);
+}
+
+/* Test to determine if a process really exists and is processing signals.
+ */
+bool __stdcall
+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
+mychild (int pid)
+{
+ pinfo p (pid);
+ return p && p->ppid == myself->pid;
+}
+
+/* Handle all subprocess requests
+ */
+#define vchild (*((pinfo *) val))
+int __stdcall
+proc_subproc (DWORD what, DWORD val)
+{
+ int rc = 1;
+ int potential_match;
+ _pinfo *child;
+ int clearing;
+ waitq *w;
+
+#define wval ((waitq *) val)
+
+ sigproc_printf ("args: %x, %d", what, val);
+
+ if (!get_proc_lock (what, val)) // Serialize access to this function
+ {
+ system_printf ("couldn't get proc lock. what %d, val %d", what, val);
+ goto out1;
+ }
+
+ switch (what)
+ {
+ /* Add a new subprocess to the children arrays.
+ * (usually called from the main thread)
+ */
+ case PROC_ADDCHILD:
+ /* Filled up process table? */
+ if (nprocs >= NPROCS)
+ {
+ 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?
+ 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);
+ procs[nprocs] = vchild;
+ rc = procs[nprocs].wait ();
+ if (rc)
+ {
+ sigproc_printf ("added pid %d to proc table, slot %d", vchild->pid,
+ nprocs);
+ nprocs++;
+ }
+ break;
+
+ /* Handle a wait4() operation. Allocates an event for the calling
+ * thread which is signaled when the appropriate pid exits or stops.
+ * (usually called from the main thread)
+ */
+ case PROC_WAIT:
+ wval->ev = NULL; // Don't know event flag yet
+
+ if (wval->pid <= 0)
+ child = NULL; // Not looking for a specific pid
+ else if (!mychild (wval->pid))
+ goto out; // invalid pid. flag no such child
+
+ wval->status = 0; // Don't know status yet
+ sigproc_printf ("wval->pid %d, wval->options %d", wval->pid, wval->options);
+
+ /* If the first time for this thread, create a new event, otherwise
+ * reset the event.
+ */
+ if ((wval->ev = wval->thread_ev) == NULL)
+ {
+ wval->ev = wval->thread_ev = CreateEvent (&sec_none_nih, TRUE,
+ FALSE, NULL);
+ ProtectHandle1 (wval->ev, wq_ev);
+ }
+
+ ResetEvent (wval->ev);
+ w = waitq_head.next;
+ waitq_head.next = wval; /* Add at the beginning. */
+ wval->next = w; /* Link in rest of the list. */
+ clearing = 0;
+ goto scan_wait;
+
+ /* Clear all waiting threads. Called from exceptions.cc prior to
+ the main thread's dispatch to a signal handler function.
+ (called from wait_sig thread) */
+ case PROC_CLEARWAIT:
+ /* Clear all "wait"ing threads. */
+ if (val)
+ sigproc_printf ("clear waiting threads");
+ else
+ sigproc_printf ("looking for processes to reap");
+ clearing = val;
+
+ scan_wait:
+ /* Scan the linked list of wait()ing threads. If a wait's parameters
+ match this pid, then activate it. */
+ for (w = &waitq_head; w->next != NULL; w = w->next)
+ {
+ if ((potential_match = checkstate (w)) > 0)
+ sigproc_printf ("released waiting thread");
+ else if (!clearing && !(w->next->options & WNOHANG) && potential_match < 0)
+ sigproc_printf ("only found non-terminated children");
+ else if (potential_match <= 0) // nothing matched
+ {
+ sigproc_printf ("waiting thread found no children");
+ HANDLE oldw = w->next->ev;
+ w->next->pid = 0;
+ if (clearing)
+ w->next->status = -1; /* flag that a signal was received */
+ else if (!potential_match || !(w->next->options & WNOHANG))
+ w->next->ev = NULL;
+ if (!SetEvent (oldw))
+ system_printf ("couldn't wake up wait event %p, %E", oldw);
+ w->next = w->next->next;
+ }
+ if (w->next == NULL)
+ break;
+ }
+
+ if (!clearing)
+ sigproc_printf ("finished processing terminated/stopped child");
+ else
+ {
+ waitq_head.next = NULL;
+ sigproc_printf ("finished clearing");
+ }
+
+ // FIXMENOW: What is supposed to happen here?
+ if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN)
+ while (nprocs)
+ remove_proc (0);
+ break;
+ }
+
+out:
+ sync_proc_subproc->release (); // Release the lock
+out1:
+ sigproc_printf ("returning %d", rc);
+ return rc;
+}
+
+// FIXME: This is inelegant
+void
+_cygtls::remove_wq (DWORD wait)
+{
+ if (sync_proc_subproc && sync_proc_subproc->acquire (wait))
+ {
+ for (waitq *w = &waitq_head; w->next != NULL; w = w->next)
+ if (w->next == &wq)
+ {
+ ForceCloseHandle1 (wq.thread_ev, wq_ev);
+ w->next = wq.next;
+ break;
+ }
+ sync_proc_subproc->release ();
+ }
+}
+
+/* Terminate the wait_subproc thread.
+ * Called on process exit.
+ * Also called by spawn_guts to disassociate any subprocesses from this
+ * process. Subprocesses will then know to clean up after themselves and
+ * will not become procs.
+ */
+void __stdcall
+proc_terminate (void)
+{
+ sigproc_printf ("nprocs %d", nprocs);
+ /* Signal processing is assumed to be blocked in this routine. */
+ if (nprocs)
+ {
+ sync_proc_subproc->acquire (WPSP);
+
+ (void) proc_subproc (PROC_CLEARWAIT, 1);
+
+ /* Clean out proc processes from the pid list. */
+ int i;
+ for (i = 0; i < nprocs; i++)
+ {
+ procs[i]->ppid = 1;
+ if (!proc_exists (procs[i]))
+ procs[i]->process_state = PID_EXITED; /* CGF FIXME - still needed? */
+ procs[i].release ();
+ }
+ nprocs = 0;
+ sync_proc_subproc->release ();
+ }
+ sigproc_printf ("leaving");
+}
+
+/* Clear pending signal */
+void __stdcall
+sig_clear (int target_sig)
+{
+ if (GetCurrentThreadId () != sigtid)
+ sig_send (myself, -target_sig);
+ else
+ {
+ sigpacket *q;
+ sigpacket *save = sigq.save ();
+ sigq.reset ();
+ while ((q = sigq.next ()))
+ if (q->si.si_signo == target_sig)
+ {
+ q->si.si_signo = __SIGDELETE;
+ break;
+ }
+ sigq.restore (save);
+ }
+ return;
+}
+
+extern "C" int
+sigpending (sigset_t *mask)
+{
+ sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING);
+ if (outset == SIG_BAD_MASK)
+ return -1;
+ *mask = outset;
+ return 0;
+}
+
+/* Force the wait_sig thread to wake up and scan for pending signals */
+void __stdcall
+sig_dispatch_pending (bool fast)
+{
+ if (exit_state || GetCurrentThreadId () == sigtid || !sigq.start.next)
+ {
+#ifdef DEBUGGING
+ sigproc_printf ("exit_state %d, cur thread id %p, sigtid %p, sigq.start.next %p",
+ exit_state, GetCurrentThreadId (), sigtid, sigq.start.next);
+#endif
+ return;
+ }
+
+#ifdef DEBUGGING
+ sigproc_printf ("flushing");
+#endif
+ (void) sig_send (myself, fast ? __SIGFLUSHFAST : __SIGFLUSH);
+}
+
+/* Message initialization. Called from dll_crt0_1
+ *
+ * This routine starts the signal handling thread. The wait_sig_inited
+ * event is used to signal that the thread is ready to handle signals.
+ * We don't wait for this during initialization but instead detect it
+ * in sig_send to gain a little concurrency.
+ */
+void __stdcall
+sigproc_init ()
+{
+ wait_sig_inited = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (wait_sig_inited);
+
+ /* sync_proc_subproc is used by proc_subproc. It serialises
+ * access to the children and proc arrays.
+ */
+ new_muto (sync_proc_subproc);
+
+ /* local event signaled when main thread has been dispatched
+ to a signal handler function. */
+ signal_arrived = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (signal_arrived);
+
+ hwait_sig = new cygthread (wait_sig, cygself, "sig");
+ hwait_sig->zap_h ();
+
+ global_sigs[SIGSTOP].sa_flags = SA_RESTART | SA_NODEFER;
+ sigproc_printf ("process/signal handling enabled(%x)", myself->process_state);
+ return;
+}
+
+/* Called on process termination to terminate signal and process threads.
+ */
+void __stdcall
+sigproc_terminate (void)
+{
+ extern HANDLE hExeced;
+ hwait_sig = NULL;
+
+ if (myself->sendsig == INVALID_HANDLE_VALUE)
+ sigproc_printf ("sigproc handling not active");
+ else
+ {
+ sigproc_printf ("entering");
+ // finished with anything it is doing
+ if (!hExeced)
+ {
+ HANDLE sendsig = myself->sendsig;
+ myself->sendsig = INVALID_HANDLE_VALUE;
+ CloseHandle (sendsig);
+ }
+ }
+ proc_terminate (); // Terminate process handling thread
+
+ return;
+}
+
+int __stdcall
+sig_send (_pinfo *p, int sig)
+{
+ siginfo_t si;
+ si.si_signo = sig;
+ si.si_code = SI_KERNEL;
+ si.si_pid = si.si_uid = si.si_errno = 0;
+ return sig_send (p, si);
+}
+
+/* Send a signal to another process by raising its signal semaphore.
+ If pinfo *p == NULL, send to the current process.
+ If sending to this process, wait for notification that a signal has
+ completed before returning. */
+int __stdcall
+sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
+{
+ int rc = 1;
+ bool its_me;
+ HANDLE sendsig;
+ sigpacket pack;
+
+ pack.wakeup = NULL;
+ bool wait_for_completion;
+ if (!(its_me = (p == NULL || p == myself || p == myself_nowait)))
+ wait_for_completion = false;
+ else
+ {
+ if (no_signals_available ())
+ {
+ sigproc_printf ("hwait_sig %p, myself->sendsig %p, exit_state %d",
+ hwait_sig, myself->sendsig, exit_state);
+ goto out; // Either exiting or not yet initializing
+ }
+ if (wait_sig_inited)
+ wait_for_sigthread ();
+ wait_for_completion = p != myself_nowait && _my_tls.isinitialized ();
+ p = myself;
+ }
+
+ /* It is possible that the process is not yet ready to receive messages
+ * or that it has exited. Detect this.
+ */
+ if (!proc_can_be_signalled (p)) /* Is the process accepting messages? */
+ {
+ sigproc_printf ("invalid pid %d(%x), signal %d",
+ p->pid, p->process_state, si.si_signo);
+ goto out;
+ }
+
+ if (its_me)
+ sendsig = myself->sendsig;
+ else
+ {
+ for (int i = 0; !p->dwProcessId && i < 10000; i++)
+ low_priority_sleep (0);
+ HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId);
+ if (!hp)
+ {
+ sigproc_printf ("OpenProcess failed, %E");
+ __seterrno ();
+ 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,
+ DUPLICATE_SAME_ACCESS) || !sendsig)
+ {
+ CloseHandle (hp);
+ sigproc_printf ("DuplicateHandle failed, %E");
+ __seterrno ();
+ goto out;
+ }
+ CloseHandle (hp);
+ VerifyHandle (sendsig);
+ }
+
+ sigproc_printf ("sendsig %p, pid %d, signal %d, its_me %d", sendsig, p->pid, si.si_signo, its_me);
+
+ sigset_t pending;
+ if (!its_me)
+ pack.mask = NULL;
+ else if (si.si_signo == __SIGPENDING)
+ pack.mask = &pending;
+ else if (si.si_signo == __SIGFLUSH || si.si_signo > 0)
+ pack.mask = &myself->getsigmask ();
+ else
+ pack.mask = NULL;
+
+ pack.si = si;
+ if (!pack.si.si_pid)
+ pack.si.si_pid = myself->pid;
+ if (!pack.si.si_uid)
+ pack.si.si_uid = myself->uid;
+ pack.pid = myself->pid;
+ pack.tls = (_cygtls *) tls;
+ if (wait_for_completion)
+ {
+ pack.wakeup = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ sigproc_printf ("wakeup %p", pack.wakeup);
+ ProtectHandle (pack.wakeup);
+ }
+
+ DWORD nb;
+ if (!WriteFile (sendsig, &pack, sizeof (pack), &nb, NULL) || nb != sizeof (pack))
+ {
+ /* Couldn't send to the pipe. This probably means that the
+ process is exiting. */
+ if (!its_me)
+ {
+ sigproc_printf ("WriteFile for pipe %p failed, %E", sendsig);
+ __seterrno ();
+ ForceCloseHandle (sendsig);
+ }
+ else
+ {
+ if (no_signals_available ())
+ sigproc_printf ("I'm going away now");
+ else
+ system_printf ("error sending signal %d to pid %d, pipe handle %p, %E",
+ si.si_signo, p->pid, sendsig);
+ }
+ goto out;
+ }
+
+
+ /* No need to wait for signal completion unless this was a signal to
+ this process.
+
+ If it was a signal to this process, wait for a dispatched signal.
+ Otherwise just wait for the wait_sig to signal that it has finished
+ processing the signal. */
+ if (wait_for_completion)
+ {
+ sigproc_printf ("Waiting for pack.wakeup %p", pack.wakeup);
+ rc = WaitForSingleObject (pack.wakeup, WSSC);
+ }
+ else
+ {
+ rc = WAIT_OBJECT_0;
+ sigproc_printf ("Not waiting for sigcomplete. its_me %d signal %d",
+ its_me, si.si_signo);
+ if (!its_me)
+ ForceCloseHandle (sendsig);
+ }
+
+ if (pack.wakeup)
+ {
+ ForceCloseHandle (pack.wakeup);
+ pack.wakeup = NULL;
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ rc = 0; // Successful exit
+ else
+ {
+ if (!no_signals_available ())
+ system_printf ("wait for sig_complete event failed, signal %d, rc %d, %E",
+ si.si_signo, rc);
+ set_errno (ENOSYS);
+ rc = -1;
+ }
+
+ if (wait_for_completion && si.si_signo != __SIGFLUSHFAST)
+ _my_tls.call_signal_handler ();
+
+out:
+ if (pack.wakeup)
+ ForceCloseHandle (pack.wakeup);
+ if (si.si_signo != __SIGPENDING)
+ /* nothing */;
+ else if (!rc)
+ rc = (int) pending;
+ else
+ rc = SIG_BAD_MASK;
+ sigproc_printf ("returning %p from sending signal %d", rc, si.si_signo);
+ return rc;
+}
+
+/* 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)
+{
+ 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;
+}
+
+/* Check the state of all of our children to see if any are stopped or
+ * terminated.
+ */
+static int __stdcall
+checkstate (waitq *parent_w)
+{
+ int potential_match = 0;
+
+ sigproc_printf ("nprocs %d", nprocs);
+
+ /* Check already dead processes first to see if they match the criteria
+ * given in w->next. */
+ int res;
+ for (int i = 0; i < nprocs; i++)
+ if ((res = stopped_or_terminated (parent_w, procs[i])))
+ {
+ remove_proc (i);
+ potential_match = 1;
+ goto out;
+ }
+
+ potential_match = -!!nprocs;
+
+out:
+ sigproc_printf ("returning %d", potential_match);
+ return potential_match;
+}
+
+/* Remove a proc from procs by swapping it with the last child in the list.
+ Also releases shared memory of exited processes. */
+static void __stdcall
+remove_proc (int ci)
+{
+ if (!proc_exists (procs[ci]))
+ {
+ sigproc_printf ("removing procs[%d], pid %d, nprocs %d", ci, procs[ci]->pid,
+ nprocs);
+ procs[ci].release ();
+ if (ci < --nprocs)
+ procs[ci] = procs[nprocs];
+ }
+}
+
+/* Check status of child process vs. waitq member.
+
+ parent_w is the pointer to the parent of the waitq member in question.
+ child is the subprocess being considered.
+
+ Returns non-zero if waiting thread released. */
+static bool __stdcall
+stopped_or_terminated (waitq *parent_w, _pinfo *child)
+{
+ int might_match;
+ waitq *w = parent_w->next;
+
+ sigproc_printf ("considering pid %d", child->pid);
+ if (w->pid == -1)
+ might_match = 1;
+ else if (w->pid == 0)
+ might_match = child->pgid == myself->pgid;
+ else if (w->pid < 0)
+ might_match = child->pgid == -w->pid;
+ else
+ might_match = (w->pid == child->pid);
+
+ if (!might_match)
+ return 0;
+
+ int terminated;
+
+ if (!((terminated = child->process_state == PID_ZOMBIE) ||
+ ((w->options & WUNTRACED) && child->stopsig)))
+ return 0;
+
+ parent_w->next = w->next; /* successful wait. remove from wait queue */
+ w->pid = child->pid;
+
+ if (!terminated)
+ {
+ sigproc_printf ("stopped child");
+ w->status = (child->stopsig << 8) | 0x7f;
+ child->stopsig = 0;
+ }
+ else /* Should only get here when child has been moved to the procs array */
+ {
+ w->status = child->exitcode;
+
+ add_rusage (&myself->rusage_children, &child->rusage_children);
+ add_rusage (&myself->rusage_children, &child->rusage_self);
+
+ if (w->rusage)
+ {
+ add_rusage ((struct rusage *) w->rusage, &child->rusage_children);
+ add_rusage ((struct rusage *) w->rusage, &child->rusage_self);
+ }
+ }
+
+ if (!SetEvent (w->ev)) /* wake up wait4 () immediately */
+ system_printf ("couldn't wake up wait event %p, %E", w->ev);
+ return true;
+}
+
+static void
+talktome ()
+{
+ winpids pids ((DWORD) PID_MAP_RW);
+ for (unsigned i = 0; i < pids.npids; i++)
+ if (pids[i]->hello_pid == myself->pid)
+ if (!IsBadWritePtr (pids[i], sizeof (_pinfo)))
+ pids[i]->commune_recv ();
+}
+
+void
+pending_signals::add (sigpacket& pack)
+{
+ sigpacket *se;
+ if (sigs[pack.si.si_signo].si.si_signo)
+ return;
+ se = sigs + pack.si.si_signo;
+ *se = pack;
+ se->mask = &myself->getsigmask ();
+ se->next = NULL;
+ if (end)
+ end->next = se;
+ end = se;
+ if (!start.next)
+ start.next = se;
+}
+
+void
+pending_signals::del ()
+{
+ sigpacket *next = curr->next;
+ prev->next = next;
+ curr->si.si_signo = 0;
+#ifdef DEBUGGING
+ curr->next = NULL;
+#endif
+ if (end == curr)
+ end = prev;
+ curr = next;
+}
+
+sigpacket *
+pending_signals::next ()
+{
+ sigpacket *res;
+ prev = curr;
+ if (!curr || !(curr = curr->next))
+ res = NULL;
+ else
+ res = curr;
+ return res;
+}
+
+/* Process signals by waiting for signal data to arrive in a pipe.
+ Set a completion event if one was specified. */
+static DWORD WINAPI
+wait_sig (VOID *self)
+{
+ HANDLE readsig;
+ char sa_buf[1024];
+ Static bool holding_signals;
+
+ /* Initialization */
+ (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY);
+
+ if (!CreatePipe (&readsig, &myself->sendsig, sec_user_nih (sa_buf), 0))
+ api_fatal ("couldn't create signal pipe, %E");
+ sigCONT = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+
+ /* Setting dwProcessId flags that this process is now capable of receiving
+ signals. Prior to this, dwProcessId was set to the windows pid of
+ of the original windows process which spawned us unless this was a
+ "toplevel" process. */
+ myself->dwProcessId = GetCurrentProcessId ();
+ myself->process_state |= PID_ACTIVE;
+ myself->process_state &= ~PID_INITIALIZING;
+
+ sigproc_printf ("myself->dwProcessId %u", myself->dwProcessId);
+#if 0
+ /* If we've been execed, then there is still a stub left in the previous
+ windows process waiting to see if it's started a cygwin process or not.
+ Signalling subproc_ready indicates that we are a cygwin process. */
+ if (child_proc_info && child_proc_info->type == PROC_EXEC)
+ {
+ debug_printf ("subproc_ready %p", child_proc_info->subproc_ready);
+ if (!SetEvent (child_proc_info->subproc_ready))
+ system_printf ("SetEvent (subproc_ready) failed, %E");
+ ForceCloseHandle1 (child_proc_info->subproc_ready, subproc_ready);
+ /* Initialize an "indirect" pid block so that if someone looks up this
+ process via its Windows PID it will be redirected to the appropriate
+ Cygwin PID shared memory block. */
+ static pinfo NO_COPY myself_identity;
+ myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED);
+ }
+#endif
+
+ SetEvent (wait_sig_inited);
+ sigtid = GetCurrentThreadId ();
+
+ exception_list el;
+ _my_tls.init_threadlist_exceptions (&el);
+ debug_printf ("entering ReadFile loop, readsig %p, myself->sendsig %p",
+ readsig, myself->sendsig);
+
+ for (;;)
+ {
+ DWORD nb;
+ sigpacket pack;
+ if (!ReadFile (readsig, &pack, sizeof (pack), &nb, NULL))
+ break;
+ if (myself->sendsig == INVALID_HANDLE_VALUE)
+ break;
+
+ if (nb != sizeof (pack))
+ {
+ system_printf ("short read from signal pipe: %d != %d", nb,
+ sizeof (pack));
+ continue;
+ }
+
+ if (!pack.si.si_signo)
+ {
+#ifdef DEBUGGING
+ system_printf ("zero signal?");
+#endif
+ continue;
+ }
+
+ sigset_t dummy_mask;
+ if (!pack.mask)
+ {
+ dummy_mask = myself->getsigmask ();
+ pack.mask = &dummy_mask;
+ }
+
+ sigpacket *q;
+ bool clearwait = false;
+ switch (pack.si.si_signo)
+ {
+ case __SIGCOMMUNE:
+ talktome ();
+ break;
+ case __SIGSTRACE:
+ strace.hello ();
+ break;
+ case __SIGPENDING:
+ *pack.mask = 0;
+ unsigned bit;
+ sigq.reset ();
+ while ((q = sigq.next ()))
+ if (myself->getsigmask () & (bit = SIGTOMASK (q->si.si_signo)))
+ *pack.mask |= bit;
+ break;
+ case __SIGHOLD:
+ holding_signals = 1;
+ break;
+ case __SIGNOHOLD:
+ holding_signals = 0;
+ /* fall through, intentionally */
+ case __SIGFLUSH:
+ case __SIGFLUSHFAST:
+ sigq.reset ();
+ while ((q = sigq.next ()))
+ {
+ int sig = q->si.si_signo;
+ if (sig == __SIGDELETE || q->process () > 0)
+ sigq.del ();
+ if (sig == __SIGNOHOLD && q->si.si_signo == SIGCHLD)
+ clearwait = true;
+ }
+ break;
+ default:
+ if (pack.si.si_signo < 0)
+ sig_clear (-pack.si.si_signo);
+ else if (holding_signals)
+ sigq.add (pack);
+ else
+ {
+ int sig = pack.si.si_signo;
+ // FIXME: Not quite right when taking threads into consideration.
+ // Do we need a per-thread queue?
+ if (sigq.sigs[sig].si.si_signo)
+ sigproc_printf ("sig %d already queued", pack.si.si_signo);
+ else
+ {
+ int sigres = pack.process ();
+ if (sigres <= 0)
+ {
+#ifdef DEBUGGING2
+ if (!sigres)
+ system_printf ("Failed to arm signal %d from pid %d", pack.sig, pack.pid);
+#endif
+ sigq.add (pack); // FIXME: Shouldn't add this in !sh condition
+ }
+ }
+ if (sig == SIGCHLD)
+ clearwait = true;
+ }
+ break;
+ }
+ if (clearwait)
+ proc_subproc (PROC_CLEARWAIT, 0);
+ if (pack.wakeup)
+ {
+ SetEvent (pack.wakeup);
+ sigproc_printf ("signalled %p", pack.wakeup);
+ }
+ }
+
+ sigproc_printf ("done");
+ ExitThread (0);
+}
diff --git a/winsup/cygwin/sigproc.h b/winsup/cygwin/sigproc.h
new file mode 100644
index 00000000000..8f648b1d512
--- /dev/null
+++ b/winsup/cygwin/sigproc.h
@@ -0,0 +1,97 @@
+/* sigproc.h
+
+ Copyright 1997, 1998, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _SIGPROC_H
+#define _SIGPROC_H
+#include <signal.h>
+
+#ifdef NSIG
+enum
+{
+ __SIGFLUSH = -(NSIG + 1),
+ __SIGSTRACE = -(NSIG + 2),
+ __SIGCOMMUNE = -(NSIG + 3),
+ __SIGPENDING = -(NSIG + 4),
+ __SIGDELETE = -(NSIG + 5),
+ __SIGFLUSHFAST = -(NSIG + 6),
+ __SIGHOLD = -(NSIG + 7),
+ __SIGNOHOLD = -(NSIG + 8)
+};
+#endif
+
+#define SIG_BAD_MASK (1 << (SIGKILL - 1))
+
+enum procstuff
+{
+ PROC_ADDCHILD = 1, // add a new subprocess to list
+ PROC_CHILDTERMINATED = 2, // a child died
+ PROC_CLEARWAIT = 3, // clear all waits - signal arrived
+ PROC_WAIT = 4, // setup for wait() for subproc
+ PROC_CHILDSTOPPED = 5, // register process as "stopped"
+ PROC_CHILDCONTINUED = 6, // remove process from "stopped" list
+ PROC_NOTHING = 7 // nothing, really
+};
+
+struct sigpacket
+{
+ siginfo_t si;
+ pid_t pid;
+ class _cygtls *tls;
+ sigset_t *mask;
+ union
+ {
+ HANDLE wakeup;
+ struct sigpacket *next;
+ };
+ int __stdcall process () __attribute__ ((regparm (1)));
+};
+
+extern HANDLE signal_arrived;
+extern HANDLE sigCONT;
+
+bool __stdcall my_parent_is_alive ();
+void __stdcall sig_dispatch_pending (bool fast = false);
+#ifdef _PINFO_H
+extern "C" void __stdcall set_signal_mask (sigset_t newmask, sigset_t = myself->getsigmask ());
+#endif
+int __stdcall handle_sigprocmask (int sig, const sigset_t *set,
+ sigset_t *oldset, sigset_t& opmask)
+ __attribute__ ((regparm (3)));
+
+extern "C" void __stdcall reset_signal_arrived ();
+void __stdcall sig_clear (int) __attribute__ ((regparm (1)));
+void __stdcall sig_set_pending (int) __attribute__ ((regparm (1)));
+int __stdcall handle_sigsuspend (sigset_t);
+
+int __stdcall proc_subproc (DWORD, DWORD) __attribute__ ((regparm (2)));
+
+class _pinfo;
+void __stdcall proc_terminate ();
+void __stdcall sigproc_init ();
+void __stdcall sigproc_terminate ();
+bool __stdcall proc_exists (_pinfo *) __attribute__ ((regparm(1)));
+bool __stdcall pid_exists (pid_t) __attribute__ ((regparm(1)));
+int __stdcall sig_send (_pinfo *, siginfo_t&, class _cygtls *tls = NULL) __attribute__ ((regparm (3)));
+int __stdcall sig_send (_pinfo *, int) __attribute__ ((regparm (2)));
+void __stdcall signal_fixup_after_exec ();
+void __stdcall wait_for_sigthread ();
+void __stdcall sigalloc ();
+
+int kill_pgrp (pid_t, siginfo_t&);
+int killsys (pid_t, int);
+
+extern char myself_nowait_dummy[];
+
+extern struct sigaction *global_sigs;
+
+#define WAIT_SIG_PRIORITY THREAD_PRIORITY_TIME_CRITICAL
+
+#define myself_nowait ((_pinfo *)myself_nowait_dummy)
+#endif /*_SIGPROC_H*/
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
new file mode 100644
index 00000000000..43bd1481f18
--- /dev/null
+++ b/winsup/cygwin/spawn.cc
@@ -0,0 +1,1088 @@
+/* spawn.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <process.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <ctype.h>
+#include "cygerrno.h"
+#include <sys/cygwin.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "cygheap.h"
+#include "child_info.h"
+#include "shared_info.h"
+#include "pinfo.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "registry.h"
+#include "environ.h"
+#include "cygthread.h"
+
+#define LINE_BUF_CHUNK (CYG_MAX_PATH * 2)
+
+static suffix_info std_suffixes[] =
+{
+ suffix_info (".exe", 1), suffix_info ("", 1),
+ suffix_info (".com"), suffix_info (".cmd"),
+ suffix_info (".bat"), suffix_info (".dll"),
+ suffix_info (NULL)
+};
+
+HANDLE hExeced;
+DWORD dwExeced;
+
+/* Add .exe to PROG if not already present and see if that exists.
+ If not, return PROG (converted from posix to win32 rules if necessary).
+ The result is always BUF.
+
+ Returns (possibly NULL) suffix */
+
+static const char *
+perhaps_suffix (const char *prog, path_conv& buf)
+{
+ char *ext;
+
+ debug_printf ("prog '%s'", prog);
+ buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes);
+
+ if (!buf.exists () || buf.isdir ())
+ ext = NULL;
+ else if (buf.known_suffix)
+ ext = (char *) buf + (buf.known_suffix - buf.get_win32 ());
+ else
+ ext = strchr (buf, '\0');
+
+ debug_printf ("buf %s, suffix found '%s'", (char *) buf, ext);
+ return ext;
+}
+
+/* Find an executable name, possibly by appending known executable
+ suffixes to it. The win32-translated name is placed in 'buf'.
+ Any found suffix is returned in known_suffix.
+
+ If the file is not found and !null_if_not_found then the win32 version
+ of name is placed in buf and returned. Otherwise the contents of buf
+ is undefined and NULL is returned. */
+
+const char * __stdcall
+find_exec (const char *name, path_conv& buf, const char *mywinenv,
+ unsigned opt, const char **known_suffix)
+{
+ const char *suffix = "";
+ debug_printf ("find_exec (%s)", name);
+ const char *retval = buf;
+ char tmp[CYG_MAX_PATH];
+ const char *posix = (opt & FE_NATIVE) ? NULL : name;
+ bool has_slash = strchr (name, '/');
+
+ /* Check to see if file can be opened as is first.
+ Win32 systems always check . first, but PATH may not be set up to
+ do this. */
+ if ((has_slash || opt & FE_CWD)
+ && (suffix = perhaps_suffix (name, buf)) != NULL)
+ {
+ if (posix && !has_slash)
+ {
+ tmp[0] = '.';
+ tmp[1] = '/';
+ strcpy (tmp + 2, name);
+ posix = tmp;
+ }
+ goto out;
+ }
+
+ win_env *winpath;
+ const char *path;
+ const char *posix_path;
+
+ /* Return the error condition if this is an absolute path or if there
+ is no PATH to search. */
+ if (has_slash || strchr (name, '\\') || isdrive (name)
+ || !(winpath = getwinenv (mywinenv))
+ || !(path = winpath->get_native ()) || *path == '\0')
+ goto errout;
+
+ debug_printf ("%s%s", mywinenv, path);
+
+ posix = (opt & FE_NATIVE) ? NULL : tmp;
+ posix_path = winpath->get_posix () - 1;
+ /* Iterate over the specified path, looking for the file with and
+ without executable extensions. */
+ do
+ {
+ posix_path++;
+ char *eotmp = strccpy (tmp, &path, ';');
+ /* An empty path or '.' means the current directory, but we've
+ already tried that. */
+ if (opt & FE_CWD && (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0')))
+ continue;
+
+ *eotmp++ = '\\';
+ strcpy (eotmp, name);
+
+ debug_printf ("trying %s", tmp);
+
+ if ((suffix = perhaps_suffix (tmp, buf)) != NULL)
+ {
+ if (posix == tmp)
+ {
+ eotmp = strccpy (tmp, &posix_path, ':');
+ if (eotmp == tmp)
+ *eotmp++ = '.';
+ *eotmp++ = '/';
+ strcpy (eotmp, name);
+ }
+ goto out;
+ }
+ }
+ while (*path && *++path && (posix_path = strchr (posix_path, ':')));
+
+ errout:
+ posix = NULL;
+ /* Couldn't find anything in the given path.
+ Take the appropriate action based on null_if_not_found. */
+ if (opt & FE_NNF)
+ retval = NULL;
+ else if (opt & FE_NATIVE)
+ buf.check (name);
+ else
+ retval = name;
+
+ out:
+ if (posix)
+ buf.set_path (posix);
+ debug_printf ("%s = find_exec (%s)", (char *) buf, name);
+ if (known_suffix)
+ *known_suffix = suffix ?: strchr (buf, '\0');
+ return retval;
+}
+
+/* Utility for spawn_guts. */
+
+static HANDLE
+handle (int n, int direction)
+{
+ fhandler_base *fh = cygheap->fdtab[n];
+
+ if (!fh)
+ return INVALID_HANDLE_VALUE;
+ if (fh->close_on_exec ())
+ return INVALID_HANDLE_VALUE;
+ if (direction == 0)
+ return fh->get_handle ();
+ return fh->get_output_handle ();
+}
+
+int
+iscmd (const char *argv0, const char *what)
+{
+ int n;
+ n = strlen (argv0) - strlen (what);
+ if (n >= 2 && argv0[1] != ':')
+ return 0;
+ return n >= 0 && strcasematch (argv0 + n, what) &&
+ (n == 0 || isdirsep (argv0[n - 1]));
+}
+
+class linebuf
+{
+ public:
+ size_t ix;
+ char *buf;
+ size_t alloced;
+ linebuf () : ix (0), buf (NULL), alloced (0) {}
+ ~linebuf () {if (buf) free (buf);}
+ void add (const char *what, int len);
+ void add (const char *what) {add (what, strlen (what));}
+ void prepend (const char *what, int len);
+};
+
+void
+linebuf::add (const char *what, int len)
+{
+ size_t newix;
+ if ((newix = ix + len) >= alloced || !buf)
+ {
+ alloced += LINE_BUF_CHUNK + newix;
+ buf = (char *) realloc (buf, alloced + 1);
+ }
+ memcpy (buf + ix, what, len);
+ ix = newix;
+ buf[ix] = '\0';
+}
+
+void
+linebuf::prepend (const char *what, int len)
+{
+ int buflen;
+ size_t newix;
+ if ((newix = ix + len) >= alloced)
+ {
+ alloced += LINE_BUF_CHUNK + newix;
+ buf = (char *) realloc (buf, alloced + 1);
+ buf[ix] = '\0';
+ }
+ if ((buflen = strlen (buf)))
+ memmove (buf + len, buf, buflen + 1);
+ else
+ buf[newix] = '\0';
+ memcpy (buf, what, len);
+ ix = newix;
+}
+
+class av
+{
+ char **argv;
+ int calloced;
+ public:
+ int error;
+ int argc;
+ av (int ac, const char * const *av) : calloced (0), error (false), argc (ac)
+ {
+ argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *));
+ memcpy (argv, av, (argc + 1) * sizeof (char *));
+ }
+ ~av ()
+ {
+ if (argv)
+ {
+ for (int i = 0; i < calloced; i++)
+ if (argv[i])
+ cfree (argv[i]);
+ cfree (argv);
+ }
+ }
+ int unshift (const char *what, int conv = 0);
+ operator char **() {return argv;}
+ void all_calloced () {calloced = argc;}
+ void replace0_maybe (const char *arg0)
+ {
+ /* Note: Assumes that argv array has not yet been "unshifted" */
+ if (!calloced
+ && (argv[0] = cstrdup1 (arg0)))
+ calloced = true;
+ else
+ error = errno;
+ }
+ void dup_maybe (int i)
+ {
+ if (i >= calloced
+ && !(argv[i] = cstrdup1 (argv[i])))
+ error = errno;
+ }
+ void dup_all ()
+ {
+ for (int i = calloced; i < argc; i++)
+ if (!(argv[i] = cstrdup1 (argv[i])))
+ error = errno;
+ }
+};
+
+int
+av::unshift (const char *what, int conv)
+{
+ char **av;
+ av = (char **) crealloc (argv, (argc + 2) * sizeof (char *));
+ if (!av)
+ return 0;
+
+ argv = av;
+ memmove (argv + 1, argv, (argc + 1) * sizeof (char *));
+ char buf[CYG_MAX_PATH + 1];
+ if (conv)
+ {
+ cygwin_conv_to_posix_path (what, buf);
+ char *p = strchr (buf, '\0') - 4;
+ if (p > buf && strcasematch (p, ".exe"))
+ *p = '\0';
+ what = buf;
+ }
+ if (!(*argv = cstrdup1 (what)))
+ error = errno;
+ argc++;
+ calloced++;
+ return 1;
+}
+
+struct pthread_cleanup
+{
+ _sig_func_ptr oldint;
+ _sig_func_ptr oldquit;
+ sigset_t oldmask;
+ pthread_cleanup (): oldint (NULL), oldquit (NULL), oldmask ((sigset_t) -1) {}
+};
+
+static void
+do_cleanup (void *args)
+{
+# define cleanup ((pthread_cleanup *) args)
+ if (cleanup->oldint)
+ signal (SIGINT, cleanup->oldint);
+ if (cleanup->oldquit)
+ signal (SIGQUIT, cleanup->oldquit);
+ if (cleanup->oldmask != (sigset_t) -1)
+ sigprocmask (SIG_SETMASK, &(cleanup->oldmask), NULL);
+# undef cleanup
+}
+
+
+static int __stdcall
+spawn_guts (const char * prog_arg, const char *const *argv,
+ const char *const envp[], int mode)
+{
+ bool rc;
+ pid_t cygpid;
+
+ MALLOC_CHECK;
+
+ if (prog_arg == NULL)
+ {
+ syscall_printf ("prog_arg is NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ syscall_printf ("spawn_guts (%d, %.9500s)", mode, prog_arg);
+
+ if (argv == NULL)
+ {
+ syscall_printf ("argv is NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ path_conv real_path;
+
+ linebuf one_line;
+
+ 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;
+ if (mode != _P_OVERLAY)
+ chtype = PROC_SPAWN;
+ else
+ chtype = PROC_EXEC;
+
+ HANDLE subproc_ready;
+ if (1 || chtype != PROC_EXEC)
+ subproc_ready = NULL;
+ else
+ {
+ subproc_ready = CreateEvent (&sec_all, TRUE, FALSE, NULL);
+ ProtectHandleINH (subproc_ready);
+ }
+
+ init_child_info (chtype, &ciresrv, subproc_ready);
+
+ ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info));
+ ciresrv.moreinfo->old_title = NULL;
+
+ /* CreateProcess takes one long string that is the command line (sigh).
+ We need to quote any argument that has whitespace or embedded "'s. */
+
+ int ac;
+ for (ac = 0; argv[ac]; ac++)
+ /* nothing */;
+
+ av newargv (ac, argv);
+
+ int null_app_name = 0;
+ if (ac == 3 && argv[1][0] == '/' && argv[1][1] == 'c' &&
+ (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
+ {
+ real_path.check (prog_arg);
+ one_line.add ("\"");
+ if (!real_path.error)
+ one_line.add (real_path);
+ else
+ one_line.add (argv[0]);
+ one_line.add ("\"");
+ one_line.add (" ");
+ one_line.add (argv[1]);
+ one_line.add (" ");
+ one_line.add (argv[2]);
+ strcpy (real_path, argv[0]);
+ null_app_name = 1;
+ goto skip_arg_parsing;
+ }
+
+ const char *ext;
+ if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ MALLOC_CHECK;
+
+ /* If the file name ends in either .exe, .com, .bat, or .cmd we assume
+ that it is NOT a script file */
+ while (*ext == '\0')
+ {
+ HANDLE hnd = CreateFile (real_path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sec_none_nih, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if (hnd == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ DWORD done;
+
+ char buf[2 * CYG_MAX_PATH + 1];
+ buf[0] = buf[1] = buf[2] = buf[sizeof (buf) - 1] = '\0';
+ if (!ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0))
+ {
+ CloseHandle (hnd);
+ __seterrno ();
+ return -1;
+ }
+
+ CloseHandle (hnd);
+
+ if (buf[0] == 'M' && buf[1] == 'Z')
+ break;
+
+ debug_printf ("%s is a script", (char *) real_path);
+
+ if (real_path.has_acls () && allow_ntsec
+ && check_file_access (real_path, X_OK))
+ {
+ debug_printf ("... but not executable");
+ break;
+ }
+
+ char *pgm, *arg1;
+
+ if (buf[0] != '#' || buf[1] != '!')
+ {
+ pgm = (char *) "/bin/sh";
+ arg1 = NULL;
+ }
+ else
+ {
+ char *ptr;
+ pgm = buf + 2;
+ pgm += strspn (pgm, " \t");
+ for (ptr = pgm, arg1 = NULL;
+ *ptr && *ptr != '\r' && *ptr != '\n';
+ ptr++)
+ if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
+ {
+ /* Null terminate the initial command and step over
+ any additional white space. If we've hit the
+ end of the line, exit the loop. Otherwise, we've
+ found the first argument. Position the current
+ pointer on the last known white space. */
+ *ptr = '\0';
+ char *newptr = ptr + 1;
+ newptr += strspn (newptr, " \t");
+ if (!*newptr || *newptr == '\r' || *newptr == '\n')
+ break;
+ arg1 = newptr;
+ ptr = newptr - 1;
+ }
+
+ *ptr = '\0';
+ }
+
+ /* Replace argv[0] with the full path to the script if this is the
+ first time through the loop. */
+ newargv.replace0_maybe (prog_arg);
+
+ /* pointers:
+ * pgm interpreter name
+ * arg1 optional string
+ */
+ if (arg1)
+ newargv.unshift (arg1);
+
+ /* FIXME: This should not be using FE_NATIVE. It should be putting
+ the posix path on the argv list. */
+ find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext);
+ newargv.unshift (real_path, 1);
+ }
+
+ if (real_path.iscygexec ())
+ newargv.dup_all ();
+ else
+ {
+ for (int i = 0; i < newargv.argc; i++)
+ {
+ char *p = NULL;
+ const char *a;
+
+ newargv.dup_maybe (i);
+ a = i ? newargv[i] : (char *) real_path;
+ int len = strlen (a);
+ if (len != 0 && !strpbrk (a, " \t\n\r\""))
+ one_line.add (a, len);
+ else
+ {
+ one_line.add ("\"", 1);
+ /* Handle embedded special characters " and \.
+ A " is always preceded by a \.
+ A \ is not special unless it precedes a ". If it does,
+ then all preceding \'s must be doubled to avoid having
+ the Windows command line parser interpret the \ as quoting
+ the ". This rule applies to a string of \'s before the end
+ of the string, since cygwin/windows uses a " to delimit the
+ argument. */
+ for (; (p = strpbrk (a, "\"\\")); a = ++p)
+ {
+ one_line.add (a, p - a);
+ /* Find length of string of backslashes */
+ int n = strspn (p, "\\");
+ if (!n)
+ one_line.add ("\\\"", 2); /* No backslashes, so it must be a ".
+ The " has to be protected with a backslash. */
+ else
+ {
+ one_line.add (p, n); /* Add the run of backslashes */
+ /* Need to double up all of the preceding
+ backslashes if they precede a quote or EOS. */
+ if (!p[n] || p[n] == '"')
+ one_line.add (p, n);
+ p += n - 1; /* Point to last backslash */
+ }
+ }
+ if (*a)
+ one_line.add (a);
+ one_line.add ("\"", 1);
+ }
+ MALLOC_CHECK;
+ one_line.add (" ", 1);
+ MALLOC_CHECK;
+ }
+
+ MALLOC_CHECK;
+ if (one_line.ix)
+ one_line.buf[one_line.ix - 1] = '\0';
+ else
+ one_line.add ("", 1);
+ MALLOC_CHECK;
+
+ if (one_line.ix > 32767)
+ {
+ debug_printf ("Command line too long (>32K), return E2BIG");
+ set_errno (E2BIG);
+ return -1;
+ }
+ }
+
+ char *envblock;
+ newargv.all_calloced ();
+ if (newargv.error)
+ {
+ set_errno (newargv.error);
+ return -1;
+ }
+
+ ciresrv.moreinfo->argc = newargv.argc;
+ ciresrv.moreinfo->argv = newargv;
+ ciresrv.hexec_proc = hexec_proc;
+
+ if (mode != _P_OVERLAY ||
+ !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc,
+ &ciresrv.moreinfo->myself_pinfo, 0,
+ TRUE, DUPLICATE_SAME_ACCESS))
+ ciresrv.moreinfo->myself_pinfo = NULL;
+ else
+ VerifyHandle (ciresrv.moreinfo->myself_pinfo);
+
+ skip_arg_parsing:
+ PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = handle (0, 0); /* Get input handle */
+ si.hStdOutput = handle (1, 1); /* Get output handle */
+ si.hStdError = handle (2, 1); /* Get output handle */
+ si.cb = sizeof (si);
+
+ int flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (hMainProc);
+
+ if (mode == _P_DETACH || !set_console_state_for_spawn ())
+ flags |= DETACHED_PROCESS;
+ if (mode != _P_OVERLAY)
+ flags |= CREATE_SUSPENDED;
+#if 0 //someday
+ else
+ myself->dwProcessId = 0;
+#endif
+
+ /* 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 (cygheap->fdtab.need_fixup_before ())
+ flags |= CREATE_SUSPENDED;
+
+ const char *runpath = null_app_name ? NULL : (const char *) real_path;
+
+ syscall_printf ("null_app_name %d (%s, %.9500s)", null_app_name, runpath, one_line.buf);
+
+ void *newheap;
+
+ cygbench ("spawn-guts");
+
+ cygheap->fdtab.set_file_pointers_for_exec ();
+ cygheap->user.deimpersonate ();
+
+ /* When ruid != euid we create the new process under the current original
+ account and impersonate in child, this way maintaining the different
+ 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. */
+ if (!cygheap->user.issetuid ()
+ || (cygheap->user.saved_uid == cygheap->user.real_uid
+ && cygheap->user.saved_gid == cygheap->user.real_gid
+ && !cygheap->user.groups.issetgroups ()))
+ {
+ ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc,
+ real_path.iscygexec ());
+ newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcess (runpath, /* image name - with full path */
+ one_line.buf, /* what was passed to exec */
+ &sec_none_nih,/* process security attrs */
+ &sec_none_nih,/* thread security attrs */
+ TRUE, /* inherit handles from parent */
+ flags,
+ envblock, /* environment */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+ }
+ else
+ {
+ /* Give access to myself */
+ if (mode == _P_OVERLAY)
+ myself.set_acl();
+
+ /* allow the child to interact with our window station/desktop */
+ HANDLE hwst, hdsk;
+ SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION;
+ DWORD n;
+ char wstname[1024];
+ char dskname[1024];
+
+ hwst = GetProcessWindowStation ();
+ SetUserObjectSecurity (hwst, &dsi, get_null_sd ());
+ GetUserObjectInformation (hwst, UOI_NAME, wstname, 1024, &n);
+ hdsk = GetThreadDesktop (GetCurrentThreadId ());
+ SetUserObjectSecurity (hdsk, &dsi, get_null_sd ());
+ GetUserObjectInformation (hdsk, UOI_NAME, dskname, 1024, &n);
+ strcat (wstname, "\\");
+ strcat (wstname, dskname);
+ si.lpDesktop = wstname;
+
+ ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc,
+ real_path.iscygexec ());
+ newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcessAsUser (cygheap->user.token (),
+ runpath, /* image name - with full path */
+ one_line.buf, /* what was passed to exec */
+ &sec_none_nih, /* process security attrs */
+ &sec_none_nih, /* thread security attrs */
+ TRUE, /* inherit handles from parent */
+ flags,
+ envblock, /* environment */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+ }
+
+ /* Restore impersonation. In case of _P_OVERLAY this isn't
+ allowed since it would overwrite child data. */
+ if (mode != _P_OVERLAY || !rc)
+ cygheap->user.reimpersonate ();
+
+ MALLOC_CHECK;
+ if (envblock)
+ free (envblock);
+ MALLOC_CHECK;
+
+ /* Set errno now so that debugging messages from it appear before our
+ final debugging message [this is a general rule for debugging
+ messages]. */
+ if (!rc)
+ {
+ __seterrno ();
+ syscall_printf ("CreateProcess failed, %E");
+#if 0 // someday
+ if (mode == _P_OVERLAY)
+ myself->dwProcessId = GetCurrentProcessId ();
+#endif
+ if (subproc_ready)
+ ForceCloseHandle (subproc_ready);
+ cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
+ return -1;
+ }
+
+ /* FIXME: There is a small race here */
+
+ int res;
+ pthread_cleanup cleanup;
+ pthread_cleanup_push (do_cleanup, (void *) &cleanup);
+ if (mode == _P_SYSTEM)
+ {
+ sigset_t child_block;
+ cleanup.oldint = signal (SIGINT, SIG_IGN);
+ cleanup.oldquit = signal (SIGQUIT, SIG_IGN);
+ sigemptyset (&child_block);
+ sigaddset (&child_block, SIGCHLD);
+ (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup.oldmask);
+ }
+
+ /* Fixup the parent data structures if needed and resume the child's
+ main thread. */
+ if (!cygheap->fdtab.need_fixup_before ())
+ cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
+ else
+ {
+ cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
+ cygheap_setup_for_child_cleanup (newheap, &ciresrv, 1);
+ if (mode == _P_OVERLAY)
+ {
+ ResumeThread (pi.hThread);
+ cygthread::terminate ();
+ }
+ }
+
+ if (mode != _P_OVERLAY)
+ cygpid = cygwin_pid (pi.dwProcessId);
+ else
+ cygpid = myself->pid;
+
+ /* We print the original program name here so the user can see that too. */
+ syscall_printf ("%d = spawn_guts (%s, %.9500s)",
+ rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf);
+
+ /* Name the handle similarly to proc_subproc. */
+ ProtectHandle1 (pi.hProcess, childhProc);
+
+ if (mode == _P_OVERLAY)
+ {
+ /* These are both duplicated in the child code. We do this here,
+ primarily for strace. */
+ strace.execing = 1;
+ hExeced = pi.hProcess;
+ dwExeced = pi.dwProcessId;
+ strcpy (myself->progname, real_path);
+ close_all_files ();
+ }
+ else
+ {
+ myself->set_has_pgid_children ();
+ ProtectHandle (pi.hThread);
+ pinfo child (cygpid, PID_IN_USE);
+ if (!child)
+ {
+ syscall_printf ("pinfo failed");
+ if (get_errno () != ENOMEM)
+ set_errno (EAGAIN);
+ res = -1;
+ goto out;
+ }
+ child->dwProcessId = pi.dwProcessId;
+ child.hProcess = pi.hProcess;
+ if (!child.remember ())
+ {
+ syscall_printf ("process table full");
+ set_errno (EAGAIN);
+ res = -1;
+ goto out;
+ }
+
+ strcpy (child->progname, real_path);
+ /* FIXME: This introduces an unreferenced, open handle into the child.
+ The purpose is to keep the pid shared memory open so that all of
+ the fields filled out by child.remember do not disappear and so there
+ is not a brief period during which the pid is not available.
+ 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);
+ /* Start the child running */
+ ResumeThread (pi.hThread);
+ }
+
+ ForceCloseHandle (pi.hThread);
+
+ sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
+
+ bool exited;
+
+ res = 0;
+ exited = false;
+#if 0
+ if (mode == _P_OVERLAY)
+ {
+ int nwait = 3;
+ HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, subproc_ready};
+ for (int i = 0; i < 100; i++)
+ {
+ switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ sigproc_printf ("subprocess exited");
+ DWORD exitcode;
+ if (!GetExitCodeProcess (pi.hProcess, &exitcode))
+ exitcode = 1;
+ res |= exitcode;
+ exited = true;
+ break;
+ case WAIT_OBJECT_0 + 1:
+ sigproc_printf ("signal arrived");
+ reset_signal_arrived ();
+ continue;
+ case WAIT_OBJECT_0 + 2:
+ if (!myself->cygstarted)
+ {
+ nwait = 2;
+ sigproc_terminate ();
+ continue;
+ }
+ break;
+ case WAIT_FAILED:
+ system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E",
+ nwait, myself->pid, myself->dwProcessId);
+ system_printf ("waitbuf[0] %p %d", waitbuf[0],
+ WaitForSingleObject (waitbuf[0], 0));
+ system_printf ("waitbuf[1] %p %d", waitbuf[1],
+ WaitForSingleObject (waitbuf[1], 0));
+ system_printf ("waitbuf[w] %p %d", waitbuf[2],
+ WaitForSingleObject (waitbuf[2], 0));
+ set_errno (ECHILD);
+ try_to_debug ();
+ return -1;
+ }
+ break;
+ }
+
+ ForceCloseHandle (subproc_ready);
+ sigproc_printf ("P_OVERLAY res %p", res);
+ }
+#endif
+
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+
+ 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;
+}
+
+extern "C" int
+cwait (int *result, int pid, int)
+{
+ return waitpid (pid, result, 0);
+}
+
+/*
+ * 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)
+{
+ int ret;
+#ifdef NEWVFORK
+ vfork_save *vf = vfork_storage.val ();
+
+ 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);
+
+ 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);
+ }
+ }
+#endif
+ 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
+ */
+
+extern "C" int
+spawnl (int mode, const char *path, const char *arg0, ...)
+{
+ int i;
+ va_list args;
+ const char *argv[256];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ va_end (args);
+
+ 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];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ envp = va_arg (args, const char * const *);
+ va_end (args);
+
+ 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];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ va_end (args);
+
+ 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];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ envp = va_arg (args, const char * const *);
+ va_end (args);
+
+ 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 ());
+}
+
+extern "C" int
+spawnvp (int mode, const char *path, const char * const *argv)
+{
+ return spawnvpe (mode, path, argv, cur_environ ());
+}
+
+extern "C" int
+spawnvpe (int mode, const char *file, const char * const *argv,
+ const char * const *envp)
+{
+ path_conv buf;
+ return spawnve (mode, find_exec (file, buf), argv, envp);
+}
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
new file mode 100644
index 00000000000..ef77bd527f2
--- /dev/null
+++ b/winsup/cygwin/tty.cc
@@ -0,0 +1,494 @@
+/* tty.cc
+
+ Copyright 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <utmp.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "cygserver.h"
+#include "shared_info.h"
+#include "cygthread.h"
+
+extern fhandler_tty_master *tty_master;
+
+extern "C" int
+grantpt (int fd)
+{
+ return 0;
+}
+
+extern "C" int
+unlockpt (int fd)
+{
+ return 0;
+}
+
+extern "C" int
+revoke (char *ttyname)
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" int
+ttyslot (void)
+{
+ if (NOTSTATE (myself, PID_USETTY))
+ return -1;
+ return myself->ctty;
+}
+
+void __stdcall
+tty_init (void)
+{
+ if (!myself->cygstarted && NOTSTATE (myself, PID_CYGPARENT))
+ cygheap->fdtab.get_debugger_info ();
+
+ if (NOTSTATE (myself, PID_USETTY))
+ return;
+ if (myself->ctty == -1)
+ if (NOTSTATE (myself, PID_CYGPARENT))
+ myself->ctty = attach_tty (myself->ctty);
+ else
+ return;
+ if (myself->ctty == -1)
+ termios_printf ("Can't attach to tty");
+}
+
+/* Create session's master tty */
+
+void __stdcall
+create_tty_master (int ttynum)
+{
+ device ttym = *ttym_dev;
+ ttym.setunit (ttynum); /* CGF FIXME device */
+ tty_master = (fhandler_tty_master *) build_fh_dev (ttym);
+ if (tty_master->init ())
+ api_fatal ("Can't create master tty");
+ else
+ {
+ /* Log utmp entry */
+ struct utmp our_utmp;
+ DWORD len = sizeof our_utmp.ut_host;
+
+ bzero ((char *) &our_utmp, sizeof (utmp));
+ (void) time (&our_utmp.ut_time);
+ strncpy (our_utmp.ut_name, getlogin (), sizeof (our_utmp.ut_name));
+ GetComputerName (our_utmp.ut_host, &len);
+ __small_sprintf (our_utmp.ut_line, "tty%d", ttynum);
+ if ((len = strlen (our_utmp.ut_line)) >= UT_IDLEN)
+ len -= UT_IDLEN;
+ else
+ len = 0;
+ strncpy (our_utmp.ut_id, our_utmp.ut_line + len, UT_IDLEN);
+ our_utmp.ut_type = USER_PROCESS;
+ our_utmp.ut_pid = myself->pid;
+ myself->ctty = ttynum;
+ login (&our_utmp);
+ }
+}
+
+void __stdcall
+tty_terminate (void)
+{
+ if (NOTSTATE (myself, PID_USETTY))
+ return;
+ cygwin_shared->tty.terminate ();
+}
+
+int __stdcall
+attach_tty (int num)
+{
+ if (num != -1)
+ {
+ return cygwin_shared->tty.connect_tty (num);
+ }
+ if (NOTSTATE (myself, PID_USETTY))
+ return -1;
+ return cygwin_shared->tty.allocate_tty (true);
+}
+
+void
+tty_list::terminate (void)
+{
+ int ttynum = myself->ctty;
+
+ /* Keep master running till there are connected clients */
+ if (ttynum != -1 && ttys[ttynum].master_pid == GetCurrentProcessId ())
+ {
+ tty *t = ttys + ttynum;
+ CloseHandle (t->from_master);
+ CloseHandle (t->to_master);
+ /* Wait for children which rely on tty handling in this process to
+ go away */
+ for (int i = 0; ; i++)
+ {
+ if (!t->slave_alive ())
+ break;
+ if (i >= 100)
+ {
+ small_printf ("waiting for children using tty%d to terminate\n",
+ ttynum);
+ i = 0;
+ }
+
+ low_priority_sleep (200);
+ }
+
+ termios_printf ("tty %d master about to finish", ttynum);
+ ForceCloseHandle1 (t->to_slave, to_pty);
+ ForceCloseHandle1 (t->from_slave, from_pty);
+ CloseHandle (tty_master->inuse);
+ t->init ();
+
+ char buf[20];
+ __small_sprintf (buf, "tty%d", ttynum);
+ logout (buf);
+ }
+}
+
+int
+tty_list::connect_tty (int ttynum)
+{
+ if (ttynum < 0 || ttynum >= NTTYS)
+ {
+ termios_printf ("ttynum (%d) out of range", ttynum);
+ return -1;
+ }
+ if (!ttys[ttynum].exists ())
+ {
+ termios_printf ("tty %d was not allocated", ttynum);
+ return -1;
+ }
+
+ return ttynum;
+}
+
+void
+tty_list::init (void)
+{
+ for (int i = 0; i < NTTYS; i++)
+ {
+ ttys[i].init ();
+ ttys[i].setntty (i);
+ }
+}
+
+/* Search for tty class for our console. Allocate new tty if our process is
+ the only cygwin process in the current console.
+ Return tty number or -1 if error.
+ If flag == 0, just find a free tty.
+ */
+int
+tty_list::allocate_tty (bool with_console)
+{
+ HWND console;
+ int freetty = -1;
+ HANDLE hmaster = NULL;
+
+ /* FIXME: This whole function needs a protective mutex. */
+
+ if (WaitForSingleObject (tty_mutex, INFINITE) == WAIT_FAILED)
+ termios_printf ("WFSO for tty_mutex %p failed, %E", tty_mutex);
+
+ if (!with_console)
+ console = NULL;
+ else if (!(console = GetConsoleWindow ()))
+ {
+ char oldtitle[TITLESIZE];
+
+ if (!GetConsoleTitle (oldtitle, TITLESIZE))
+ {
+ termios_printf ("Can't read console title");
+ goto out;
+ }
+
+ char buf[40];
+
+ __small_sprintf (buf, "cygwin.find.console.%d", myself->pid);
+ SetConsoleTitle (buf);
+ for (int times = 0; times < 25; times++)
+ {
+ Sleep (10);
+ if ((console = FindWindow (NULL, buf)))
+ break;
+ }
+ SetConsoleTitle (oldtitle);
+ Sleep (40);
+ if (console == NULL)
+ {
+ termios_printf ("Can't find console window");
+ goto out;
+ }
+ }
+
+ /* Is a tty allocated for console? */
+ for (int i = 0; i < NTTYS; i++)
+ {
+ if (!ttys[i].exists ())
+ {
+ if (freetty < 0) /* Scanning? */
+ freetty = i; /* Yes. */
+ if (!with_console) /* Do we want to attach this to a console? */
+ break; /* No. We've got one. */
+ }
+
+ /* FIXME: Is this right? We can potentially query a "nonexistent"
+ tty slot after falling through from the above? */
+ if (with_console && ttys[i].gethwnd () == console)
+ {
+ termios_printf ("console %x already associated with tty%d",
+ console, i);
+ /* Is the master alive? */
+ hmaster = OpenProcess (PROCESS_DUP_HANDLE, FALSE, ttys[i].master_pid);
+ if (hmaster)
+ {
+ CloseHandle (hmaster);
+ freetty = i;
+ goto out;
+ }
+ /* Master is dead */
+ freetty = i;
+ break;
+ }
+ }
+
+ /* There is no tty allocated to console, allocate the first free found */
+ if (freetty == -1)
+ goto out;
+
+ tty *t;
+ t = ttys + freetty;
+ t->init ();
+ t->setsid (-1);
+ t->setpgid (myself->pgid);
+ t->sethwnd (console);
+
+out:
+ if (freetty < 0)
+ {
+ ReleaseMutex (tty_mutex);
+ system_printf ("No tty allocated");
+ }
+ else if (!with_console)
+ {
+ termios_printf ("tty%d allocated", freetty);
+ /* exit with tty_mutex still held -- caller has more work to do */
+ }
+ else
+ {
+ termios_printf ("console %p associated with tty%d", console, freetty);
+ if (!hmaster)
+ create_tty_master (freetty);
+ ReleaseMutex (tty_mutex);
+ }
+ return freetty;
+}
+
+bool
+tty::slave_alive ()
+{
+ return alive (TTY_SLAVE_ALIVE);
+}
+
+bool
+tty::master_alive ()
+{
+ return alive (TTY_MASTER_ALIVE);
+}
+
+bool
+tty::alive (const char *fmt)
+{
+ HANDLE ev;
+ char buf[CYG_MAX_PATH];
+
+ shared_name (buf, fmt, ntty);
+ if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf)))
+ CloseHandle (ev);
+ return ev != NULL;
+}
+
+HANDLE
+tty::open_output_mutex ()
+{
+ return open_mutex (OUTPUT_MUTEX);
+}
+
+HANDLE
+tty::open_input_mutex ()
+{
+ return open_mutex (INPUT_MUTEX);
+}
+
+HANDLE
+tty::open_mutex (const char *mutex)
+{
+ char buf[CYG_MAX_PATH];
+ shared_name (buf, mutex, ntty);
+ return OpenMutex (MUTEX_ALL_ACCESS, TRUE, buf);
+}
+
+HANDLE
+tty::create_inuse (const char *fmt)
+{
+ HANDLE h;
+ char buf[CYG_MAX_PATH];
+
+ shared_name (buf, fmt, ntty);
+ h = CreateEvent (&sec_all, TRUE, FALSE, buf);
+ termios_printf ("%s %p", buf, h);
+ if (!h)
+ termios_printf ("couldn't open inuse event, %E", buf);
+ return h;
+}
+
+void
+tty::init (void)
+{
+ output_stopped = 0;
+ setsid (0);
+ pgid = 0;
+ hwnd = NULL;
+ to_slave = NULL;
+ from_slave = NULL;
+ was_opened = 0;
+}
+
+HANDLE
+tty::get_event (const char *fmt, BOOL manual_reset)
+{
+ HANDLE hev;
+ char buf[CYG_MAX_PATH];
+
+ shared_name (buf, fmt, ntty);
+ if (!(hev = CreateEvent (&sec_all, manual_reset, FALSE, buf)))
+ {
+ termios_printf ("couldn't create %s", buf);
+ set_errno (ENOENT); /* FIXME this can't be the right errno */
+ return NULL;
+ }
+
+ termios_printf ("created event %s", buf);
+ return hev;
+}
+
+bool
+tty::make_pipes (fhandler_pty_master *ptym)
+{
+ /* Create communication pipes */
+
+ /* FIXME: should this be sec_none_nih? */
+ if (!CreatePipe (&from_master, &to_slave, &sec_all, 128 * 1024))
+ {
+ termios_printf ("can't create input pipe");
+ set_errno (ENOENT);
+ return false;
+ }
+
+ // ProtectHandle1INH (to_slave, to_pty);
+ if (!CreatePipe (&from_slave, &to_master, &sec_all, 128 * 1024))
+ {
+ termios_printf ("can't create output pipe");
+ set_errno (ENOENT);
+ return false;
+ }
+ // ProtectHandle1INH (from_slave, from_pty);
+ termios_printf ("tty%d from_slave %p, to_slave %p", ntty, from_slave,
+ to_slave);
+
+ DWORD pipe_mode = PIPE_NOWAIT;
+ if (!SetNamedPipeHandleState (to_slave, &pipe_mode, NULL, NULL))
+ termios_printf ("can't set to_slave to non-blocking mode");
+ ptym->set_io_handle (from_slave);
+ ptym->set_output_handle (to_slave);
+ return true;
+}
+
+bool
+tty::common_init (fhandler_pty_master *ptym)
+{
+ /* Set termios information. Force initialization. */
+ ptym->tcinit (this, true);
+
+ if (!make_pipes (ptym))
+ return false;
+ ptym->need_nl = 0;
+
+ /* Save our pid */
+
+ master_pid = GetCurrentProcessId ();
+
+ /* We do not open allow the others to open us (for handle duplication)
+ but rely on cygheap->inherited_ctty for descendant processes.
+ In the future the cygserver may allow access by others. */
+
+#ifdef USE_SERVER
+ if (wincap.has_security ())
+ {
+ if (cygserver_running == CYGSERVER_UNKNOWN)
+ cygserver_init ();
+ }
+#endif
+
+ /* Create synchronisation events */
+
+ if (ptym->get_major () != DEV_TTYM_MAJOR)
+ {
+ ptym->output_done_event = ptym->ioctl_done_event =
+ ptym->ioctl_request_event = NULL;
+ }
+ else
+ {
+ if (!(ptym->output_done_event = get_event (OUTPUT_DONE_EVENT)))
+ return false;
+ if (!(ptym->ioctl_done_event = get_event (IOCTL_DONE_EVENT)))
+ return false;
+ if (!(ptym->ioctl_request_event = get_event (IOCTL_REQUEST_EVENT)))
+ return false;
+ }
+
+ if (!(ptym->input_available_event = get_event (INPUT_AVAILABLE_EVENT, TRUE)))
+ return false;
+
+ char buf[CYG_MAX_PATH];
+ shared_name (buf, OUTPUT_MUTEX, ntty);
+ if (!(ptym->output_mutex = CreateMutex (&sec_all, FALSE, buf)))
+ {
+ termios_printf ("can't create %s", buf);
+ set_errno (ENOENT);
+ return false;
+ }
+
+ shared_name (buf, INPUT_MUTEX, ntty);
+ if (!(ptym->input_mutex = CreateMutex (&sec_all, FALSE, buf)))
+ {
+ termios_printf ("can't create %s", buf);
+ set_errno (ENOENT);
+ return false;
+ }
+
+ // /* screws up tty master */ ProtectHandle1INH (ptym->output_mutex, output_mutex);
+ // /* screws up tty master */ ProtectHandle1INH (ptym->input_mutex, input_mutex);
+ winsize.ws_col = 80;
+ winsize.ws_row = 25;
+
+ termios_printf ("tty%d opened", ntty);
+ return true;
+}