Merge pull request #1923 from lukaszunity/start-managed-process-fix
[mono.git] / mono / io-layer / processes.c
index 43da2d850cb3253366e5c0da0c76160755b481ae..5f0805878cf7173c6c492460d6254159a2c3e859 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#ifdef HAVE_SIGNAL_H
 #include <signal.h>
-#include <sys/wait.h>
+#endif
 #include <sys/time.h>
-#include <sys/resource.h>
 #include <fcntl.h>
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
 #include <ctype.h>
 
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
 #ifdef HAVE_SYS_MKDEV_H
 #include <sys/mkdev.h>
 #endif
 
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
 /* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
 #ifdef __APPLE__
 #include <TargetConditionals.h>
 #endif
 #endif
 
-#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__)
+#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
 #include <sys/proc.h>
 #include <sys/sysctl.h>
 #  if !defined(__OpenBSD__)
 #    include <sys/utsname.h>
 #  endif
+#  if defined(__FreeBSD__)
+#    include <sys/user.h>  /* struct kinfo_proc */
+#  endif
 #endif
 
 #ifdef PLATFORM_SOLARIS
 #include <mono/utils/mono-membar.h>
 #include <mono/utils/mono-mutex.h>
 #include <mono/utils/mono-signal-handler.h>
+#include <mono/utils/mono-proclib.h>
 
 /* The process' environment strings */
-#if defined(__APPLE__) && !defined (__arm__)
+#if defined(__APPLE__) && !defined (__arm__) && !defined (__aarch64__)
 /* Apple defines this in crt_externs.h but doesn't provide that header for 
  * arm-apple-darwin9.  We'll manually define the symbol on Apple as it does
  * in fact exist on all implementations (so far) 
@@ -103,7 +118,7 @@ static guint32 process_wait (gpointer handle, guint32 timeout, gboolean alertabl
 static void process_close (gpointer handle, gpointer data);
 static gboolean is_pid_valid (pid_t pid);
 
-#if !defined(__OpenBSD__)
+#if !(defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__))
 static FILE *
 open_process_map (int pid, const char *mode);
 #endif
@@ -137,7 +152,6 @@ static void process_add_sigchld_handler (void);
  * signal handler)
  */
 static struct MonoProcess *mono_processes = NULL;
-static volatile gint32 mono_processes_read_lock = 0;
 static volatile gint32 mono_processes_cleaning_up = 0;
 static mono_mutex_t mono_processes_mutex;
 static void mono_processes_cleanup (void);
@@ -164,7 +178,9 @@ is_pid_valid (pid_t pid)
 {
        gboolean result = FALSE;
 
-#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__)
+#if defined(HOST_WATCHOS)
+       result = TRUE; // TODO: Rewrite using sysctl
+#elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
        if (((kill(pid, 0) == 0) || (errno == EPERM)) && pid != 0)
                result = TRUE;
 #elif defined(__HAIKU__)
@@ -489,6 +505,21 @@ CreateProcessWithLogonW (const gunichar2 *username,
        return CreateProcess (appname, cmdline, NULL, NULL, FALSE, create_flags, env, cwd, startup, process_info);
 }
 
+static gboolean
+is_readable_or_executable (const char *prog)
+{
+       struct stat buf;
+       int a = access (prog, R_OK);
+       int b = access (prog, X_OK);
+       if (a != 0 && b != 0)
+               return FALSE;
+       if (stat (prog, &buf))
+               return FALSE;
+       if (S_ISREG (buf.st_mode))
+               return TRUE;
+       return FALSE;
+}
+
 static gboolean
 is_executable (const char *prog)
 {
@@ -537,9 +568,9 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
        int dummy;
        struct MonoProcess *mono_process;
        gboolean fork_failed = FALSE;
-       
+
        mono_once (&process_sig_chld_once, process_add_sigchld_handler);
-       
+
        /* appname and cmdline specify the executable and its args:
         *
         * If appname is not NULL, it is the name of the executable.
@@ -621,7 +652,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        prog = g_strdup (unquoted);
 
                        /* Executable existing ? */
-                       if (!is_executable (prog)) {
+                       if (!is_readable_or_executable (prog)) {
                                DEBUG ("%s: Couldn't find executable %s",
                                           __func__, prog);
                                g_free (unquoted);
@@ -637,8 +668,8 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        prog = g_strdup_printf ("%s/%s", curdir, unquoted);
                        g_free (curdir);
 
-                       /* And make sure it's executable */
-                       if (!is_executable (prog)) {
+                       /* And make sure it's readable */
+                       if (!is_readable_or_executable (prog)) {
                                DEBUG ("%s: Couldn't find executable %s",
                                           __func__, prog);
                                g_free (unquoted);
@@ -729,7 +760,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        prog = g_strdup (token);
                        
                        /* Executable existing ? */
-                       if (!is_executable (prog)) {
+                       if (!is_readable_or_executable (prog)) {
                                DEBUG ("%s: Couldn't find executable %s",
                                           __func__, token);
                                g_free (token);
@@ -750,8 +781,10 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 
                        /* I assume X_OK is the criterion to use,
                         * rather than F_OK
+                        *
+                        * X_OK is too strict *if* the target is a CLR binary
                         */
-                       if (!is_executable (prog)) {
+                       if (!is_readable_or_executable (prog)) {
                                g_free (prog);
                                prog = g_find_program_in_path (token);
                                if (prog == NULL) {
@@ -784,11 +817,11 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 
                if (newapp != NULL) {
                        if (appname != NULL) {
-                               newcmd = utf16_concat (newapp, utf16_space,
+                               newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space,
                                                       appname, utf16_space,
                                                       cmdline, NULL);
                        } else {
-                               newcmd = utf16_concat (newapp, utf16_space,
+                               newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space,
                                                       cmdline, NULL);
                        }
                        
@@ -808,6 +841,12 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                                goto free_strings;
                        }
                }
+       } else {
+               if (!is_executable (prog)) {
+                       DEBUG ("%s: Executable permisson not set on %s", __func__, prog);
+                       SetLastError (ERROR_ACCESS_DENIED);
+                       goto free_strings;
+               }
        }
 
        if (args_after_prog != NULL && *args_after_prog) {
@@ -838,8 +877,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
        }
        
-       g_strlcpy (process_handle.proc_name, prog,
-                  _WAPI_PROC_NAME_MAX_LEN - 1);
+       process_handle.proc_name = g_strdup (prog);
 
        process_set_defaults (&process_handle);
        
@@ -952,7 +990,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                }
                
                /* Close all file descriptors */
-               for (i = getdtablesize () - 1; i > 2; i--)
+               for (i = wapi_getdtablesize () - 1; i > 2; i--)
                        close (i);
 
 #ifdef DEBUG_ENABLED
@@ -1005,7 +1043,6 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                _wapi_handle_ref (handle);
                mono_process->handle = handle;
 
-               process_handle_data->self = _wapi_getpid ();
                process_handle_data->mono_process = mono_process;
 
                mono_mutex_lock (&mono_processes_mutex);
@@ -1076,12 +1113,9 @@ process_set_name (WapiHandle_process *process_handle)
        if (utf8_progname) {
                slash = strrchr (utf8_progname, '/');
                if (slash)
-                       g_strlcpy (process_handle->proc_name, slash+1,
-                                  _WAPI_PROC_NAME_MAX_LEN - 1);
+                       process_handle->proc_name = g_strdup (slash+1);
                else
-                       g_strlcpy (process_handle->proc_name, utf8_progname,
-                                  _WAPI_PROC_NAME_MAX_LEN - 1);
-
+                       process_handle->proc_name = g_strdup (utf8_progname);
                g_free (utf8_progname);
        }
 }
@@ -1139,85 +1173,21 @@ GetProcessId (gpointer handle)
        return process_handle->id;
 }
 
-/* Returns the process id as a convenience to the functions that call this */
-static pid_t
-signal_process_if_gone (gpointer handle)
+static gboolean
+process_open_compare (gpointer handle, gpointer user_data)
 {
+       pid_t wanted_pid;
        WapiHandle_process *process_handle;
-       
+       pid_t checking_pid;
+
        g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
        
-       /* Make sure the process is signalled if it has exited - if
-        * the parent process didn't wait for it then it won't be
-        */
        process_handle = lookup_process_handle (handle);
-       if (!process_handle) {
-               /* It's possible that the handle has vanished during
-                * the _wapi_search_handle before it gets here, so
-                * don't spam the console with warnings.
-                */
-/*             g_warning ("%s: error looking up process handle %p",
-  __func__, handle);*/
-               
-               return 0;
-       }
+       g_assert (process_handle);
        
        DEBUG ("%s: looking at process %d", __func__, process_handle->id);
 
-       if (kill (process_handle->id, 0) == -1 &&
-           (errno == ESRCH ||
-            errno == EPERM)) {
-               /* The process is dead, (EPERM tells us a new process
-                * has that ID, but as it's owned by someone else it
-                * can't be the one listed in our shared memory file)
-                */
-               _wapi_shared_handle_set_signal_state (handle, TRUE);
-       }
-
-       return process_handle->id;
-}
-
-#if 0
-static gboolean
-process_enum (gpointer handle, gpointer user_data)
-{
-       GArray *processes = user_data;
-       pid_t pid = signal_process_if_gone (handle);
-       int i;
-       
-       if (pid == 0)
-               return FALSE;
-       
-       /* Ignore processes that have already exited (ie they are signalled) */
-       if (!_wapi_handle_issignalled (handle)) {
-               DEBUG ("%s: process %d added to array", __func__, pid);
-
-               /* This ensures that duplicates aren't returned (see
-                * the comment above _wapi_search_handle () for why
-                * it's needed
-                */
-               for (i = 0; i < processes->len; i++) {
-                       if (g_array_index (processes, pid_t, i) == pid) {
-                               /* We've already got this one, return
-                                * FALSE to keep searching
-                                */
-                               return FALSE;
-                       }
-               }
-               
-               g_array_append_val (processes, pid);
-       }
-       
-       /* Return false to keep searching */
-       return FALSE;
-}
-#endif /* 0 */
-
-static gboolean
-process_open_compare (gpointer handle, gpointer user_data)
-{
-       pid_t wanted_pid;
-       pid_t checking_pid = signal_process_if_gone (handle);
+       checking_pid = process_handle->id;
 
        if (checking_pid == 0)
                return FALSE;
@@ -1309,7 +1279,12 @@ GetExitCodeProcess (gpointer process, guint32 *code)
                
                return FALSE;
        }
-       
+
+       if (process_handle->id == _wapi_getpid ()) {
+               *code = STILL_ACTIVE;
+               return TRUE;
+       }
+
        /* A process handle is only signalled if the process has exited
         * and has been waited for */
 
@@ -1339,11 +1314,19 @@ GetProcessTimes (gpointer process, WapiFileTime *create_time,
                /* Not sure if w32 allows NULLs here or not */
                return FALSE;
        
-       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process))
-               /* This is a pseudo handle, so just fail for now
-                */
-               return FALSE;
-       
+       if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
+               gpointer pid = GINT_TO_POINTER (WAPI_HANDLE_TO_PID(process));
+               gint64 start_ticks, user_ticks, kernel_ticks;
+
+               mono_process_get_times (pid, &start_ticks, &user_ticks, &kernel_ticks);
+
+               _wapi_guint64_to_filetime (start_ticks, create_time);
+               _wapi_guint64_to_filetime (user_ticks, kernel_time);
+               _wapi_guint64_to_filetime (kernel_ticks, user_time);
+
+               return TRUE;
+       }
+
        process_handle = lookup_process_handle (process);
        if (!process_handle) {
                DEBUG ("%s: Can't find process %p", __func__, process);
@@ -1386,8 +1369,8 @@ typedef struct
        gpointer address_end;
        char *perms;
        gpointer address_offset;
-       dev_t device;
-       ino_t inode;
+       guint64 device;
+       guint64 inode;
        char *filename;
 } WapiProcModule;
 
@@ -1456,7 +1439,7 @@ static GSList *load_modules (void)
                mod->perms = g_strdup ("r--p");
                mod->address_offset = 0;
                mod->device = makedev (0, 0);
-               mod->inode = (ino_t) i;
+               mod->inode = i;
                mod->filename = g_strdup (name); 
                
                if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
@@ -1470,7 +1453,7 @@ static GSList *load_modules (void)
        
        return(ret);
 }
-#elif defined(__OpenBSD__)
+#elif defined(__OpenBSD__) || defined(__FreeBSD__)
 #include <link.h>
 static int load_modules_callback (struct dl_phdr_info *info, size_t size, void *ptr)
 {
@@ -1508,7 +1491,7 @@ static GSList *load_modules (void)
                                        info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr);
                mod->perms = g_strdup ("r--p");
                mod->address_offset = 0;
-               mod->inode = (ino_t) i;
+               mod->inode = i;
                mod->filename = g_strdup (info->dlpi_name); 
 
                DEBUG ("%s: inode=%d, filename=%s, address_start=%p, address_end=%p", __func__,
@@ -1570,8 +1553,8 @@ static GSList *load_modules (FILE *fp)
        char *maj_dev_start, *min_dev_start, *inode_start, prot_buf[5];
        gpointer address_start, address_end, address_offset;
        guint32 maj_dev, min_dev;
-       ino_t inode;
-       dev_t device;
+       guint64 inode;
+       guint64 device;
        
        while (fgets (buf, sizeof(buf), fp)) {
                p = buf;
@@ -1644,7 +1627,7 @@ static GSList *load_modules (FILE *fp)
                if (!g_ascii_isxdigit (*inode_start)) {
                        continue;
                }
-               inode = (ino_t)strtol (inode_start, &endp, 10);
+               inode = (guint64)strtol (inode_start, &endp, 10);
                p = endp;
                if (!g_ascii_isspace (*p)) {
                        continue;
@@ -1692,6 +1675,7 @@ static gboolean match_procname_to_modulename (char *procname, char *modulename)
        if (procname == NULL || modulename == NULL)
                return (FALSE);
 
+       DEBUG ("%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename);
        pname = mono_path_resolve_symlinks (procname);
        mname = mono_path_resolve_symlinks (modulename);
 
@@ -1720,10 +1704,11 @@ static gboolean match_procname_to_modulename (char *procname, char *modulename)
        g_free (pname);
        g_free (mname);
 
+       DEBUG ("%s: result is %d", __func__, result);
        return result;
 }
 
-#if !defined(__OpenBSD__)
+#if !(defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__))
 static FILE *
 open_process_map (int pid, const char *mode)
 {
@@ -1746,11 +1731,13 @@ open_process_map (int pid, const char *mode)
 }
 #endif
 
+static char *get_process_name_from_proc (pid_t pid);
+
 gboolean EnumProcessModules (gpointer process, gpointer *modules,
                             guint32 size, guint32 *needed)
 {
        WapiHandle_process *process_handle;
-#if !defined(__OpenBSD__) && !defined(PLATFORM_MACOSX)
+#if !defined(__OpenBSD__) && !defined(PLATFORM_MACOSX) && !defined(__FreeBSD__)
        FILE *fp;
 #endif
        GSList *mods = NULL;
@@ -1774,6 +1761,7 @@ gboolean EnumProcessModules (gpointer process, gpointer *modules,
 
        if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
                pid = WAPI_HANDLE_TO_PID (process);
+               proc_name = get_process_name_from_proc (pid);
        } else {
                process_handle = lookup_process_handle (process);
                if (!process_handle) {
@@ -1785,7 +1773,7 @@ gboolean EnumProcessModules (gpointer process, gpointer *modules,
                proc_name = process_handle->proc_name;
        }
        
-#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__HAIKU__)
+#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__)
        mods = load_modules ();
        if (!proc_name) {
                modules[0] = NULL;
@@ -1840,7 +1828,7 @@ gboolean EnumProcessModules (gpointer process, gpointer *modules,
 static char *
 get_process_name_from_proc (pid_t pid)
 {
-#if defined(__OpenBSD__)
+#if defined(__OpenBSD__) || defined(__FreeBSD__)
        int mib [6];
        size_t size;
        struct kinfo_proc *pi;
@@ -1898,6 +1886,30 @@ get_process_name_from_proc (pid_t pid)
 
        free(pi);
 #endif
+#elif defined(__FreeBSD__)
+       mib [0] = CTL_KERN;
+       mib [1] = KERN_PROC;
+       mib [2] = KERN_PROC_PID;
+       mib [3] = pid;
+       if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0) {
+               DEBUG ("%s: sysctl() failed: %d", __func__, errno);
+               return(ret);
+       }
+
+       if ((pi = malloc(size)) == NULL)
+               return(ret);
+
+       if (sysctl (mib, 4, pi, &size, NULL, 0) < 0) {
+               if (errno == ENOMEM) {
+                       free(pi);
+                       DEBUG ("%s: Didn't allocate enough memory for kproc info", __func__);
+               }
+               return(ret);
+       }
+
+       if (strlen (pi->ki_comm) > 0)
+               ret = g_strdup (pi->ki_comm);
+       free(pi);
 #elif defined(__OpenBSD__)
        mib [0] = CTL_KERN;
        mib [1] = KERN_PROC;
@@ -1907,8 +1919,10 @@ get_process_name_from_proc (pid_t pid)
        mib [5] = 0;
 
 retry:
-       if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0)
+       if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) {
+               DEBUG ("%s: sysctl() failed: %d", __func__, errno);
                return(ret);
+       }
 
        if ((pi = malloc(size)) == NULL)
                return(ret);
@@ -1924,8 +1938,13 @@ retry:
                return(ret);
        }
 
+#if defined(__OpenBSD__)
        if (strlen (pi->p_comm) > 0)
                ret = g_strdup (pi->p_comm);
+#elif defined(__FreeBSD__)
+       if (strlen (pi->ki_comm) > 0)
+               ret = g_strdup (pi->ki_comm);
+#endif
 
        free(pi);
 #elif defined(__HAIKU__)
@@ -2032,7 +2051,7 @@ get_module_name (gpointer process, gpointer module,
        char *procname_ext = NULL;
        glong len;
        gsize bytes;
-#if !defined(__OpenBSD__) && !defined(PLATFORM_MACOSX)
+#if !defined(__OpenBSD__) && !defined(PLATFORM_MACOSX) && !defined(__FreeBSD__)
        FILE *fp;
 #endif
        GSList *mods = NULL;
@@ -2044,7 +2063,7 @@ get_module_name (gpointer process, gpointer module,
        DEBUG ("%s: Getting module base name, process handle %p module %p",
                   __func__, process, module);
 
-       size = size * sizeof(gunichar2); /* adjust for unicode characters */
+       size = size * sizeof (gunichar2); /* adjust for unicode characters */
 
        if (basename == NULL || size == 0)
                return 0;
@@ -2066,11 +2085,11 @@ get_module_name (gpointer process, gpointer module,
        }
 
        /* Look up the address in /proc/<pid>/maps */
-#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__HAIKU__)
-       {
-               mods = load_modules ();
+#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__)
+       mods = load_modules ();
 #else
-       if ((fp = open_process_map (pid, "r")) == NULL) {
+       fp = open_process_map (pid, "r");
+       if (fp == NULL) {
                if (errno == EACCES && module == NULL && base == TRUE) {
                        procname_ext = get_process_name_from_proc (pid);
                } else {
@@ -2078,47 +2097,45 @@ get_module_name (gpointer process, gpointer module,
                         * for now
                         */
                        g_free (proc_name);
-                       return(0);
+                       return 0;
                }
        } else {
                mods = load_modules (fp);
                fclose (fp);
+       }
 #endif
-               count = g_slist_length (mods);
-
-               /* If module != NULL compare the address.
-                * If module == NULL we are looking for the main module.
-                * The best we can do for now check it the module name end with the process name.
-                */
-               for (i = 0; i < count; i++) {
-                       found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
-                       if (procname_ext == NULL &&
-                           ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||    
-                            (module != NULL && found_module->address_start == module))) {
-                               if (base) {
-                                       procname_ext = g_path_get_basename (found_module->filename);
-                               } else {
-                                       procname_ext = g_strdup (found_module->filename);
-                               }
-                       }
+       count = g_slist_length (mods);
 
-                       free_procmodule (found_module);
+       /* If module != NULL compare the address.
+        * If module == NULL we are looking for the main module.
+        * The best we can do for now check it the module name end with the process name.
+        */
+       for (i = 0; i < count; i++) {
+               found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+               if (procname_ext == NULL &&
+                       ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
+                        (module != NULL && found_module->address_start == module))) {
+                       if (base)
+                               procname_ext = g_path_get_basename (found_module->filename);
+                       else
+                               procname_ext = g_strdup (found_module->filename);
                }
 
-               if (procname_ext == NULL)
-               {
-                       /* If it's *still* null, we might have hit the
-                        * case where reading /proc/$pid/maps gives an
-                        * empty file for this user.
-                        */
-                       procname_ext = get_process_name_from_proc (pid);
-               }
+               free_procmodule (found_module);
+       }
 
-               g_slist_free (mods);
-               g_free (proc_name);
+       if (procname_ext == NULL) {
+               /* If it's *still* null, we might have hit the
+                * case where reading /proc/$pid/maps gives an
+                * empty file for this user.
+                */
+               procname_ext = get_process_name_from_proc (pid);
        }
 
-       if (procname_ext != NULL) {
+       g_slist_free (mods);
+       g_free (proc_name);
+
+       if (procname_ext) {
                DEBUG ("%s: Process name is [%s]", __func__,
                           procname_ext);
 
@@ -2126,7 +2143,7 @@ get_module_name (gpointer process, gpointer module,
                if (procname == NULL) {
                        /* bugger */
                        g_free (procname_ext);
-                       return(0);
+                       return 0;
                }
                
                len = (bytes / 2);
@@ -2148,10 +2165,10 @@ get_module_name (gpointer process, gpointer module,
                g_free (procname);
                g_free (procname_ext);
                
-               return(len);
+               return len;
        }
        
-       return(0);
+       return 0;
 }
 
 guint32
@@ -2174,7 +2191,7 @@ GetModuleInformation (gpointer process, gpointer module,
 {
        WapiHandle_process *process_handle;
        pid_t pid;
-#if !defined(__OpenBSD__) && !defined(PLATFORM_MACOSX)
+#if !defined(__OpenBSD__) && !defined(PLATFORM_MACOSX) && !defined(__FreeBSD__)
        FILE *fp;
 #endif
        GSList *mods = NULL;
@@ -2205,7 +2222,7 @@ GetModuleInformation (gpointer process, gpointer module,
                proc_name = g_strdup (process_handle->proc_name);
        }
 
-#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__HAIKU__)
+#if defined(PLATFORM_MACOSX) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
        mods = load_modules ();
 #else
        /* Look up the address in /proc/<pid>/maps */
@@ -2298,6 +2315,7 @@ SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
 gboolean
 TerminateProcess (gpointer process, gint32 exitCode)
 {
+#if defined(HAVE_KILL)
        WapiHandle_process *process_handle;
        int signo;
        int ret;
@@ -2335,6 +2353,10 @@ TerminateProcess (gpointer process, gint32 exitCode)
        }
        
        return (ret == 0);
+#else
+       g_error ("kill() is not supported by this platform");
+       return FALSE;
+#endif
 }
 
 guint32
@@ -2466,9 +2488,9 @@ mono_processes_cleanup (void)
 {
        struct MonoProcess *mp;
        struct MonoProcess *prev = NULL;
-       struct MonoProcess *candidate = NULL;
+       GSList *finished = NULL;
+       GSList *l;
        gpointer unref_handle;
-       int spin;
 
        DEBUG ("%s", __func__);
 
@@ -2476,9 +2498,8 @@ mono_processes_cleanup (void)
        if (InterlockedCompareExchange (&mono_processes_cleaning_up, 1, 0) != 0)
                return;
 
-       mp = mono_processes;
-       while (mp != NULL) {
-               if (mp->pid == 0 && mp->handle != NULL) {
+       for (mp = mono_processes; mp; mp = mp->next) {
+               if (mp->pid == 0 && mp->handle) {
                        /* This process has exited and we need to remove the artifical ref
                         * on the handle */
                        mono_mutex_lock (&mono_processes_mutex);
@@ -2487,63 +2508,53 @@ mono_processes_cleanup (void)
                        mono_mutex_unlock (&mono_processes_mutex);
                        if (unref_handle)
                                _wapi_handle_unref (unref_handle);
-                       continue;
                }
-               mp = mp->next;
        }
 
-       mp = mono_processes;
-       spin = 0;
-       while (mp != NULL) {
-               if ((mp->handle_count == 0 && mp->pid == 0) || candidate != NULL) {
-                       if (spin > 0) {
-                               _wapi_handle_spin (spin);
-                               spin <<= 1;
-                       }
-
-                       /* We've found a candidate */
-                       mono_mutex_lock (&mono_processes_mutex);
-                       if (candidate == NULL) {
-                               /* unlink it */
-                               if (mp == mono_processes) {
-                                       mono_processes = mp->next;
-                               } else {
-                                       prev->next = mp->next;
-                               }
-                               candidate = mp;
-                       }
+       /*
+        * Remove processes which exited from the mono_processes list.
+        * We need to synchronize with the sigchld handler here, which runs
+        * asynchronously. The handler requires that the mono_processes list
+        * remain valid.
+        */
+       mono_mutex_lock (&mono_processes_mutex);
 
-                       /* It's still safe to traverse the structure.*/
-                       mono_memory_barrier ();
+       mp = mono_processes;
+       while (mp) {
+               if (mp->handle_count == 0 && mp->freeable) {
+                       /*
+                        * Unlink the entry.
+                        * This code can run parallel with the sigchld handler, but the
+                        * modifications it makes are safe.
+                        */
+                       if (mp == mono_processes)
+                               mono_processes = mp->next;
+                       else
+                               prev->next = mp->next;
+                       finished = g_slist_prepend (finished, mp);
 
-                       if (mono_processes_read_lock != 0) {
-                               /* The sigchld handler is watching us. Spin a bit and try again */
-                               if (spin == 0) {
-                                       spin = 1;
-                               } else if (spin >= 8) {
-                                       /* Just give up for now */
-                                       mono_mutex_unlock (&mono_processes_mutex);
-                                       break;
-                               }
-                       } else {
-                               /* We've modified the list of processes, and we know the sigchld handler
-                                * isn't executing, so even if it executes at any moment, it'll see the
-                                * new version of the list. So now we can free the candidate. */
-                               DEBUG ("%s: freeing candidate %p", __func__, candidate);
-                               mp = candidate->next;
-                               MONO_SEM_DESTROY (&candidate->exit_sem);
-                               g_free (candidate);
-                               candidate = NULL;
-                       }
+                       mp = mp->next;
+               } else {
+                       prev = mp;
+                       mp = mp->next;
+               }
+       }
 
-                       mono_mutex_unlock (&mono_processes_mutex);
+       mono_memory_barrier ();
 
-                       continue;
-               }
-               spin = 0;
-               prev = mp;
-               mp = mp->next;
+       for (l = finished; l; l = l->next) {
+               /*
+                * All the entries in the finished list are unlinked from mono_processes, and
+                * they have the 'finished' flag set, which means the sigchld handler is done
+                * accessing them.
+                */
+               mp = l->data;
+               MONO_SEM_DESTROY (&mp->exit_sem);
+               g_free (mp);
        }
+       g_slist_free (finished);
+
+       mono_mutex_unlock (&mono_processes_mutex);
 
        DEBUG ("%s done", __func__);
 
@@ -2558,7 +2569,9 @@ process_close (gpointer handle, gpointer data)
        DEBUG ("%s", __func__);
 
        process_handle = (WapiHandle_process *) data;
-       if (process_handle->mono_process && process_handle->self == _wapi_getpid ())
+       g_free (process_handle->proc_name);
+       process_handle->proc_name = NULL;
+       if (process_handle->mono_process)
                InterlockedDecrement (&process_handle->mono_process->handle_count);
        mono_processes_cleanup ();
 }
@@ -2572,8 +2585,6 @@ MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, sigi
 
        DEBUG ("SIG CHILD handler for pid: %i\n", info->si_pid);
 
-       InterlockedIncrement (&mono_processes_read_lock);
-
        do {
                do {
                        pid = waitpid (-1, &status, WNOHANG);
@@ -2583,20 +2594,25 @@ MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, sigi
                        break;
 
                DEBUG ("child ended: %i", pid);
-               p = mono_processes;
-               while (p != NULL) {
+
+               /*
+                * This can run concurrently with the code in the rest of this module.
+                */
+               for (p = mono_processes; p; p = p->next) {
                        if (p->pid == pid) {
-                               p->pid = 0; /* this pid doesn't exist anymore, clear it */
-                               p->status = status;
-                               MONO_SEM_POST (&p->exit_sem);
                                break;
                        }
-                       p = p->next;
+               }
+               if (p) {
+                       p->pid = 0; /* this pid doesn't exist anymore, clear it */
+                       p->status = status;
+                       MONO_SEM_POST (&p->exit_sem);
+                       mono_memory_barrier ();
+                       /* Mark this as freeable, the pointer becomes invalid afterwards */
+                       p->freeable = TRUE;
                }
        } while (1);
 
-       InterlockedDecrement (&mono_processes_read_lock);
-
        DEBUG ("SIG CHILD handler: done looping.");
 }
 
@@ -2620,19 +2636,11 @@ static guint32
 process_wait (gpointer handle, guint32 timeout, gboolean alertable)
 {
        WapiHandle_process *process_handle;
-       pid_t pid, ret;
+       pid_t pid G_GNUC_UNUSED, ret;
        int status;
        guint32 start;
        guint32 now;
        struct MonoProcess *mp;
-       gboolean spin;
-       gpointer current_thread;
-
-       current_thread = wapi_get_current_thread_handle ();
-       if (!current_thread) {
-               SetLastError (ERROR_INVALID_HANDLE);
-               return WAIT_FAILED;
-       }
 
        /* FIXME: We can now easily wait on processes that aren't our own children,
         * but WaitFor*Object won't call us for pseudo handles. */
@@ -2659,48 +2667,33 @@ process_wait (gpointer handle, guint32 timeout, gboolean alertable)
        /* We don't need to lock mono_processes here, the entry
         * has a handle_count > 0 which means it will not be freed. */
        mp = process_handle->mono_process;
-       if (mp && process_handle->self != _wapi_getpid ()) {
-               /* mono_process points to memory in another process' address space: we can't use it */
-               mp = NULL;
-       }
+       g_assert (mp);
 
        start = mono_msec_ticks ();
        now = start;
-       spin = mp == NULL;
 
        while (1) {
-               if (mp != NULL) {
-                       /* We have a semaphore we can wait on */
-                       if (timeout != INFINITE) {
-                               DEBUG ("%s (%p, %u): waiting on semaphore for %li ms...", 
-                                       __func__, handle, timeout, (timeout - (now - start)));
+               if (timeout != INFINITE) {
+                       DEBUG ("%s (%p, %u): waiting on semaphore for %li ms...", 
+                                  __func__, handle, timeout, (timeout - (now - start)));
 
-                               ret = MONO_SEM_TIMEDWAIT_ALERTABLE (&mp->exit_sem, (timeout - (now - start)), alertable);
-                       } else {
-                               DEBUG ("%s (%p, %u): waiting on semaphore forever...", 
-                                       __func__, handle, timeout);
-                               ret = MONO_SEM_WAIT_ALERTABLE (&mp->exit_sem, alertable);
-                       }
+                       ret = MONO_SEM_TIMEDWAIT_ALERTABLE (&mp->exit_sem, (timeout - (now - start)), alertable);
+               } else {
+                       DEBUG ("%s (%p, %u): waiting on semaphore forever...", 
+                                  __func__, handle, timeout);
+                       ret = MONO_SEM_WAIT_ALERTABLE (&mp->exit_sem, alertable);
+               }
 
-                       if (ret == -1 && errno != EINTR && errno != ETIMEDOUT) {
-                               DEBUG ("%s (%p, %u): sem_timedwait failure: %s", 
-                                       __func__, handle, timeout, g_strerror (errno));
-                               /* Should we return a failure here? */
-                       }
+               if (ret == -1 && errno != EINTR && errno != ETIMEDOUT) {
+                       DEBUG ("%s (%p, %u): sem_timedwait failure: %s", 
+                                  __func__, handle, timeout, g_strerror (errno));
+                       /* Should we return a failure here? */
+               }
 
-                       if (ret == 0) {
-                               /* Success, process has exited */
-                               MONO_SEM_POST (&mp->exit_sem);
-                               break;
-                       }
-               } else {
-                       /* We did not create this process, so we can't waidpid / sem_wait it.
-                        * We need to poll for the pid existence */
-                       DEBUG ("%s (%p, %u): polling on pid...", __func__, handle, timeout);
-                       if (!is_pid_valid (pid)) {
-                               /* Success, process has exited */
-                               break;
-                       }
+               if (ret == 0) {
+                       /* Success, process has exited */
+                       MONO_SEM_POST (&mp->exit_sem);
+                       break;
                }
 
                if (timeout == 0) {
@@ -2713,14 +2706,8 @@ process_wait (gpointer handle, guint32 timeout, gboolean alertable)
                        DEBUG ("%s (%p, %u): WAIT_TIMEOUT", __func__, handle, timeout);
                        return WAIT_TIMEOUT;
                }
-
-               if (spin) {
-                       /* "timeout - (now - start)" will not underflow, since timeout is always >=0,
-                        * and we passed the check just above */
-                       _wapi_handle_spin (MIN (100, timeout - (now - start)));
-               }
                
-               if (alertable && _wapi_thread_apc_pending (current_thread)) {
+               if (alertable && _wapi_thread_cur_apc_pending ()) {
                        DEBUG ("%s (%p, %u): WAIT_IO_COMPLETION", __func__, handle, timeout);
                        return WAIT_IO_COMPLETION;
                }
@@ -2733,11 +2720,10 @@ process_wait (gpointer handle, guint32 timeout, gboolean alertable)
        g_assert (ret == 0);
 
        status = mp ? mp->status : 0;
-       if (WIFSIGNALED (status)) {
+       if (WIFSIGNALED (status))
                process_handle->exitstatus = 128 + WTERMSIG (status);
-       } else {
+       else
                process_handle->exitstatus = WEXITSTATUS (status);
-       }
        _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
 
        process_handle->exited = TRUE;
@@ -2745,7 +2731,7 @@ process_wait (gpointer handle, guint32 timeout, gboolean alertable)
        DEBUG ("%s (%p, %u): Setting pid %d signalled, exit status %d",
                   __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
 
-       _wapi_shared_handle_set_signal_state (handle, TRUE);
+       _wapi_handle_set_signal_state (handle, TRUE, TRUE);
 
        _wapi_handle_unlock_shared_handles ();