Merge pull request #3854 from lateralusX/jlorenss/win-clang-toolchain
[mono.git] / mono / metadata / w32process-unix.c
1 /*
2  * process.c: System.Diagnostics.Process support
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
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.
10  */
11
12 #include <config.h>
13 #include <glib.h>
14
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>
33
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
40
41 #ifdef HAVE_SYS_MKDEV_H
42 #include <sys/mkdev.h>
43 #endif
44
45 #ifdef HAVE_UTIME_H
46 #include <utime.h>
47 #endif
48
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>
69
70 #ifndef MAXPATHLEN
71 #define MAXPATHLEN 242
72 #endif
73
74 #define STILL_ACTIVE ((int) 0x00000103)
75
76 #define LOGDEBUG(...)
77 /* define LOGDEBUG(...) g_message(__VA_ARGS__)  */
78
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) 
85  */
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
95
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;
107
108 typedef struct {
109         gpointer input;
110         gpointer output;
111         gpointer error;
112 } StartupHandles;
113
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;
123
124 /*
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).
130  */
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.
139          */
140         gpointer handle;
141         gboolean freeable;
142         MonoProcess *next;
143 };
144
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;
159
160 #if HAVE_SIGACTION
161 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
162 #endif
163
164 static gchar *cli_launcher;
165
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;
180
181 static gpointer current_process;
182
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;
187
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)
191 {
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
213 }
214
215 static void
216 process_details (gpointer data)
217 {
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);
221 }
222
223 static const gchar*
224 process_typename (void)
225 {
226         return "Process";
227 }
228
229 static gsize
230 process_typesize (void)
231 {
232         return sizeof (MonoW32HandleProcess);
233 }
234
235 static MonoW32HandleWaitRet
236 process_wait (gpointer handle, guint32 timeout, gboolean *alerted)
237 {
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;
244
245         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
246
247         if (alerted)
248                 *alerted = FALSE;
249
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;
254         }
255
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;
260         }
261
262         pid = process_handle->id;
263
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;
267         }
268
269         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
270
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;
276
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);
281
282                 /* We alway create a MonoProcess for a child process */
283                 g_assert (!process_handle->child);
284
285                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout);
286
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__);
290
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);
297                         }
298
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;
301                 }
302
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;
305         }
306
307         start = mono_msec_ticks ();
308         now = start;
309
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);
319                 }
320
321                 if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
322                         /* Success, process has exited */
323                         mono_os_sem_post (&mp->exit_sem);
324                         break;
325                 }
326
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;
330                 }
331
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;
336                 }
337
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;
342                 }
343         }
344
345         /* Process must have exited */
346         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout);
347
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);
354
355         process_handle->exited = TRUE;
356
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);
359
360         mono_w32handle_set_signal_state (handle, TRUE, TRUE);
361
362         return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
363 }
364
365 static void
366 processes_cleanup (void)
367 {
368         static gint32 cleaning_up;
369         MonoProcess *mp;
370         MonoProcess *prev = NULL;
371         GSList *finished = NULL;
372         GSList *l;
373         gpointer unref_handle;
374
375         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
376
377         /* Ensure we're not in here in multiple threads at once, nor recursive. */
378         if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
379                 return;
380
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);
391                 }
392         }
393
394         /*
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.
399          */
400         mono_os_mutex_lock (&processes_mutex);
401
402         mp = processes;
403         while (mp) {
404                 if (mp->handle_count == 0 && mp->freeable) {
405                         /*
406                          * Unlink the entry.
407                          * This code can run parallel with the sigchld handler, but the
408                          * modifications it makes are safe.
409                          */
410                         if (mp == processes)
411                                 processes = mp->next;
412                         else
413                                 prev->next = mp->next;
414                         finished = g_slist_prepend (finished, mp);
415
416                         mp = mp->next;
417                 } else {
418                         prev = mp;
419                         mp = mp->next;
420                 }
421         }
422
423         mono_memory_barrier ();
424
425         for (l = finished; l; l = l->next) {
426                 /*
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.
430                  */
431                 mp = (MonoProcess *)l->data;
432                 mono_os_sem_destroy (&mp->exit_sem);
433                 g_free (mp);
434         }
435         g_slist_free (finished);
436
437         mono_os_mutex_unlock (&processes_mutex);
438
439         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
440
441         InterlockedExchange (&cleaning_up, 0);
442 }
443
444 static void
445 process_close (gpointer handle, gpointer data)
446 {
447         MonoW32HandleProcess *process_handle;
448
449         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
450
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 ();
457 }
458
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 */
469 };
470
471 static void
472 process_set_defaults (MonoW32HandleProcess *process_handle)
473 {
474         /* These seem to be the defaults on w2k */
475         process_handle->min_working_set = 204800;
476         process_handle->max_working_set = 1413120;
477
478         _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
479 }
480
481 static void
482 process_set_name (MonoW32HandleProcess *process_handle)
483 {
484         char *progname, *utf8_progname, *slash;
485
486         progname = g_get_prgname ();
487         utf8_progname = mono_utf8_from_external (progname);
488
489         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: using [%s] as prog name", __func__, progname);
490
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);
498         }
499 }
500
501 void
502 mono_w32process_init (void)
503 {
504         MonoW32HandleProcess process_handle;
505
506         mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS, &process_ops);
507
508         mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS,
509                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
510
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);
515
516         current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
517         g_assert (current_process);
518
519         mono_os_mutex_init (&processes_mutex);
520 }
521
522 void
523 mono_w32process_cleanup (void)
524 {
525         g_free (cli_launcher);
526 }
527
528 static int
529 len16 (const gunichar2 *str)
530 {
531         int len = 0;
532
533         while (*str++ != 0)
534                 len++;
535
536         return len;
537 }
538
539 static gunichar2 *
540 utf16_concat (const gunichar2 *first, ...)
541 {
542         va_list args;
543         int total = 0, i;
544         const gunichar2 *s;
545         const gunichar2 *p;
546         gunichar2 *ret;
547
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);
553
554         ret = g_new (gunichar2, total + 1);
555         if (ret == NULL)
556                 return NULL;
557
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;
566         }
567         va_end (args);
568
569         return ret;
570 }
571
572 guint32
573 mono_w32process_get_pid (gpointer handle)
574 {
575         MonoW32HandleProcess *process_handle;
576         gboolean res;
577
578         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
579         if (!res) {
580                 SetLastError (ERROR_INVALID_HANDLE);
581                 return 0;
582         }
583
584         return process_handle->id;
585 }
586
587 typedef struct {
588         guint32 pid;
589         gpointer handle;
590 } GetProcessForeachData;
591
592 static gboolean
593 get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
594 {
595         GetProcessForeachData *foreach_data;
596         MonoW32HandleProcess *process_handle;
597         pid_t pid;
598
599         foreach_data = (GetProcessForeachData*) user_data;
600
601         if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS)
602                 return FALSE;
603
604         process_handle = (MonoW32HandleProcess*) handle_specific;
605
606         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id);
607
608         pid = process_handle->id;
609         if (pid == 0)
610                 return FALSE;
611
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;
619
620         mono_w32handle_ref (handle);
621         foreach_data->handle = handle;
622         return TRUE;
623 }
624
625 HANDLE
626 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
627 {
628         GetProcessForeachData foreach_data;
629         gpointer handle;
630
631         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for process %d", __func__, pid);
632
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;
640         }
641
642         if (process_is_alive (pid)) {
643                 /* non-child process */
644                 MonoW32HandleProcess process_handle;
645
646                 memset (&process_handle, 0, sizeof (process_handle));
647                 process_handle.id = pid;
648                 process_handle.proc_name = mono_w32process_get_name (pid);
649
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__);
653
654                         SetLastError (ERROR_OUTOFMEMORY);
655                         return NULL;
656                 }
657
658                 return handle;
659         }
660
661         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid);
662
663         SetLastError (ERROR_PROC_NOT_FOUND);
664         return NULL;
665 }
666
667 static gboolean
668 match_procname_to_modulename (char *procname, char *modulename)
669 {
670         char* lastsep = NULL;
671         char* lastsep2 = NULL;
672         char* pname = NULL;
673         char* mname = NULL;
674         gboolean result = FALSE;
675
676         if (procname == NULL || modulename == NULL)
677                 return (FALSE);
678
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);
682
683         if (!strcmp (pname, mname))
684                 result = TRUE;
685
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;
700                                 }
701                         }
702                 }
703         }
704
705         g_free (pname);
706         g_free (mname);
707
708         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
709         return result;
710 }
711
712 gboolean
713 mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed)
714 {
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;
723
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.
732          */
733         if (size < sizeof(gpointer))
734                 return FALSE;
735
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;
740         }
741
742         pid = process_handle->id;
743         proc_name = g_strdup (process_handle->proc_name);
744
745         if (!proc_name) {
746                 modules[0] = NULL;
747                 *needed = sizeof(gpointer);
748                 return TRUE;
749         }
750
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;
757         }
758
759         count = g_slist_length (mods);
760
761         /* count + 1 to leave slot 0 for the main module */
762         *needed = sizeof(gpointer) * (count + 1);
763
764         /*
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.
771          */
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;
781         }
782
783         for (i = 0; i < count; i++) {
784                 mono_w32process_module_free ((MonoW32ProcessModule *)g_slist_nth_data (mods, i));
785         }
786         g_slist_free (mods);
787         g_free (proc_name);
788
789         return TRUE;
790 }
791
792 guint32
793 mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
794 {
795         gint pid, len;
796         gsize bytes;
797         gchar *path;
798         gunichar2 *proc_path;
799
800         size *= sizeof (gunichar2); /* adjust for unicode characters */
801
802         if (basename == NULL || size == 0)
803                 return 0;
804
805         pid = mono_w32process_get_pid (process);
806
807         path = mono_w32process_get_path (pid);
808         if (path == NULL)
809                 return 0;
810
811         proc_path = mono_unicode_from_external (path, &bytes);
812         g_free (path);
813
814         if (proc_path == NULL)
815                 return 0;
816
817         len = (bytes / 2);
818
819         /* Add the terminator */
820         bytes += 2;
821
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);
828         }
829
830         g_free (proc_path);
831
832         return len;
833 }
834
835 guint32
836 mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
837 {
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;
850
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);
853
854         size = size * sizeof (gunichar2); /* adjust for unicode characters */
855
856         if (basename == NULL || size == 0)
857                 return 0;
858
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;
863         }
864
865         pid = process_handle->id;
866         proc_name = g_strdup (process_handle->proc_name);
867
868         mods = mono_w32process_get_modules (pid);
869         if (!mods) {
870                 g_free (proc_name);
871                 return 0;
872         }
873
874         count = g_slist_length (mods);
875
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.
879          */
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);
886                 }
887
888                 mono_w32process_module_free (found_module);
889         }
890
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.
895                  */
896                 procname_ext = mono_w32process_get_name (pid);
897         }
898
899         g_slist_free (mods);
900         g_free (proc_name);
901
902         if (procname_ext) {
903                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
904                            procname_ext);
905
906                 procname = mono_unicode_from_external (procname_ext, &bytes);
907                 if (procname == NULL) {
908                         /* bugger */
909                         g_free (procname_ext);
910                         return 0;
911                 }
912
913                 len = (bytes / 2);
914
915                 /* Add the terminator */
916                 bytes += 2;
917
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);
920
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);
925
926                         memcpy (basename, procname, bytes);
927                 }
928
929                 g_free (procname);
930                 g_free (procname_ext);
931
932                 return len;
933         }
934
935         return 0;
936 }
937
938 gboolean
939 mono_w32process_module_get_information (gpointer process, gpointer module, WapiModuleInfo *modinfo, guint32 size)
940 {
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;
950
951         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
952                    __func__, process, module);
953
954         if (modinfo == NULL || size < sizeof (WapiModuleInfo))
955                 return FALSE;
956
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;
961         }
962
963         pid = process_handle->id;
964         proc_name = g_strdup (process_handle->proc_name);
965
966         mods = mono_w32process_get_modules (pid);
967         if (!mods) {
968                 g_free (proc_name);
969                 return FALSE;
970         }
971
972         count = g_slist_length (mods);
973
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.
977          */
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;
987                         }
988
989                         mono_w32process_module_free (found_module);
990         }
991
992         g_slist_free (mods);
993         g_free (proc_name);
994
995         return ret;
996 }
997
998 static void
999 switch_dir_separators (char *path)
1000 {
1001         size_t i, pathLength = strlen(path);
1002         
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] = '/';
1008         }
1009 }
1010
1011 #if HAVE_SIGACTION
1012
1013 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
1014 {
1015         int status;
1016         int pid;
1017         MonoProcess *p;
1018
1019         do {
1020                 do {
1021                         pid = waitpid (-1, &status, WNOHANG);
1022                 } while (pid == -1 && errno == EINTR);
1023
1024                 if (pid <= 0)
1025                         break;
1026
1027                 /*
1028                  * This can run concurrently with the code in the rest of this module.
1029                  */
1030                 for (p = processes; p; p = p->next) {
1031                         if (p->pid != pid)
1032                                 continue;
1033
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;
1041                 }
1042         } while (1);
1043 }
1044
1045 static void
1046 process_add_sigchld_handler (void)
1047 {
1048         struct sigaction sa;
1049
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");
1055 }
1056
1057 #endif
1058
1059 static gboolean
1060 is_readable_or_executable (const char *prog)
1061 {
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;
1072 }
1073
1074 static gboolean
1075 is_executable (const char *prog)
1076 {
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;
1085 }
1086
1087 static gboolean
1088 is_managed_binary (const char *filename)
1089 {
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;
1103         
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.
1107          */
1108         if (file < 0) {
1109                 errno = original_errno;
1110                 return FALSE;
1111         }
1112
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);
1116
1117         /* We know we need to read a header field at offset 60. */
1118         if (file_size < 64)
1119                 goto leave;
1120
1121         num_read = read (file, buffer, 2);
1122
1123         if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
1124                 goto leave;
1125
1126         new_offset = lseek (file, 60, SEEK_SET);
1127
1128         if (new_offset != 60)
1129                 goto leave;
1130         
1131         num_read = read (file, buffer, 4);
1132
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);
1139         
1140         if (pe_header_offset + 24 > file_size)
1141                 goto leave;
1142
1143         new_offset = lseek (file, pe_header_offset, SEEK_SET);
1144
1145         if (new_offset != pe_header_offset)
1146                 goto leave;
1147
1148         num_read = read (file, buffer, 4);
1149
1150         if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
1151                 goto leave;
1152
1153         /*
1154          * Verify that the header we want in the optional header data
1155          * is present in this binary.
1156          */
1157         new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
1158
1159         if (new_offset != pe_header_offset + 20)
1160                 goto leave;
1161
1162         num_read = read (file, buffer, 2);
1163
1164         if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
1165                 goto leave;
1166
1167         optional_header_offset = pe_header_offset + 24;
1168
1169         /* Read the PE magic number */
1170         new_offset = lseek (file, optional_header_offset, SEEK_SET);
1171         
1172         if (new_offset != optional_header_offset)
1173                 goto leave;
1174
1175         num_read = read (file, buffer, 2);
1176
1177         if (num_read != 2)
1178                 goto leave;
1179
1180         magic_number = (buffer[0] | (buffer[1] << 8));
1181         
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;
1188
1189         /* Read the CLR header address and size fields. These will be
1190          * zero if the binary is not managed.
1191          */
1192         new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET);
1193
1194         if (new_offset != optional_header_offset + clr_header_offset)
1195                 goto leave;
1196
1197         num_read = read (file, buffer, 8);
1198         
1199         /* We are not concerned with endianness, only with
1200          * whether it is zero or not.
1201          */
1202         first_word = *(guint32 *)&buffer[0];
1203         second_word = *(guint32 *)&buffer[4];
1204         
1205         if ((num_read != 8) || (first_word == 0) || (second_word == 0))
1206                 goto leave;
1207         
1208         managed = TRUE;
1209
1210 leave:
1211         close (file);
1212         errno = original_errno;
1213         return managed;
1214 }
1215
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)
1219 {
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;
1232
1233 #if HAVE_SIGACTION
1234         mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
1235 #endif
1236
1237         /* appname and cmdline specify the executable and its args:
1238          *
1239          * If appname is not NULL, it is the name of the executable.
1240          * Otherwise the executable is the first token in cmdline.
1241          *
1242          * Executable searching:
1243          *
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.
1247          *
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:
1251          *
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
1257          *
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 :-(
1262          */
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__);
1268
1269                         SetLastError (ERROR_PATH_NOT_FOUND);
1270                         goto free_strings;
1271                 }
1272
1273                 switch_dir_separators(cmd);
1274         }
1275
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__);
1280
1281                         SetLastError (ERROR_PATH_NOT_FOUND);
1282                         goto free_strings;
1283                 }
1284         }
1285
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__);
1290
1291                         SetLastError (ERROR_PATH_NOT_FOUND);
1292                         goto free_strings;
1293                 }
1294
1295                 /* Turn all the slashes round the right way */
1296                 switch_dir_separators(dir);
1297         }
1298
1299
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...
1307                          */
1308                         g_memmove (cmd, cmd+2, strlen (cmd)-2);
1309                         cmd[strlen (cmd)-2] = '\0';
1310                 }
1311
1312                 unquoted = g_shell_unquote (cmd, NULL);
1313                 if (unquoted[0] == '/') {
1314                         /* Assume full path given */
1315                         prog = g_strdup (unquoted);
1316
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;
1324                         }
1325                 } else {
1326                         /* Search for file named by cmd in the current
1327                          * directory
1328                          */
1329                         char *curdir = g_get_current_dir ();
1330
1331                         prog = g_strdup_printf ("%s/%s", curdir, unquoted);
1332                         g_free (curdir);
1333
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;
1341                         }
1342                 }
1343                 g_free (unquoted);
1344
1345                 args_after_prog = args;
1346         } else {
1347                 char *token = NULL;
1348                 char quote;
1349
1350                 /* Dig out the first token from args, taking quotation
1351                  * marks into account
1352                  */
1353
1354                 /* First, strip off all leading whitespace */
1355                 args = g_strchug (args);
1356
1357                 /* args_after_prog points to the contents of args
1358                  * after token has been set (otherwise argv[0] is
1359                  * duplicated)
1360                  */
1361                 args_after_prog = args;
1362
1363                 /* Assume the opening quote will always be the first
1364                  * character
1365                  */
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.
1378                                  */
1379                         }
1380                 }
1381
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;
1389                                 }
1390                         }
1391                 }
1392
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;
1397                 }
1398
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__);
1402
1403                         SetLastError (ERROR_PATH_NOT_FOUND);
1404                         goto free_strings;
1405                 }
1406
1407                 /* Turn all the slashes round the right way. Only for
1408                  * the prg. name
1409                  */
1410                 switch_dir_separators(token);
1411
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...
1416                          */
1417                         g_memmove (token, token+2, strlen (token)-2);
1418                         token[strlen (token)-2] = '\0';
1419                 }
1420
1421                 if (token[0] == '/') {
1422                         /* Assume full path given */
1423                         prog = g_strdup (token);
1424
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;
1432                         }
1433                 } else {
1434                         char *curdir = g_get_current_dir ();
1435
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
1440                          */
1441
1442                         prog = g_strdup_printf ("%s/%s", curdir, token);
1443                         g_free (curdir);
1444
1445                         /* I assume X_OK is the criterion to use,
1446                          * rather than F_OK
1447                          *
1448                          * X_OK is too strict *if* the target is a CLR binary
1449                          */
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);
1455
1456                                         g_free (token);
1457                                         SetLastError (ERROR_FILE_NOT_FOUND);
1458                                         goto free_strings;
1459                                 }
1460                         }
1461                 }
1462
1463                 g_free (token);
1464         }
1465
1466         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Exec prog [%s] args [%s]",
1467                 __func__, prog, args_after_prog);
1468
1469         /* Check for CLR binaries; if found, we will try to invoke
1470          * them using the same mono binary that started us.
1471          */
1472         if (is_managed_binary (prog)) {
1473                 gunichar2 *newapp, *newcmd;
1474                 gsize bytes_ignored;
1475
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);
1482
1483                         g_free (newapp);
1484
1485                         if (newcmd) {
1486                                 ret = process_create (NULL, newcmd, new_environ, cwd, startup_handles, process_info);
1487
1488                                 g_free (newcmd);
1489
1490                                 goto free_strings;
1491                         }
1492                 }
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;
1498                 }
1499         }
1500
1501         if (args_after_prog != NULL && *args_after_prog) {
1502                 char *qprog;
1503
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);
1509         }
1510
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;
1517         }
1518
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));
1527         }
1528
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 :-(
1532          *
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.
1536          */
1537         if (new_environ) {
1538                 gunichar2 *new_environp;
1539
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++;
1545                 }
1546
1547                 /* +2: one for the process handle value, and the last
1548                  * one is NULL
1549                  */
1550                 env_strings = g_new0 (char *, env_count + 2);
1551
1552                 /* Copy each environ string into 'strings' turning it
1553                  * into utf8 (or the requested encoding) at the same
1554                  * time
1555                  */
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++;
1562                         }
1563                 }
1564         } else {
1565                 for (i = 0; environ[i] != NULL; i++)
1566                         env_count++;
1567
1568                 /* +2: one for the process handle value, and the last
1569                  * one is NULL
1570                  */
1571                 env_strings = g_new0 (char *, env_count + 2);
1572
1573                 /* Copy each environ string into 'strings' turning it
1574                  * into utf8 (or the requested encoding) at the same
1575                  * time
1576                  */
1577                 env_count = 0;
1578                 for (i = 0; environ[i] != NULL; i++) {
1579                         env_strings[env_count] = g_strdup (environ[i]);
1580                         env_count++;
1581                 }
1582         }
1583
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__);
1591         }
1592
1593 #if HAVE_SIGACTION
1594         /* FIXME: block SIGCHLD */
1595 #endif
1596
1597         switch (pid = fork ()) {
1598         case -1: /* Error */ {
1599                 SetLastError (ERROR_OUTOFMEMORY);
1600                 ret = FALSE;
1601                 break;
1602         }
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]);
1610                 }
1611
1612                 /* should we detach from the process group? */
1613
1614                 /* Connect stdin, stdout and stderr */
1615                 dup2 (in_fd, 0);
1616                 dup2 (out_fd, 1);
1617                 dup2 (err_fd, 2);
1618
1619                 /* Close all file descriptors */
1620                 for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
1621                         close (i);
1622
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]);
1628
1629                 for (i = 0; env_strings[i] != NULL; i++)
1630                         g_message ("env %d: [%s]", i, env_strings[i]);
1631 #endif
1632
1633                 /* set cwd */
1634                 if (dir != NULL && chdir (dir) == -1) {
1635                         /* set error */
1636                         _exit (-1);
1637                 }
1638
1639                 /* exec */
1640                 execve (argv[0], argv, env_strings);
1641
1642                 /* set error */
1643                 _exit (-1);
1644
1645                 break;
1646         }
1647         default: /* Parent */ {
1648                 MonoW32HandleProcess process_handle;
1649
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);
1655
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);
1661
1662                 process_handle.mono_process = mono_process;
1663
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__);
1667
1668                         mono_os_sem_destroy (&mono_process->exit_sem);
1669                         g_free (mono_process);
1670
1671                         SetLastError (ERROR_OUTOFMEMORY);
1672                         ret = FALSE;
1673                         break;
1674                 }
1675
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;
1680
1681                 mono_os_mutex_lock (&processes_mutex);
1682                 mono_process->next = processes;
1683                 processes = mono_process;
1684                 mono_os_mutex_unlock (&processes_mutex);
1685
1686                 if (process_info != NULL) {
1687                         process_info->process_handle = handle;
1688                         process_info->pid = pid;
1689
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;
1693                 }
1694
1695                 break;
1696         }
1697         }
1698
1699 #if HAVE_SIGACTION
1700         /* FIXME: unblock SIGCHLD */
1701 #endif
1702
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]);
1708         }
1709
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);
1725
1726         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p for pid %d", __func__, handle, pid);
1727
1728         /* Check if something needs to be cleaned up. */
1729         processes_cleanup ();
1730
1731         return ret;
1732 #else
1733         SetLastError (ERROR_NOT_SUPPORTED);
1734         return FALSE;
1735 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1736 }
1737
1738 MonoBoolean
1739 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
1740 {
1741         const gunichar2 *lpFile;
1742         const gunichar2 *lpParameters;
1743         const gunichar2 *lpDirectory;
1744         gunichar2 *args;
1745         gboolean ret;
1746
1747         if (!proc_start_info->filename) {
1748                 /* w2k returns TRUE for this, for some reason. */
1749                 ret = TRUE;
1750                 goto done;
1751         }
1752
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;
1757
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 :-(
1762          */
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;
1768         }
1769         ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1770         g_free (args);
1771
1772         if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
1773                 goto done;
1774
1775         if (!ret) {
1776                 static char *handler;
1777                 static gunichar2 *handler_utf16;
1778
1779                 if (handler_utf16 == (gunichar2 *)-1) {
1780                         ret = FALSE;
1781                         goto done;
1782                 }
1783
1784 #ifdef PLATFORM_MACOSX
1785                 handler = g_strdup ("/usr/bin/open");
1786 #else
1787                 /*
1788                  * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
1789                  * if that fails, try to use gnome-open, then kfmclient
1790                  */
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);
1806                                 }
1807                         }
1808                 }
1809 #endif
1810                 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
1811                 g_free (handler);
1812
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.
1818                  */
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;
1825                 }
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;
1833                 }
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;
1837         }
1838
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;
1850         }
1851
1852         return ret;
1853 }
1854
1855 /* Only used when UseShellExecute is false */
1856 static gboolean
1857 process_get_complete_path (const gunichar2 *appname, gchar **completed)
1858 {
1859         gchar *utf8app;
1860         gchar *found;
1861
1862         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
1863
1864         if (g_path_is_absolute (utf8app)) {
1865                 *completed = g_shell_quote (utf8app);
1866                 g_free (utf8app);
1867                 return TRUE;
1868         }
1869
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;
1874         }
1875         
1876         found = g_find_program_in_path (utf8app);
1877         if (found == NULL) {
1878                 *completed = NULL;
1879                 g_free (utf8app);
1880                 return FALSE;
1881         }
1882
1883         *completed = g_shell_quote (found);
1884         g_free (found);
1885         g_free (utf8app);
1886         return TRUE;
1887 }
1888
1889 static gboolean
1890 process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
1891 {
1892         gchar *spath = NULL;
1893
1894         *shell_path = NULL;
1895         *cmd = proc_start_info->arguments;
1896
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);
1901         }
1902
1903         return (*shell_path != NULL) ? TRUE : FALSE;
1904 }
1905
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)
1909 {
1910         gboolean ret;
1911         gunichar2 *dir;
1912         StartupHandles startup_handles;
1913         gunichar2 *shell_path = NULL;
1914         gchar *env_vars = NULL;
1915         MonoString *cmd = NULL;
1916
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;
1921
1922         if (process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
1923                 process_info->pid = -ERROR_FILE_NOT_FOUND;
1924                 return FALSE;
1925         }
1926
1927         if (process_info->env_keys) {
1928                 gint i, len; 
1929                 MonoString *ms;
1930                 MonoString *key, *value;
1931                 gunichar2 *str, *ptr;
1932                 gunichar2 *equals16;
1933
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;
1938
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);
1943                 }
1944
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;
1951
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);
1955
1956                         memcpy (ptr, equals16, sizeof (gunichar2));
1957                         ptr++;
1958
1959                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
1960                         ptr += mono_string_length (value);
1961                         ptr++;
1962                 }
1963
1964                 g_free (equals16);
1965                 env_vars = (gchar *) str;
1966         }
1967         
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;
1971
1972         ret = process_create (shell_path, cmd ? mono_string_chars (cmd): NULL, env_vars, dir, &startup_handles, process_info);
1973
1974         g_free (env_vars);
1975         if (shell_path != NULL)
1976                 g_free (shell_path);
1977
1978         if (!ret)
1979                 process_info->pid = -GetLastError ();
1980
1981         return ret;
1982 }
1983
1984 /* Returns an array of pids */
1985 MonoArray *
1986 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
1987 {
1988         MonoError error;
1989         MonoArray *procs;
1990         gpointer *pidarray;
1991         int i, count;
1992
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;
1997         }
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;
2002         }
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]);
2008         }
2009         g_free (pidarray);
2010
2011         return procs;
2012 }
2013
2014 void
2015 mono_w32process_set_cli_launcher (gchar *path)
2016 {
2017         g_free (cli_launcher);
2018         cli_launcher = g_strdup (path);
2019 }
2020
2021 gpointer
2022 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2023 {
2024         mono_w32handle_ref (current_process);
2025         return current_process;
2026 }
2027
2028 MonoBoolean
2029 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
2030 {
2031         MonoW32HandleProcess *process_handle;
2032         gboolean res;
2033
2034         if (!exitcode)
2035                 return FALSE;
2036
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;
2041         }
2042
2043         if (process_handle->id == wapi_getpid ()) {
2044                 *exitcode = STILL_ACTIVE;
2045                 return TRUE;
2046         }
2047
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);
2053
2054         *exitcode = mono_w32handle_issignalled (handle) ? process_handle->exitstatus : STILL_ACTIVE;
2055         return TRUE;
2056 }
2057
2058 MonoBoolean
2059 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
2060 {
2061         return CloseHandle (handle);
2062 }
2063
2064 MonoBoolean
2065 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
2066 {
2067 #ifdef HAVE_KILL
2068         MonoW32HandleProcess *process_handle;
2069         int ret;
2070         pid_t pid;
2071         gboolean res;
2072
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;
2078         }
2079
2080         pid = process_handle->id;
2081
2082         ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
2083         if (ret == 0)
2084                 return TRUE;
2085
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;
2091         }
2092
2093         return FALSE;
2094 #else
2095         g_error ("kill() is not supported by this platform");
2096 #endif
2097 }
2098
2099 MonoBoolean
2100 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
2101 {
2102         MonoW32HandleProcess *process_handle;
2103         gboolean res;
2104
2105         if (!min || !max)
2106                 return FALSE;
2107
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;
2112         }
2113
2114         if (!process_handle->child)
2115                 return FALSE;
2116
2117         *min = process_handle->min_working_set;
2118         *max = process_handle->max_working_set;
2119         return TRUE;
2120 }
2121
2122 MonoBoolean
2123 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
2124 {
2125         MonoW32HandleProcess *process_handle;
2126         gboolean res;
2127
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;
2132         }
2133
2134         if (!process_handle->child)
2135                 return FALSE;
2136
2137         process_handle->min_working_set = min;
2138         process_handle->max_working_set = max;
2139         return TRUE;
2140 }
2141
2142 gint32
2143 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
2144 {
2145 #ifdef HAVE_GETPRIORITY
2146         MonoW32HandleProcess *process_handle;
2147         gint ret;
2148         pid_t pid;
2149         gboolean res;
2150
2151         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2152         if (!res) {
2153                 SetLastError (ERROR_INVALID_HANDLE);
2154                 return 0;
2155         }
2156
2157         pid = process_handle->id;
2158
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);
2172                 }
2173                 return 0;
2174         }
2175
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;
2188
2189         return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2190 #else
2191         SetLastError (ERROR_NOT_SUPPORTED);
2192         return 0;
2193 #endif
2194 }
2195
2196 MonoBoolean
2197 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
2198 {
2199 #ifdef HAVE_SETPRIORITY
2200         MonoW32HandleProcess *process_handle;
2201         int ret;
2202         int prio;
2203         pid_t pid;
2204         gboolean res;
2205
2206         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2207         if (!res) {
2208                 SetLastError (ERROR_INVALID_HANDLE);
2209                 return FALSE;
2210         }
2211
2212         pid = process_handle->id;
2213
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;
2236         }
2237
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);
2250                 }
2251         }
2252
2253         return ret == 0;
2254 #else
2255         SetLastError (ERROR_NOT_SUPPORTED);
2256         return FALSE;
2257 #endif
2258 }
2259
2260 static void
2261 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
2262 {
2263         processtime->lowDateTime = ticks & 0xFFFFFFFF;
2264         processtime->highDateTime = ticks >> 32;
2265 }
2266
2267 static void
2268 wapifiletime_to_processtime (WapiFileTime wapi_filetime, ProcessTime *processtime)
2269 {
2270         processtime->lowDateTime = wapi_filetime.dwLowDateTime;
2271         processtime->highDateTime = wapi_filetime.dwHighDateTime;
2272 }
2273
2274 MonoBoolean
2275 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
2276 {
2277         MonoW32HandleProcess *process_handle;
2278         ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime;
2279         gboolean res;
2280
2281         if (!creation_time || !exit_time || !kernel_time || !user_time) {
2282                 /* Not sure if w32 allows NULLs here or not */
2283                 return FALSE;
2284         }
2285
2286         creation_processtime = (ProcessTime*) creation_time;
2287         exit_processtime = (ProcessTime*) exit_time;
2288         kernel_processtime = (ProcessTime*) kernel_time;
2289         user_processtime = (ProcessTime*) user_time;
2290
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));
2295
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;
2300         }
2301
2302         if (!process_handle->child) {
2303                 gint64 start_ticks, user_ticks, kernel_ticks;
2304
2305                 mono_process_get_times (GINT_TO_POINTER (process_handle->id),
2306                         &start_ticks, &user_ticks, &kernel_ticks);
2307
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;
2312         }
2313
2314         wapifiletime_to_processtime (process_handle->create_time, creation_processtime);
2315
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);
2320
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);
2327                 }
2328         }
2329 #endif
2330
2331         return TRUE;
2332 }