-/*
- * process.c: System.Diagnostics.Process support
+/**
+ * \file
+ * System.Diagnostics.Process support
*
* Author:
* Dick Porter (dick@ximian.com)
/*
* Process describes processes we create.
* It contains a semaphore that can be waited on in order to wait
- * for process termination. It's accessed in our SIGCHLD handler,
- * when status is updated (and pid cleared, to not clash with
- * subsequent processes that may get executed).
+ * for process termination.
*/
typedef struct _Process {
pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
* the process has exited, so that the information there isn't lost.
*/
gpointer handle;
- gboolean freeable;
gboolean signalled;
struct _Process *next;
} Process;
static gchar *cli_launcher;
-/* 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 '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
- * the read lock isn't locked. Then we can free the entry, since
- * we know that nobody is using the old version of the list (including
- * the unlinked entry).
- * 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 Process *processes;
static mono_mutex_t processes_mutex;
{
#if defined(HOST_WATCHOS)
return TRUE; // TODO: Rewrite using sysctl
-#elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
+#elif defined(HOST_DARWIN) || defined(__OpenBSD__) || defined(__FreeBSD__)
if (pid == 0)
return FALSE;
if (kill (pid, 0) == 0)
Process *process;
gboolean res;
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT ")", __func__, handle, timeout);
if (alerted)
*alerted = FALSE;
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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): Process already exited", __func__, handle, timeout);
return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
}
pid = process_handle->pid;
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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): 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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): 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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on non-child process", __func__, handle, timeout);
if (!process_is_alive (pid)) {
/* assume the process has exited */
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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): non-child process is not alive anymore (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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): non-child process wait failed, error : %s (%d))", __func__, handle, timeout, g_strerror (errno), errno);
return MONO_W32HANDLE_WAIT_RET_FAILED;
}
while (1) {
if (timeout != MONO_INFINITE_WAIT) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore for %li ms...",
- __func__, handle, timeout, (long)(timeout - (now - start)));
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore for %" G_GINT64_FORMAT " ms...",
+ __func__, handle, timeout, timeout - (now - start));
ret = mono_os_sem_timedwait (&process->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
} else {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore forever...",
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore forever...",
__func__, handle, timeout);
ret = mono_os_sem_wait (&process->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
}
}
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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): 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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): 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 alerted", __func__, handle, timeout);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): wait alerted", __func__, handle, timeout);
*alerted = TRUE;
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);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): Waited successfully", __func__, handle, timeout);
status = process->status;
if (WIFSIGNALED (status))
process_handle->exited = TRUE;
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Setting pid %d signalled, exit status %d",
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %" G_GUINT32_FORMAT "): Setting pid %d signalled, exit status %d",
__func__, handle, timeout, process_handle->pid, process_handle->exitstatus);
mono_w32handle_set_signal_state (handle, TRUE, TRUE);
static gint32 cleaning_up;
Process *process;
Process *prev = NULL;
- GSList *finished = NULL;
- GSList *l;
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
return;
+ /*
+ * This needs to be done outside the lock but atomically, hence the CAS above.
+ */
for (process = processes; process; process = process->next) {
if (process->signalled && process->handle) {
/* This process has exited and we need to remove the artifical ref
* on the handle */
- mono_w32handle_unref (process->handle);
+ mono_w32handle_close (process->handle);
process->handle = NULL;
}
}
- /*
- * 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 processes list
- * remain valid.
- */
mono_os_mutex_lock (&processes_mutex);
- for (process = processes; process; process = process->next) {
- if (process->handle_count == 0 && process->freeable) {
+ for (process = processes; process;) {
+ Process *next = process->next;
+ if (process->handle_count == 0 && process->signalled) {
/*
* Unlink the entry.
- * This code can run parallel with the sigchld handler, but the
- * modifications it makes are safe.
*/
if (process == processes)
processes = process->next;
else
prev->next = process->next;
- finished = g_slist_prepend (finished, process);
+
+ mono_os_sem_destroy (&process->exit_sem);
+ g_free (process);
} else {
prev = process;
}
+ process = next;
}
- mono_memory_barrier ();
-
- for (l = finished; l; l = l->next) {
- /*
- * 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.
- */
- process = (Process *)l->data;
- mono_os_sem_destroy (&process->exit_sem);
- g_free (process);
- }
- g_slist_free (finished);
-
mono_os_mutex_unlock (&processes_mutex);
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
process_set_name (&process_handle);
current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
- g_assert (current_process);
+ g_assert (current_process != INVALID_HANDLE_VALUE);
mono_os_mutex_init (&processes_mutex);
}
if (mono_w32handle_issignalled (handle))
return FALSE;
- mono_w32handle_ref (handle);
- foreach_data->handle = handle;
+ foreach_data->handle = mono_w32handle_duplicate (handle);
return TRUE;
}
g_free (pname);
g_free (mname);
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %" G_GINT32_FORMAT, __func__, result);
return result;
}
bytes += 2;
if (size < bytes) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " smaller than needed (%zd); truncating", __func__, size, bytes);
memcpy (basename, proc_path, size);
} else {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)", __func__, size, bytes);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " larger than needed (%zd)", __func__, size, bytes);
memcpy (basename, proc_path, bytes);
}
char *pname = NULL;
gboolean res;
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
- __func__, process, module);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p basename %p size %" G_GUINT32_FORMAT,
+ __func__, process, module, basename, size);
size = size * sizeof (gunichar2); /* adjust for unicode characters */
pname = g_strdup (process_handle->pname);
mods = mono_w32process_get_modules (pid);
- if (!mods) {
+ if (!mods && module != NULL) {
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't get modules %p", __func__, process);
g_free (pname);
return 0;
}
}
if (procname_ext == NULL) {
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find procname_ext from procmods %p", __func__, process);
/* 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 = mono_w32process_get_name (pid);
+ if (!procname_ext)
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find procname_ext from proc_get_name %p pid %d", __func__, process, pid);
}
g_slist_free (mods);
procname = mono_unicode_from_external (procname_ext, &bytes);
if (procname == NULL) {
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't get procname %p", __func__, process);
/* bugger */
g_free (procname_ext);
return 0;
bytes += 2;
if (size < bytes) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " smaller than needed (%zd); truncating", __func__, size, bytes);
memcpy (basename, procname, size);
} else {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %" G_GUINT32_FORMAT " larger than needed (%zd)",
__func__, size, bytes);
memcpy (basename, procname, bytes);
return len;
}
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find procname_ext %p", __func__, process);
return 0;
}
#if HAVE_SIGACTION
MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
+{
+ /*
+ * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
+ * mono_w32process_signal_finished ().
+ */
+ int old_errno = errno;
+
+ mono_gc_finalize_notify ();
+
+ errno = old_errno;
+}
+
+static void
+process_add_sigchld_handler (void)
+{
+ struct sigaction sa;
+
+ sa.sa_sigaction = mono_sigchld_signal_handler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;
+ g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
+}
+
+#endif
+
+/*
+ * mono_w32process_signal_finished:
+ *
+ * Signal the exit semaphore for processes which have finished.
+ */
+void
+mono_w32process_signal_finished (void)
{
int status;
int pid;
if (pid <= 0)
break;
- /*
- * This can run concurrently with the code in the rest of this module.
- */
+ mono_os_mutex_lock (&processes_mutex);
+
for (process = processes; process; process = process->next) {
if (process->pid != pid)
continue;
process->signalled = TRUE;
process->status = status;
mono_os_sem_post (&process->exit_sem);
- mono_memory_barrier ();
- /* Mark this as freeable, the pointer becomes invalid afterwards */
- process->freeable = TRUE;
break;
}
- } while (1);
-}
-static void
-process_add_sigchld_handler (void)
-{
- struct sigaction sa;
-
- sa.sa_sigaction = mono_sigchld_signal_handler;
- sigemptyset (&sa.sa_mask);
- sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
- g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
+ mono_os_mutex_unlock (&processes_mutex);
+ } while (1);
}
-#endif
-
static gboolean
is_readable_or_executable (const char *prog)
{
dup2 (err_fd, 2);
/* Close all file descriptors */
- for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
+ for (i = eg_getdtablesize() - 1; i > 2; i--)
close (i);
#ifdef DEBUG_ENABLED
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
dir == NULL?".":dir);
for (i = 0; argv[i] != NULL; i++)
- g_message ("arg %d: [%s]", i, argv[i]);
+ g_message ("arg %" G_GUINT32_FORMAT ": [%s]", i, argv[i]);
for (i = 0; env_strings[i] != NULL; i++)
- g_message ("env %d: [%s]", i, env_strings[i]);
+ g_message ("env %" G_GUINT32_FORMAT ": [%s]", i, env_strings[i]);
#endif
/* set cwd */
/* Keep the process handle artificially alive until the process
* exits so that the information in the handle isn't lost. */
- mono_w32handle_ref (handle);
- process->handle = handle;
+ process->handle = mono_w32handle_duplicate (handle);
mono_os_mutex_lock (&processes_mutex);
process->next = processes;
goto done;
}
-#ifdef PLATFORM_MACOSX
+#ifdef HOST_DARWIN
handler = g_strdup ("/usr/bin/open");
#else
/*
gpointer
ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
{
- mono_w32handle_ref (current_process);
return current_process;
}
&start_ticks, &user_ticks, &kernel_ticks);
ticks_to_processtime (start_ticks, creation_processtime);
- ticks_to_processtime (user_ticks, kernel_processtime);
- ticks_to_processtime (kernel_ticks, user_processtime);
+ ticks_to_processtime (kernel_ticks, kernel_processtime);
+ ticks_to_processtime (user_ticks, user_processtime);
return TRUE;
}
}
if (map_size < sizeof(IMAGE_NT_HEADERS32) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %d", __func__, map_size);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size);
mono_w32error_set_last (ERROR_BAD_LENGTH);
return(NULL);
}
if (map_size < sizeof(IMAGE_NT_HEADERS64) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %d", __func__, map_size);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size);
mono_w32error_set_last (ERROR_BAD_LENGTH);
return(NULL);
/* Check basic file size */
if (statbuf.st_size < sizeof(IMAGE_DOS_HEADER)) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File %s is too small: %lld", __func__, filename_ext, statbuf.st_size);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: File %s is too small: %lld", __func__, filename_ext, (long long) statbuf.st_size);
mono_w32error_set_last (ERROR_BAD_LENGTH);
g_free (filename_ext);