Merge pull request #3936 from kumpera/monoclass_reorg2
[mono-project.git] / mono / metadata / w32process-unix.c
blob82663cacb221dcee14986ecf82b56625595eab8e
1 /*
2 * process.c: System.Diagnostics.Process support
4 * Author:
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.
12 #include <config.h>
13 #include <glib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <pthread.h>
18 #include <sched.h>
19 #include <sys/time.h>
20 #include <errno.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #ifdef HAVE_SIGNAL_H
25 #include <signal.h>
26 #endif
27 #include <sys/time.h>
28 #include <fcntl.h>
29 #ifdef HAVE_SYS_PARAM_H
30 #include <sys/param.h>
31 #endif
32 #include <ctype.h>
34 #ifdef HAVE_SYS_WAIT_H
35 #include <sys/wait.h>
36 #endif
37 #ifdef HAVE_SYS_RESOURCE_H
38 #include <sys/resource.h>
39 #endif
41 #ifdef HAVE_SYS_MKDEV_H
42 #include <sys/mkdev.h>
43 #endif
45 #ifdef HAVE_UTIME_H
46 #include <utime.h>
47 #endif
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>
70 #ifndef MAXPATHLEN
71 #define MAXPATHLEN 242
72 #endif
74 #define STILL_ACTIVE ((int) 0x00000103)
76 #define LOGDEBUG(...)
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())
88 #else
89 static char *mono_environ[1] = { NULL };
90 #define environ mono_environ
91 #endif /* defined (TARGET_OSX) */
92 #else
93 extern char **environ;
94 #endif
96 typedef enum {
97 STARTF_USESHOWWINDOW=0x001,
98 STARTF_USESIZE=0x002,
99 STARTF_USEPOSITION=0x004,
100 STARTF_USECOUNTCHARS=0x008,
101 STARTF_USEFILLATTRIBUTE=0x010,
102 STARTF_RUNFULLSCREEN=0x020,
103 STARTF_FORCEONFEEDBACK=0x040,
104 STARTF_FORCEOFFFEEDBACK=0x080,
105 STARTF_USESTDHANDLES=0x100
106 } StartupFlags;
108 typedef struct {
109 gpointer input;
110 gpointer output;
111 gpointer error;
112 } StartupHandles;
114 typedef struct {
115 #if G_BYTE_ORDER == G_BIG_ENDIAN
116 guint32 highDateTime;
117 guint32 lowDateTime;
118 #else
119 guint32 lowDateTime;
120 guint32 highDateTime;
121 #endif
122 } ProcessTime;
125 * MonoProcess describes processes we create.
126 * It contains a semaphore that can be waited on in order to wait
127 * for process termination. It's accessed in our SIGCHLD handler,
128 * when status is updated (and pid cleared, to not clash with
129 * subsequent processes that may get executed).
131 typedef struct _MonoProcess MonoProcess;
132 struct _MonoProcess {
133 pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
134 MonoSemType exit_sem; /* this semaphore will be released when the process exits */
135 int status; /* the exit status */
136 gint32 handle_count; /* the number of handles to this mono_process instance */
137 /* we keep a ref to the creating _WapiHandle_process handle until
138 * the process has exited, so that the information there isn't lost.
140 gpointer handle;
141 gboolean freeable;
142 MonoProcess *next;
145 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
146 typedef struct {
147 pid_t id;
148 gboolean child;
149 guint32 exitstatus;
150 gpointer main_thread;
151 WapiFileTime create_time;
152 WapiFileTime exit_time;
153 char *proc_name;
154 size_t min_working_set;
155 size_t max_working_set;
156 gboolean exited;
157 MonoProcess *mono_process;
158 } MonoW32HandleProcess;
160 #if HAVE_SIGACTION
161 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
162 #endif
164 static gchar *cli_launcher;
166 /* The signal-safe logic to use processes goes like this:
167 * - The list must be safe to traverse for the signal handler at all times.
168 * It's safe to: prepend an entry (which is a single store to 'processes'),
169 * unlink an entry (assuming the unlinked entry isn't freed and doesn't
170 * change its 'next' pointer so that it can still be traversed).
171 * When cleaning up we first unlink an entry, then we verify that
172 * the read lock isn't locked. Then we can free the entry, since
173 * we know that nobody is using the old version of the list (including
174 * the unlinked entry).
175 * We also need to lock when adding and cleaning up so that those two
176 * operations don't mess with eachother. (This lock is not used in the
177 * signal handler) */
178 static MonoProcess *processes;
179 static mono_mutex_t processes_mutex;
181 static gpointer current_process;
183 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
184 static const gunichar2 *utf16_space = utf16_space_bytes;
185 static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
186 static const gunichar2 *utf16_quote = utf16_quote_bytes;
188 /* Check if a pid is valid - i.e. if a process exists with this pid. */
189 static gboolean
190 process_is_alive (pid_t pid)
192 #if defined(HOST_WATCHOS)
193 return TRUE; // TODO: Rewrite using sysctl
194 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
195 if (pid == 0)
196 return FALSE;
197 if (kill (pid, 0) == 0)
198 return TRUE;
199 if (errno == EPERM)
200 return TRUE;
201 return FALSE;
202 #elif defined(__HAIKU__)
203 team_info teamInfo;
204 if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
205 return TRUE;
206 return FALSE;
207 #else
208 gchar *dir = g_strdup_printf ("/proc/%d", pid);
209 gboolean result = access (dir, F_OK) == 0;
210 g_free (dir);
211 return result;
212 #endif
215 static void
216 process_details (gpointer data)
218 MonoW32HandleProcess *process_handle = (MonoW32HandleProcess *) data;
219 g_print ("id: %d, exited: %s, exitstatus: %d",
220 process_handle->id, process_handle->exited ? "true" : "false", process_handle->exitstatus);
223 static const gchar*
224 process_typename (void)
226 return "Process";
229 static gsize
230 process_typesize (void)
232 return sizeof (MonoW32HandleProcess);
235 static MonoW32HandleWaitRet
236 process_wait (gpointer handle, guint32 timeout, gboolean *alerted)
238 MonoW32HandleProcess *process_handle;
239 pid_t pid G_GNUC_UNUSED, ret;
240 int status;
241 gint64 start, now;
242 MonoProcess *mp;
243 gboolean res;
245 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
247 if (alerted)
248 *alerted = FALSE;
250 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
251 if (!res) {
252 g_warning ("%s: error looking up process handle %p", __func__, handle);
253 return MONO_W32HANDLE_WAIT_RET_FAILED;
256 if (process_handle->exited) {
257 /* We've already done this one */
258 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Process already exited", __func__, handle, timeout);
259 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
262 pid = process_handle->id;
264 if (pid == mono_process_current_pid ()) {
265 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout);
266 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
269 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
271 /* We don't need to lock processes here, the entry
272 * has a handle_count > 0 which means it will not be freed. */
273 mp = process_handle->mono_process;
274 if (!mp) {
275 pid_t res;
277 /* This path is used when calling Process.HasExited, so
278 * it is only used to poll the state of the process, not
279 * to actually wait on it to exit */
280 g_assert (timeout == 0);
282 /* We alway create a MonoProcess for a child process */
283 g_assert (!process_handle->child);
285 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout);
287 res = waitpid (pid, &status, WNOHANG);
288 if (res != -1)
289 g_error ("%s: a process handle without a mono_process is not a child process", __func__);
291 if (errno == ECHILD) {
292 if (!process_is_alive (pid)) {
293 /* assume the process had exited */
294 process_handle->exited = TRUE;
295 process_handle->exitstatus = -1;
296 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
299 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully (2)", __func__, handle, timeout);
300 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
303 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);
304 return MONO_W32HANDLE_WAIT_RET_FAILED;
307 start = mono_msec_ticks ();
308 now = start;
310 while (1) {
311 if (timeout != INFINITE) {
312 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore for %li ms...",
313 __func__, handle, timeout, (long)(timeout - (now - start)));
314 ret = mono_os_sem_timedwait (&mp->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
315 } else {
316 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore forever...",
317 __func__, handle, timeout);
318 ret = mono_os_sem_wait (&mp->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
321 if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
322 /* Success, process has exited */
323 mono_os_sem_post (&mp->exit_sem);
324 break;
327 if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
328 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout (timeout = 0)", __func__, handle, timeout);
329 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
332 now = mono_msec_ticks ();
333 if (now - start >= timeout) {
334 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout", __func__, handle, timeout);
335 return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
338 if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
339 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait alerted", __func__, handle, timeout);
340 *alerted = TRUE;
341 return MONO_W32HANDLE_WAIT_RET_ALERTED;
345 /* Process must have exited */
346 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout);
348 status = mp->status;
349 if (WIFSIGNALED (status))
350 process_handle->exitstatus = 128 + WTERMSIG (status);
351 else
352 process_handle->exitstatus = WEXITSTATUS (status);
353 _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
355 process_handle->exited = TRUE;
357 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Setting pid %d signalled, exit status %d",
358 __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
360 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
362 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
365 static void
366 processes_cleanup (void)
368 static gint32 cleaning_up;
369 MonoProcess *mp;
370 MonoProcess *prev = NULL;
371 GSList *finished = NULL;
372 GSList *l;
373 gpointer unref_handle;
375 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
377 /* Ensure we're not in here in multiple threads at once, nor recursive. */
378 if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
379 return;
381 for (mp = processes; mp; mp = mp->next) {
382 if (mp->pid == 0 && mp->handle) {
383 /* This process has exited and we need to remove the artifical ref
384 * on the handle */
385 mono_os_mutex_lock (&processes_mutex);
386 unref_handle = mp->handle;
387 mp->handle = NULL;
388 mono_os_mutex_unlock (&processes_mutex);
389 if (unref_handle)
390 mono_w32handle_unref (unref_handle);
395 * Remove processes which exited from the processes list.
396 * We need to synchronize with the sigchld handler here, which runs
397 * asynchronously. The handler requires that the processes list
398 * remain valid.
400 mono_os_mutex_lock (&processes_mutex);
402 mp = processes;
403 while (mp) {
404 if (mp->handle_count == 0 && mp->freeable) {
406 * Unlink the entry.
407 * This code can run parallel with the sigchld handler, but the
408 * modifications it makes are safe.
410 if (mp == processes)
411 processes = mp->next;
412 else
413 prev->next = mp->next;
414 finished = g_slist_prepend (finished, mp);
416 mp = mp->next;
417 } else {
418 prev = mp;
419 mp = mp->next;
423 mono_memory_barrier ();
425 for (l = finished; l; l = l->next) {
427 * All the entries in the finished list are unlinked from processes, and
428 * they have the 'finished' flag set, which means the sigchld handler is done
429 * accessing them.
431 mp = (MonoProcess *)l->data;
432 mono_os_sem_destroy (&mp->exit_sem);
433 g_free (mp);
435 g_slist_free (finished);
437 mono_os_mutex_unlock (&processes_mutex);
439 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
441 InterlockedExchange (&cleaning_up, 0);
444 static void
445 process_close (gpointer handle, gpointer data)
447 MonoW32HandleProcess *process_handle;
449 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
451 process_handle = (MonoW32HandleProcess *) data;
452 g_free (process_handle->proc_name);
453 process_handle->proc_name = NULL;
454 if (process_handle->mono_process)
455 InterlockedDecrement (&process_handle->mono_process->handle_count);
456 processes_cleanup ();
459 static MonoW32HandleOps process_ops = {
460 process_close, /* close_shared */
461 NULL, /* signal */
462 NULL, /* own */
463 NULL, /* is_owned */
464 process_wait, /* special_wait */
465 NULL, /* prewait */
466 process_details, /* details */
467 process_typename, /* typename */
468 process_typesize, /* typesize */
471 static void
472 process_set_defaults (MonoW32HandleProcess *process_handle)
474 /* These seem to be the defaults on w2k */
475 process_handle->min_working_set = 204800;
476 process_handle->max_working_set = 1413120;
478 _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
481 static void
482 process_set_name (MonoW32HandleProcess *process_handle)
484 char *progname, *utf8_progname, *slash;
486 progname = g_get_prgname ();
487 utf8_progname = mono_utf8_from_external (progname);
489 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: using [%s] as prog name", __func__, progname);
491 if (utf8_progname) {
492 slash = strrchr (utf8_progname, '/');
493 if (slash)
494 process_handle->proc_name = g_strdup (slash+1);
495 else
496 process_handle->proc_name = g_strdup (utf8_progname);
497 g_free (utf8_progname);
501 void
502 mono_w32process_init (void)
504 MonoW32HandleProcess process_handle;
506 mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS, &process_ops);
508 mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS,
509 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
511 memset (&process_handle, 0, sizeof (process_handle));
512 process_handle.id = wapi_getpid ();
513 process_set_defaults (&process_handle);
514 process_set_name (&process_handle);
516 current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
517 g_assert (current_process);
519 mono_os_mutex_init (&processes_mutex);
522 void
523 mono_w32process_cleanup (void)
525 g_free (cli_launcher);
528 static int
529 len16 (const gunichar2 *str)
531 int len = 0;
533 while (*str++ != 0)
534 len++;
536 return len;
539 static gunichar2 *
540 utf16_concat (const gunichar2 *first, ...)
542 va_list args;
543 int total = 0, i;
544 const gunichar2 *s;
545 const gunichar2 *p;
546 gunichar2 *ret;
548 va_start (args, first);
549 total += len16 (first);
550 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *))
551 total += len16 (s);
552 va_end (args);
554 ret = g_new (gunichar2, total + 1);
555 if (ret == NULL)
556 return NULL;
558 ret [total] = 0;
559 i = 0;
560 for (s = first; *s != 0; s++)
561 ret [i++] = *s;
562 va_start (args, first);
563 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
564 for (p = s; *p != 0; p++)
565 ret [i++] = *p;
567 va_end (args);
569 return ret;
572 guint32
573 mono_w32process_get_pid (gpointer handle)
575 MonoW32HandleProcess *process_handle;
576 gboolean res;
578 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
579 if (!res) {
580 SetLastError (ERROR_INVALID_HANDLE);
581 return 0;
584 return process_handle->id;
587 typedef struct {
588 guint32 pid;
589 gpointer handle;
590 } GetProcessForeachData;
592 static gboolean
593 get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
595 GetProcessForeachData *foreach_data;
596 MonoW32HandleProcess *process_handle;
597 pid_t pid;
599 foreach_data = (GetProcessForeachData*) user_data;
601 if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS)
602 return FALSE;
604 process_handle = (MonoW32HandleProcess*) handle_specific;
606 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id);
608 pid = process_handle->id;
609 if (pid == 0)
610 return FALSE;
612 /* It's possible to have more than one process handle with the
613 * same pid, but only the one running process can be
614 * unsignalled. */
615 if (foreach_data->pid != pid)
616 return FALSE;
617 if (mono_w32handle_issignalled (handle))
618 return FALSE;
620 mono_w32handle_ref (handle);
621 foreach_data->handle = handle;
622 return TRUE;
625 HANDLE
626 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
628 GetProcessForeachData foreach_data;
629 gpointer handle;
631 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for process %d", __func__, pid);
633 memset (&foreach_data, 0, sizeof (foreach_data));
634 foreach_data.pid = pid;
635 mono_w32handle_foreach (get_process_foreach_callback, &foreach_data);
636 handle = foreach_data.handle;
637 if (handle) {
638 /* get_process_foreach_callback already added a ref */
639 return handle;
642 if (process_is_alive (pid)) {
643 /* non-child process */
644 MonoW32HandleProcess process_handle;
646 memset (&process_handle, 0, sizeof (process_handle));
647 process_handle.id = pid;
648 process_handle.proc_name = mono_w32process_get_name (pid);
650 handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
651 if (handle == INVALID_HANDLE_VALUE) {
652 g_warning ("%s: error creating process handle", __func__);
654 SetLastError (ERROR_OUTOFMEMORY);
655 return NULL;
658 return handle;
661 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid);
663 SetLastError (ERROR_PROC_NOT_FOUND);
664 return NULL;
667 static gboolean
668 match_procname_to_modulename (char *procname, char *modulename)
670 char* lastsep = NULL;
671 char* lastsep2 = NULL;
672 char* pname = NULL;
673 char* mname = NULL;
674 gboolean result = FALSE;
676 if (procname == NULL || modulename == NULL)
677 return (FALSE);
679 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename);
680 pname = mono_path_resolve_symlinks (procname);
681 mname = mono_path_resolve_symlinks (modulename);
683 if (!strcmp (pname, mname))
684 result = TRUE;
686 if (!result) {
687 lastsep = strrchr (mname, '/');
688 if (lastsep)
689 if (!strcmp (lastsep+1, pname))
690 result = TRUE;
691 if (!result) {
692 lastsep2 = strrchr (pname, '/');
693 if (lastsep2){
694 if (lastsep) {
695 if (!strcmp (lastsep+1, lastsep2+1))
696 result = TRUE;
697 } else {
698 if (!strcmp (mname, lastsep2+1))
699 result = TRUE;
705 g_free (pname);
706 g_free (mname);
708 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
709 return result;
712 gboolean
713 mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed)
715 MonoW32HandleProcess *process_handle;
716 GSList *mods = NULL;
717 MonoW32ProcessModule *module;
718 guint32 count, avail = size / sizeof(gpointer);
719 int i;
720 pid_t pid;
721 char *proc_name = NULL;
722 gboolean res;
724 /* Store modules in an array of pointers (main module as
725 * modules[0]), using the load address for each module as a
726 * token. (Use 'NULL' as an alternative for the main module
727 * so that the simple implementation can just return one item
728 * for now.) Get the info from /proc/<pid>/maps on linux,
729 * /proc/<pid>/map on FreeBSD, other systems will have to
730 * implement /dev/kmem reading or whatever other horrid
731 * technique is needed.
733 if (size < sizeof(gpointer))
734 return FALSE;
736 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
737 if (!res) {
738 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
739 return FALSE;
742 pid = process_handle->id;
743 proc_name = g_strdup (process_handle->proc_name);
745 if (!proc_name) {
746 modules[0] = NULL;
747 *needed = sizeof(gpointer);
748 return TRUE;
751 mods = mono_w32process_get_modules (pid);
752 if (!mods) {
753 modules[0] = NULL;
754 *needed = sizeof(gpointer);
755 g_free (proc_name);
756 return TRUE;
759 count = g_slist_length (mods);
761 /* count + 1 to leave slot 0 for the main module */
762 *needed = sizeof(gpointer) * (count + 1);
765 * Use the NULL shortcut, as the first line in
766 * /proc/<pid>/maps isn't the executable, and we need
767 * that first in the returned list. Check the module name
768 * to see if it ends with the proc name and substitute
769 * the first entry with it. FIXME if this turns out to
770 * be a problem.
772 modules[0] = NULL;
773 for (i = 0; i < (avail - 1) && i < count; i++) {
774 module = (MonoW32ProcessModule *)g_slist_nth_data (mods, i);
775 if (modules[0] != NULL)
776 modules[i] = module->address_start;
777 else if (match_procname_to_modulename (proc_name, module->filename))
778 modules[0] = module->address_start;
779 else
780 modules[i + 1] = module->address_start;
783 for (i = 0; i < count; i++) {
784 mono_w32process_module_free ((MonoW32ProcessModule *)g_slist_nth_data (mods, i));
786 g_slist_free (mods);
787 g_free (proc_name);
789 return TRUE;
792 guint32
793 mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
795 gint pid, len;
796 gsize bytes;
797 gchar *path;
798 gunichar2 *proc_path;
800 size *= sizeof (gunichar2); /* adjust for unicode characters */
802 if (basename == NULL || size == 0)
803 return 0;
805 pid = mono_w32process_get_pid (process);
807 path = mono_w32process_get_path (pid);
808 if (path == NULL)
809 return 0;
811 proc_path = mono_unicode_from_external (path, &bytes);
812 g_free (path);
814 if (proc_path == NULL)
815 return 0;
817 len = (bytes / 2);
819 /* Add the terminator */
820 bytes += 2;
822 if (size < bytes) {
823 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
824 memcpy (basename, proc_path, size);
825 } else {
826 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)", __func__, size, bytes);
827 memcpy (basename, proc_path, bytes);
830 g_free (proc_path);
832 return len;
835 guint32
836 mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
838 MonoW32HandleProcess *process_handle;
839 pid_t pid;
840 gunichar2 *procname;
841 char *procname_ext = NULL;
842 glong len;
843 gsize bytes;
844 GSList *mods = NULL;
845 MonoW32ProcessModule *found_module;
846 guint32 count;
847 int i;
848 char *proc_name = NULL;
849 gboolean res;
851 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
852 __func__, process, module);
854 size = size * sizeof (gunichar2); /* adjust for unicode characters */
856 if (basename == NULL || size == 0)
857 return 0;
859 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
860 if (!res) {
861 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
862 return 0;
865 pid = process_handle->id;
866 proc_name = g_strdup (process_handle->proc_name);
868 mods = mono_w32process_get_modules (pid);
869 if (!mods) {
870 g_free (proc_name);
871 return 0;
874 count = g_slist_length (mods);
876 /* If module != NULL compare the address.
877 * If module == NULL we are looking for the main module.
878 * The best we can do for now check it the module name end with the process name.
880 for (i = 0; i < count; i++) {
881 found_module = (MonoW32ProcessModule *)g_slist_nth_data (mods, i);
882 if (procname_ext == NULL &&
883 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
884 (module != NULL && found_module->address_start == module))) {
885 procname_ext = g_path_get_basename (found_module->filename);
888 mono_w32process_module_free (found_module);
891 if (procname_ext == NULL) {
892 /* If it's *still* null, we might have hit the
893 * case where reading /proc/$pid/maps gives an
894 * empty file for this user.
896 procname_ext = mono_w32process_get_name (pid);
899 g_slist_free (mods);
900 g_free (proc_name);
902 if (procname_ext) {
903 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
904 procname_ext);
906 procname = mono_unicode_from_external (procname_ext, &bytes);
907 if (procname == NULL) {
908 /* bugger */
909 g_free (procname_ext);
910 return 0;
913 len = (bytes / 2);
915 /* Add the terminator */
916 bytes += 2;
918 if (size < bytes) {
919 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
921 memcpy (basename, procname, size);
922 } else {
923 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
924 __func__, size, bytes);
926 memcpy (basename, procname, bytes);
929 g_free (procname);
930 g_free (procname_ext);
932 return len;
935 return 0;
938 gboolean
939 mono_w32process_module_get_information (gpointer process, gpointer module, WapiModuleInfo *modinfo, guint32 size)
941 MonoW32HandleProcess *process_handle;
942 pid_t pid;
943 GSList *mods = NULL;
944 MonoW32ProcessModule *found_module;
945 guint32 count;
946 int i;
947 gboolean ret = FALSE;
948 char *proc_name = NULL;
949 gboolean res;
951 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
952 __func__, process, module);
954 if (modinfo == NULL || size < sizeof (WapiModuleInfo))
955 return FALSE;
957 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
958 if (!res) {
959 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
960 return FALSE;
963 pid = process_handle->id;
964 proc_name = g_strdup (process_handle->proc_name);
966 mods = mono_w32process_get_modules (pid);
967 if (!mods) {
968 g_free (proc_name);
969 return FALSE;
972 count = g_slist_length (mods);
974 /* If module != NULL compare the address.
975 * If module == NULL we are looking for the main module.
976 * The best we can do for now check it the module name end with the process name.
978 for (i = 0; i < count; i++) {
979 found_module = (MonoW32ProcessModule *)g_slist_nth_data (mods, i);
980 if (ret == FALSE &&
981 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
982 (module != NULL && found_module->address_start == module))) {
983 modinfo->lpBaseOfDll = found_module->address_start;
984 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
985 modinfo->EntryPoint = found_module->address_offset;
986 ret = TRUE;
989 mono_w32process_module_free (found_module);
992 g_slist_free (mods);
993 g_free (proc_name);
995 return ret;
998 static void
999 switch_dir_separators (char *path)
1001 size_t i, pathLength = strlen(path);
1003 /* Turn all the slashes round the right way, except for \' */
1004 /* There are probably other characters that need to be excluded as well. */
1005 for (i = 0; i < pathLength; i++) {
1006 if (path[i] == '\\' && i < pathLength - 1 && path[i+1] != '\'' )
1007 path[i] = '/';
1011 #if HAVE_SIGACTION
1013 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
1015 int status;
1016 int pid;
1017 MonoProcess *p;
1019 do {
1020 do {
1021 pid = waitpid (-1, &status, WNOHANG);
1022 } while (pid == -1 && errno == EINTR);
1024 if (pid <= 0)
1025 break;
1028 * This can run concurrently with the code in the rest of this module.
1030 for (p = processes; p; p = p->next) {
1031 if (p->pid != pid)
1032 continue;
1034 p->pid = 0; /* this pid doesn't exist anymore, clear it */
1035 p->status = status;
1036 mono_os_sem_post (&p->exit_sem);
1037 mono_memory_barrier ();
1038 /* Mark this as freeable, the pointer becomes invalid afterwards */
1039 p->freeable = TRUE;
1040 break;
1042 } while (1);
1045 static void
1046 process_add_sigchld_handler (void)
1048 struct sigaction sa;
1050 sa.sa_sigaction = mono_sigchld_signal_handler;
1051 sigemptyset (&sa.sa_mask);
1052 sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1053 g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
1054 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
1057 #endif
1059 static gboolean
1060 is_readable_or_executable (const char *prog)
1062 struct stat buf;
1063 int a = access (prog, R_OK);
1064 int b = access (prog, X_OK);
1065 if (a != 0 && b != 0)
1066 return FALSE;
1067 if (stat (prog, &buf))
1068 return FALSE;
1069 if (S_ISREG (buf.st_mode))
1070 return TRUE;
1071 return FALSE;
1074 static gboolean
1075 is_executable (const char *prog)
1077 struct stat buf;
1078 if (access (prog, X_OK) != 0)
1079 return FALSE;
1080 if (stat (prog, &buf))
1081 return FALSE;
1082 if (S_ISREG (buf.st_mode))
1083 return TRUE;
1084 return FALSE;
1087 static gboolean
1088 is_managed_binary (const char *filename)
1090 int original_errno = errno;
1091 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1092 int file = open (filename, O_RDONLY | O_LARGEFILE);
1093 #else
1094 int file = open (filename, O_RDONLY);
1095 #endif
1096 off_t new_offset;
1097 unsigned char buffer[8];
1098 off_t file_size, optional_header_offset;
1099 off_t pe_header_offset, clr_header_offset;
1100 gboolean managed = FALSE;
1101 int num_read;
1102 guint32 first_word, second_word, magic_number;
1104 /* If we are unable to open the file, then we definitely
1105 * can't say that it is managed. The child mono process
1106 * probably wouldn't be able to open it anyway.
1108 if (file < 0) {
1109 errno = original_errno;
1110 return FALSE;
1113 /* Retrieve the length of the file for future sanity checks. */
1114 file_size = lseek (file, 0, SEEK_END);
1115 lseek (file, 0, SEEK_SET);
1117 /* We know we need to read a header field at offset 60. */
1118 if (file_size < 64)
1119 goto leave;
1121 num_read = read (file, buffer, 2);
1123 if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
1124 goto leave;
1126 new_offset = lseek (file, 60, SEEK_SET);
1128 if (new_offset != 60)
1129 goto leave;
1131 num_read = read (file, buffer, 4);
1133 if (num_read != 4)
1134 goto leave;
1135 pe_header_offset = buffer[0]
1136 | (buffer[1] << 8)
1137 | (buffer[2] << 16)
1138 | (buffer[3] << 24);
1140 if (pe_header_offset + 24 > file_size)
1141 goto leave;
1143 new_offset = lseek (file, pe_header_offset, SEEK_SET);
1145 if (new_offset != pe_header_offset)
1146 goto leave;
1148 num_read = read (file, buffer, 4);
1150 if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
1151 goto leave;
1154 * Verify that the header we want in the optional header data
1155 * is present in this binary.
1157 new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
1159 if (new_offset != pe_header_offset + 20)
1160 goto leave;
1162 num_read = read (file, buffer, 2);
1164 if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
1165 goto leave;
1167 optional_header_offset = pe_header_offset + 24;
1169 /* Read the PE magic number */
1170 new_offset = lseek (file, optional_header_offset, SEEK_SET);
1172 if (new_offset != optional_header_offset)
1173 goto leave;
1175 num_read = read (file, buffer, 2);
1177 if (num_read != 2)
1178 goto leave;
1180 magic_number = (buffer[0] | (buffer[1] << 8));
1182 if (magic_number == 0x10B) // PE32
1183 clr_header_offset = 208;
1184 else if (magic_number == 0x20B) // PE32+
1185 clr_header_offset = 224;
1186 else
1187 goto leave;
1189 /* Read the CLR header address and size fields. These will be
1190 * zero if the binary is not managed.
1192 new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET);
1194 if (new_offset != optional_header_offset + clr_header_offset)
1195 goto leave;
1197 num_read = read (file, buffer, 8);
1199 /* We are not concerned with endianness, only with
1200 * whether it is zero or not.
1202 first_word = *(guint32 *)&buffer[0];
1203 second_word = *(guint32 *)&buffer[4];
1205 if ((num_read != 8) || (first_word == 0) || (second_word == 0))
1206 goto leave;
1208 managed = TRUE;
1210 leave:
1211 close (file);
1212 errno = original_errno;
1213 return managed;
1216 static gboolean
1217 process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new_environ,
1218 const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info)
1220 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1221 char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
1222 char *dir = NULL, **env_strings = NULL, **argv = NULL;
1223 guint32 i, env_count = 0;
1224 gboolean ret = FALSE;
1225 gpointer handle = NULL;
1226 GError *gerr = NULL;
1227 int in_fd, out_fd, err_fd;
1228 pid_t pid = 0;
1229 int startup_pipe [2] = {-1, -1};
1230 int dummy;
1231 MonoProcess *mono_process;
1233 #if HAVE_SIGACTION
1234 mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
1235 #endif
1237 /* appname and cmdline specify the executable and its args:
1239 * If appname is not NULL, it is the name of the executable.
1240 * Otherwise the executable is the first token in cmdline.
1242 * Executable searching:
1244 * If appname is not NULL, it can specify the full path and
1245 * file name, or else a partial name and the current directory
1246 * will be used. There is no additional searching.
1248 * If appname is NULL, the first whitespace-delimited token in
1249 * cmdline is used. If the name does not contain a full
1250 * directory path, the search sequence is:
1252 * 1) The directory containing the current process
1253 * 2) The current working directory
1254 * 3) The windows system directory (Ignored)
1255 * 4) The windows directory (Ignored)
1256 * 5) $PATH
1258 * Just to make things more interesting, tokens can contain
1259 * white space if they are surrounded by quotation marks. I'm
1260 * beginning to understand just why windows apps are generally
1261 * so crap, with an API like this :-(
1263 if (appname != NULL) {
1264 cmd = mono_unicode_to_external (appname);
1265 if (cmd == NULL) {
1266 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL",
1267 __func__);
1269 SetLastError (ERROR_PATH_NOT_FOUND);
1270 goto free_strings;
1273 switch_dir_separators(cmd);
1276 if (cmdline != NULL) {
1277 args = mono_unicode_to_external (cmdline);
1278 if (args == NULL) {
1279 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1281 SetLastError (ERROR_PATH_NOT_FOUND);
1282 goto free_strings;
1286 if (cwd != NULL) {
1287 dir = mono_unicode_to_external (cwd);
1288 if (dir == NULL) {
1289 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1291 SetLastError (ERROR_PATH_NOT_FOUND);
1292 goto free_strings;
1295 /* Turn all the slashes round the right way */
1296 switch_dir_separators(dir);
1300 /* We can't put off locating the executable any longer :-( */
1301 if (cmd != NULL) {
1302 char *unquoted;
1303 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
1304 /* Strip off the drive letter. I can't
1305 * believe that CP/M holdover is still
1306 * visible...
1308 g_memmove (cmd, cmd+2, strlen (cmd)-2);
1309 cmd[strlen (cmd)-2] = '\0';
1312 unquoted = g_shell_unquote (cmd, NULL);
1313 if (unquoted[0] == '/') {
1314 /* Assume full path given */
1315 prog = g_strdup (unquoted);
1317 /* Executable existing ? */
1318 if (!is_readable_or_executable (prog)) {
1319 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1320 __func__, prog);
1321 g_free (unquoted);
1322 SetLastError (ERROR_FILE_NOT_FOUND);
1323 goto free_strings;
1325 } else {
1326 /* Search for file named by cmd in the current
1327 * directory
1329 char *curdir = g_get_current_dir ();
1331 prog = g_strdup_printf ("%s/%s", curdir, unquoted);
1332 g_free (curdir);
1334 /* And make sure it's readable */
1335 if (!is_readable_or_executable (prog)) {
1336 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1337 __func__, prog);
1338 g_free (unquoted);
1339 SetLastError (ERROR_FILE_NOT_FOUND);
1340 goto free_strings;
1343 g_free (unquoted);
1345 args_after_prog = args;
1346 } else {
1347 char *token = NULL;
1348 char quote;
1350 /* Dig out the first token from args, taking quotation
1351 * marks into account
1354 /* First, strip off all leading whitespace */
1355 args = g_strchug (args);
1357 /* args_after_prog points to the contents of args
1358 * after token has been set (otherwise argv[0] is
1359 * duplicated)
1361 args_after_prog = args;
1363 /* Assume the opening quote will always be the first
1364 * character
1366 if (args[0] == '\"' || args [0] == '\'') {
1367 quote = args [0];
1368 for (i = 1; args[i] != '\0' && args[i] != quote; i++);
1369 if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) {
1370 /* We found the first token */
1371 token = g_strndup (args+1, i-1);
1372 args_after_prog = g_strchug (args + i + 1);
1373 } else {
1374 /* Quotation mark appeared in the
1375 * middle of the token. Just give the
1376 * whole first token, quotes and all,
1377 * to exec.
1382 if (token == NULL) {
1383 /* No quote mark, or malformed */
1384 for (i = 0; args[i] != '\0'; i++) {
1385 if (g_ascii_isspace (args[i])) {
1386 token = g_strndup (args, i);
1387 args_after_prog = args + i + 1;
1388 break;
1393 if (token == NULL && args[0] != '\0') {
1394 /* Must be just one token in the string */
1395 token = g_strdup (args);
1396 args_after_prog = NULL;
1399 if (token == NULL) {
1400 /* Give up */
1401 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find what to exec", __func__);
1403 SetLastError (ERROR_PATH_NOT_FOUND);
1404 goto free_strings;
1407 /* Turn all the slashes round the right way. Only for
1408 * the prg. name
1410 switch_dir_separators(token);
1412 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
1413 /* Strip off the drive letter. I can't
1414 * believe that CP/M holdover is still
1415 * visible...
1417 g_memmove (token, token+2, strlen (token)-2);
1418 token[strlen (token)-2] = '\0';
1421 if (token[0] == '/') {
1422 /* Assume full path given */
1423 prog = g_strdup (token);
1425 /* Executable existing ? */
1426 if (!is_readable_or_executable (prog)) {
1427 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1428 __func__, token);
1429 g_free (token);
1430 SetLastError (ERROR_FILE_NOT_FOUND);
1431 goto free_strings;
1433 } else {
1434 char *curdir = g_get_current_dir ();
1436 /* FIXME: Need to record the directory
1437 * containing the current process, and check
1438 * that for the new executable as the first
1439 * place to look
1442 prog = g_strdup_printf ("%s/%s", curdir, token);
1443 g_free (curdir);
1445 /* I assume X_OK is the criterion to use,
1446 * rather than F_OK
1448 * X_OK is too strict *if* the target is a CLR binary
1450 if (!is_readable_or_executable (prog)) {
1451 g_free (prog);
1452 prog = g_find_program_in_path (token);
1453 if (prog == NULL) {
1454 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s", __func__, token);
1456 g_free (token);
1457 SetLastError (ERROR_FILE_NOT_FOUND);
1458 goto free_strings;
1463 g_free (token);
1466 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Exec prog [%s] args [%s]",
1467 __func__, prog, args_after_prog);
1469 /* Check for CLR binaries; if found, we will try to invoke
1470 * them using the same mono binary that started us.
1472 if (is_managed_binary (prog)) {
1473 gunichar2 *newapp, *newcmd;
1474 gsize bytes_ignored;
1476 newapp = mono_unicode_from_external (cli_launcher ? cli_launcher : "mono", &bytes_ignored);
1477 if (newapp) {
1478 if (appname)
1479 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, NULL);
1480 else
1481 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, NULL);
1483 g_free (newapp);
1485 if (newcmd) {
1486 ret = process_create (NULL, newcmd, new_environ, cwd, startup_handles, process_info);
1488 g_free (newcmd);
1490 goto free_strings;
1493 } else {
1494 if (!is_executable (prog)) {
1495 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Executable permisson not set on %s", __func__, prog);
1496 SetLastError (ERROR_ACCESS_DENIED);
1497 goto free_strings;
1501 if (args_after_prog != NULL && *args_after_prog) {
1502 char *qprog;
1504 qprog = g_shell_quote (prog);
1505 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
1506 g_free (qprog);
1507 } else {
1508 full_prog = g_shell_quote (prog);
1511 ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
1512 if (ret == FALSE) {
1513 g_message ("process_create: %s\n", gerr->message);
1514 g_error_free (gerr);
1515 gerr = NULL;
1516 goto free_strings;
1519 if (startup_handles) {
1520 in_fd = GPOINTER_TO_UINT (startup_handles->input);
1521 out_fd = GPOINTER_TO_UINT (startup_handles->output);
1522 err_fd = GPOINTER_TO_UINT (startup_handles->error);
1523 } else {
1524 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
1525 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
1526 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
1529 /* new_environ is a block of NULL-terminated strings, which
1530 * is itself NULL-terminated. Of course, passing an array of
1531 * string pointers would have made things too easy :-(
1533 * If new_environ is not NULL it specifies the entire set of
1534 * environment variables in the new process. Otherwise the
1535 * new process inherits the same environment.
1537 if (new_environ) {
1538 gunichar2 *new_environp;
1540 /* Count the number of strings */
1541 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1542 env_count++;
1543 while (*new_environp)
1544 new_environp++;
1547 /* +2: one for the process handle value, and the last
1548 * one is NULL
1550 env_strings = g_new0 (char *, env_count + 2);
1552 /* Copy each environ string into 'strings' turning it
1553 * into utf8 (or the requested encoding) at the same
1554 * time
1556 env_count = 0;
1557 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1558 env_strings[env_count] = mono_unicode_to_external (new_environp);
1559 env_count++;
1560 while (*new_environp) {
1561 new_environp++;
1564 } else {
1565 for (i = 0; environ[i] != NULL; i++)
1566 env_count++;
1568 /* +2: one for the process handle value, and the last
1569 * one is NULL
1571 env_strings = g_new0 (char *, env_count + 2);
1573 /* Copy each environ string into 'strings' turning it
1574 * into utf8 (or the requested encoding) at the same
1575 * time
1577 env_count = 0;
1578 for (i = 0; environ[i] != NULL; i++) {
1579 env_strings[env_count] = g_strdup (environ[i]);
1580 env_count++;
1584 /* Create a pipe to make sure the child doesn't exit before
1585 * we can add the process to the linked list of processes */
1586 if (pipe (startup_pipe) == -1) {
1587 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1588 * This is just for a very hard to hit race condition in the first place */
1589 startup_pipe [0] = startup_pipe [1] = -1;
1590 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__);
1593 #if HAVE_SIGACTION
1594 /* FIXME: block SIGCHLD */
1595 #endif
1597 switch (pid = fork ()) {
1598 case -1: /* Error */ {
1599 SetLastError (ERROR_OUTOFMEMORY);
1600 ret = FALSE;
1601 break;
1603 case 0: /* Child */ {
1604 if (startup_pipe [0] != -1) {
1605 /* Wait until the parent has updated it's internal data */
1606 ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1);
1607 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: child: parent has completed its setup", __func__);
1608 close (startup_pipe [0]);
1609 close (startup_pipe [1]);
1612 /* should we detach from the process group? */
1614 /* Connect stdin, stdout and stderr */
1615 dup2 (in_fd, 0);
1616 dup2 (out_fd, 1);
1617 dup2 (err_fd, 2);
1619 /* Close all file descriptors */
1620 for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
1621 close (i);
1623 #ifdef DEBUG_ENABLED
1624 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1625 dir == NULL?".":dir);
1626 for (i = 0; argv[i] != NULL; i++)
1627 g_message ("arg %d: [%s]", i, argv[i]);
1629 for (i = 0; env_strings[i] != NULL; i++)
1630 g_message ("env %d: [%s]", i, env_strings[i]);
1631 #endif
1633 /* set cwd */
1634 if (dir != NULL && chdir (dir) == -1) {
1635 /* set error */
1636 _exit (-1);
1639 /* exec */
1640 execve (argv[0], argv, env_strings);
1642 /* set error */
1643 _exit (-1);
1645 break;
1647 default: /* Parent */ {
1648 MonoW32HandleProcess process_handle;
1650 memset (&process_handle, 0, sizeof (process_handle));
1651 process_handle.id = pid;
1652 process_handle.child = TRUE;
1653 process_handle.proc_name = g_strdup (prog);
1654 process_set_defaults (&process_handle);
1656 /* Add our mono_process into the linked list of processes */
1657 mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess));
1658 mono_process->pid = pid;
1659 mono_process->handle_count = 1;
1660 mono_os_sem_init (&mono_process->exit_sem, 0);
1662 process_handle.mono_process = mono_process;
1664 handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
1665 if (handle == INVALID_HANDLE_VALUE) {
1666 g_warning ("%s: error creating process handle", __func__);
1668 mono_os_sem_destroy (&mono_process->exit_sem);
1669 g_free (mono_process);
1671 SetLastError (ERROR_OUTOFMEMORY);
1672 ret = FALSE;
1673 break;
1676 /* Keep the process handle artificially alive until the process
1677 * exits so that the information in the handle isn't lost. */
1678 mono_w32handle_ref (handle);
1679 mono_process->handle = handle;
1681 mono_os_mutex_lock (&processes_mutex);
1682 mono_process->next = processes;
1683 processes = mono_process;
1684 mono_os_mutex_unlock (&processes_mutex);
1686 if (process_info != NULL) {
1687 process_info->process_handle = handle;
1688 process_info->pid = pid;
1690 /* FIXME: we might need to handle the thread info some day */
1691 process_info->thread_handle = INVALID_HANDLE_VALUE;
1692 process_info->tid = 0;
1695 break;
1699 #if HAVE_SIGACTION
1700 /* FIXME: unblock SIGCHLD */
1701 #endif
1703 if (startup_pipe [1] != -1) {
1704 /* Write 1 byte, doesn't matter what */
1705 ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
1706 close (startup_pipe [0]);
1707 close (startup_pipe [1]);
1710 free_strings:
1711 if (cmd)
1712 g_free (cmd);
1713 if (full_prog)
1714 g_free (full_prog);
1715 if (prog)
1716 g_free (prog);
1717 if (args)
1718 g_free (args);
1719 if (dir)
1720 g_free (dir);
1721 if (env_strings)
1722 g_strfreev (env_strings);
1723 if (argv)
1724 g_strfreev (argv);
1726 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p for pid %d", __func__, handle, pid);
1728 /* Check if something needs to be cleaned up. */
1729 processes_cleanup ();
1731 return ret;
1732 #else
1733 SetLastError (ERROR_NOT_SUPPORTED);
1734 return FALSE;
1735 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1738 MonoBoolean
1739 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
1741 const gunichar2 *lpFile;
1742 const gunichar2 *lpParameters;
1743 const gunichar2 *lpDirectory;
1744 gunichar2 *args;
1745 gboolean ret;
1747 if (!proc_start_info->filename) {
1748 /* w2k returns TRUE for this, for some reason. */
1749 ret = TRUE;
1750 goto done;
1753 lpFile = proc_start_info->filename ? mono_string_chars (proc_start_info->filename) : NULL;
1754 lpParameters = proc_start_info->arguments ? mono_string_chars (proc_start_info->arguments) : NULL;
1755 lpDirectory = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) != 0 ?
1756 mono_string_chars (proc_start_info->working_directory) : NULL;
1758 /* Put both executable and parameters into the second argument
1759 * to process_create (), so it searches $PATH. The conversion
1760 * into and back out of utf8 is because there is no
1761 * g_strdup_printf () equivalent for gunichar2 :-(
1763 args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1764 if (args == NULL) {
1765 SetLastError (ERROR_INVALID_DATA);
1766 ret = FALSE;
1767 goto done;
1769 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1770 g_free (args);
1772 if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
1773 goto done;
1775 if (!ret) {
1776 static char *handler;
1777 static gunichar2 *handler_utf16;
1779 if (handler_utf16 == (gunichar2 *)-1) {
1780 ret = FALSE;
1781 goto done;
1784 #ifdef PLATFORM_MACOSX
1785 handler = g_strdup ("/usr/bin/open");
1786 #else
1788 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
1789 * if that fails, try to use gnome-open, then kfmclient
1791 handler = g_find_program_in_path ("xdg-open");
1792 if (handler == NULL){
1793 handler = g_find_program_in_path ("gnome-open");
1794 if (handler == NULL){
1795 handler = g_find_program_in_path ("kfmclient");
1796 if (handler == NULL){
1797 handler_utf16 = (gunichar2 *) -1;
1798 ret = FALSE;
1799 goto done;
1800 } else {
1801 /* kfmclient needs exec argument */
1802 char *old = handler;
1803 handler = g_strconcat (old, " exec",
1804 NULL);
1805 g_free (old);
1809 #endif
1810 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
1811 g_free (handler);
1813 /* Put quotes around the filename, in case it's a url
1814 * that contains #'s (process_create() calls
1815 * g_shell_parse_argv(), which deliberately throws
1816 * away anything after an unquoted #). Fixes bug
1817 * 371567.
1819 args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
1820 lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1821 if (args == NULL) {
1822 SetLastError (ERROR_INVALID_DATA);
1823 ret = FALSE;
1824 goto done;
1826 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1827 g_free (args);
1828 if (!ret) {
1829 if (GetLastError () != ERROR_OUTOFMEMORY)
1830 SetLastError (ERROR_INVALID_DATA);
1831 ret = FALSE;
1832 goto done;
1834 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
1835 CloseHandle (process_info->process_handle);
1836 process_info->process_handle = NULL;
1839 done:
1840 if (ret == FALSE) {
1841 process_info->pid = -GetLastError ();
1842 } else {
1843 process_info->thread_handle = NULL;
1844 #if !defined(MONO_CROSS_COMPILE)
1845 process_info->pid = mono_w32process_get_pid (process_info->process_handle);
1846 #else
1847 process_info->pid = 0;
1848 #endif
1849 process_info->tid = 0;
1852 return ret;
1855 /* Only used when UseShellExecute is false */
1856 static gboolean
1857 process_get_complete_path (const gunichar2 *appname, gchar **completed)
1859 gchar *utf8app;
1860 gchar *found;
1862 utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
1864 if (g_path_is_absolute (utf8app)) {
1865 *completed = g_shell_quote (utf8app);
1866 g_free (utf8app);
1867 return TRUE;
1870 if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
1871 *completed = g_shell_quote (utf8app);
1872 g_free (utf8app);
1873 return TRUE;
1876 found = g_find_program_in_path (utf8app);
1877 if (found == NULL) {
1878 *completed = NULL;
1879 g_free (utf8app);
1880 return FALSE;
1883 *completed = g_shell_quote (found);
1884 g_free (found);
1885 g_free (utf8app);
1886 return TRUE;
1889 static gboolean
1890 process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
1892 gchar *spath = NULL;
1894 *shell_path = NULL;
1895 *cmd = proc_start_info->arguments;
1897 process_get_complete_path (mono_string_chars (proc_start_info->filename), &spath);
1898 if (spath != NULL) {
1899 *shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
1900 g_free (spath);
1903 return (*shell_path != NULL) ? TRUE : FALSE;
1906 MonoBoolean
1907 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info,
1908 HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info)
1910 gboolean ret;
1911 gunichar2 *dir;
1912 StartupHandles startup_handles;
1913 gunichar2 *shell_path = NULL;
1914 gchar *env_vars = NULL;
1915 MonoString *cmd = NULL;
1917 memset (&startup_handles, 0, sizeof (startup_handles));
1918 startup_handles.input = stdin_handle;
1919 startup_handles.output = stdout_handle;
1920 startup_handles.error = stderr_handle;
1922 if (process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
1923 process_info->pid = -ERROR_FILE_NOT_FOUND;
1924 return FALSE;
1927 if (process_info->env_keys) {
1928 gint i, len;
1929 MonoString *ms;
1930 MonoString *key, *value;
1931 gunichar2 *str, *ptr;
1932 gunichar2 *equals16;
1934 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
1935 ms = mono_array_get (process_info->env_values, MonoString *, i);
1936 if (ms == NULL)
1937 continue;
1939 len += mono_string_length (ms) * sizeof (gunichar2);
1940 ms = mono_array_get (process_info->env_keys, MonoString *, i);
1941 len += mono_string_length (ms) * sizeof (gunichar2);
1942 len += 2 * sizeof (gunichar2);
1945 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
1946 ptr = str = g_new0 (gunichar2, len + 1);
1947 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
1948 value = mono_array_get (process_info->env_values, MonoString *, i);
1949 if (value == NULL)
1950 continue;
1952 key = mono_array_get (process_info->env_keys, MonoString *, i);
1953 memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
1954 ptr += mono_string_length (key);
1956 memcpy (ptr, equals16, sizeof (gunichar2));
1957 ptr++;
1959 memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
1960 ptr += mono_string_length (value);
1961 ptr++;
1964 g_free (equals16);
1965 env_vars = (gchar *) str;
1968 /* The default dir name is "". Turn that into NULL to mean "current directory" */
1969 dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ?
1970 mono_string_chars (proc_start_info->working_directory) : NULL;
1972 ret = process_create (shell_path, cmd ? mono_string_chars (cmd): NULL, env_vars, dir, &startup_handles, process_info);
1974 g_free (env_vars);
1975 if (shell_path != NULL)
1976 g_free (shell_path);
1978 if (!ret)
1979 process_info->pid = -GetLastError ();
1981 return ret;
1984 /* Returns an array of pids */
1985 MonoArray *
1986 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
1988 MonoError error;
1989 MonoArray *procs;
1990 gpointer *pidarray;
1991 int i, count;
1993 pidarray = mono_process_list (&count);
1994 if (!pidarray) {
1995 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
1996 return NULL;
1998 procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error);
1999 if (mono_error_set_pending_exception (&error)) {
2000 g_free (pidarray);
2001 return NULL;
2003 if (sizeof (guint32) == sizeof (gpointer)) {
2004 memcpy (mono_array_addr (procs, guint32, 0), pidarray, count * sizeof (gint32));
2005 } else {
2006 for (i = 0; i < count; ++i)
2007 *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]);
2009 g_free (pidarray);
2011 return procs;
2014 void
2015 mono_w32process_set_cli_launcher (gchar *path)
2017 g_free (cli_launcher);
2018 cli_launcher = g_strdup (path);
2021 gpointer
2022 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2024 mono_w32handle_ref (current_process);
2025 return current_process;
2028 MonoBoolean
2029 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
2031 MonoW32HandleProcess *process_handle;
2032 gboolean res;
2034 if (!exitcode)
2035 return FALSE;
2037 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2038 if (!res) {
2039 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2040 return FALSE;
2043 if (process_handle->id == wapi_getpid ()) {
2044 *exitcode = STILL_ACTIVE;
2045 return TRUE;
2048 /* A process handle is only signalled if the process has exited
2049 * and has been waited for. Make sure any process exit has been
2050 * noticed before checking if the process is signalled.
2051 * Fixes bug 325463. */
2052 mono_w32handle_wait_one (handle, 0, TRUE);
2054 *exitcode = mono_w32handle_issignalled (handle) ? process_handle->exitstatus : STILL_ACTIVE;
2055 return TRUE;
2058 MonoBoolean
2059 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
2061 return CloseHandle (handle);
2064 MonoBoolean
2065 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
2067 #ifdef HAVE_KILL
2068 MonoW32HandleProcess *process_handle;
2069 int ret;
2070 pid_t pid;
2071 gboolean res;
2073 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2074 if (!res) {
2075 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2076 SetLastError (ERROR_INVALID_HANDLE);
2077 return FALSE;
2080 pid = process_handle->id;
2082 ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
2083 if (ret == 0)
2084 return TRUE;
2086 switch (errno) {
2087 case EINVAL: SetLastError (ERROR_INVALID_PARAMETER); break;
2088 case EPERM: SetLastError (ERROR_ACCESS_DENIED); break;
2089 case ESRCH: SetLastError (ERROR_PROC_NOT_FOUND); break;
2090 default: SetLastError (ERROR_GEN_FAILURE); break;
2093 return FALSE;
2094 #else
2095 g_error ("kill() is not supported by this platform");
2096 #endif
2099 MonoBoolean
2100 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
2102 MonoW32HandleProcess *process_handle;
2103 gboolean res;
2105 if (!min || !max)
2106 return FALSE;
2108 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2109 if (!res) {
2110 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2111 return FALSE;
2114 if (!process_handle->child)
2115 return FALSE;
2117 *min = process_handle->min_working_set;
2118 *max = process_handle->max_working_set;
2119 return TRUE;
2122 MonoBoolean
2123 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
2125 MonoW32HandleProcess *process_handle;
2126 gboolean res;
2128 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2129 if (!res) {
2130 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2131 return FALSE;
2134 if (!process_handle->child)
2135 return FALSE;
2137 process_handle->min_working_set = min;
2138 process_handle->max_working_set = max;
2139 return TRUE;
2142 gint32
2143 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
2145 #ifdef HAVE_GETPRIORITY
2146 MonoW32HandleProcess *process_handle;
2147 gint ret;
2148 pid_t pid;
2149 gboolean res;
2151 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2152 if (!res) {
2153 SetLastError (ERROR_INVALID_HANDLE);
2154 return 0;
2157 pid = process_handle->id;
2159 errno = 0;
2160 ret = getpriority (PRIO_PROCESS, pid);
2161 if (ret == -1 && errno != 0) {
2162 switch (errno) {
2163 case EPERM:
2164 case EACCES:
2165 SetLastError (ERROR_ACCESS_DENIED);
2166 break;
2167 case ESRCH:
2168 SetLastError (ERROR_PROC_NOT_FOUND);
2169 break;
2170 default:
2171 SetLastError (ERROR_GEN_FAILURE);
2173 return 0;
2176 if (ret == 0)
2177 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2178 else if (ret < -15)
2179 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME;
2180 else if (ret < -10)
2181 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH;
2182 else if (ret < 0)
2183 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2184 else if (ret > 10)
2185 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE;
2186 else if (ret > 0)
2187 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2189 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2190 #else
2191 SetLastError (ERROR_NOT_SUPPORTED);
2192 return 0;
2193 #endif
2196 MonoBoolean
2197 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
2199 #ifdef HAVE_SETPRIORITY
2200 MonoW32HandleProcess *process_handle;
2201 int ret;
2202 int prio;
2203 pid_t pid;
2204 gboolean res;
2206 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2207 if (!res) {
2208 SetLastError (ERROR_INVALID_HANDLE);
2209 return FALSE;
2212 pid = process_handle->id;
2214 switch (priorityClass) {
2215 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
2216 prio = 19;
2217 break;
2218 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
2219 prio = 10;
2220 break;
2221 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL:
2222 prio = 0;
2223 break;
2224 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
2225 prio = -5;
2226 break;
2227 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH:
2228 prio = -11;
2229 break;
2230 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME:
2231 prio = -20;
2232 break;
2233 default:
2234 SetLastError (ERROR_INVALID_PARAMETER);
2235 return FALSE;
2238 ret = setpriority (PRIO_PROCESS, pid, prio);
2239 if (ret == -1) {
2240 switch (errno) {
2241 case EPERM:
2242 case EACCES:
2243 SetLastError (ERROR_ACCESS_DENIED);
2244 break;
2245 case ESRCH:
2246 SetLastError (ERROR_PROC_NOT_FOUND);
2247 break;
2248 default:
2249 SetLastError (ERROR_GEN_FAILURE);
2253 return ret == 0;
2254 #else
2255 SetLastError (ERROR_NOT_SUPPORTED);
2256 return FALSE;
2257 #endif
2260 static void
2261 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
2263 processtime->lowDateTime = ticks & 0xFFFFFFFF;
2264 processtime->highDateTime = ticks >> 32;
2267 static void
2268 wapifiletime_to_processtime (WapiFileTime wapi_filetime, ProcessTime *processtime)
2270 processtime->lowDateTime = wapi_filetime.dwLowDateTime;
2271 processtime->highDateTime = wapi_filetime.dwHighDateTime;
2274 MonoBoolean
2275 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
2277 MonoW32HandleProcess *process_handle;
2278 ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime;
2279 gboolean res;
2281 if (!creation_time || !exit_time || !kernel_time || !user_time) {
2282 /* Not sure if w32 allows NULLs here or not */
2283 return FALSE;
2286 creation_processtime = (ProcessTime*) creation_time;
2287 exit_processtime = (ProcessTime*) exit_time;
2288 kernel_processtime = (ProcessTime*) kernel_time;
2289 user_processtime = (ProcessTime*) user_time;
2291 memset (creation_processtime, 0, sizeof (ProcessTime));
2292 memset (exit_processtime, 0, sizeof (ProcessTime));
2293 memset (kernel_processtime, 0, sizeof (ProcessTime));
2294 memset (user_processtime, 0, sizeof (ProcessTime));
2296 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2297 if (!res) {
2298 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2299 return FALSE;
2302 if (!process_handle->child) {
2303 gint64 start_ticks, user_ticks, kernel_ticks;
2305 mono_process_get_times (GINT_TO_POINTER (process_handle->id),
2306 &start_ticks, &user_ticks, &kernel_ticks);
2308 ticks_to_processtime (start_ticks, creation_processtime);
2309 ticks_to_processtime (user_ticks, kernel_processtime);
2310 ticks_to_processtime (kernel_ticks, user_processtime);
2311 return TRUE;
2314 wapifiletime_to_processtime (process_handle->create_time, creation_processtime);
2316 /* A process handle is only signalled if the process has
2317 * exited, otherwise exit_processtime isn't set */
2318 if (mono_w32handle_issignalled (handle))
2319 wapifiletime_to_processtime (process_handle->exit_time, exit_processtime);
2321 #ifdef HAVE_GETRUSAGE
2322 if (process_handle->id == getpid ()) {
2323 struct rusage time_data;
2324 if (getrusage (RUSAGE_SELF, &time_data) == 0) {
2325 ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime);
2326 ticks_to_processtime ((guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10, kernel_processtime);
2329 #endif
2331 return TRUE;