From d2ad946be65be041c538d6cbc0769d1ee526f92f Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 9 Nov 2016 08:00:11 -0500 Subject: [PATCH] [process] Allocate a handle even for non-child processes (#3905) * [w32handle] Inline only use of mono_w32handle_search * [w32handle] Make special_wait return MonoW32HandleWaitRet This is to avoid a unnecessary translation from WAIT_* to MonoW32HandleWaitRet * [process] Use some more referencesource * [process] Rename mono_processes to processes * [process] Allocate a handle even for non-child processes * [process] Create the handle only once the process has started --- .../System/System.Diagnostics/Process.cs | 55 +- .../monitoring/system/diagnosticts/Process.cs | 2 + mono/metadata/w32handle.c | 79 +-- mono/metadata/w32handle.h | 23 +- mono/metadata/w32process-unix.c | 518 ++++++++---------- 5 files changed, 265 insertions(+), 412 deletions(-) diff --git a/mcs/class/System/System.Diagnostics/Process.cs b/mcs/class/System/System.Diagnostics/Process.cs index 455d60b0d22..74f1990f516 100644 --- a/mcs/class/System/System.Diagnostics/Process.cs +++ b/mcs/class/System/System.Diagnostics/Process.cs @@ -469,16 +469,6 @@ namespace System.Diagnostics [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static IntPtr GetProcess_internal(int pid); - public static Process GetProcessById(int processId) - { - IntPtr proc = GetProcess_internal(processId); - - if (proc == IntPtr.Zero) - throw new ArgumentException ("Can't find process with ID " + processId.ToString ()); - - return (new Process (new SafeProcessHandle (proc, false), processId)); - } - [MonoTODO ("There is no support for retrieving process information from a remote machine")] public static Process GetProcessById(int processId, string machineName) { if (machineName == null) @@ -487,34 +477,17 @@ namespace System.Diagnostics if (!IsLocalMachine (machineName)) throw new NotImplementedException (); - return GetProcessById (processId); + IntPtr proc = GetProcess_internal(processId); + + if (proc == IntPtr.Zero) + throw new ArgumentException ("Can't find process with ID " + processId.ToString ()); + + return (new Process (new SafeProcessHandle (proc, false), processId)); } [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static int[] GetProcesses_internal(); - public static Process[] GetProcesses () - { - int [] pids = GetProcesses_internal (); - if (pids == null) - return new Process [0]; - - var proclist = new List (pids.Length); - for (int i = 0; i < pids.Length; i++) { - try { - proclist.Add (GetProcessById (pids [i])); - } catch (SystemException) { - /* The process might exit - * between - * GetProcesses_internal and - * GetProcessById - */ - } - } - - return proclist.ToArray (); - } - [MonoTODO ("There is no support for retrieving process information from a remote machine")] public static Process[] GetProcesses(string machineName) { if (machineName == null) @@ -523,21 +496,14 @@ namespace System.Diagnostics if (!IsLocalMachine (machineName)) throw new NotImplementedException (); - return GetProcesses (); - } - - public static Process[] GetProcessesByName(string processName) - { int [] pids = GetProcesses_internal (); if (pids == null) return new Process [0]; - + var proclist = new List (pids.Length); for (int i = 0; i < pids.Length; i++) { try { - Process p = GetProcessById (pids [i]); - if (String.Compare (processName, p.ProcessName, true) == 0) - proclist.Add (p); + proclist.Add (GetProcessById (pids [i])); } catch (SystemException) { /* The process might exit * between @@ -550,11 +516,6 @@ namespace System.Diagnostics return proclist.ToArray (); } - [MonoTODO] - public static Process[] GetProcessesByName(string processName, string machineName) { - throw new NotImplementedException(); - } - private static bool IsLocalMachine (string machineName) { if (machineName == "." || machineName.Length == 0) diff --git a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs index 1bae2cf63bf..7c5307fa155 100644 --- a/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs +++ b/mcs/class/referencesource/System/services/monitoring/system/diagnosticts/Process.cs @@ -1618,6 +1618,7 @@ namespace System.Diagnostics { return new Process(machineName, ProcessManager.IsRemoteMachine(machineName), processId, null); } +#endif /// /// @@ -1683,6 +1684,7 @@ namespace System.Diagnostics { return GetProcesses("."); } +#if !MONO /// /// /// Creates a new diff --git a/mono/metadata/w32handle.c b/mono/metadata/w32handle.c index 6e2a7659ab2..e6d1ddb9363 100644 --- a/mono/metadata/w32handle.c +++ b/mono/metadata/w32handle.c @@ -586,57 +586,6 @@ done: mono_os_mutex_unlock (&scan_mutex); } -typedef struct { - MonoW32HandleType type; - gboolean (*search_user_callback)(gpointer handle, gpointer data); - gpointer search_user_data; - gpointer handle; - gpointer handle_specific; -} SearchData; - -static gboolean -search_callback (gpointer handle, gpointer handle_specific, gpointer user_data) -{ - SearchData *search_data = (SearchData*) user_data; - - if (search_data->type != mono_w32handle_get_type (handle)) - return FALSE; - - if (!search_data->search_user_callback (handle, search_data->search_user_data)) - return FALSE; - - mono_w32handle_ref (handle); - search_data->handle = handle; - search_data->handle_specific = handle_specific; - return TRUE; -} - -/* This might list some shared handles twice if they are already - * opened by this process, and the check function returns FALSE the - * first time. Shared handles that are created during the search are - * unreffed if the check function returns FALSE, so callers must not - * rely on the handle persisting (unless the check function returns - * TRUE) - * The caller owns the returned handle. - */ -gpointer mono_w32handle_search (MonoW32HandleType type, - gboolean (*check)(gpointer test, gpointer user), - gpointer user_data, - gpointer *handle_specific, - gboolean search_shared) -{ - SearchData search_data; - - memset (&search_data, 0, sizeof (search_data)); - search_data.type = type; - search_data.search_user_callback = check; - search_data.search_user_data = user_data; - mono_w32handle_foreach (search_callback, &search_data); - if (handle_specific) - *handle_specific = search_data.handle_specific; - return search_data.handle; -} - static gboolean mono_w32handle_ref_core (gpointer handle, MonoW32HandleBase *handle_data) { @@ -876,7 +825,8 @@ gboolean mono_w32handle_ops_isowned (gpointer handle) } } -guint32 mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted) +MonoW32HandleWaitRet +mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted) { MonoW32HandleBase *handle_data; MonoW32HandleType type; @@ -1182,30 +1132,7 @@ mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable) mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_W32HANDLE, "%s: handle %p has special wait", __func__, handle); - switch (mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL)) { - case WAIT_OBJECT_0: - ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0; - break; - case WAIT_ABANDONED_0: - ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0; - break; - case WAIT_IO_COMPLETION: - ret = MONO_W32HANDLE_WAIT_RET_ALERTED; - break; - case WAIT_TIMEOUT: - ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; - break; - case WAIT_FAILED: - ret = MONO_W32HANDLE_WAIT_RET_FAILED; - break; - default: - g_assert_not_reached (); - } - - if (alerted) - ret = MONO_W32HANDLE_WAIT_RET_ALERTED; - - return ret; + return mono_w32handle_ops_specialwait (handle, timeout, alertable ? &alerted : NULL); } if (!mono_w32handle_test_capabilities (handle, MONO_W32HANDLE_CAP_WAIT)) { diff --git a/mono/metadata/w32handle.h b/mono/metadata/w32handle.h index 92cf87ba119..b67ada79afc 100644 --- a/mono/metadata/w32handle.h +++ b/mono/metadata/w32handle.h @@ -29,6 +29,14 @@ typedef enum { MONO_W32HANDLE_COUNT } MonoW32HandleType; +typedef enum { + MONO_W32HANDLE_WAIT_RET_SUCCESS_0 = 0, + MONO_W32HANDLE_WAIT_RET_ABANDONED_0 = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS, + MONO_W32HANDLE_WAIT_RET_ALERTED = -1, + MONO_W32HANDLE_WAIT_RET_TIMEOUT = -2, + MONO_W32HANDLE_WAIT_RET_FAILED = -3, +} MonoW32HandleWaitRet; + typedef struct { void (*close)(gpointer handle, gpointer data); @@ -55,7 +63,7 @@ typedef struct * instead of using the normal handle signal mechanism. * Returns the WaitForSingleObject return code. */ - guint32 (*special_wait)(gpointer handle, guint32 timeout, gboolean *alerted); + MonoW32HandleWaitRet (*special_wait)(gpointer handle, guint32 timeout, gboolean *alerted); /* Called by WaitForSingleObject and WaitForMultipleObjects, * if the handle in question needs some preprocessing before the @@ -103,9 +111,6 @@ mono_w32handle_get_type (gpointer handle); gboolean mono_w32handle_lookup (gpointer handle, MonoW32HandleType type, gpointer *handle_specific); -gpointer -mono_w32handle_search (MonoW32HandleType type, gboolean (*check)(gpointer, gpointer), gpointer user_data, gpointer *handle_specific, gboolean search_shared); - void mono_w32handle_foreach (gboolean (*on_each)(gpointer handle, gpointer data, gpointer user_data), gpointer user_data); @@ -136,7 +141,7 @@ mono_w32handle_ops_own (gpointer handle, guint32 *statuscode); gboolean mono_w32handle_ops_isowned (gpointer handle); -guint32 +MonoW32HandleWaitRet mono_w32handle_ops_specialwait (gpointer handle, guint32 timeout, gboolean *alerted); void @@ -166,14 +171,6 @@ mono_w32handle_trylock_handle (gpointer handle); int mono_w32handle_unlock_handle (gpointer handle); -typedef enum { - MONO_W32HANDLE_WAIT_RET_SUCCESS_0 = 0, - MONO_W32HANDLE_WAIT_RET_ABANDONED_0 = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS, - MONO_W32HANDLE_WAIT_RET_ALERTED = -1, - MONO_W32HANDLE_WAIT_RET_TIMEOUT = -2, - MONO_W32HANDLE_WAIT_RET_FAILED = -3, -} MonoW32HandleWaitRet; - MonoW32HandleWaitRet mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable); diff --git a/mono/metadata/w32process-unix.c b/mono/metadata/w32process-unix.c index f4ae2a5b09f..82663cacb22 100644 --- a/mono/metadata/w32process-unix.c +++ b/mono/metadata/w32process-unix.c @@ -93,18 +93,6 @@ static char *mono_environ[1] = { NULL }; extern char **environ; #endif -/* - * Handles > _WAPI_PROCESS_UNHANDLED are pseudo handles which represent processes - * not started by the runtime. - */ -/* This marks a system process that we don't have a handle on */ -/* FIXME: Cope with PIDs > sizeof guint */ -#define _WAPI_PROCESS_UNHANDLED (1 << (8*sizeof(pid_t)-1)) - -#define WAPI_IS_PSEUDO_PROCESS_HANDLE(handle) ((GPOINTER_TO_UINT(handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) -#define WAPI_PID_TO_HANDLE(pid) GINT_TO_POINTER (_WAPI_PROCESS_UNHANDLED + (pid)) -#define WAPI_HANDLE_TO_PID(handle) (GPOINTER_TO_UINT ((handle)) - _WAPI_PROCESS_UNHANDLED) - typedef enum { STARTF_USESHOWWINDOW=0x001, STARTF_USESIZE=0x002, @@ -157,6 +145,7 @@ struct _MonoProcess { /* MonoW32HandleProcess is a structure containing all the required information for process handling. */ typedef struct { pid_t id; + gboolean child; guint32 exitstatus; gpointer main_thread; WapiFileTime create_time; @@ -174,9 +163,9 @@ static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIA static gchar *cli_launcher; -/* The signal-safe logic to use mono_processes goes like this: +/* The signal-safe logic to use processes goes like this: * - The list must be safe to traverse for the signal handler at all times. - * It's safe to: prepend an entry (which is a single store to 'mono_processes'), + * It's safe to: prepend an entry (which is a single store to 'processes'), * unlink an entry (assuming the unlinked entry isn't freed and doesn't * change its 'next' pointer so that it can still be traversed). * When cleaning up we first unlink an entry, then we verify that @@ -186,9 +175,8 @@ static gchar *cli_launcher; * We also need to lock when adding and cleaning up so that those two * operations don't mess with eachother. (This lock is not used in the * signal handler) */ -static MonoProcess *mono_processes; -static mono_mutex_t mono_processes_mutex; -static volatile gint32 mono_processes_cleaning_up; +static MonoProcess *processes; +static mono_mutex_t processes_mutex; static gpointer current_process; @@ -197,6 +185,33 @@ static const gunichar2 *utf16_space = utf16_space_bytes; static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 }; static const gunichar2 *utf16_quote = utf16_quote_bytes; +/* Check if a pid is valid - i.e. if a process exists with this pid. */ +static gboolean +process_is_alive (pid_t pid) +{ +#if defined(HOST_WATCHOS) + return TRUE; // TODO: Rewrite using sysctl +#elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__) + if (pid == 0) + return FALSE; + if (kill (pid, 0) == 0) + return TRUE; + if (errno == EPERM) + return TRUE; + return FALSE; +#elif defined(__HAIKU__) + team_info teamInfo; + if (get_team_info ((team_id)pid, &teamInfo) == B_OK) + return TRUE; + return FALSE; +#else + gchar *dir = g_strdup_printf ("/proc/%d", pid); + gboolean result = access (dir, F_OK) == 0; + g_free (dir); + return result; +#endif +} + static void process_details (gpointer data) { @@ -217,7 +232,7 @@ process_typesize (void) return sizeof (MonoW32HandleProcess); } -static guint32 +static MonoW32HandleWaitRet process_wait (gpointer handle, guint32 timeout, gboolean *alerted) { MonoW32HandleProcess *process_handle; @@ -227,10 +242,6 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) MonoProcess *mp; gboolean res; - /* FIXME: We can now easily wait on processes that aren't our own children, - * but WaitFor*Object won't call us for pseudo handles. */ - g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout); if (alerted) @@ -239,49 +250,58 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); if (!res) { g_warning ("%s: error looking up process handle %p", __func__, handle); - return WAIT_FAILED; + return MONO_W32HANDLE_WAIT_RET_FAILED; } if (process_handle->exited) { /* We've already done this one */ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Process already exited", __func__, handle, timeout); - return WAIT_OBJECT_0; + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; } pid = process_handle->id; + if (pid == mono_process_current_pid ()) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout); + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid); - /* We don't need to lock mono_processes here, the entry + /* We don't need to lock processes here, the entry * has a handle_count > 0 which means it will not be freed. */ mp = process_handle->mono_process; if (!mp) { pid_t res; - if (pid == mono_process_current_pid ()) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout); - return WAIT_TIMEOUT; - } - /* This path is used when calling Process.HasExited, so * it is only used to poll the state of the process, not * to actually wait on it to exit */ g_assert (timeout == 0); + /* We alway create a MonoProcess for a child process */ + g_assert (!process_handle->child); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout); res = waitpid (pid, &status, WNOHANG); - if (res == 0) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process WAIT_TIMEOUT", __func__, handle, timeout); - return WAIT_TIMEOUT; - } - if (res > 0) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully", __func__, handle, timeout); - return WAIT_OBJECT_0; + if (res != -1) + g_error ("%s: a process handle without a mono_process is not a child process", __func__); + + if (errno == ECHILD) { + if (!process_is_alive (pid)) { + /* assume the process had exited */ + process_handle->exited = TRUE; + process_handle->exitstatus = -1; + mono_w32handle_set_signal_state (handle, TRUE, TRUE); + } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully (2)", __func__, handle, timeout); + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; } - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process WAIT_FAILED, error : %s (%d))", __func__, handle, timeout, g_strerror (errno), errno); - return WAIT_FAILED; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process wait failed, error : %s (%d))", __func__, handle, timeout, g_strerror (errno), errno); + return MONO_W32HANDLE_WAIT_RET_FAILED; } start = mono_msec_ticks (); @@ -305,27 +325,27 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) } if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): WAIT_TIMEOUT (timeout = 0)", __func__, handle, timeout); - return WAIT_TIMEOUT; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout (timeout = 0)", __func__, handle, timeout); + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; } now = mono_msec_ticks (); if (now - start >= timeout) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): WAIT_TIMEOUT", __func__, handle, timeout); - return WAIT_TIMEOUT; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout", __func__, handle, timeout); + return MONO_W32HANDLE_WAIT_RET_TIMEOUT; } if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): WAIT_IO_COMPLETION", __func__, handle, timeout); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait alerted", __func__, handle, timeout); *alerted = TRUE; - return WAIT_IO_COMPLETION; + return MONO_W32HANDLE_WAIT_RET_ALERTED; } } /* Process must have exited */ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout); - status = mp ? mp->status : 0; + status = mp->status; if (WIFSIGNALED (status)) process_handle->exitstatus = 128 + WTERMSIG (status); else @@ -339,12 +359,13 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) mono_w32handle_set_signal_state (handle, TRUE, TRUE); - return WAIT_OBJECT_0; + return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; } static void processes_cleanup (void) { + static gint32 cleaning_up; MonoProcess *mp; MonoProcess *prev = NULL; GSList *finished = NULL; @@ -354,31 +375,31 @@ processes_cleanup (void) mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__); /* Ensure we're not in here in multiple threads at once, nor recursive. */ - if (InterlockedCompareExchange (&mono_processes_cleaning_up, 1, 0) != 0) + if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0) return; - for (mp = mono_processes; mp; mp = mp->next) { + for (mp = 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_os_mutex_lock (&mono_processes_mutex); + mono_os_mutex_lock (&processes_mutex); unref_handle = mp->handle; mp->handle = NULL; - mono_os_mutex_unlock (&mono_processes_mutex); + mono_os_mutex_unlock (&processes_mutex); if (unref_handle) mono_w32handle_unref (unref_handle); } } /* - * Remove processes which exited from the mono_processes list. + * Remove processes which exited from the processes list. * We need to synchronize with the sigchld handler here, which runs - * asynchronously. The handler requires that the mono_processes list + * asynchronously. The handler requires that the processes list * remain valid. */ - mono_os_mutex_lock (&mono_processes_mutex); + mono_os_mutex_lock (&processes_mutex); - mp = mono_processes; + mp = processes; while (mp) { if (mp->handle_count == 0 && mp->freeable) { /* @@ -386,8 +407,8 @@ processes_cleanup (void) * This code can run parallel with the sigchld handler, but the * modifications it makes are safe. */ - if (mp == mono_processes) - mono_processes = mp->next; + if (mp == processes) + processes = mp->next; else prev->next = mp->next; finished = g_slist_prepend (finished, mp); @@ -403,7 +424,7 @@ processes_cleanup (void) for (l = finished; l; l = l->next) { /* - * All the entries in the finished list are unlinked from mono_processes, and + * All the entries in the finished list are unlinked from processes, and * they have the 'finished' flag set, which means the sigchld handler is done * accessing them. */ @@ -413,11 +434,11 @@ processes_cleanup (void) } g_slist_free (finished); - mono_os_mutex_unlock (&mono_processes_mutex); + mono_os_mutex_unlock (&processes_mutex); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__); - InterlockedDecrement (&mono_processes_cleaning_up); + InterlockedExchange (&cleaning_up, 0); } static void @@ -495,7 +516,7 @@ mono_w32process_init (void) current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle); g_assert (current_process); - mono_os_mutex_init (&mono_processes_mutex); + mono_os_mutex_init (&processes_mutex); } void @@ -504,31 +525,6 @@ mono_w32process_cleanup (void) g_free (cli_launcher); } -/* Check if a pid is valid - i.e. if a process exists with this pid. */ -static gboolean -is_pid_valid (pid_t pid) -{ - gboolean result = FALSE; - -#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__) - team_info teamInfo; - if (get_team_info ((team_id)pid, &teamInfo) == B_OK) - result = TRUE; -#else - char *dir = g_strdup_printf ("/proc/%d", pid); - if (!access (dir, F_OK)) - result = TRUE; - g_free (dir); -#endif - - return result; -} - static int len16 (const gunichar2 *str) { @@ -579,11 +575,6 @@ mono_w32process_get_pid (gpointer handle) MonoW32HandleProcess *process_handle; gboolean res; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) { - /* This is a pseudo handle */ - return WAPI_HANDLE_TO_PID (handle); - } - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); if (!res) { SetLastError (ERROR_INVALID_HANDLE); @@ -593,58 +584,84 @@ mono_w32process_get_pid (gpointer handle) return process_handle->id; } +typedef struct { + guint32 pid; + gpointer handle; +} GetProcessForeachData; + static gboolean -process_open_compare (gpointer handle, gpointer user_data) +get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointer user_data) { - gboolean res; + GetProcessForeachData *foreach_data; MonoW32HandleProcess *process_handle; - pid_t wanted_pid, checking_pid; + pid_t pid; - g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)); + foreach_data = (GetProcessForeachData*) user_data; - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) - g_error ("%s: unknown process handle %p", __func__, handle); + if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS) + return FALSE; + + process_handle = (MonoW32HandleProcess*) handle_specific; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id); - checking_pid = process_handle->id; - if (checking_pid == 0) + pid = process_handle->id; + if (pid == 0) return FALSE; - wanted_pid = GPOINTER_TO_UINT (user_data); - /* It's possible to have more than one process handle with the * same pid, but only the one running process can be - * unsignalled. - * If the handle is blown away in the window between - * returning TRUE here and mono_w32handle_search pinging - * the timestamp, the search will continue. */ - return checking_pid == wanted_pid && !mono_w32handle_issignalled (handle); + * unsignalled. */ + if (foreach_data->pid != pid) + return FALSE; + if (mono_w32handle_issignalled (handle)) + return FALSE; + + mono_w32handle_ref (handle); + foreach_data->handle = handle; + return TRUE; } HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) { + GetProcessForeachData foreach_data; gpointer handle; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for process %d", __func__, pid); - handle = mono_w32handle_search (MONO_W32HANDLE_PROCESS, process_open_compare, GUINT_TO_POINTER (pid), NULL, TRUE); + memset (&foreach_data, 0, sizeof (foreach_data)); + foreach_data.pid = pid; + mono_w32handle_foreach (get_process_foreach_callback, &foreach_data); + handle = foreach_data.handle; if (handle) { - /* mono_w32handle_search () already added a ref */ + /* get_process_foreach_callback already added a ref */ return handle; } - if (is_pid_valid (pid)) { - /* Return a pseudo handle for processes we don't have handles for */ - return WAPI_PID_TO_HANDLE (pid); - } else { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid); + if (process_is_alive (pid)) { + /* non-child process */ + MonoW32HandleProcess process_handle; - SetLastError (ERROR_PROC_NOT_FOUND); - return NULL; + memset (&process_handle, 0, sizeof (process_handle)); + process_handle.id = pid; + process_handle.proc_name = mono_w32process_get_name (pid); + + handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating process handle", __func__); + + SetLastError (ERROR_OUTOFMEMORY); + return NULL; + } + + return handle; } + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid); + + SetLastError (ERROR_PROC_NOT_FOUND); + return NULL; } static gboolean @@ -716,20 +733,15 @@ mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 si if (size < sizeof(gpointer)) return FALSE; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) { - pid = WAPI_HANDLE_TO_PID (process); - proc_name = mono_w32process_get_name (pid); - } else { - res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process); - return FALSE; - } - - pid = process_handle->id; - proc_name = g_strdup (process_handle->proc_name); + res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process); + return FALSE; } + pid = process_handle->id; + proc_name = g_strdup (process_handle->proc_name); + if (!proc_name) { modules[0] = NULL; *needed = sizeof(gpointer); @@ -844,21 +856,15 @@ mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *b if (basename == NULL || size == 0) return 0; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) { - /* This is a pseudo handle */ - pid = (pid_t)WAPI_HANDLE_TO_PID (process); - proc_name = mono_w32process_get_name (pid); - } else { - res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process); - return 0; - } - - pid = process_handle->id; - proc_name = g_strdup (process_handle->proc_name); + res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process); + return 0; } + pid = process_handle->id; + proc_name = g_strdup (process_handle->proc_name); + mods = mono_w32process_get_modules (pid); if (!mods) { g_free (proc_name); @@ -948,20 +954,15 @@ mono_w32process_module_get_information (gpointer process, gpointer module, WapiM if (modinfo == NULL || size < sizeof (WapiModuleInfo)) return FALSE; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) { - pid = (pid_t)WAPI_HANDLE_TO_PID (process); - proc_name = mono_w32process_get_name (pid); - } else { - res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process); - return FALSE; - } - - pid = process_handle->id; - proc_name = g_strdup (process_handle->proc_name); + res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process); + return FALSE; } + pid = process_handle->id; + proc_name = g_strdup (process_handle->proc_name); + mods = mono_w32process_get_modules (pid); if (!mods) { g_free (proc_name); @@ -1026,18 +1027,17 @@ MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, sigi /* * 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) { - break; - } - } - if (p) { + for (p = processes; p; p = p->next) { + if (p->pid != pid) + continue; + p->pid = 0; /* this pid doesn't exist anymore, clear it */ p->status = status; mono_os_sem_post (&p->exit_sem); mono_memory_barrier (); /* Mark this as freeable, the pointer becomes invalid afterwards */ p->freeable = TRUE; + break; } } while (1); } @@ -1223,15 +1223,12 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new guint32 i, env_count = 0; gboolean ret = FALSE; gpointer handle = NULL; - MonoW32HandleProcess process_handle = {0}, *process_handle_data; GError *gerr = NULL; int in_fd, out_fd, err_fd; pid_t pid = 0; int startup_pipe [2] = {-1, -1}; int dummy; MonoProcess *mono_process; - gboolean fork_failed = FALSE; - gboolean res; #if HAVE_SIGACTION mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler); @@ -1529,19 +1526,6 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE)); } - process_handle.proc_name = g_strdup (prog); - - process_set_defaults (&process_handle); - - handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle); - if (handle == INVALID_HANDLE_VALUE) { - g_warning ("%s: error creating process handle", __func__); - - ret = FALSE; - SetLastError (ERROR_OUTOFMEMORY); - goto free_strings; - } - /* new_environ is a block of NULL-terminated strings, which * is itself NULL-terminated. Of course, passing an array of * string pointers would have made things too easy :-( @@ -1598,7 +1582,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new } /* Create a pipe to make sure the child doesn't exit before - * we can add the process to the linked list of mono_processes */ + * we can add the process to the linked list of processes */ if (pipe (startup_pipe) == -1) { /* Could not create the pipe to synchroniz process startup. We'll just not synchronize. * This is just for a very hard to hit race condition in the first place */ @@ -1606,11 +1590,14 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: new process startup not synchronized. We may not notice if the newly created process exits immediately.", __func__); } +#if HAVE_SIGACTION + /* FIXME: block SIGCHLD */ +#endif + switch (pid = fork ()) { case -1: /* Error */ { SetLastError (ERROR_OUTOFMEMORY); ret = FALSE; - fork_failed = TRUE; break; } case 0: /* Child */ { @@ -1658,47 +1645,60 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new break; } default: /* Parent */ { - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle_data); - if (!res) { - g_warning ("%s: error looking up process handle %p", __func__, handle); - mono_w32handle_unref (handle); - } else { - process_handle_data->id = pid; + MonoW32HandleProcess process_handle; - /* Add our mono_process into the linked list of mono_processes */ - mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess)); - mono_process->pid = pid; - mono_process->handle_count = 1; - mono_os_sem_init (&mono_process->exit_sem, 0); + memset (&process_handle, 0, sizeof (process_handle)); + process_handle.id = pid; + process_handle.child = TRUE; + process_handle.proc_name = g_strdup (prog); + process_set_defaults (&process_handle); - /* Keep the process handle artificially alive until the process - * exits so that the information in the handle isn't lost. */ - mono_w32handle_ref (handle); - mono_process->handle = handle; + /* Add our mono_process into the linked list of processes */ + mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess)); + mono_process->pid = pid; + mono_process->handle_count = 1; + mono_os_sem_init (&mono_process->exit_sem, 0); - process_handle_data->mono_process = mono_process; + process_handle.mono_process = mono_process; - mono_os_mutex_lock (&mono_processes_mutex); - mono_process->next = mono_processes; - mono_processes = mono_process; - mono_os_mutex_unlock (&mono_processes_mutex); + handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle); + if (handle == INVALID_HANDLE_VALUE) { + g_warning ("%s: error creating process handle", __func__); - if (process_info != NULL) { - process_info->process_handle = handle; - process_info->pid = pid; + mono_os_sem_destroy (&mono_process->exit_sem); + g_free (mono_process); - /* FIXME: we might need to handle the thread info some day */ - process_info->thread_handle = INVALID_HANDLE_VALUE; - process_info->tid = 0; - } + SetLastError (ERROR_OUTOFMEMORY); + ret = FALSE; + break; + } + + /* Keep the process handle artificially alive until the process + * exits so that the information in the handle isn't lost. */ + mono_w32handle_ref (handle); + mono_process->handle = handle; + + mono_os_mutex_lock (&processes_mutex); + mono_process->next = processes; + processes = mono_process; + mono_os_mutex_unlock (&processes_mutex); + + if (process_info != NULL) { + process_info->process_handle = handle; + process_info->pid = pid; + + /* FIXME: we might need to handle the thread info some day */ + process_info->thread_handle = INVALID_HANDLE_VALUE; + process_info->tid = 0; } break; } } - if (fork_failed) - mono_w32handle_unref (handle); +#if HAVE_SIGACTION + /* FIXME: unblock SIGCHLD */ +#endif if (startup_pipe [1] != -1) { /* Write 1 byte, doesn't matter what */ @@ -2029,25 +2029,11 @@ MonoBoolean ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode) { MonoW32HandleProcess *process_handle; - guint32 pid; gboolean res; if (!exitcode) return FALSE; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) { - pid = WAPI_HANDLE_TO_PID (handle); - /* This is a pseudo handle, so we don't know what the exit - * code was, but we can check whether it's alive or not */ - if (is_pid_valid (pid)) { - *exitcode = STILL_ACTIVE; - return TRUE; - } else { - *exitcode = -1; - return TRUE; - } - } - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); if (!res) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); @@ -2072,8 +2058,6 @@ ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gin MonoBoolean ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle) { - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) - return TRUE; return CloseHandle (handle); } @@ -2084,23 +2068,17 @@ ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint3 MonoW32HandleProcess *process_handle; int ret; pid_t pid; + gboolean res; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) { - /* This is a pseudo handle */ - pid = (pid_t)WAPI_HANDLE_TO_PID (handle); - } else { - gboolean res; - - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); - SetLastError (ERROR_INVALID_HANDLE); - return FALSE; - } - - pid = process_handle->id; + res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); + SetLastError (ERROR_INVALID_HANDLE); + return FALSE; } + pid = process_handle->id; + ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM); if (ret == 0) return TRUE; @@ -2127,15 +2105,15 @@ ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handl if (!min || !max) return FALSE; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) - return FALSE; - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); if (!res) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); return FALSE; } + if (!process_handle->child) + return FALSE; + *min = process_handle->min_working_set; *max = process_handle->max_working_set; return TRUE; @@ -2147,15 +2125,15 @@ ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handl MonoW32HandleProcess *process_handle; gboolean res; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) - return FALSE; - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); if (!res) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); return FALSE; } + if (!process_handle->child) + return FALSE; + process_handle->min_working_set = min; process_handle->max_working_set = max; return TRUE; @@ -2168,22 +2146,16 @@ ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle) MonoW32HandleProcess *process_handle; gint ret; pid_t pid; + gboolean res; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) { - /* This is a pseudo handle */ - pid = (pid_t)WAPI_HANDLE_TO_PID (handle); - } else { - gboolean res; - - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - SetLastError (ERROR_INVALID_HANDLE); - return 0; - } - - pid = process_handle->id; + res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + SetLastError (ERROR_INVALID_HANDLE); + return 0; } + pid = process_handle->id; + errno = 0; ret = getpriority (PRIO_PROCESS, pid); if (ret == -1 && errno != 0) { @@ -2229,22 +2201,16 @@ ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint3 int ret; int prio; pid_t pid; + gboolean res; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) { - /* This is a pseudo handle */ - pid = (pid_t)WAPI_HANDLE_TO_PID (handle); - } else { - gboolean res; - - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - SetLastError (ERROR_INVALID_HANDLE); - return FALSE; - } - - pid = process_handle->id; + res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + SetLastError (ERROR_INVALID_HANDLE); + return FALSE; } + pid = process_handle->id; + switch (priorityClass) { case MONO_W32PROCESS_PRIORITY_CLASS_IDLE: prio = 19; @@ -2327,10 +2293,16 @@ ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 memset (kernel_processtime, 0, sizeof (ProcessTime)); memset (user_processtime, 0, sizeof (ProcessTime)); - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) { + res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); + return FALSE; + } + + if (!process_handle->child) { gint64 start_ticks, user_ticks, kernel_ticks; - mono_process_get_times (GINT_TO_POINTER (WAPI_HANDLE_TO_PID (handle)), + mono_process_get_times (GINT_TO_POINTER (process_handle->id), &start_ticks, &user_ticks, &kernel_ticks); ticks_to_processtime (start_ticks, creation_processtime); @@ -2339,12 +2311,6 @@ ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 return TRUE; } - res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); - if (!res) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle); - return FALSE; - } - wapifiletime_to_processtime (process_handle->create_time, creation_processtime); /* A process handle is only signalled if the process has -- 2.25.1