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 != MONO_INFINITE_WAIT) {
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;
713 GSList *mods = NULL, *mods_iter;
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);
764 * Use the NULL shortcut, as the first line in
765 * /proc/<pid>/maps isn't the executable, and we need
766 * that first in the returned list. Check the module name
767 * to see if it ends with the proc name and substitute
768 * the first entry with it. FIXME if this turns out to
773 for (i = 0; mods_iter; i++) {
775 module = (MonoW32ProcessModule *)mods_iter->data;
776 if (modules[0] != NULL)
777 modules[i] = module->address_start;
778 else if (match_procname_to_modulename (proc_name, module->filename))
779 modules[0] = module->address_start;
781 modules[i + 1] = module->address_start;
783 mono_w32process_module_free ((MonoW32ProcessModule *)mods_iter->data);
784 mods_iter = g_slist_next (mods_iter);
788 /* count + 1 to leave slot 0 for the main module */
789 *needed = sizeof(gpointer) * (count + 1);
798 mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
803 gunichar2 *proc_path;
805 size *= sizeof (gunichar2); /* adjust for unicode characters */
807 if (basename == NULL || size == 0)
810 pid = mono_w32process_get_pid (process);
812 path = mono_w32process_get_path (pid);
816 proc_path = mono_unicode_from_external (path, &bytes);
819 if (proc_path == NULL)
824 /* Add the terminator */
828 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
829 memcpy (basename, proc_path, size);
831 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)", __func__, size, bytes);
832 memcpy (basename, proc_path, bytes);
841 mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
843 MonoW32HandleProcess *process_handle;
846 char *procname_ext = NULL;
849 GSList *mods = NULL, *mods_iter;
850 MonoW32ProcessModule *found_module;
851 char *proc_name = NULL;
854 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
855 __func__, process, module);
857 size = size * sizeof (gunichar2); /* adjust for unicode characters */
859 if (basename == NULL || size == 0)
862 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
863 /* This is a pseudo handle */
864 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
865 proc_name = mono_w32process_get_name (pid);
867 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
869 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
873 pid = process_handle->id;
874 proc_name = g_strdup (process_handle->proc_name);
877 mods = mono_w32process_get_modules (pid);
883 /* If module != NULL compare the address.
884 * If module == NULL we are looking for the main module.
885 * The best we can do for now check it the module name end with the process name.
887 for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
888 found_module = (MonoW32ProcessModule *)mods_iter->data;
889 if (procname_ext == NULL &&
890 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
891 (module != NULL && found_module->address_start == module))) {
892 procname_ext = g_path_get_basename (found_module->filename);
895 mono_w32process_module_free (found_module);
898 if (procname_ext == NULL) {
899 /* If it's *still* null, we might have hit the
900 * case where reading /proc/$pid/maps gives an
901 * empty file for this user.
903 procname_ext = mono_w32process_get_name (pid);
910 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
913 procname = mono_unicode_from_external (procname_ext, &bytes);
914 if (procname == NULL) {
916 g_free (procname_ext);
922 /* Add the terminator */
926 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
928 memcpy (basename, procname, size);
930 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
931 __func__, size, bytes);
933 memcpy (basename, procname, bytes);
937 g_free (procname_ext);
946 mono_w32process_module_get_information (gpointer process, gpointer module, WapiModuleInfo *modinfo, guint32 size)
948 MonoW32HandleProcess *process_handle;
950 GSList *mods = NULL, *mods_iter;
951 MonoW32ProcessModule *found_module;
952 gboolean ret = FALSE;
953 char *proc_name = NULL;
956 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
957 __func__, process, module);
959 if (modinfo == NULL || size < sizeof (WapiModuleInfo))
962 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
963 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
964 proc_name = mono_w32process_get_name (pid);
966 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
968 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
972 pid = process_handle->id;
973 proc_name = g_strdup (process_handle->proc_name);
976 mods = mono_w32process_get_modules (pid);
982 /* If module != NULL compare the address.
983 * If module == NULL we are looking for the main module.
984 * The best we can do for now check it the module name end with the process name.
986 for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
987 found_module = (MonoW32ProcessModule *)mods_iter->data;
989 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
990 (module != NULL && found_module->address_start == module))) {
991 modinfo->lpBaseOfDll = found_module->address_start;
992 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
993 modinfo->EntryPoint = found_module->address_offset;
997 mono_w32process_module_free (found_module);
1000 g_slist_free (mods);
1007 switch_dir_separators (char *path)
1009 size_t i, pathLength = strlen(path);
1011 /* Turn all the slashes round the right way, except for \' */
1012 /* There are probably other characters that need to be excluded as well. */
1013 for (i = 0; i < pathLength; i++) {
1014 if (path[i] == '\\' && i < pathLength - 1 && path[i+1] != '\'' )
1021 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
1029 pid = waitpid (-1, &status, WNOHANG);
1030 } while (pid == -1 && errno == EINTR);
1036 * This can run concurrently with the code in the rest of this module.
1038 for (p = processes; p; p = p->next) {
1042 p->pid = 0; /* this pid doesn't exist anymore, clear it */
1044 mono_os_sem_post (&p->exit_sem);
1045 mono_memory_barrier ();
1046 /* Mark this as freeable, the pointer becomes invalid afterwards */
1054 process_add_sigchld_handler (void)
1056 struct sigaction sa;
1058 sa.sa_sigaction = mono_sigchld_signal_handler;
1059 sigemptyset (&sa.sa_mask);
1060 sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1061 g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
1062 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
1068 is_readable_or_executable (const char *prog)
1071 int a = access (prog, R_OK);
1072 int b = access (prog, X_OK);
1073 if (a != 0 && b != 0)
1075 if (stat (prog, &buf))
1077 if (S_ISREG (buf.st_mode))
1083 is_executable (const char *prog)
1086 if (access (prog, X_OK) != 0)
1088 if (stat (prog, &buf))
1090 if (S_ISREG (buf.st_mode))
1096 is_managed_binary (const char *filename)
1098 int original_errno = errno;
1099 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1100 int file = open (filename, O_RDONLY | O_LARGEFILE);
1102 int file = open (filename, O_RDONLY);
1105 unsigned char buffer[8];
1106 off_t file_size, optional_header_offset;
1107 off_t pe_header_offset, clr_header_offset;
1108 gboolean managed = FALSE;
1110 guint32 first_word, second_word, magic_number;
1112 /* If we are unable to open the file, then we definitely
1113 * can't say that it is managed. The child mono process
1114 * probably wouldn't be able to open it anyway.
1117 errno = original_errno;
1121 /* Retrieve the length of the file for future sanity checks. */
1122 file_size = lseek (file, 0, SEEK_END);
1123 lseek (file, 0, SEEK_SET);
1125 /* We know we need to read a header field at offset 60. */
1129 num_read = read (file, buffer, 2);
1131 if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
1134 new_offset = lseek (file, 60, SEEK_SET);
1136 if (new_offset != 60)
1139 num_read = read (file, buffer, 4);
1143 pe_header_offset = buffer[0]
1146 | (buffer[3] << 24);
1148 if (pe_header_offset + 24 > file_size)
1151 new_offset = lseek (file, pe_header_offset, SEEK_SET);
1153 if (new_offset != pe_header_offset)
1156 num_read = read (file, buffer, 4);
1158 if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
1162 * Verify that the header we want in the optional header data
1163 * is present in this binary.
1165 new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
1167 if (new_offset != pe_header_offset + 20)
1170 num_read = read (file, buffer, 2);
1172 if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
1175 optional_header_offset = pe_header_offset + 24;
1177 /* Read the PE magic number */
1178 new_offset = lseek (file, optional_header_offset, SEEK_SET);
1180 if (new_offset != optional_header_offset)
1183 num_read = read (file, buffer, 2);
1188 magic_number = (buffer[0] | (buffer[1] << 8));
1190 if (magic_number == 0x10B) // PE32
1191 clr_header_offset = 208;
1192 else if (magic_number == 0x20B) // PE32+
1193 clr_header_offset = 224;
1197 /* Read the CLR header address and size fields. These will be
1198 * zero if the binary is not managed.
1200 new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET);
1202 if (new_offset != optional_header_offset + clr_header_offset)
1205 num_read = read (file, buffer, 8);
1207 /* We are not concerned with endianness, only with
1208 * whether it is zero or not.
1210 first_word = *(guint32 *)&buffer[0];
1211 second_word = *(guint32 *)&buffer[4];
1213 if ((num_read != 8) || (first_word == 0) || (second_word == 0))
1220 errno = original_errno;
1225 process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new_environ,
1226 const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info)
1228 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1229 char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
1230 char *dir = NULL, **env_strings = NULL, **argv = NULL;
1231 guint32 i, env_count = 0;
1232 gboolean ret = FALSE;
1233 gpointer handle = NULL;
1234 GError *gerr = NULL;
1235 int in_fd, out_fd, err_fd;
1237 int startup_pipe [2] = {-1, -1};
1239 MonoProcess *mono_process;
1242 mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
1245 /* appname and cmdline specify the executable and its args:
1247 * If appname is not NULL, it is the name of the executable.
1248 * Otherwise the executable is the first token in cmdline.
1250 * Executable searching:
1252 * If appname is not NULL, it can specify the full path and
1253 * file name, or else a partial name and the current directory
1254 * will be used. There is no additional searching.
1256 * If appname is NULL, the first whitespace-delimited token in
1257 * cmdline is used. If the name does not contain a full
1258 * directory path, the search sequence is:
1260 * 1) The directory containing the current process
1261 * 2) The current working directory
1262 * 3) The windows system directory (Ignored)
1263 * 4) The windows directory (Ignored)
1266 * Just to make things more interesting, tokens can contain
1267 * white space if they are surrounded by quotation marks. I'm
1268 * beginning to understand just why windows apps are generally
1269 * so crap, with an API like this :-(
1271 if (appname != NULL) {
1272 cmd = mono_unicode_to_external (appname);
1274 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL",
1277 SetLastError (ERROR_PATH_NOT_FOUND);
1281 switch_dir_separators(cmd);
1284 if (cmdline != NULL) {
1285 args = mono_unicode_to_external (cmdline);
1287 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1289 SetLastError (ERROR_PATH_NOT_FOUND);
1295 dir = mono_unicode_to_external (cwd);
1297 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1299 SetLastError (ERROR_PATH_NOT_FOUND);
1303 /* Turn all the slashes round the right way */
1304 switch_dir_separators(dir);
1308 /* We can't put off locating the executable any longer :-( */
1311 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
1312 /* Strip off the drive letter. I can't
1313 * believe that CP/M holdover is still
1316 g_memmove (cmd, cmd+2, strlen (cmd)-2);
1317 cmd[strlen (cmd)-2] = '\0';
1320 unquoted = g_shell_unquote (cmd, NULL);
1321 if (unquoted[0] == '/') {
1322 /* Assume full path given */
1323 prog = g_strdup (unquoted);
1325 /* Executable existing ? */
1326 if (!is_readable_or_executable (prog)) {
1327 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1330 SetLastError (ERROR_FILE_NOT_FOUND);
1334 /* Search for file named by cmd in the current
1337 char *curdir = g_get_current_dir ();
1339 prog = g_strdup_printf ("%s/%s", curdir, unquoted);
1342 /* And make sure it's readable */
1343 if (!is_readable_or_executable (prog)) {
1344 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1347 SetLastError (ERROR_FILE_NOT_FOUND);
1353 args_after_prog = args;
1358 /* Dig out the first token from args, taking quotation
1359 * marks into account
1362 /* First, strip off all leading whitespace */
1363 args = g_strchug (args);
1365 /* args_after_prog points to the contents of args
1366 * after token has been set (otherwise argv[0] is
1369 args_after_prog = args;
1371 /* Assume the opening quote will always be the first
1374 if (args[0] == '\"' || args [0] == '\'') {
1376 for (i = 1; args[i] != '\0' && args[i] != quote; i++);
1377 if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) {
1378 /* We found the first token */
1379 token = g_strndup (args+1, i-1);
1380 args_after_prog = g_strchug (args + i + 1);
1382 /* Quotation mark appeared in the
1383 * middle of the token. Just give the
1384 * whole first token, quotes and all,
1390 if (token == NULL) {
1391 /* No quote mark, or malformed */
1392 for (i = 0; args[i] != '\0'; i++) {
1393 if (g_ascii_isspace (args[i])) {
1394 token = g_strndup (args, i);
1395 args_after_prog = args + i + 1;
1401 if (token == NULL && args[0] != '\0') {
1402 /* Must be just one token in the string */
1403 token = g_strdup (args);
1404 args_after_prog = NULL;
1407 if (token == NULL) {
1409 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find what to exec", __func__);
1411 SetLastError (ERROR_PATH_NOT_FOUND);
1415 /* Turn all the slashes round the right way. Only for
1418 switch_dir_separators(token);
1420 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
1421 /* Strip off the drive letter. I can't
1422 * believe that CP/M holdover is still
1425 g_memmove (token, token+2, strlen (token)-2);
1426 token[strlen (token)-2] = '\0';
1429 if (token[0] == '/') {
1430 /* Assume full path given */
1431 prog = g_strdup (token);
1433 /* Executable existing ? */
1434 if (!is_readable_or_executable (prog)) {
1435 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1438 SetLastError (ERROR_FILE_NOT_FOUND);
1442 char *curdir = g_get_current_dir ();
1444 /* FIXME: Need to record the directory
1445 * containing the current process, and check
1446 * that for the new executable as the first
1450 prog = g_strdup_printf ("%s/%s", curdir, token);
1453 /* I assume X_OK is the criterion to use,
1456 * X_OK is too strict *if* the target is a CLR binary
1458 if (!is_readable_or_executable (prog)) {
1460 prog = g_find_program_in_path (token);
1462 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s", __func__, token);
1465 SetLastError (ERROR_FILE_NOT_FOUND);
1474 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Exec prog [%s] args [%s]",
1475 __func__, prog, args_after_prog);
1477 /* Check for CLR binaries; if found, we will try to invoke
1478 * them using the same mono binary that started us.
1480 if (is_managed_binary (prog)) {
1481 gunichar2 *newapp, *newcmd;
1482 gsize bytes_ignored;
1484 newapp = mono_unicode_from_external (cli_launcher ? cli_launcher : "mono", &bytes_ignored);
1487 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, NULL);
1489 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, NULL);
1494 ret = process_create (NULL, newcmd, new_environ, cwd, startup_handles, process_info);
1502 if (!is_executable (prog)) {
1503 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Executable permisson not set on %s", __func__, prog);
1504 SetLastError (ERROR_ACCESS_DENIED);
1509 if (args_after_prog != NULL && *args_after_prog) {
1512 qprog = g_shell_quote (prog);
1513 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
1516 full_prog = g_shell_quote (prog);
1519 ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
1521 g_message ("process_create: %s\n", gerr->message);
1522 g_error_free (gerr);
1527 if (startup_handles) {
1528 in_fd = GPOINTER_TO_UINT (startup_handles->input);
1529 out_fd = GPOINTER_TO_UINT (startup_handles->output);
1530 err_fd = GPOINTER_TO_UINT (startup_handles->error);
1532 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
1533 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
1534 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
1537 /* new_environ is a block of NULL-terminated strings, which
1538 * is itself NULL-terminated. Of course, passing an array of
1539 * string pointers would have made things too easy :-(
1541 * If new_environ is not NULL it specifies the entire set of
1542 * environment variables in the new process. Otherwise the
1543 * new process inherits the same environment.
1546 gunichar2 *new_environp;
1548 /* Count the number of strings */
1549 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1551 while (*new_environp)
1555 /* +2: one for the process handle value, and the last
1558 env_strings = g_new0 (char *, env_count + 2);
1560 /* Copy each environ string into 'strings' turning it
1561 * into utf8 (or the requested encoding) at the same
1565 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1566 env_strings[env_count] = mono_unicode_to_external (new_environp);
1568 while (*new_environp) {
1573 for (i = 0; environ[i] != NULL; i++)
1576 /* +2: one for the process handle value, and the last
1579 env_strings = g_new0 (char *, env_count + 2);
1581 /* Copy each environ string into 'strings' turning it
1582 * into utf8 (or the requested encoding) at the same
1586 for (i = 0; environ[i] != NULL; i++) {
1587 env_strings[env_count] = g_strdup (environ[i]);
1592 /* Create a pipe to make sure the child doesn't exit before
1593 * we can add the process to the linked list of processes */
1594 if (pipe (startup_pipe) == -1) {
1595 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1596 * This is just for a very hard to hit race condition in the first place */
1597 startup_pipe [0] = startup_pipe [1] = -1;
1598 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__);
1602 /* FIXME: block SIGCHLD */
1605 switch (pid = fork ()) {
1606 case -1: /* Error */ {
1607 SetLastError (ERROR_OUTOFMEMORY);
1611 case 0: /* Child */ {
1612 if (startup_pipe [0] != -1) {
1613 /* Wait until the parent has updated it's internal data */
1614 ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1);
1615 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: child: parent has completed its setup", __func__);
1616 close (startup_pipe [0]);
1617 close (startup_pipe [1]);
1620 /* should we detach from the process group? */
1622 /* Connect stdin, stdout and stderr */
1627 /* Close all file descriptors */
1628 for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
1631 #ifdef DEBUG_ENABLED
1632 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1633 dir == NULL?".":dir);
1634 for (i = 0; argv[i] != NULL; i++)
1635 g_message ("arg %d: [%s]", i, argv[i]);
1637 for (i = 0; env_strings[i] != NULL; i++)
1638 g_message ("env %d: [%s]", i, env_strings[i]);
1642 if (dir != NULL && chdir (dir) == -1) {
1648 execve (argv[0], argv, env_strings);
1655 default: /* Parent */ {
1656 MonoW32HandleProcess process_handle;
1658 memset (&process_handle, 0, sizeof (process_handle));
1659 process_handle.id = pid;
1660 process_handle.proc_name = g_strdup (prog);
1661 process_set_defaults (&process_handle);
1663 /* Add our mono_process into the linked list of processes */
1664 mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess));
1665 mono_process->pid = pid;
1666 mono_process->handle_count = 1;
1667 mono_os_sem_init (&mono_process->exit_sem, 0);
1669 process_handle.mono_process = mono_process;
1671 handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
1672 if (handle == INVALID_HANDLE_VALUE) {
1673 g_warning ("%s: error creating process handle", __func__);
1675 mono_os_sem_destroy (&mono_process->exit_sem);
1676 g_free (mono_process);
1678 SetLastError (ERROR_OUTOFMEMORY);
1683 /* Keep the process handle artificially alive until the process
1684 * exits so that the information in the handle isn't lost. */
1685 mono_w32handle_ref (handle);
1686 mono_process->handle = handle;
1688 mono_os_mutex_lock (&processes_mutex);
1689 mono_process->next = processes;
1690 processes = mono_process;
1691 mono_os_mutex_unlock (&processes_mutex);
1693 if (process_info != NULL) {
1694 process_info->process_handle = handle;
1695 process_info->pid = pid;
1697 /* FIXME: we might need to handle the thread info some day */
1698 process_info->thread_handle = INVALID_HANDLE_VALUE;
1699 process_info->tid = 0;
1707 /* FIXME: unblock SIGCHLD */
1710 if (startup_pipe [1] != -1) {
1711 /* Write 1 byte, doesn't matter what */
1712 ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
1713 close (startup_pipe [0]);
1714 close (startup_pipe [1]);
1729 g_strfreev (env_strings);
1733 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p for pid %d", __func__, handle, pid);
1735 /* Check if something needs to be cleaned up. */
1736 processes_cleanup ();
1740 SetLastError (ERROR_NOT_SUPPORTED);
1742 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1746 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
1748 const gunichar2 *lpFile;
1749 const gunichar2 *lpParameters;
1750 const gunichar2 *lpDirectory;
1754 if (!proc_start_info->filename) {
1755 /* w2k returns TRUE for this, for some reason. */
1760 lpFile = proc_start_info->filename ? mono_string_chars (proc_start_info->filename) : NULL;
1761 lpParameters = proc_start_info->arguments ? mono_string_chars (proc_start_info->arguments) : NULL;
1762 lpDirectory = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) != 0 ?
1763 mono_string_chars (proc_start_info->working_directory) : NULL;
1765 /* Put both executable and parameters into the second argument
1766 * to process_create (), so it searches $PATH. The conversion
1767 * into and back out of utf8 is because there is no
1768 * g_strdup_printf () equivalent for gunichar2 :-(
1770 args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1772 SetLastError (ERROR_INVALID_DATA);
1776 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1779 if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
1783 static char *handler;
1784 static gunichar2 *handler_utf16;
1786 if (handler_utf16 == (gunichar2 *)-1) {
1791 #ifdef PLATFORM_MACOSX
1792 handler = g_strdup ("/usr/bin/open");
1795 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
1796 * if that fails, try to use gnome-open, then kfmclient
1798 handler = g_find_program_in_path ("xdg-open");
1799 if (handler == NULL){
1800 handler = g_find_program_in_path ("gnome-open");
1801 if (handler == NULL){
1802 handler = g_find_program_in_path ("kfmclient");
1803 if (handler == NULL){
1804 handler_utf16 = (gunichar2 *) -1;
1808 /* kfmclient needs exec argument */
1809 char *old = handler;
1810 handler = g_strconcat (old, " exec",
1817 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
1820 /* Put quotes around the filename, in case it's a url
1821 * that contains #'s (process_create() calls
1822 * g_shell_parse_argv(), which deliberately throws
1823 * away anything after an unquoted #). Fixes bug
1826 args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
1827 lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1829 SetLastError (ERROR_INVALID_DATA);
1833 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1836 if (GetLastError () != ERROR_OUTOFMEMORY)
1837 SetLastError (ERROR_INVALID_DATA);
1841 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
1842 CloseHandle (process_info->process_handle);
1843 process_info->process_handle = NULL;
1848 process_info->pid = -GetLastError ();
1850 process_info->thread_handle = NULL;
1851 #if !defined(MONO_CROSS_COMPILE)
1852 process_info->pid = mono_w32process_get_pid (process_info->process_handle);
1854 process_info->pid = 0;
1856 process_info->tid = 0;
1862 /* Only used when UseShellExecute is false */
1864 process_get_complete_path (const gunichar2 *appname, gchar **completed)
1869 utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
1871 if (g_path_is_absolute (utf8app)) {
1872 *completed = g_shell_quote (utf8app);
1877 if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
1878 *completed = g_shell_quote (utf8app);
1883 found = g_find_program_in_path (utf8app);
1884 if (found == NULL) {
1890 *completed = g_shell_quote (found);
1897 process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
1899 gchar *spath = NULL;
1902 *cmd = proc_start_info->arguments;
1904 process_get_complete_path (mono_string_chars (proc_start_info->filename), &spath);
1905 if (spath != NULL) {
1906 *shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
1910 return (*shell_path != NULL) ? TRUE : FALSE;
1914 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info,
1915 HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info)
1919 StartupHandles startup_handles;
1920 gunichar2 *shell_path = NULL;
1921 gchar *env_vars = NULL;
1922 MonoString *cmd = NULL;
1924 memset (&startup_handles, 0, sizeof (startup_handles));
1925 startup_handles.input = stdin_handle;
1926 startup_handles.output = stdout_handle;
1927 startup_handles.error = stderr_handle;
1929 if (process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
1930 process_info->pid = -ERROR_FILE_NOT_FOUND;
1934 if (process_info->env_keys) {
1937 MonoString *key, *value;
1938 gunichar2 *str, *ptr;
1939 gunichar2 *equals16;
1941 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
1942 ms = mono_array_get (process_info->env_values, MonoString *, i);
1946 len += mono_string_length (ms) * sizeof (gunichar2);
1947 ms = mono_array_get (process_info->env_keys, MonoString *, i);
1948 len += mono_string_length (ms) * sizeof (gunichar2);
1949 len += 2 * sizeof (gunichar2);
1952 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
1953 ptr = str = g_new0 (gunichar2, len + 1);
1954 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
1955 value = mono_array_get (process_info->env_values, MonoString *, i);
1959 key = mono_array_get (process_info->env_keys, MonoString *, i);
1960 memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
1961 ptr += mono_string_length (key);
1963 memcpy (ptr, equals16, sizeof (gunichar2));
1966 memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
1967 ptr += mono_string_length (value);
1972 env_vars = (gchar *) str;
1975 /* The default dir name is "". Turn that into NULL to mean "current directory" */
1976 dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ?
1977 mono_string_chars (proc_start_info->working_directory) : NULL;
1979 ret = process_create (shell_path, cmd ? mono_string_chars (cmd): NULL, env_vars, dir, &startup_handles, process_info);
1982 if (shell_path != NULL)
1983 g_free (shell_path);
1986 process_info->pid = -GetLastError ();
1991 /* Returns an array of pids */
1993 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
2000 pidarray = mono_process_list (&count);
2002 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
2005 procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error);
2006 if (mono_error_set_pending_exception (&error)) {
2010 if (sizeof (guint32) == sizeof (gpointer)) {
2011 memcpy (mono_array_addr (procs, guint32, 0), pidarray, count * sizeof (gint32));
2013 for (i = 0; i < count; ++i)
2014 *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]);
2022 mono_w32process_set_cli_launcher (gchar *path)
2024 g_free (cli_launcher);
2025 cli_launcher = g_strdup (path);
2029 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2031 mono_w32handle_ref (current_process);
2032 return current_process;
2036 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
2038 MonoW32HandleProcess *process_handle;
2045 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2046 pid = WAPI_HANDLE_TO_PID (handle);
2047 /* This is a pseudo handle, so we don't know what the exit
2048 * code was, but we can check whether it's alive or not */
2049 if (is_pid_valid (pid)) {
2050 *exitcode = STILL_ACTIVE;
2058 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2060 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2064 if (process_handle->id == wapi_getpid ()) {
2065 *exitcode = STILL_ACTIVE;
2069 /* A process handle is only signalled if the process has exited
2070 * and has been waited for. Make sure any process exit has been
2071 * noticed before checking if the process is signalled.
2072 * Fixes bug 325463. */
2073 mono_w32handle_wait_one (handle, 0, TRUE);
2075 *exitcode = mono_w32handle_issignalled (handle) ? process_handle->exitstatus : STILL_ACTIVE;
2080 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
2082 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2084 return CloseHandle (handle);
2088 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
2091 MonoW32HandleProcess *process_handle;
2095 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2096 /* This is a pseudo handle */
2097 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2101 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2103 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2104 SetLastError (ERROR_INVALID_HANDLE);
2108 pid = process_handle->id;
2111 ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
2116 case EINVAL: SetLastError (ERROR_INVALID_PARAMETER); break;
2117 case EPERM: SetLastError (ERROR_ACCESS_DENIED); break;
2118 case ESRCH: SetLastError (ERROR_PROC_NOT_FOUND); break;
2119 default: SetLastError (ERROR_GEN_FAILURE); break;
2124 g_error ("kill() is not supported by this platform");
2129 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
2131 MonoW32HandleProcess *process_handle;
2137 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2140 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2142 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2146 *min = process_handle->min_working_set;
2147 *max = process_handle->max_working_set;
2152 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
2154 MonoW32HandleProcess *process_handle;
2157 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2160 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2162 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2166 process_handle->min_working_set = min;
2167 process_handle->max_working_set = max;
2172 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
2174 #ifdef HAVE_GETPRIORITY
2175 MonoW32HandleProcess *process_handle;
2179 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2180 /* This is a pseudo handle */
2181 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2185 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2187 SetLastError (ERROR_INVALID_HANDLE);
2191 pid = process_handle->id;
2195 ret = getpriority (PRIO_PROCESS, pid);
2196 if (ret == -1 && errno != 0) {
2200 SetLastError (ERROR_ACCESS_DENIED);
2203 SetLastError (ERROR_PROC_NOT_FOUND);
2206 SetLastError (ERROR_GEN_FAILURE);
2212 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2214 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME;
2216 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH;
2218 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2220 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE;
2222 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2224 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2226 SetLastError (ERROR_NOT_SUPPORTED);
2232 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
2234 #ifdef HAVE_SETPRIORITY
2235 MonoW32HandleProcess *process_handle;
2240 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2241 /* This is a pseudo handle */
2242 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2246 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2248 SetLastError (ERROR_INVALID_HANDLE);
2252 pid = process_handle->id;
2255 switch (priorityClass) {
2256 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
2259 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
2262 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL:
2265 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
2268 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH:
2271 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME:
2275 SetLastError (ERROR_INVALID_PARAMETER);
2279 ret = setpriority (PRIO_PROCESS, pid, prio);
2284 SetLastError (ERROR_ACCESS_DENIED);
2287 SetLastError (ERROR_PROC_NOT_FOUND);
2290 SetLastError (ERROR_GEN_FAILURE);
2296 SetLastError (ERROR_NOT_SUPPORTED);
2302 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
2304 processtime->lowDateTime = ticks & 0xFFFFFFFF;
2305 processtime->highDateTime = ticks >> 32;
2309 wapifiletime_to_processtime (WapiFileTime wapi_filetime, ProcessTime *processtime)
2311 processtime->lowDateTime = wapi_filetime.dwLowDateTime;
2312 processtime->highDateTime = wapi_filetime.dwHighDateTime;
2316 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
2318 MonoW32HandleProcess *process_handle;
2319 ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime;
2322 if (!creation_time || !exit_time || !kernel_time || !user_time) {
2323 /* Not sure if w32 allows NULLs here or not */
2327 creation_processtime = (ProcessTime*) creation_time;
2328 exit_processtime = (ProcessTime*) exit_time;
2329 kernel_processtime = (ProcessTime*) kernel_time;
2330 user_processtime = (ProcessTime*) user_time;
2332 memset (creation_processtime, 0, sizeof (ProcessTime));
2333 memset (exit_processtime, 0, sizeof (ProcessTime));
2334 memset (kernel_processtime, 0, sizeof (ProcessTime));
2335 memset (user_processtime, 0, sizeof (ProcessTime));
2337 if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2338 gint64 start_ticks, user_ticks, kernel_ticks;
2340 mono_process_get_times (GINT_TO_POINTER (WAPI_HANDLE_TO_PID (handle)),
2341 &start_ticks, &user_ticks, &kernel_ticks);
2343 ticks_to_processtime (start_ticks, creation_processtime);
2344 ticks_to_processtime (user_ticks, kernel_processtime);
2345 ticks_to_processtime (kernel_ticks, user_processtime);
2349 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2351 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2355 wapifiletime_to_processtime (process_handle->create_time, creation_processtime);
2357 /* A process handle is only signalled if the process has
2358 * exited, otherwise exit_processtime isn't set */
2359 if (mono_w32handle_issignalled (handle))
2360 wapifiletime_to_processtime (process_handle->exit_time, exit_processtime);
2362 #ifdef HAVE_GETRUSAGE
2363 if (process_handle->id == getpid ()) {
2364 struct rusage time_data;
2365 if (getrusage (RUSAGE_SELF, &time_data) == 0) {
2366 ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime);
2367 ticks_to_processtime ((guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10, kernel_processtime);