From: Ludovic Henry Date: Thu, 16 Feb 2017 18:10:33 +0000 (-0500) Subject: [process] Allocate a handle even for non-child processes (#4393) X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=mono.git;a=commitdiff_plain;h=b6f511dc167873896a163599d2de9ce1e34ecc6c [process] Allocate a handle even for non-child processes (#4393) --- diff --git a/mcs/class/System/Test/System.Diagnostics/ProcessTest.cs b/mcs/class/System/Test/System.Diagnostics/ProcessTest.cs index dcf83695f06..45d578d3f58 100644 --- a/mcs/class/System/Test/System.Diagnostics/ProcessTest.cs +++ b/mcs/class/System/Test/System.Diagnostics/ProcessTest.cs @@ -14,6 +14,7 @@ using System.Diagnostics; using System.IO; using System.Text; using System.Threading; +using System.Runtime.InteropServices; using NUnit.Framework; @@ -1130,5 +1131,57 @@ namespace MonoTests.System.Diagnostics string v = Process.GetProcessById (1).ProcessName; } + + [Test] + public void NonChildProcessWaitForExit () + { + if (!RunningOnUnix) + Assert.Ignore ("accessing parent pid, only available on unix"); + + using (Process process = Process.GetProcessById (getppid ())) + { + Assert.IsFalse (process.WaitForExit (10), "#1"); + Assert.IsFalse (process.HasExited, "#2"); + Assert.Throws(delegate { int exitCode = process.ExitCode; }, "#3"); + + process.Exited += (s, e) => Assert.Fail ("#4"); + + Assert.IsFalse (process.WaitForExit (10), "#5"); + Assert.IsFalse (process.HasExited, "#6"); + Assert.Throws(delegate { int exitCode = process.ExitCode; }, "#7"); + } + } + + [Test] + public void NonChildProcessName () + { + if (!RunningOnUnix) + Assert.Ignore ("accessing parent pid, only available on unix"); + + using (Process process = Process.GetProcessById (getppid ())) + { + string pname = process.ProcessName; + Assert.IsNotNull (pname, "#1"); + AssertHelper.IsNotEmpty (pname, "#2"); + } + } + + [Test] + public void NonChildProcessId () + { + if (!RunningOnUnix) + Assert.Ignore ("accessing parent pid, only available on unix"); + + int ppid; + using (Process process = Process.GetProcessById (ppid = getppid ())) + { + int pid = process.Id; + Assert.AreEqual (ppid, pid, "#1"); + AssertHelper.Greater (pid, 0, "#2"); + } + } + + [DllImport ("libc")] + static extern int getppid(); } } diff --git a/mono/metadata/w32process-unix.c b/mono/metadata/w32process-unix.c index 4b463886f7b..45d45a87452 100644 --- a/mono/metadata/w32process-unix.c +++ b/mono/metadata/w32process-unix.c @@ -98,18 +98,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, @@ -162,6 +150,7 @@ typedef struct _Process { /* MonoW32HandleProcess is a structure containing all the required information for process handling. */ typedef struct { pid_t pid; + gboolean child; guint32 exitstatus; gpointer main_thread; guint64 create_time; @@ -563,6 +552,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) { @@ -593,10 +609,6 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) Process *process; 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 (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout); if (alerted) @@ -616,33 +628,23 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) pid = process_handle->pid; - 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 processes here, the entry - * has a handle_count > 0 which means it will not be freed. */ - process = process_handle->process; - if (!process) { - 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 MONO_W32HANDLE_WAIT_RET_TIMEOUT; - } + 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; + } - /* 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); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid); + if (!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 MONO_W32HANDLE_WAIT_RET_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); + if (!process_is_alive (pid)) { + /* assume the process has 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 is not alive anymore (2)", __func__, handle, timeout); return MONO_W32HANDLE_WAIT_RET_SUCCESS_0; } @@ -650,6 +652,11 @@ process_wait (gpointer handle, guint32 timeout, gboolean *alerted) return MONO_W32HANDLE_WAIT_RET_FAILED; } + /* We don't need to lock processes here, the entry + * has a handle_count > 0 which means it will not be freed. */ + process = process_handle->process; + g_assert (process); + start = mono_msec_ticks (); now = start; @@ -865,31 +872,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) { @@ -940,11 +922,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) { mono_w32error_set_last (ERROR_INVALID_HANDLE); @@ -971,8 +948,6 @@ get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointe if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS) return FALSE; - g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)); - process_handle = (MonoW32HandleProcess*) handle_specific; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->pid); @@ -1011,9 +986,23 @@ ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) 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); + if (process_is_alive (pid)) { + /* non-child process */ + MonoW32HandleProcess process_handle; + + memset (&process_handle, 0, sizeof (process_handle)); + process_handle.pid = pid; + process_handle.pname = 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__); + + mono_w32error_set_last (ERROR_OUTOFMEMORY); + return NULL; + } + + return handle; } mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid); @@ -1091,20 +1080,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); - pname = 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->pid; - pname = g_strdup (process_handle->pname); + 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->pid; + pname = g_strdup (process_handle->pname); + if (!pname) { modules[0] = NULL; *needed = sizeof(gpointer); @@ -1220,21 +1204,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); - pname = 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->pid; - pname = g_strdup (process_handle->pname); + 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->pid; + pname = g_strdup (process_handle->pname); + mods = mono_w32process_get_modules (pid); if (!mods) { g_free (pname); @@ -1320,20 +1298,15 @@ mono_w32process_module_get_information (gpointer process, gpointer module, MODUL if (modinfo == NULL || size < sizeof (MODULEINFO)) return FALSE; - if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) { - pid = (pid_t)WAPI_HANDLE_TO_PID (process); - pname = 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->pid; - pname = g_strdup (process_handle->pname); + 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->pid; + pname = g_strdup (process_handle->pname); + mods = mono_w32process_get_modules (pid); if (!mods) { g_free (pname); @@ -2013,6 +1986,7 @@ process_create (const gunichar2 *appname, const gunichar2 *cmdline, memset (&process_handle, 0, sizeof (process_handle)); process_handle.pid = pid; + process_handle.child = TRUE; process_handle.pname = g_strdup (prog); process_set_defaults (&process_handle); @@ -2347,25 +2321,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); @@ -2390,8 +2350,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 mono_w32handle_close (handle); } @@ -2402,23 +2360,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); - mono_w32error_set_last (ERROR_INVALID_HANDLE); - return FALSE; - } - - pid = process_handle->pid; + 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); + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; } + pid = process_handle->pid; + ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM); if (ret == 0) return TRUE; @@ -2445,15 +2397,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; @@ -2465,15 +2417,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; @@ -2486,22 +2438,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) { - mono_w32error_set_last (ERROR_INVALID_HANDLE); - return 0; - } - - pid = process_handle->pid; + res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return 0; } + pid = process_handle->pid; + errno = 0; ret = getpriority (PRIO_PROCESS, pid); if (ret == -1 && errno != 0) { @@ -2547,22 +2493,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) { - mono_w32error_set_last (ERROR_INVALID_HANDLE); - return FALSE; - } - - pid = process_handle->pid; + res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle); + if (!res) { + mono_w32error_set_last (ERROR_INVALID_HANDLE); + return FALSE; } + pid = process_handle->pid; + switch (priorityClass) { case MONO_W32PROCESS_PRIORITY_CLASS_IDLE: prio = 19; @@ -2638,10 +2578,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->pid), &start_ticks, &user_ticks, &kernel_ticks); ticks_to_processtime (start_ticks, creation_processtime); @@ -2650,12 +2596,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; - } - ticks_to_processtime (process_handle->create_time, creation_processtime); /* A process handle is only signalled if the process has