2 * process.c: System.Diagnostics.Process support
5 * Dick Porter (dick@ximian.com)
7 * Copyright 2002 Ximian, Inc.
8 * Copyright 2002-2006 Novell, Inc.
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include <sys/types.h>
29 #ifdef HAVE_SYS_PARAM_H
30 #include <sys/param.h>
34 #ifdef HAVE_SYS_WAIT_H
37 #ifdef HAVE_SYS_RESOURCE_H
38 #include <sys/resource.h>
41 #ifdef HAVE_SYS_MKDEV_H
42 #include <sys/mkdev.h>
49 #include <mono/metadata/w32process.h>
50 #include <mono/metadata/w32process-internals.h>
51 #include <mono/metadata/w32process-unix-internals.h>
52 #include <mono/metadata/class.h>
53 #include <mono/metadata/class-internals.h>
54 #include <mono/metadata/object.h>
55 #include <mono/metadata/object-internals.h>
56 #include <mono/metadata/metadata.h>
57 #include <mono/metadata/metadata-internals.h>
58 #include <mono/metadata/exception.h>
59 #include <mono/io-layer/io-layer.h>
60 #include <mono/metadata/w32handle.h>
61 #include <mono/utils/mono-membar.h>
62 #include <mono/utils/mono-logger-internals.h>
63 #include <mono/utils/strenc.h>
64 #include <mono/utils/mono-proclib.h>
65 #include <mono/utils/mono-path.h>
66 #include <mono/utils/mono-lazy-init.h>
67 #include <mono/utils/mono-signal-handler.h>
68 #include <mono/utils/mono-time.h>
71 #define MAXPATHLEN 242
74 #define STILL_ACTIVE ((int) 0x00000103)
77 /* define LOGDEBUG(...) g_message(__VA_ARGS__) */
79 /* The process' environment strings */
80 #if defined(__APPLE__)
81 #if defined (TARGET_OSX)
82 /* Apple defines this in crt_externs.h but doesn't provide that header for
83 * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
84 * in fact exist on all implementations (so far)
86 gchar ***_NSGetEnviron(void);
87 #define environ (*_NSGetEnviron())
89 static char *mono_environ[1] = { NULL };
90 #define environ mono_environ
91 #endif /* defined (TARGET_OSX) */
93 extern char **environ;
97 * Handles > _WAPI_PROCESS_UNHANDLED are pseudo handles which represent processes
98 * not started by the runtime.
100 /* This marks a system process that we don't have a handle on */
101 /* FIXME: Cope with PIDs > sizeof guint */
102 #define _WAPI_PROCESS_UNHANDLED (1 << (8*sizeof(pid_t)-1))
104 #define WAPI_IS_PSEUDO_PROCESS_HANDLE(handle) ((GPOINTER_TO_UINT(handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED)
105 #define WAPI_PID_TO_HANDLE(pid) GINT_TO_POINTER (_WAPI_PROCESS_UNHANDLED + (pid))
106 #define WAPI_HANDLE_TO_PID(handle) (GPOINTER_TO_UINT ((handle)) - _WAPI_PROCESS_UNHANDLED)
109 STARTF_USESHOWWINDOW=0x001,
110 STARTF_USESIZE=0x002,
111 STARTF_USEPOSITION=0x004,
112 STARTF_USECOUNTCHARS=0x008,
113 STARTF_USEFILLATTRIBUTE=0x010,
114 STARTF_RUNFULLSCREEN=0x020,
115 STARTF_FORCEONFEEDBACK=0x040,
116 STARTF_FORCEOFFFEEDBACK=0x080,
117 STARTF_USESTDHANDLES=0x100
127 #if G_BYTE_ORDER == G_BIG_ENDIAN
128 guint32 highDateTime;
132 guint32 highDateTime;
137 * MonoProcess describes processes we create.
138 * It contains a semaphore that can be waited on in order to wait
139 * for process termination. It's accessed in our SIGCHLD handler,
140 * when status is updated (and pid cleared, to not clash with
141 * subsequent processes that may get executed).
143 typedef struct _MonoProcess MonoProcess;
144 struct _MonoProcess {
145 pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
146 MonoSemType exit_sem; /* this semaphore will be released when the process exits */
147 int status; /* the exit status */
148 gint32 handle_count; /* the number of handles to this mono_process instance */
149 /* we keep a ref to the creating _WapiHandle_process handle until
150 * the process has exited, so that the information there isn't lost.
157 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
161 gpointer main_thread;
162 WapiFileTime create_time;
163 WapiFileTime exit_time;
165 size_t min_working_set;
166 size_t max_working_set;
168 MonoProcess *mono_process;
169 } MonoW32HandleProcess;
172 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
175 static gchar *cli_launcher;
177 /* The signal-safe logic to use processes goes like this:
178 * - The list must be safe to traverse for the signal handler at all times.
179 * It's safe to: prepend an entry (which is a single store to 'processes'),
180 * unlink an entry (assuming the unlinked entry isn't freed and doesn't
181 * change its 'next' pointer so that it can still be traversed).
182 * When cleaning up we first unlink an entry, then we verify that
183 * the read lock isn't locked. Then we can free the entry, since
184 * we know that nobody is using the old version of the list (including
185 * the unlinked entry).
186 * We also need to lock when adding and cleaning up so that those two
187 * operations don't mess with eachother. (This lock is not used in the
189 static MonoProcess *processes;
190 static mono_mutex_t processes_mutex;
192 static gpointer current_process;
194 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
195 static const gunichar2 *utf16_space = utf16_space_bytes;
196 static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
197 static const gunichar2 *utf16_quote = utf16_quote_bytes;
200 process_details (gpointer data)
202 MonoW32HandleProcess *process_handle = (MonoW32HandleProcess *) data;
203 g_print ("id: %d, exited: %s, exitstatus: %d",
204 process_handle->id, process_handle->exited ? "true" : "false", process_handle->exitstatus);
208 process_typename (void)
214 process_typesize (void)
216 return sizeof (MonoW32HandleProcess);
219 static MonoW32HandleWaitRet
220 process_wait (gpointer handle, guint32 timeout, gboolean *alerted)
222 MonoW32HandleProcess *process_handle;
223 pid_t pid G_GNUC_UNUSED, ret;
229 /* FIXME: We can now easily wait on processes that aren't our own children,
230 * but WaitFor*Object won't call us for pseudo handles. */
231 g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
233 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
238 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
240 g_warning ("%s: error looking up process handle %p", __func__, handle);
241 return MONO_W32HANDLE_WAIT_RET_FAILED;
244 if (process_handle->exited) {
245 /* We've already done this one */
246 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Process already exited", __func__, handle, timeout);
247 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
250 pid = process_handle->id;
252 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
254 /* We don't need to lock processes here, the entry
255 * has a handle_count > 0 which means it will not be freed. */
256 mp = process_handle->mono_process;
260 if (pid == mono_process_current_pid ()) {
261 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout);
262 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
265 /* This path is used when calling Process.HasExited, so
266 * it is only used to poll the state of the process, not
267 * to actually wait on it to exit */
268 g_assert (timeout == 0);
270 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout);
272 res = waitpid (pid, &status, WNOHANG);
274 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process wait timeout", __func__, handle, timeout);
275 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
278 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully", __func__, handle, timeout);
279 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
282 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);
283 return MONO_W32HANDLE_WAIT_RET_FAILED;
286 start = mono_msec_ticks ();
290 if (timeout != INFINITE) {
291 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore for %li ms...",
292 __func__, handle, timeout, (long)(timeout - (now - start)));
293 ret = mono_os_sem_timedwait (&mp->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
295 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore forever...",
296 __func__, handle, timeout);
297 ret = mono_os_sem_wait (&mp->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
300 if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
301 /* Success, process has exited */
302 mono_os_sem_post (&mp->exit_sem);
306 if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
307 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout (timeout = 0)", __func__, handle, timeout);
308 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
311 now = mono_msec_ticks ();
312 if (now - start >= timeout) {
313 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout", __func__, handle, timeout);
314 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
317 if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
318 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait alerted", __func__, handle, timeout);
320 return MONO_W32HANDLE_WAIT_RET_ALERTED;
324 /* Process must have exited */
325 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout);
328 if (WIFSIGNALED (status))
329 process_handle->exitstatus = 128 + WTERMSIG (status);
331 process_handle->exitstatus = WEXITSTATUS (status);
332 _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
334 process_handle->exited = TRUE;
336 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Setting pid %d signalled, exit status %d",
337 __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
339 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
341 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
345 processes_cleanup (void)
347 static gint32 cleaning_up;
349 MonoProcess *prev = NULL;
350 GSList *finished = NULL;
352 gpointer unref_handle;
354 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
356 /* Ensure we're not in here in multiple threads at once, nor recursive. */
357 if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
360 for (mp = processes; mp; mp = mp->next) {
361 if (mp->pid == 0 && mp->handle) {
362 /* This process has exited and we need to remove the artifical ref
364 mono_os_mutex_lock (&processes_mutex);
365 unref_handle = mp->handle;
367 mono_os_mutex_unlock (&processes_mutex);
369 mono_w32handle_unref (unref_handle);
374 * Remove processes which exited from the processes list.
375 * We need to synchronize with the sigchld handler here, which runs
376 * asynchronously. The handler requires that the processes list
379 mono_os_mutex_lock (&processes_mutex);
383 if (mp->handle_count == 0 && mp->freeable) {
386 * This code can run parallel with the sigchld handler, but the
387 * modifications it makes are safe.
390 processes = mp->next;
392 prev->next = mp->next;
393 finished = g_slist_prepend (finished, mp);
402 mono_memory_barrier ();
404 for (l = finished; l; l = l->next) {
406 * All the entries in the finished list are unlinked from processes, and
407 * they have the 'finished' flag set, which means the sigchld handler is done
410 mp = (MonoProcess *)l->data;
411 mono_os_sem_destroy (&mp->exit_sem);
414 g_slist_free (finished);
416 mono_os_mutex_unlock (&processes_mutex);
418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
420 InterlockedExchange (&cleaning_up, 0);
424 process_close (gpointer handle, gpointer data)
426 MonoW32HandleProcess *process_handle;
428 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
430 process_handle = (MonoW32HandleProcess *) data;
431 g_free (process_handle->proc_name);
432 process_handle->proc_name = NULL;
433 if (process_handle->mono_process)
434 InterlockedDecrement (&process_handle->mono_process->handle_count);
435 processes_cleanup ();
438 static MonoW32HandleOps process_ops = {
439 process_close, /* close_shared */
443 process_wait, /* special_wait */
445 process_details, /* details */
446 process_typename, /* typename */
447 process_typesize, /* typesize */
451 process_set_defaults (MonoW32HandleProcess *process_handle)
453 /* These seem to be the defaults on w2k */
454 process_handle->min_working_set = 204800;
455 process_handle->max_working_set = 1413120;
457 _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
461 process_set_name (MonoW32HandleProcess *process_handle)
463 char *progname, *utf8_progname, *slash;
465 progname = g_get_prgname ();
466 utf8_progname = mono_utf8_from_external (progname);
468 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: using [%s] as prog name", __func__, progname);
471 slash = strrchr (utf8_progname, '/');
473 process_handle->proc_name = g_strdup (slash+1);
475 process_handle->proc_name = g_strdup (utf8_progname);
476 g_free (utf8_progname);
481 mono_w32process_init (void)
483 MonoW32HandleProcess process_handle;
485 mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS, &process_ops);
487 mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS,
488 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
490 memset (&process_handle, 0, sizeof (process_handle));
491 process_handle.id = wapi_getpid ();
492 process_set_defaults (&process_handle);
493 process_set_name (&process_handle);
495 current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
496 g_assert (current_process);
498 mono_os_mutex_init (&processes_mutex);
502 mono_w32process_cleanup (void)
504 g_free (cli_launcher);
507 /* Check if a pid is valid - i.e. if a process exists with this pid. */
509 is_pid_valid (pid_t pid)
511 gboolean result = FALSE;
513 #if defined(HOST_WATCHOS)
514 result = TRUE; // TODO: Rewrite using sysctl
515 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
516 if (((kill(pid, 0) == 0) || (errno == EPERM)) && pid != 0)
518 #elif defined(__HAIKU__)
520 if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
523 char *dir = g_strdup_printf ("/proc/%d", pid);
524 if (!access (dir, F_OK))
533 len16 (const gunichar2 *str)
544 utf16_concat (const gunichar2 *first, ...)
552 va_start (args, first);
553 total += len16 (first);
554 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *))
558 ret = g_new (gunichar2, total + 1);
564 for (s = first; *s != 0; s++)
566 va_start (args, first);
567 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
568 for (p = s; *p != 0; p++)
577 mono_w32process_get_pid (gpointer handle)
579 MonoW32HandleProcess *process_handle;
582 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
583 /* This is a pseudo handle */
584 return WAPI_HANDLE_TO_PID (handle);
587 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
589 SetLastError (ERROR_INVALID_HANDLE);
593 return process_handle->id;
599 } GetProcessForeachData;
602 get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
604 GetProcessForeachData *foreach_data;
605 MonoW32HandleProcess *process_handle;
608 foreach_data = (GetProcessForeachData*) user_data;
610 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS)
613 g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
615 process_handle = (MonoW32HandleProcess*) handle_specific;
617 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id);
619 pid = process_handle->id;
623 /* It's possible to have more than one process handle with the
624 * same pid, but only the one running process can be
626 if (foreach_data->pid != pid)
628 if (mono_w32handle_issignalled (handle))
631 mono_w32handle_ref (handle);
632 foreach_data->handle = handle;
637 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
639 GetProcessForeachData foreach_data;
642 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for process %d", __func__, pid);
644 memset (&foreach_data, 0, sizeof (foreach_data));
645 foreach_data.pid = pid;
646 mono_w32handle_foreach (get_process_foreach_callback, &foreach_data);
647 handle = foreach_data.handle;
649 /* get_process_foreach_callback already added a ref */
653 if (is_pid_valid (pid)) {
654 /* Return a pseudo handle for processes we don't have handles for */
655 return WAPI_PID_TO_HANDLE (pid);
658 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid);
660 SetLastError (ERROR_PROC_NOT_FOUND);
665 match_procname_to_modulename (char *procname, char *modulename)
667 char* lastsep = NULL;
668 char* lastsep2 = NULL;
671 gboolean result = FALSE;
673 if (procname == NULL || modulename == NULL)
676 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename);
677 pname = mono_path_resolve_symlinks (procname);
678 mname = mono_path_resolve_symlinks (modulename);
680 if (!strcmp (pname, mname))
684 lastsep = strrchr (mname, '/');
686 if (!strcmp (lastsep+1, pname))
689 lastsep2 = strrchr (pname, '/');
692 if (!strcmp (lastsep+1, lastsep2+1))
695 if (!strcmp (mname, lastsep2+1))
705 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
710 mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed)
712 MonoW32HandleProcess *process_handle;
714 MonoW32ProcessModule *module;
715 guint32 count, avail = size / sizeof(gpointer);
718 char *proc_name = NULL;
721 /* Store modules in an array of pointers (main module as
722 * modules[0]), using the load address for each module as a
723 * token. (Use 'NULL' as an alternative for the main module
724 * so that the simple implementation can just return one item
725 * for now.) Get the info from /proc/<pid>/maps on linux,
726 * /proc/<pid>/map on FreeBSD, other systems will have to
727 * implement /dev/kmem reading or whatever other horrid
728 * technique is needed.
730 if (size < sizeof(gpointer))
733 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
734 pid = WAPI_HANDLE_TO_PID (process);
735 proc_name = mono_w32process_get_name (pid);
737 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
739 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
743 pid = process_handle->id;
744 proc_name = g_strdup (process_handle->proc_name);
749 *needed = sizeof(gpointer);
753 mods = mono_w32process_get_modules (pid);
756 *needed = sizeof(gpointer);
761 count = g_slist_length (mods);
763 /* count + 1 to leave slot 0 for the main module */
764 *needed = sizeof(gpointer) * (count + 1);
767 * Use the NULL shortcut, as the first line in
768 * /proc/<pid>/maps isn't the executable, and we need
769 * that first in the returned list. Check the module name
770 * to see if it ends with the proc name and substitute
771 * the first entry with it. FIXME if this turns out to
775 for (i = 0; i < (avail - 1) && i < count; i++) {
776 module = (MonoW32ProcessModule *)g_slist_nth_data (mods, i);
777 if (modules[0] != NULL)
778 modules[i] = module->address_start;
779 else if (match_procname_to_modulename (proc_name, module->filename))
780 modules[0] = module->address_start;
782 modules[i + 1] = module->address_start;
785 for (i = 0; i < count; i++) {
786 mono_w32process_module_free ((MonoW32ProcessModule *)g_slist_nth_data (mods, i));
795 mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
800 gunichar2 *proc_path;
802 size *= sizeof (gunichar2); /* adjust for unicode characters */
804 if (basename == NULL || size == 0)
807 pid = mono_w32process_get_pid (process);
809 path = mono_w32process_get_path (pid);
813 proc_path = mono_unicode_from_external (path, &bytes);
816 if (proc_path == NULL)
821 /* Add the terminator */
825 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
826 memcpy (basename, proc_path, size);
828 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)", __func__, size, bytes);
829 memcpy (basename, proc_path, bytes);
838 mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
840 MonoW32HandleProcess *process_handle;
843 char *procname_ext = NULL;
847 MonoW32ProcessModule *found_module;
850 char *proc_name = NULL;
853 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
854 __func__, process, module);
856 size = size * sizeof (gunichar2); /* adjust for unicode characters */
858 if (basename == NULL || size == 0)
861 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
862 /* This is a pseudo handle */
863 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
864 proc_name = mono_w32process_get_name (pid);
866 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
868 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
872 pid = process_handle->id;
873 proc_name = g_strdup (process_handle->proc_name);
876 mods = mono_w32process_get_modules (pid);
882 count = g_slist_length (mods);
884 /* If module != NULL compare the address.
885 * If module == NULL we are looking for the main module.
886 * The best we can do for now check it the module name end with the process name.
888 for (i = 0; i < count; i++) {
889 found_module = (MonoW32ProcessModule *)g_slist_nth_data (mods, i);
890 if (procname_ext == NULL &&
891 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
892 (module != NULL && found_module->address_start == module))) {
893 procname_ext = g_path_get_basename (found_module->filename);
896 mono_w32process_module_free (found_module);
899 if (procname_ext == NULL) {
900 /* If it's *still* null, we might have hit the
901 * case where reading /proc/$pid/maps gives an
902 * empty file for this user.
904 procname_ext = mono_w32process_get_name (pid);
911 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
914 procname = mono_unicode_from_external (procname_ext, &bytes);
915 if (procname == NULL) {
917 g_free (procname_ext);
923 /* Add the terminator */
927 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
929 memcpy (basename, procname, size);
931 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
932 __func__, size, bytes);
934 memcpy (basename, procname, bytes);
938 g_free (procname_ext);
947 mono_w32process_module_get_information (gpointer process, gpointer module, WapiModuleInfo *modinfo, guint32 size)
949 MonoW32HandleProcess *process_handle;
952 MonoW32ProcessModule *found_module;
955 gboolean ret = FALSE;
956 char *proc_name = NULL;
959 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
960 __func__, process, module);
962 if (modinfo == NULL || size < sizeof (WapiModuleInfo))
965 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
966 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
967 proc_name = mono_w32process_get_name (pid);
969 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
971 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
975 pid = process_handle->id;
976 proc_name = g_strdup (process_handle->proc_name);
979 mods = mono_w32process_get_modules (pid);
985 count = g_slist_length (mods);
987 /* If module != NULL compare the address.
988 * If module == NULL we are looking for the main module.
989 * The best we can do for now check it the module name end with the process name.
991 for (i = 0; i < count; i++) {
992 found_module = (MonoW32ProcessModule *)g_slist_nth_data (mods, i);
994 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
995 (module != NULL && found_module->address_start == module))) {
996 modinfo->lpBaseOfDll = found_module->address_start;
997 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
998 modinfo->EntryPoint = found_module->address_offset;
1002 mono_w32process_module_free (found_module);
1005 g_slist_free (mods);
1012 switch_dir_separators (char *path)
1014 size_t i, pathLength = strlen(path);
1016 /* Turn all the slashes round the right way, except for \' */
1017 /* There are probably other characters that need to be excluded as well. */
1018 for (i = 0; i < pathLength; i++) {
1019 if (path[i] == '\\' && i < pathLength - 1 && path[i+1] != '\'' )
1026 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
1034 pid = waitpid (-1, &status, WNOHANG);
1035 } while (pid == -1 && errno == EINTR);
1041 * This can run concurrently with the code in the rest of this module.
1043 for (p = processes; p; p = p->next) {
1047 p->pid = 0; /* this pid doesn't exist anymore, clear it */
1049 mono_os_sem_post (&p->exit_sem);
1050 mono_memory_barrier ();
1051 /* Mark this as freeable, the pointer becomes invalid afterwards */
1059 process_add_sigchld_handler (void)
1061 struct sigaction sa;
1063 sa.sa_sigaction = mono_sigchld_signal_handler;
1064 sigemptyset (&sa.sa_mask);
1065 sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1066 g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
1067 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
1073 is_readable_or_executable (const char *prog)
1076 int a = access (prog, R_OK);
1077 int b = access (prog, X_OK);
1078 if (a != 0 && b != 0)
1080 if (stat (prog, &buf))
1082 if (S_ISREG (buf.st_mode))
1088 is_executable (const char *prog)
1091 if (access (prog, X_OK) != 0)
1093 if (stat (prog, &buf))
1095 if (S_ISREG (buf.st_mode))
1101 is_managed_binary (const char *filename)
1103 int original_errno = errno;
1104 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1105 int file = open (filename, O_RDONLY | O_LARGEFILE);
1107 int file = open (filename, O_RDONLY);
1110 unsigned char buffer[8];
1111 off_t file_size, optional_header_offset;
1112 off_t pe_header_offset, clr_header_offset;
1113 gboolean managed = FALSE;
1115 guint32 first_word, second_word, magic_number;
1117 /* If we are unable to open the file, then we definitely
1118 * can't say that it is managed. The child mono process
1119 * probably wouldn't be able to open it anyway.
1122 errno = original_errno;
1126 /* Retrieve the length of the file for future sanity checks. */
1127 file_size = lseek (file, 0, SEEK_END);
1128 lseek (file, 0, SEEK_SET);
1130 /* We know we need to read a header field at offset 60. */
1134 num_read = read (file, buffer, 2);
1136 if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
1139 new_offset = lseek (file, 60, SEEK_SET);
1141 if (new_offset != 60)
1144 num_read = read (file, buffer, 4);
1148 pe_header_offset = buffer[0]
1151 | (buffer[3] << 24);
1153 if (pe_header_offset + 24 > file_size)
1156 new_offset = lseek (file, pe_header_offset, SEEK_SET);
1158 if (new_offset != pe_header_offset)
1161 num_read = read (file, buffer, 4);
1163 if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
1167 * Verify that the header we want in the optional header data
1168 * is present in this binary.
1170 new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
1172 if (new_offset != pe_header_offset + 20)
1175 num_read = read (file, buffer, 2);
1177 if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
1180 optional_header_offset = pe_header_offset + 24;
1182 /* Read the PE magic number */
1183 new_offset = lseek (file, optional_header_offset, SEEK_SET);
1185 if (new_offset != optional_header_offset)
1188 num_read = read (file, buffer, 2);
1193 magic_number = (buffer[0] | (buffer[1] << 8));
1195 if (magic_number == 0x10B) // PE32
1196 clr_header_offset = 208;
1197 else if (magic_number == 0x20B) // PE32+
1198 clr_header_offset = 224;
1202 /* Read the CLR header address and size fields. These will be
1203 * zero if the binary is not managed.
1205 new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET);
1207 if (new_offset != optional_header_offset + clr_header_offset)
1210 num_read = read (file, buffer, 8);
1212 /* We are not concerned with endianness, only with
1213 * whether it is zero or not.
1215 first_word = *(guint32 *)&buffer[0];
1216 second_word = *(guint32 *)&buffer[4];
1218 if ((num_read != 8) || (first_word == 0) || (second_word == 0))
1225 errno = original_errno;
1230 process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new_environ,
1231 const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info)
1233 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1234 char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
1235 char *dir = NULL, **env_strings = NULL, **argv = NULL;
1236 guint32 i, env_count = 0;
1237 gboolean ret = FALSE;
1238 gpointer handle = NULL;
1239 GError *gerr = NULL;
1240 int in_fd, out_fd, err_fd;
1242 int startup_pipe [2] = {-1, -1};
1244 MonoProcess *mono_process;
1247 mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
1250 /* appname and cmdline specify the executable and its args:
1252 * If appname is not NULL, it is the name of the executable.
1253 * Otherwise the executable is the first token in cmdline.
1255 * Executable searching:
1257 * If appname is not NULL, it can specify the full path and
1258 * file name, or else a partial name and the current directory
1259 * will be used. There is no additional searching.
1261 * If appname is NULL, the first whitespace-delimited token in
1262 * cmdline is used. If the name does not contain a full
1263 * directory path, the search sequence is:
1265 * 1) The directory containing the current process
1266 * 2) The current working directory
1267 * 3) The windows system directory (Ignored)
1268 * 4) The windows directory (Ignored)
1271 * Just to make things more interesting, tokens can contain
1272 * white space if they are surrounded by quotation marks. I'm
1273 * beginning to understand just why windows apps are generally
1274 * so crap, with an API like this :-(
1276 if (appname != NULL) {
1277 cmd = mono_unicode_to_external (appname);
1279 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL",
1282 SetLastError (ERROR_PATH_NOT_FOUND);
1286 switch_dir_separators(cmd);
1289 if (cmdline != NULL) {
1290 args = mono_unicode_to_external (cmdline);
1292 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1294 SetLastError (ERROR_PATH_NOT_FOUND);
1300 dir = mono_unicode_to_external (cwd);
1302 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1304 SetLastError (ERROR_PATH_NOT_FOUND);
1308 /* Turn all the slashes round the right way */
1309 switch_dir_separators(dir);
1313 /* We can't put off locating the executable any longer :-( */
1316 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
1317 /* Strip off the drive letter. I can't
1318 * believe that CP/M holdover is still
1321 g_memmove (cmd, cmd+2, strlen (cmd)-2);
1322 cmd[strlen (cmd)-2] = '\0';
1325 unquoted = g_shell_unquote (cmd, NULL);
1326 if (unquoted[0] == '/') {
1327 /* Assume full path given */
1328 prog = g_strdup (unquoted);
1330 /* Executable existing ? */
1331 if (!is_readable_or_executable (prog)) {
1332 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1335 SetLastError (ERROR_FILE_NOT_FOUND);
1339 /* Search for file named by cmd in the current
1342 char *curdir = g_get_current_dir ();
1344 prog = g_strdup_printf ("%s/%s", curdir, unquoted);
1347 /* And make sure it's readable */
1348 if (!is_readable_or_executable (prog)) {
1349 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1352 SetLastError (ERROR_FILE_NOT_FOUND);
1358 args_after_prog = args;
1363 /* Dig out the first token from args, taking quotation
1364 * marks into account
1367 /* First, strip off all leading whitespace */
1368 args = g_strchug (args);
1370 /* args_after_prog points to the contents of args
1371 * after token has been set (otherwise argv[0] is
1374 args_after_prog = args;
1376 /* Assume the opening quote will always be the first
1379 if (args[0] == '\"' || args [0] == '\'') {
1381 for (i = 1; args[i] != '\0' && args[i] != quote; i++);
1382 if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) {
1383 /* We found the first token */
1384 token = g_strndup (args+1, i-1);
1385 args_after_prog = g_strchug (args + i + 1);
1387 /* Quotation mark appeared in the
1388 * middle of the token. Just give the
1389 * whole first token, quotes and all,
1395 if (token == NULL) {
1396 /* No quote mark, or malformed */
1397 for (i = 0; args[i] != '\0'; i++) {
1398 if (g_ascii_isspace (args[i])) {
1399 token = g_strndup (args, i);
1400 args_after_prog = args + i + 1;
1406 if (token == NULL && args[0] != '\0') {
1407 /* Must be just one token in the string */
1408 token = g_strdup (args);
1409 args_after_prog = NULL;
1412 if (token == NULL) {
1414 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find what to exec", __func__);
1416 SetLastError (ERROR_PATH_NOT_FOUND);
1420 /* Turn all the slashes round the right way. Only for
1423 switch_dir_separators(token);
1425 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
1426 /* Strip off the drive letter. I can't
1427 * believe that CP/M holdover is still
1430 g_memmove (token, token+2, strlen (token)-2);
1431 token[strlen (token)-2] = '\0';
1434 if (token[0] == '/') {
1435 /* Assume full path given */
1436 prog = g_strdup (token);
1438 /* Executable existing ? */
1439 if (!is_readable_or_executable (prog)) {
1440 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1443 SetLastError (ERROR_FILE_NOT_FOUND);
1447 char *curdir = g_get_current_dir ();
1449 /* FIXME: Need to record the directory
1450 * containing the current process, and check
1451 * that for the new executable as the first
1455 prog = g_strdup_printf ("%s/%s", curdir, token);
1458 /* I assume X_OK is the criterion to use,
1461 * X_OK is too strict *if* the target is a CLR binary
1463 if (!is_readable_or_executable (prog)) {
1465 prog = g_find_program_in_path (token);
1467 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s", __func__, token);
1470 SetLastError (ERROR_FILE_NOT_FOUND);
1479 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Exec prog [%s] args [%s]",
1480 __func__, prog, args_after_prog);
1482 /* Check for CLR binaries; if found, we will try to invoke
1483 * them using the same mono binary that started us.
1485 if (is_managed_binary (prog)) {
1486 gunichar2 *newapp, *newcmd;
1487 gsize bytes_ignored;
1489 newapp = mono_unicode_from_external (cli_launcher ? cli_launcher : "mono", &bytes_ignored);
1492 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, NULL);
1494 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, NULL);
1499 ret = process_create (NULL, newcmd, new_environ, cwd, startup_handles, process_info);
1507 if (!is_executable (prog)) {
1508 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Executable permisson not set on %s", __func__, prog);
1509 SetLastError (ERROR_ACCESS_DENIED);
1514 if (args_after_prog != NULL && *args_after_prog) {
1517 qprog = g_shell_quote (prog);
1518 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
1521 full_prog = g_shell_quote (prog);
1524 ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
1526 g_message ("process_create: %s\n", gerr->message);
1527 g_error_free (gerr);
1532 if (startup_handles) {
1533 in_fd = GPOINTER_TO_UINT (startup_handles->input);
1534 out_fd = GPOINTER_TO_UINT (startup_handles->output);
1535 err_fd = GPOINTER_TO_UINT (startup_handles->error);
1537 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
1538 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
1539 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
1542 /* new_environ is a block of NULL-terminated strings, which
1543 * is itself NULL-terminated. Of course, passing an array of
1544 * string pointers would have made things too easy :-(
1546 * If new_environ is not NULL it specifies the entire set of
1547 * environment variables in the new process. Otherwise the
1548 * new process inherits the same environment.
1551 gunichar2 *new_environp;
1553 /* Count the number of strings */
1554 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1556 while (*new_environp)
1560 /* +2: one for the process handle value, and the last
1563 env_strings = g_new0 (char *, env_count + 2);
1565 /* Copy each environ string into 'strings' turning it
1566 * into utf8 (or the requested encoding) at the same
1570 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1571 env_strings[env_count] = mono_unicode_to_external (new_environp);
1573 while (*new_environp) {
1578 for (i = 0; environ[i] != NULL; i++)
1581 /* +2: one for the process handle value, and the last
1584 env_strings = g_new0 (char *, env_count + 2);
1586 /* Copy each environ string into 'strings' turning it
1587 * into utf8 (or the requested encoding) at the same
1591 for (i = 0; environ[i] != NULL; i++) {
1592 env_strings[env_count] = g_strdup (environ[i]);
1597 /* Create a pipe to make sure the child doesn't exit before
1598 * we can add the process to the linked list of processes */
1599 if (pipe (startup_pipe) == -1) {
1600 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1601 * This is just for a very hard to hit race condition in the first place */
1602 startup_pipe [0] = startup_pipe [1] = -1;
1603 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__);
1607 /* FIXME: block SIGCHLD */
1610 switch (pid = fork ()) {
1611 case -1: /* Error */ {
1612 SetLastError (ERROR_OUTOFMEMORY);
1616 case 0: /* Child */ {
1617 if (startup_pipe [0] != -1) {
1618 /* Wait until the parent has updated it's internal data */
1619 ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1);
1620 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: child: parent has completed its setup", __func__);
1621 close (startup_pipe [0]);
1622 close (startup_pipe [1]);
1625 /* should we detach from the process group? */
1627 /* Connect stdin, stdout and stderr */
1632 /* Close all file descriptors */
1633 for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
1636 #ifdef DEBUG_ENABLED
1637 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1638 dir == NULL?".":dir);
1639 for (i = 0; argv[i] != NULL; i++)
1640 g_message ("arg %d: [%s]", i, argv[i]);
1642 for (i = 0; env_strings[i] != NULL; i++)
1643 g_message ("env %d: [%s]", i, env_strings[i]);
1647 if (dir != NULL && chdir (dir) == -1) {
1653 execve (argv[0], argv, env_strings);
1660 default: /* Parent */ {
1661 MonoW32HandleProcess process_handle;
1663 memset (&process_handle, 0, sizeof (process_handle));
1664 process_handle.id = pid;
1665 process_handle.proc_name = g_strdup (prog);
1666 process_set_defaults (&process_handle);
1668 /* Add our mono_process into the linked list of processes */
1669 mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess));
1670 mono_process->pid = pid;
1671 mono_process->handle_count = 1;
1672 mono_os_sem_init (&mono_process->exit_sem, 0);
1674 process_handle.mono_process = mono_process;
1676 handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
1677 if (handle == INVALID_HANDLE_VALUE) {
1678 g_warning ("%s: error creating process handle", __func__);
1680 mono_os_sem_destroy (&mono_process->exit_sem);
1681 g_free (mono_process);
1683 SetLastError (ERROR_OUTOFMEMORY);
1688 /* Keep the process handle artificially alive until the process
1689 * exits so that the information in the handle isn't lost. */
1690 mono_w32handle_ref (handle);
1691 mono_process->handle = handle;
1693 mono_os_mutex_lock (&processes_mutex);
1694 mono_process->next = processes;
1695 processes = mono_process;
1696 mono_os_mutex_unlock (&processes_mutex);
1698 if (process_info != NULL) {
1699 process_info->process_handle = handle;
1700 process_info->pid = pid;
1702 /* FIXME: we might need to handle the thread info some day */
1703 process_info->thread_handle = INVALID_HANDLE_VALUE;
1704 process_info->tid = 0;
1712 /* FIXME: unblock SIGCHLD */
1715 if (startup_pipe [1] != -1) {
1716 /* Write 1 byte, doesn't matter what */
1717 ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
1718 close (startup_pipe [0]);
1719 close (startup_pipe [1]);
1734 g_strfreev (env_strings);
1738 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p for pid %d", __func__, handle, pid);
1740 /* Check if something needs to be cleaned up. */
1741 processes_cleanup ();
1745 SetLastError (ERROR_NOT_SUPPORTED);
1747 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1751 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
1753 const gunichar2 *lpFile;
1754 const gunichar2 *lpParameters;
1755 const gunichar2 *lpDirectory;
1759 if (!proc_start_info->filename) {
1760 /* w2k returns TRUE for this, for some reason. */
1765 lpFile = proc_start_info->filename ? mono_string_chars (proc_start_info->filename) : NULL;
1766 lpParameters = proc_start_info->arguments ? mono_string_chars (proc_start_info->arguments) : NULL;
1767 lpDirectory = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) != 0 ?
1768 mono_string_chars (proc_start_info->working_directory) : NULL;
1770 /* Put both executable and parameters into the second argument
1771 * to process_create (), so it searches $PATH. The conversion
1772 * into and back out of utf8 is because there is no
1773 * g_strdup_printf () equivalent for gunichar2 :-(
1775 args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1777 SetLastError (ERROR_INVALID_DATA);
1781 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1784 if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
1788 static char *handler;
1789 static gunichar2 *handler_utf16;
1791 if (handler_utf16 == (gunichar2 *)-1) {
1796 #ifdef PLATFORM_MACOSX
1797 handler = g_strdup ("/usr/bin/open");
1800 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
1801 * if that fails, try to use gnome-open, then kfmclient
1803 handler = g_find_program_in_path ("xdg-open");
1804 if (handler == NULL){
1805 handler = g_find_program_in_path ("gnome-open");
1806 if (handler == NULL){
1807 handler = g_find_program_in_path ("kfmclient");
1808 if (handler == NULL){
1809 handler_utf16 = (gunichar2 *) -1;
1813 /* kfmclient needs exec argument */
1814 char *old = handler;
1815 handler = g_strconcat (old, " exec",
1822 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
1825 /* Put quotes around the filename, in case it's a url
1826 * that contains #'s (process_create() calls
1827 * g_shell_parse_argv(), which deliberately throws
1828 * away anything after an unquoted #). Fixes bug
1831 args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
1832 lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1834 SetLastError (ERROR_INVALID_DATA);
1838 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1841 if (GetLastError () != ERROR_OUTOFMEMORY)
1842 SetLastError (ERROR_INVALID_DATA);
1846 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
1847 CloseHandle (process_info->process_handle);
1848 process_info->process_handle = NULL;
1853 process_info->pid = -GetLastError ();
1855 process_info->thread_handle = NULL;
1856 #if !defined(MONO_CROSS_COMPILE)
1857 process_info->pid = mono_w32process_get_pid (process_info->process_handle);
1859 process_info->pid = 0;
1861 process_info->tid = 0;
1867 /* Only used when UseShellExecute is false */
1869 process_get_complete_path (const gunichar2 *appname, gchar **completed)
1874 utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
1876 if (g_path_is_absolute (utf8app)) {
1877 *completed = g_shell_quote (utf8app);
1882 if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
1883 *completed = g_shell_quote (utf8app);
1888 found = g_find_program_in_path (utf8app);
1889 if (found == NULL) {
1895 *completed = g_shell_quote (found);
1902 process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
1904 gchar *spath = NULL;
1907 *cmd = proc_start_info->arguments;
1909 process_get_complete_path (mono_string_chars (proc_start_info->filename), &spath);
1910 if (spath != NULL) {
1911 *shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
1915 return (*shell_path != NULL) ? TRUE : FALSE;
1919 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info,
1920 HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info)
1924 StartupHandles startup_handles;
1925 gunichar2 *shell_path = NULL;
1926 gchar *env_vars = NULL;
1927 MonoString *cmd = NULL;
1929 memset (&startup_handles, 0, sizeof (startup_handles));
1930 startup_handles.input = stdin_handle;
1931 startup_handles.output = stdout_handle;
1932 startup_handles.error = stderr_handle;
1934 if (process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
1935 process_info->pid = -ERROR_FILE_NOT_FOUND;
1939 if (process_info->env_keys) {
1942 MonoString *key, *value;
1943 gunichar2 *str, *ptr;
1944 gunichar2 *equals16;
1946 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
1947 ms = mono_array_get (process_info->env_values, MonoString *, i);
1951 len += mono_string_length (ms) * sizeof (gunichar2);
1952 ms = mono_array_get (process_info->env_keys, MonoString *, i);
1953 len += mono_string_length (ms) * sizeof (gunichar2);
1954 len += 2 * sizeof (gunichar2);
1957 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
1958 ptr = str = g_new0 (gunichar2, len + 1);
1959 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
1960 value = mono_array_get (process_info->env_values, MonoString *, i);
1964 key = mono_array_get (process_info->env_keys, MonoString *, i);
1965 memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
1966 ptr += mono_string_length (key);
1968 memcpy (ptr, equals16, sizeof (gunichar2));
1971 memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
1972 ptr += mono_string_length (value);
1977 env_vars = (gchar *) str;
1980 /* The default dir name is "". Turn that into NULL to mean "current directory" */
1981 dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ?
1982 mono_string_chars (proc_start_info->working_directory) : NULL;
1984 ret = process_create (shell_path, cmd ? mono_string_chars (cmd): NULL, env_vars, dir, &startup_handles, process_info);
1987 if (shell_path != NULL)
1988 g_free (shell_path);
1991 process_info->pid = -GetLastError ();
1996 /* Returns an array of pids */
1998 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
2005 pidarray = mono_process_list (&count);
2007 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
2010 procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error);
2011 if (mono_error_set_pending_exception (&error)) {
2015 if (sizeof (guint32) == sizeof (gpointer)) {
2016 memcpy (mono_array_addr (procs, guint32, 0), pidarray, count * sizeof (gint32));
2018 for (i = 0; i < count; ++i)
2019 *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]);
2027 mono_w32process_set_cli_launcher (gchar *path)
2029 g_free (cli_launcher);
2030 cli_launcher = g_strdup (path);
2034 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2036 mono_w32handle_ref (current_process);
2037 return current_process;
2041 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
2043 MonoW32HandleProcess *process_handle;
2050 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2051 pid = WAPI_HANDLE_TO_PID (handle);
2052 /* This is a pseudo handle, so we don't know what the exit
2053 * code was, but we can check whether it's alive or not */
2054 if (is_pid_valid (pid)) {
2055 *exitcode = STILL_ACTIVE;
2063 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2065 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2069 if (process_handle->id == wapi_getpid ()) {
2070 *exitcode = STILL_ACTIVE;
2074 /* A process handle is only signalled if the process has exited
2075 * and has been waited for. Make sure any process exit has been
2076 * noticed before checking if the process is signalled.
2077 * Fixes bug 325463. */
2078 mono_w32handle_wait_one (handle, 0, TRUE);
2080 *exitcode = mono_w32handle_issignalled (handle) ? process_handle->exitstatus : STILL_ACTIVE;
2085 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
2087 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2089 return CloseHandle (handle);
2093 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
2096 MonoW32HandleProcess *process_handle;
2100 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2101 /* This is a pseudo handle */
2102 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2106 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2108 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2109 SetLastError (ERROR_INVALID_HANDLE);
2113 pid = process_handle->id;
2116 ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
2121 case EINVAL: SetLastError (ERROR_INVALID_PARAMETER); break;
2122 case EPERM: SetLastError (ERROR_ACCESS_DENIED); break;
2123 case ESRCH: SetLastError (ERROR_PROC_NOT_FOUND); break;
2124 default: SetLastError (ERROR_GEN_FAILURE); break;
2129 g_error ("kill() is not supported by this platform");
2134 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
2136 MonoW32HandleProcess *process_handle;
2142 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2145 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2147 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2151 *min = process_handle->min_working_set;
2152 *max = process_handle->max_working_set;
2157 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
2159 MonoW32HandleProcess *process_handle;
2162 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2165 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2167 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2171 process_handle->min_working_set = min;
2172 process_handle->max_working_set = max;
2177 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
2179 #ifdef HAVE_GETPRIORITY
2180 MonoW32HandleProcess *process_handle;
2184 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2185 /* This is a pseudo handle */
2186 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2190 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2192 SetLastError (ERROR_INVALID_HANDLE);
2196 pid = process_handle->id;
2200 ret = getpriority (PRIO_PROCESS, pid);
2201 if (ret == -1 && errno != 0) {
2205 SetLastError (ERROR_ACCESS_DENIED);
2208 SetLastError (ERROR_PROC_NOT_FOUND);
2211 SetLastError (ERROR_GEN_FAILURE);
2217 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2219 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME;
2221 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH;
2223 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2225 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE;
2227 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2229 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2231 SetLastError (ERROR_NOT_SUPPORTED);
2237 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
2239 #ifdef HAVE_SETPRIORITY
2240 MonoW32HandleProcess *process_handle;
2245 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2246 /* This is a pseudo handle */
2247 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2251 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2253 SetLastError (ERROR_INVALID_HANDLE);
2257 pid = process_handle->id;
2260 switch (priorityClass) {
2261 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
2264 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
2267 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL:
2270 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
2273 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH:
2276 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME:
2280 SetLastError (ERROR_INVALID_PARAMETER);
2284 ret = setpriority (PRIO_PROCESS, pid, prio);
2289 SetLastError (ERROR_ACCESS_DENIED);
2292 SetLastError (ERROR_PROC_NOT_FOUND);
2295 SetLastError (ERROR_GEN_FAILURE);
2301 SetLastError (ERROR_NOT_SUPPORTED);
2307 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
2309 processtime->lowDateTime = ticks & 0xFFFFFFFF;
2310 processtime->highDateTime = ticks >> 32;
2314 wapifiletime_to_processtime (WapiFileTime wapi_filetime, ProcessTime *processtime)
2316 processtime->lowDateTime = wapi_filetime.dwLowDateTime;
2317 processtime->highDateTime = wapi_filetime.dwHighDateTime;
2321 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
2323 MonoW32HandleProcess *process_handle;
2324 ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime;
2327 if (!creation_time || !exit_time || !kernel_time || !user_time) {
2328 /* Not sure if w32 allows NULLs here or not */
2332 creation_processtime = (ProcessTime*) creation_time;
2333 exit_processtime = (ProcessTime*) exit_time;
2334 kernel_processtime = (ProcessTime*) kernel_time;
2335 user_processtime = (ProcessTime*) user_time;
2337 memset (creation_processtime, 0, sizeof (ProcessTime));
2338 memset (exit_processtime, 0, sizeof (ProcessTime));
2339 memset (kernel_processtime, 0, sizeof (ProcessTime));
2340 memset (user_processtime, 0, sizeof (ProcessTime));
2342 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2343 gint64 start_ticks, user_ticks, kernel_ticks;
2345 mono_process_get_times (GINT_TO_POINTER (WAPI_HANDLE_TO_PID (handle)),
2346 &start_ticks, &user_ticks, &kernel_ticks);
2348 ticks_to_processtime (start_ticks, creation_processtime);
2349 ticks_to_processtime (user_ticks, kernel_processtime);
2350 ticks_to_processtime (kernel_ticks, user_processtime);
2354 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2356 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2360 wapifiletime_to_processtime (process_handle->create_time, creation_processtime);
2362 /* A process handle is only signalled if the process has
2363 * exited, otherwise exit_processtime isn't set */
2364 if (mono_w32handle_issignalled (handle))
2365 wapifiletime_to_processtime (process_handle->exit_time, exit_processtime);
2367 #ifdef HAVE_GETRUSAGE
2368 if (process_handle->id == getpid ()) {
2369 struct rusage time_data;
2370 if (getrusage (RUSAGE_SELF, &time_data) == 0) {
2371 ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime);
2372 ticks_to_processtime ((guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10, kernel_processtime);