Merge pull request #4048 from kumpera/iface_casting_cleanup
[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 /*
97  * Handles > _WAPI_PROCESS_UNHANDLED are pseudo handles which represent processes
98  * not started by the runtime.
99  */
100 /* This marks a system process that we don't have a handle on */
101 /* FIXME: Cope with PIDs > sizeof guint */
102 #define _WAPI_PROCESS_UNHANDLED (1 << (8*sizeof(pid_t)-1))
103
104 #define WAPI_IS_PSEUDO_PROCESS_HANDLE(handle) ((GPOINTER_TO_UINT(handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED)
105 #define WAPI_PID_TO_HANDLE(pid) GINT_TO_POINTER (_WAPI_PROCESS_UNHANDLED + (pid))
106 #define WAPI_HANDLE_TO_PID(handle) (GPOINTER_TO_UINT ((handle)) - _WAPI_PROCESS_UNHANDLED)
107
108 typedef enum {
109         STARTF_USESHOWWINDOW=0x001,
110         STARTF_USESIZE=0x002,
111         STARTF_USEPOSITION=0x004,
112         STARTF_USECOUNTCHARS=0x008,
113         STARTF_USEFILLATTRIBUTE=0x010,
114         STARTF_RUNFULLSCREEN=0x020,
115         STARTF_FORCEONFEEDBACK=0x040,
116         STARTF_FORCEOFFFEEDBACK=0x080,
117         STARTF_USESTDHANDLES=0x100
118 } StartupFlags;
119
120 typedef struct {
121         gpointer input;
122         gpointer output;
123         gpointer error;
124 } StartupHandles;
125
126 typedef struct {
127 #if G_BYTE_ORDER == G_BIG_ENDIAN
128         guint32 highDateTime;
129         guint32 lowDateTime;
130 #else
131         guint32 lowDateTime;
132         guint32 highDateTime;
133 #endif
134 } ProcessTime;
135
136 /*
137  * MonoProcess describes processes we create.
138  * It contains a semaphore that can be waited on in order to wait
139  * for process termination. It's accessed in our SIGCHLD handler,
140  * when status is updated (and pid cleared, to not clash with
141  * subsequent processes that may get executed).
142  */
143 typedef struct _MonoProcess MonoProcess;
144 struct _MonoProcess {
145         pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
146         MonoSemType exit_sem; /* this semaphore will be released when the process exits */
147         int status; /* the exit status */
148         gint32 handle_count; /* the number of handles to this mono_process instance */
149         /* we keep a ref to the creating _WapiHandle_process handle until
150          * the process has exited, so that the information there isn't lost.
151          */
152         gpointer handle;
153         gboolean freeable;
154         MonoProcess *next;
155 };
156
157 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
158 typedef struct {
159         pid_t id;
160         guint32 exitstatus;
161         gpointer main_thread;
162         WapiFileTime create_time;
163         WapiFileTime exit_time;
164         char *proc_name;
165         size_t min_working_set;
166         size_t max_working_set;
167         gboolean exited;
168         MonoProcess *mono_process;
169 } MonoW32HandleProcess;
170
171 #if HAVE_SIGACTION
172 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
173 #endif
174
175 static gchar *cli_launcher;
176
177 /* The signal-safe logic to use processes goes like this:
178  * - The list must be safe to traverse for the signal handler at all times.
179  *   It's safe to: prepend an entry (which is a single store to 'processes'),
180  *   unlink an entry (assuming the unlinked entry isn't freed and doesn't
181  *   change its 'next' pointer so that it can still be traversed).
182  * When cleaning up we first unlink an entry, then we verify that
183  * the read lock isn't locked. Then we can free the entry, since
184  * we know that nobody is using the old version of the list (including
185  * the unlinked entry).
186  * We also need to lock when adding and cleaning up so that those two
187  * operations don't mess with eachother. (This lock is not used in the
188  * signal handler) */
189 static MonoProcess *processes;
190 static mono_mutex_t processes_mutex;
191
192 static gpointer current_process;
193
194 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
195 static const gunichar2 *utf16_space = utf16_space_bytes;
196 static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
197 static const gunichar2 *utf16_quote = utf16_quote_bytes;
198
199 static void
200 process_details (gpointer data)
201 {
202         MonoW32HandleProcess *process_handle = (MonoW32HandleProcess *) data;
203         g_print ("id: %d, exited: %s, exitstatus: %d",
204                 process_handle->id, process_handle->exited ? "true" : "false", process_handle->exitstatus);
205 }
206
207 static const gchar*
208 process_typename (void)
209 {
210         return "Process";
211 }
212
213 static gsize
214 process_typesize (void)
215 {
216         return sizeof (MonoW32HandleProcess);
217 }
218
219 static MonoW32HandleWaitRet
220 process_wait (gpointer handle, guint32 timeout, gboolean *alerted)
221 {
222         MonoW32HandleProcess *process_handle;
223         pid_t pid G_GNUC_UNUSED, ret;
224         int status;
225         gint64 start, now;
226         MonoProcess *mp;
227         gboolean res;
228
229         /* FIXME: We can now easily wait on processes that aren't our own children,
230          * but WaitFor*Object won't call us for pseudo handles. */
231         g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
232
233         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
234
235         if (alerted)
236                 *alerted = FALSE;
237
238         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
239         if (!res) {
240                 g_warning ("%s: error looking up process handle %p", __func__, handle);
241                 return MONO_W32HANDLE_WAIT_RET_FAILED;
242         }
243
244         if (process_handle->exited) {
245                 /* We've already done this one */
246                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Process already exited", __func__, handle, timeout);
247                 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
248         }
249
250         pid = process_handle->id;
251
252         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
253
254         /* We don't need to lock processes here, the entry
255          * has a handle_count > 0 which means it will not be freed. */
256         mp = process_handle->mono_process;
257         if (!mp) {
258                 pid_t res;
259
260                 if (pid == mono_process_current_pid ()) {
261                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout);
262                         return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
263                 }
264
265                 /* This path is used when calling Process.HasExited, so
266                  * it is only used to poll the state of the process, not
267                  * to actually wait on it to exit */
268                 g_assert (timeout == 0);
269
270                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout);
271
272                 res = waitpid (pid, &status, WNOHANG);
273                 if (res == 0) {
274                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process wait timeout", __func__, handle, timeout);
275                         return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
276                 }
277                 if (res > 0) {
278                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully", __func__, handle, timeout);
279                         return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
280                 }
281
282                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process wait failed, error : %s (%d))", __func__, handle, timeout, g_strerror (errno), errno);
283                 return MONO_W32HANDLE_WAIT_RET_FAILED;
284         }
285
286         start = mono_msec_ticks ();
287         now = start;
288
289         while (1) {
290                 if (timeout != MONO_INFINITE_WAIT) {
291                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore for %li ms...",
292                                 __func__, handle, timeout, (long)(timeout - (now - start)));
293                         ret = mono_os_sem_timedwait (&mp->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
294                 } else {
295                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore forever...",
296                                 __func__, handle, timeout);
297                         ret = mono_os_sem_wait (&mp->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
298                 }
299
300                 if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
301                         /* Success, process has exited */
302                         mono_os_sem_post (&mp->exit_sem);
303                         break;
304                 }
305
306                 if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
307                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout (timeout = 0)", __func__, handle, timeout);
308                         return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
309                 }
310
311                 now = mono_msec_ticks ();
312                 if (now - start >= timeout) {
313                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait timeout", __func__, handle, timeout);
314                         return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
315                 }
316
317                 if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
318                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): wait alerted", __func__, handle, timeout);
319                         *alerted = TRUE;
320                         return MONO_W32HANDLE_WAIT_RET_ALERTED;
321                 }
322         }
323
324         /* Process must have exited */
325         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout);
326
327         status = mp->status;
328         if (WIFSIGNALED (status))
329                 process_handle->exitstatus = 128 + WTERMSIG (status);
330         else
331                 process_handle->exitstatus = WEXITSTATUS (status);
332         _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
333
334         process_handle->exited = TRUE;
335
336         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Setting pid %d signalled, exit status %d",
337                    __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
338
339         mono_w32handle_set_signal_state (handle, TRUE, TRUE);
340
341         return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
342 }
343
344 static void
345 processes_cleanup (void)
346 {
347         static gint32 cleaning_up;
348         MonoProcess *mp;
349         MonoProcess *prev = NULL;
350         GSList *finished = NULL;
351         GSList *l;
352         gpointer unref_handle;
353
354         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
355
356         /* Ensure we're not in here in multiple threads at once, nor recursive. */
357         if (InterlockedCompareExchange (&cleaning_up, 1, 0) != 0)
358                 return;
359
360         for (mp = processes; mp; mp = mp->next) {
361                 if (mp->pid == 0 && mp->handle) {
362                         /* This process has exited and we need to remove the artifical ref
363                          * on the handle */
364                         mono_os_mutex_lock (&processes_mutex);
365                         unref_handle = mp->handle;
366                         mp->handle = NULL;
367                         mono_os_mutex_unlock (&processes_mutex);
368                         if (unref_handle)
369                                 mono_w32handle_unref (unref_handle);
370                 }
371         }
372
373         /*
374          * Remove processes which exited from the processes list.
375          * We need to synchronize with the sigchld handler here, which runs
376          * asynchronously. The handler requires that the processes list
377          * remain valid.
378          */
379         mono_os_mutex_lock (&processes_mutex);
380
381         mp = processes;
382         while (mp) {
383                 if (mp->handle_count == 0 && mp->freeable) {
384                         /*
385                          * Unlink the entry.
386                          * This code can run parallel with the sigchld handler, but the
387                          * modifications it makes are safe.
388                          */
389                         if (mp == processes)
390                                 processes = mp->next;
391                         else
392                                 prev->next = mp->next;
393                         finished = g_slist_prepend (finished, mp);
394
395                         mp = mp->next;
396                 } else {
397                         prev = mp;
398                         mp = mp->next;
399                 }
400         }
401
402         mono_memory_barrier ();
403
404         for (l = finished; l; l = l->next) {
405                 /*
406                  * All the entries in the finished list are unlinked from processes, and
407                  * they have the 'finished' flag set, which means the sigchld handler is done
408                  * accessing them.
409                  */
410                 mp = (MonoProcess *)l->data;
411                 mono_os_sem_destroy (&mp->exit_sem);
412                 g_free (mp);
413         }
414         g_slist_free (finished);
415
416         mono_os_mutex_unlock (&processes_mutex);
417
418         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
419
420         InterlockedExchange (&cleaning_up, 0);
421 }
422
423 static void
424 process_close (gpointer handle, gpointer data)
425 {
426         MonoW32HandleProcess *process_handle;
427
428         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
429
430         process_handle = (MonoW32HandleProcess *) data;
431         g_free (process_handle->proc_name);
432         process_handle->proc_name = NULL;
433         if (process_handle->mono_process)
434                 InterlockedDecrement (&process_handle->mono_process->handle_count);
435         processes_cleanup ();
436 }
437
438 static MonoW32HandleOps process_ops = {
439         process_close,          /* close_shared */
440         NULL,                           /* signal */
441         NULL,                           /* own */
442         NULL,                           /* is_owned */
443         process_wait,                   /* special_wait */
444         NULL,                           /* prewait */
445         process_details,        /* details */
446         process_typename,       /* typename */
447         process_typesize,       /* typesize */
448 };
449
450 static void
451 process_set_defaults (MonoW32HandleProcess *process_handle)
452 {
453         /* These seem to be the defaults on w2k */
454         process_handle->min_working_set = 204800;
455         process_handle->max_working_set = 1413120;
456
457         _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
458 }
459
460 static void
461 process_set_name (MonoW32HandleProcess *process_handle)
462 {
463         char *progname, *utf8_progname, *slash;
464
465         progname = g_get_prgname ();
466         utf8_progname = mono_utf8_from_external (progname);
467
468         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: using [%s] as prog name", __func__, progname);
469
470         if (utf8_progname) {
471                 slash = strrchr (utf8_progname, '/');
472                 if (slash)
473                         process_handle->proc_name = g_strdup (slash+1);
474                 else
475                         process_handle->proc_name = g_strdup (utf8_progname);
476                 g_free (utf8_progname);
477         }
478 }
479
480 void
481 mono_w32process_init (void)
482 {
483         MonoW32HandleProcess process_handle;
484
485         mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS, &process_ops);
486
487         mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS,
488                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
489
490         memset (&process_handle, 0, sizeof (process_handle));
491         process_handle.id = wapi_getpid ();
492         process_set_defaults (&process_handle);
493         process_set_name (&process_handle);
494
495         current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
496         g_assert (current_process);
497
498         mono_os_mutex_init (&processes_mutex);
499 }
500
501 void
502 mono_w32process_cleanup (void)
503 {
504         g_free (cli_launcher);
505 }
506
507 /* Check if a pid is valid - i.e. if a process exists with this pid. */
508 static gboolean
509 is_pid_valid (pid_t pid)
510 {
511         gboolean result = FALSE;
512
513 #if defined(HOST_WATCHOS)
514         result = TRUE; // TODO: Rewrite using sysctl
515 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
516         if (((kill(pid, 0) == 0) || (errno == EPERM)) && pid != 0)
517                 result = TRUE;
518 #elif defined(__HAIKU__)
519         team_info teamInfo;
520         if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
521                 result = TRUE;
522 #else
523         char *dir = g_strdup_printf ("/proc/%d", pid);
524         if (!access (dir, F_OK))
525                 result = TRUE;
526         g_free (dir);
527 #endif
528
529         return result;
530 }
531
532 static int
533 len16 (const gunichar2 *str)
534 {
535         int len = 0;
536
537         while (*str++ != 0)
538                 len++;
539
540         return len;
541 }
542
543 static gunichar2 *
544 utf16_concat (const gunichar2 *first, ...)
545 {
546         va_list args;
547         int total = 0, i;
548         const gunichar2 *s;
549         const gunichar2 *p;
550         gunichar2 *ret;
551
552         va_start (args, first);
553         total += len16 (first);
554         for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *))
555                 total += len16 (s);
556         va_end (args);
557
558         ret = g_new (gunichar2, total + 1);
559         if (ret == NULL)
560                 return NULL;
561
562         ret [total] = 0;
563         i = 0;
564         for (s = first; *s != 0; s++)
565                 ret [i++] = *s;
566         va_start (args, first);
567         for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
568                 for (p = s; *p != 0; p++)
569                         ret [i++] = *p;
570         }
571         va_end (args);
572
573         return ret;
574 }
575
576 guint32
577 mono_w32process_get_pid (gpointer handle)
578 {
579         MonoW32HandleProcess *process_handle;
580         gboolean res;
581
582         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
583                 /* This is a pseudo handle */
584                 return WAPI_HANDLE_TO_PID (handle);
585         }
586
587         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
588         if (!res) {
589                 SetLastError (ERROR_INVALID_HANDLE);
590                 return 0;
591         }
592
593         return process_handle->id;
594 }
595
596 typedef struct {
597         guint32 pid;
598         gpointer handle;
599 } GetProcessForeachData;
600
601 static gboolean
602 get_process_foreach_callback (gpointer handle, gpointer handle_specific, gpointer user_data)
603 {
604         GetProcessForeachData *foreach_data;
605         MonoW32HandleProcess *process_handle;
606         pid_t pid;
607
608         foreach_data = (GetProcessForeachData*) user_data;
609
610         if (mono_w32handle_get_type (handle) != MONO_W32HANDLE_PROCESS)
611                 return FALSE;
612
613         g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
614
615         process_handle = (MonoW32HandleProcess*) handle_specific;
616
617         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id);
618
619         pid = process_handle->id;
620         if (pid == 0)
621                 return FALSE;
622
623         /* It's possible to have more than one process handle with the
624          * same pid, but only the one running process can be
625          * unsignalled. */
626         if (foreach_data->pid != pid)
627                 return FALSE;
628         if (mono_w32handle_issignalled (handle))
629                 return FALSE;
630
631         mono_w32handle_ref (handle);
632         foreach_data->handle = handle;
633         return TRUE;
634 }
635
636 HANDLE
637 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
638 {
639         GetProcessForeachData foreach_data;
640         gpointer handle;
641
642         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for process %d", __func__, pid);
643
644         memset (&foreach_data, 0, sizeof (foreach_data));
645         foreach_data.pid = pid;
646         mono_w32handle_foreach (get_process_foreach_callback, &foreach_data);
647         handle = foreach_data.handle;
648         if (handle) {
649                 /* get_process_foreach_callback already added a ref */
650                 return handle;
651         }
652
653         if (is_pid_valid (pid)) {
654                 /* Return a pseudo handle for processes we don't have handles for */
655                 return WAPI_PID_TO_HANDLE (pid);
656         }
657
658         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid);
659
660         SetLastError (ERROR_PROC_NOT_FOUND);
661         return NULL;
662 }
663
664 static gboolean
665 match_procname_to_modulename (char *procname, char *modulename)
666 {
667         char* lastsep = NULL;
668         char* lastsep2 = NULL;
669         char* pname = NULL;
670         char* mname = NULL;
671         gboolean result = FALSE;
672
673         if (procname == NULL || modulename == NULL)
674                 return (FALSE);
675
676         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename);
677         pname = mono_path_resolve_symlinks (procname);
678         mname = mono_path_resolve_symlinks (modulename);
679
680         if (!strcmp (pname, mname))
681                 result = TRUE;
682
683         if (!result) {
684                 lastsep = strrchr (mname, '/');
685                 if (lastsep)
686                         if (!strcmp (lastsep+1, pname))
687                                 result = TRUE;
688                 if (!result) {
689                         lastsep2 = strrchr (pname, '/');
690                         if (lastsep2){
691                                 if (lastsep) {
692                                         if (!strcmp (lastsep+1, lastsep2+1))
693                                                 result = TRUE;
694                                 } else {
695                                         if (!strcmp (mname, lastsep2+1))
696                                                 result = TRUE;
697                                 }
698                         }
699                 }
700         }
701
702         g_free (pname);
703         g_free (mname);
704
705         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
706         return result;
707 }
708
709 gboolean
710 mono_w32process_try_get_modules (gpointer process, gpointer *modules, guint32 size, guint32 *needed)
711 {
712         MonoW32HandleProcess *process_handle;
713         GSList *mods = NULL, *mods_iter;
714         MonoW32ProcessModule *module;
715         guint32 count, avail = size / sizeof(gpointer);
716         int i;
717         pid_t pid;
718         char *proc_name = NULL;
719         gboolean res;
720
721         /* Store modules in an array of pointers (main module as
722          * modules[0]), using the load address for each module as a
723          * token.  (Use 'NULL' as an alternative for the main module
724          * so that the simple implementation can just return one item
725          * for now.)  Get the info from /proc/<pid>/maps on linux,
726          * /proc/<pid>/map on FreeBSD, other systems will have to
727          * implement /dev/kmem reading or whatever other horrid
728          * technique is needed.
729          */
730         if (size < sizeof(gpointer))
731                 return FALSE;
732
733         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
734                 pid = WAPI_HANDLE_TO_PID (process);
735                 proc_name = mono_w32process_get_name (pid);
736         } else {
737                 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
738                 if (!res) {
739                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
740                         return FALSE;
741                 }
742
743                 pid = process_handle->id;
744                 proc_name = g_strdup (process_handle->proc_name);
745         }
746
747         if (!proc_name) {
748                 modules[0] = NULL;
749                 *needed = sizeof(gpointer);
750                 return TRUE;
751         }
752
753         mods = mono_w32process_get_modules (pid);
754         if (!mods) {
755                 modules[0] = NULL;
756                 *needed = sizeof(gpointer);
757                 g_free (proc_name);
758                 return TRUE;
759         }
760
761         count = 0;
762
763         /*
764          * Use the NULL shortcut, as the first line in
765          * /proc/<pid>/maps isn't the executable, and we need
766          * that first in the returned list. Check the module name
767          * to see if it ends with the proc name and substitute
768          * the first entry with it.  FIXME if this turns out to
769          * be a problem.
770          */
771         modules[0] = NULL;
772         mods_iter = mods;
773         for (i = 0; mods_iter; i++) {
774                 if (i < avail - 1) {
775                         module = (MonoW32ProcessModule *)mods_iter->data;
776                         if (modules[0] != NULL)
777                                 modules[i] = module->address_start;
778                         else if (match_procname_to_modulename (proc_name, module->filename))
779                                 modules[0] = module->address_start;
780                         else
781                                 modules[i + 1] = module->address_start;
782                 }
783                 mono_w32process_module_free ((MonoW32ProcessModule *)mods_iter->data);
784                 mods_iter = g_slist_next (mods_iter);
785                 count++;
786         }
787
788         /* count + 1 to leave slot 0 for the main module */
789         *needed = sizeof(gpointer) * (count + 1);
790
791         g_slist_free (mods);
792         g_free (proc_name);
793
794         return TRUE;
795 }
796
797 guint32
798 mono_w32process_module_get_filename (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
799 {
800         gint pid, len;
801         gsize bytes;
802         gchar *path;
803         gunichar2 *proc_path;
804
805         size *= sizeof (gunichar2); /* adjust for unicode characters */
806
807         if (basename == NULL || size == 0)
808                 return 0;
809
810         pid = mono_w32process_get_pid (process);
811
812         path = mono_w32process_get_path (pid);
813         if (path == NULL)
814                 return 0;
815
816         proc_path = mono_unicode_from_external (path, &bytes);
817         g_free (path);
818
819         if (proc_path == NULL)
820                 return 0;
821
822         len = (bytes / 2);
823
824         /* Add the terminator */
825         bytes += 2;
826
827         if (size < bytes) {
828                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
829                 memcpy (basename, proc_path, size);
830         } else {
831                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)", __func__, size, bytes);
832                 memcpy (basename, proc_path, bytes);
833         }
834
835         g_free (proc_path);
836
837         return len;
838 }
839
840 guint32
841 mono_w32process_module_get_name (gpointer process, gpointer module, gunichar2 *basename, guint32 size)
842 {
843         MonoW32HandleProcess *process_handle;
844         pid_t pid;
845         gunichar2 *procname;
846         char *procname_ext = NULL;
847         glong len;
848         gsize bytes;
849         GSList *mods = NULL, *mods_iter;
850         MonoW32ProcessModule *found_module;
851         char *proc_name = NULL;
852         gboolean res;
853
854         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
855                    __func__, process, module);
856
857         size = size * sizeof (gunichar2); /* adjust for unicode characters */
858
859         if (basename == NULL || size == 0)
860                 return 0;
861
862         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
863                 /* This is a pseudo handle */
864                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
865                 proc_name = mono_w32process_get_name (pid);
866         } else {
867                 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
868                 if (!res) {
869                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
870                         return 0;
871                 }
872
873                 pid = process_handle->id;
874                 proc_name = g_strdup (process_handle->proc_name);
875         }
876
877         mods = mono_w32process_get_modules (pid);
878         if (!mods) {
879                 g_free (proc_name);
880                 return 0;
881         }
882
883         /* If module != NULL compare the address.
884          * If module == NULL we are looking for the main module.
885          * The best we can do for now check it the module name end with the process name.
886          */
887         for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
888                 found_module = (MonoW32ProcessModule *)mods_iter->data;
889                 if (procname_ext == NULL &&
890                         ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
891                          (module != NULL && found_module->address_start == module))) {
892                         procname_ext = g_path_get_basename (found_module->filename);
893                 }
894
895                 mono_w32process_module_free (found_module);
896         }
897
898         if (procname_ext == NULL) {
899                 /* If it's *still* null, we might have hit the
900                  * case where reading /proc/$pid/maps gives an
901                  * empty file for this user.
902                  */
903                 procname_ext = mono_w32process_get_name (pid);
904         }
905
906         g_slist_free (mods);
907         g_free (proc_name);
908
909         if (procname_ext) {
910                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
911                            procname_ext);
912
913                 procname = mono_unicode_from_external (procname_ext, &bytes);
914                 if (procname == NULL) {
915                         /* bugger */
916                         g_free (procname_ext);
917                         return 0;
918                 }
919
920                 len = (bytes / 2);
921
922                 /* Add the terminator */
923                 bytes += 2;
924
925                 if (size < bytes) {
926                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
927
928                         memcpy (basename, procname, size);
929                 } else {
930                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
931                                    __func__, size, bytes);
932
933                         memcpy (basename, procname, bytes);
934                 }
935
936                 g_free (procname);
937                 g_free (procname_ext);
938
939                 return len;
940         }
941
942         return 0;
943 }
944
945 gboolean
946 mono_w32process_module_get_information (gpointer process, gpointer module, WapiModuleInfo *modinfo, guint32 size)
947 {
948         MonoW32HandleProcess *process_handle;
949         pid_t pid;
950         GSList *mods = NULL, *mods_iter;
951         MonoW32ProcessModule *found_module;
952         gboolean ret = FALSE;
953         char *proc_name = NULL;
954         gboolean res;
955
956         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
957                    __func__, process, module);
958
959         if (modinfo == NULL || size < sizeof (WapiModuleInfo))
960                 return FALSE;
961
962         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
963                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
964                 proc_name = mono_w32process_get_name (pid);
965         } else {
966                 res = mono_w32handle_lookup (process, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
967                 if (!res) {
968                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
969                         return FALSE;
970                 }
971
972                 pid = process_handle->id;
973                 proc_name = g_strdup (process_handle->proc_name);
974         }
975
976         mods = mono_w32process_get_modules (pid);
977         if (!mods) {
978                 g_free (proc_name);
979                 return FALSE;
980         }
981
982         /* If module != NULL compare the address.
983          * If module == NULL we are looking for the main module.
984          * The best we can do for now check it the module name end with the process name.
985          */
986         for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
987                         found_module = (MonoW32ProcessModule *)mods_iter->data;
988                         if (ret == FALSE &&
989                                 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
990                                  (module != NULL && found_module->address_start == module))) {
991                                 modinfo->lpBaseOfDll = found_module->address_start;
992                                 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
993                                 modinfo->EntryPoint = found_module->address_offset;
994                                 ret = TRUE;
995                         }
996
997                         mono_w32process_module_free (found_module);
998         }
999
1000         g_slist_free (mods);
1001         g_free (proc_name);
1002
1003         return ret;
1004 }
1005
1006 static void
1007 switch_dir_separators (char *path)
1008 {
1009         size_t i, pathLength = strlen(path);
1010         
1011         /* Turn all the slashes round the right way, except for \' */
1012         /* There are probably other characters that need to be excluded as well. */
1013         for (i = 0; i < pathLength; i++) {
1014                 if (path[i] == '\\' && i < pathLength - 1 && path[i+1] != '\'' )
1015                         path[i] = '/';
1016         }
1017 }
1018
1019 #if HAVE_SIGACTION
1020
1021 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
1022 {
1023         int status;
1024         int pid;
1025         MonoProcess *p;
1026
1027         do {
1028                 do {
1029                         pid = waitpid (-1, &status, WNOHANG);
1030                 } while (pid == -1 && errno == EINTR);
1031
1032                 if (pid <= 0)
1033                         break;
1034
1035                 /*
1036                  * This can run concurrently with the code in the rest of this module.
1037                  */
1038                 for (p = processes; p; p = p->next) {
1039                         if (p->pid != pid)
1040                                 continue;
1041
1042                         p->pid = 0; /* this pid doesn't exist anymore, clear it */
1043                         p->status = status;
1044                         mono_os_sem_post (&p->exit_sem);
1045                         mono_memory_barrier ();
1046                         /* Mark this as freeable, the pointer becomes invalid afterwards */
1047                         p->freeable = TRUE;
1048                         break;
1049                 }
1050         } while (1);
1051 }
1052
1053 static void
1054 process_add_sigchld_handler (void)
1055 {
1056         struct sigaction sa;
1057
1058         sa.sa_sigaction = mono_sigchld_signal_handler;
1059         sigemptyset (&sa.sa_mask);
1060         sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1061         g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
1062         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
1063 }
1064
1065 #endif
1066
1067 static gboolean
1068 is_readable_or_executable (const char *prog)
1069 {
1070         struct stat buf;
1071         int a = access (prog, R_OK);
1072         int b = access (prog, X_OK);
1073         if (a != 0 && b != 0)
1074                 return FALSE;
1075         if (stat (prog, &buf))
1076                 return FALSE;
1077         if (S_ISREG (buf.st_mode))
1078                 return TRUE;
1079         return FALSE;
1080 }
1081
1082 static gboolean
1083 is_executable (const char *prog)
1084 {
1085         struct stat buf;
1086         if (access (prog, X_OK) != 0)
1087                 return FALSE;
1088         if (stat (prog, &buf))
1089                 return FALSE;
1090         if (S_ISREG (buf.st_mode))
1091                 return TRUE;
1092         return FALSE;
1093 }
1094
1095 static gboolean
1096 is_managed_binary (const char *filename)
1097 {
1098         int original_errno = errno;
1099 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1100         int file = open (filename, O_RDONLY | O_LARGEFILE);
1101 #else
1102         int file = open (filename, O_RDONLY);
1103 #endif
1104         off_t new_offset;
1105         unsigned char buffer[8];
1106         off_t file_size, optional_header_offset;
1107         off_t pe_header_offset, clr_header_offset;
1108         gboolean managed = FALSE;
1109         int num_read;
1110         guint32 first_word, second_word, magic_number;
1111         
1112         /* If we are unable to open the file, then we definitely
1113          * can't say that it is managed. The child mono process
1114          * probably wouldn't be able to open it anyway.
1115          */
1116         if (file < 0) {
1117                 errno = original_errno;
1118                 return FALSE;
1119         }
1120
1121         /* Retrieve the length of the file for future sanity checks. */
1122         file_size = lseek (file, 0, SEEK_END);
1123         lseek (file, 0, SEEK_SET);
1124
1125         /* We know we need to read a header field at offset 60. */
1126         if (file_size < 64)
1127                 goto leave;
1128
1129         num_read = read (file, buffer, 2);
1130
1131         if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
1132                 goto leave;
1133
1134         new_offset = lseek (file, 60, SEEK_SET);
1135
1136         if (new_offset != 60)
1137                 goto leave;
1138         
1139         num_read = read (file, buffer, 4);
1140
1141         if (num_read != 4)
1142                 goto leave;
1143         pe_header_offset =  buffer[0]
1144                 | (buffer[1] <<  8)
1145                 | (buffer[2] << 16)
1146                 | (buffer[3] << 24);
1147         
1148         if (pe_header_offset + 24 > file_size)
1149                 goto leave;
1150
1151         new_offset = lseek (file, pe_header_offset, SEEK_SET);
1152
1153         if (new_offset != pe_header_offset)
1154                 goto leave;
1155
1156         num_read = read (file, buffer, 4);
1157
1158         if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
1159                 goto leave;
1160
1161         /*
1162          * Verify that the header we want in the optional header data
1163          * is present in this binary.
1164          */
1165         new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
1166
1167         if (new_offset != pe_header_offset + 20)
1168                 goto leave;
1169
1170         num_read = read (file, buffer, 2);
1171
1172         if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
1173                 goto leave;
1174
1175         optional_header_offset = pe_header_offset + 24;
1176
1177         /* Read the PE magic number */
1178         new_offset = lseek (file, optional_header_offset, SEEK_SET);
1179         
1180         if (new_offset != optional_header_offset)
1181                 goto leave;
1182
1183         num_read = read (file, buffer, 2);
1184
1185         if (num_read != 2)
1186                 goto leave;
1187
1188         magic_number = (buffer[0] | (buffer[1] << 8));
1189         
1190         if (magic_number == 0x10B)  // PE32
1191                 clr_header_offset = 208;
1192         else if (magic_number == 0x20B)  // PE32+
1193                 clr_header_offset = 224;
1194         else
1195                 goto leave;
1196
1197         /* Read the CLR header address and size fields. These will be
1198          * zero if the binary is not managed.
1199          */
1200         new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET);
1201
1202         if (new_offset != optional_header_offset + clr_header_offset)
1203                 goto leave;
1204
1205         num_read = read (file, buffer, 8);
1206         
1207         /* We are not concerned with endianness, only with
1208          * whether it is zero or not.
1209          */
1210         first_word = *(guint32 *)&buffer[0];
1211         second_word = *(guint32 *)&buffer[4];
1212         
1213         if ((num_read != 8) || (first_word == 0) || (second_word == 0))
1214                 goto leave;
1215         
1216         managed = TRUE;
1217
1218 leave:
1219         close (file);
1220         errno = original_errno;
1221         return managed;
1222 }
1223
1224 static gboolean
1225 process_create (const gunichar2 *appname, const gunichar2 *cmdline, gpointer new_environ,
1226         const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info)
1227 {
1228 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1229         char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
1230         char *dir = NULL, **env_strings = NULL, **argv = NULL;
1231         guint32 i, env_count = 0;
1232         gboolean ret = FALSE;
1233         gpointer handle = NULL;
1234         GError *gerr = NULL;
1235         int in_fd, out_fd, err_fd;
1236         pid_t pid = 0;
1237         int startup_pipe [2] = {-1, -1};
1238         int dummy;
1239         MonoProcess *mono_process;
1240
1241 #if HAVE_SIGACTION
1242         mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
1243 #endif
1244
1245         /* appname and cmdline specify the executable and its args:
1246          *
1247          * If appname is not NULL, it is the name of the executable.
1248          * Otherwise the executable is the first token in cmdline.
1249          *
1250          * Executable searching:
1251          *
1252          * If appname is not NULL, it can specify the full path and
1253          * file name, or else a partial name and the current directory
1254          * will be used.  There is no additional searching.
1255          *
1256          * If appname is NULL, the first whitespace-delimited token in
1257          * cmdline is used.  If the name does not contain a full
1258          * directory path, the search sequence is:
1259          *
1260          * 1) The directory containing the current process
1261          * 2) The current working directory
1262          * 3) The windows system directory  (Ignored)
1263          * 4) The windows directory (Ignored)
1264          * 5) $PATH
1265          *
1266          * Just to make things more interesting, tokens can contain
1267          * white space if they are surrounded by quotation marks.  I'm
1268          * beginning to understand just why windows apps are generally
1269          * so crap, with an API like this :-(
1270          */
1271         if (appname != NULL) {
1272                 cmd = mono_unicode_to_external (appname);
1273                 if (cmd == NULL) {
1274                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL",
1275                                    __func__);
1276
1277                         SetLastError (ERROR_PATH_NOT_FOUND);
1278                         goto free_strings;
1279                 }
1280
1281                 switch_dir_separators(cmd);
1282         }
1283
1284         if (cmdline != NULL) {
1285                 args = mono_unicode_to_external (cmdline);
1286                 if (args == NULL) {
1287                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1288
1289                         SetLastError (ERROR_PATH_NOT_FOUND);
1290                         goto free_strings;
1291                 }
1292         }
1293
1294         if (cwd != NULL) {
1295                 dir = mono_unicode_to_external (cwd);
1296                 if (dir == NULL) {
1297                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
1298
1299                         SetLastError (ERROR_PATH_NOT_FOUND);
1300                         goto free_strings;
1301                 }
1302
1303                 /* Turn all the slashes round the right way */
1304                 switch_dir_separators(dir);
1305         }
1306
1307
1308         /* We can't put off locating the executable any longer :-( */
1309         if (cmd != NULL) {
1310                 char *unquoted;
1311                 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
1312                         /* Strip off the drive letter.  I can't
1313                          * believe that CP/M holdover is still
1314                          * visible...
1315                          */
1316                         g_memmove (cmd, cmd+2, strlen (cmd)-2);
1317                         cmd[strlen (cmd)-2] = '\0';
1318                 }
1319
1320                 unquoted = g_shell_unquote (cmd, NULL);
1321                 if (unquoted[0] == '/') {
1322                         /* Assume full path given */
1323                         prog = g_strdup (unquoted);
1324
1325                         /* Executable existing ? */
1326                         if (!is_readable_or_executable (prog)) {
1327                                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1328                                            __func__, prog);
1329                                 g_free (unquoted);
1330                                 SetLastError (ERROR_FILE_NOT_FOUND);
1331                                 goto free_strings;
1332                         }
1333                 } else {
1334                         /* Search for file named by cmd in the current
1335                          * directory
1336                          */
1337                         char *curdir = g_get_current_dir ();
1338
1339                         prog = g_strdup_printf ("%s/%s", curdir, unquoted);
1340                         g_free (curdir);
1341
1342                         /* And make sure it's readable */
1343                         if (!is_readable_or_executable (prog)) {
1344                                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1345                                            __func__, prog);
1346                                 g_free (unquoted);
1347                                 SetLastError (ERROR_FILE_NOT_FOUND);
1348                                 goto free_strings;
1349                         }
1350                 }
1351                 g_free (unquoted);
1352
1353                 args_after_prog = args;
1354         } else {
1355                 char *token = NULL;
1356                 char quote;
1357
1358                 /* Dig out the first token from args, taking quotation
1359                  * marks into account
1360                  */
1361
1362                 /* First, strip off all leading whitespace */
1363                 args = g_strchug (args);
1364
1365                 /* args_after_prog points to the contents of args
1366                  * after token has been set (otherwise argv[0] is
1367                  * duplicated)
1368                  */
1369                 args_after_prog = args;
1370
1371                 /* Assume the opening quote will always be the first
1372                  * character
1373                  */
1374                 if (args[0] == '\"' || args [0] == '\'') {
1375                         quote = args [0];
1376                         for (i = 1; args[i] != '\0' && args[i] != quote; i++);
1377                         if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) {
1378                                 /* We found the first token */
1379                                 token = g_strndup (args+1, i-1);
1380                                 args_after_prog = g_strchug (args + i + 1);
1381                         } else {
1382                                 /* Quotation mark appeared in the
1383                                  * middle of the token.  Just give the
1384                                  * whole first token, quotes and all,
1385                                  * to exec.
1386                                  */
1387                         }
1388                 }
1389
1390                 if (token == NULL) {
1391                         /* No quote mark, or malformed */
1392                         for (i = 0; args[i] != '\0'; i++) {
1393                                 if (g_ascii_isspace (args[i])) {
1394                                         token = g_strndup (args, i);
1395                                         args_after_prog = args + i + 1;
1396                                         break;
1397                                 }
1398                         }
1399                 }
1400
1401                 if (token == NULL && args[0] != '\0') {
1402                         /* Must be just one token in the string */
1403                         token = g_strdup (args);
1404                         args_after_prog = NULL;
1405                 }
1406
1407                 if (token == NULL) {
1408                         /* Give up */
1409                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find what to exec", __func__);
1410
1411                         SetLastError (ERROR_PATH_NOT_FOUND);
1412                         goto free_strings;
1413                 }
1414
1415                 /* Turn all the slashes round the right way. Only for
1416                  * the prg. name
1417                  */
1418                 switch_dir_separators(token);
1419
1420                 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
1421                         /* Strip off the drive letter.  I can't
1422                          * believe that CP/M holdover is still
1423                          * visible...
1424                          */
1425                         g_memmove (token, token+2, strlen (token)-2);
1426                         token[strlen (token)-2] = '\0';
1427                 }
1428
1429                 if (token[0] == '/') {
1430                         /* Assume full path given */
1431                         prog = g_strdup (token);
1432
1433                         /* Executable existing ? */
1434                         if (!is_readable_or_executable (prog)) {
1435                                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
1436                                            __func__, token);
1437                                 g_free (token);
1438                                 SetLastError (ERROR_FILE_NOT_FOUND);
1439                                 goto free_strings;
1440                         }
1441                 } else {
1442                         char *curdir = g_get_current_dir ();
1443
1444                         /* FIXME: Need to record the directory
1445                          * containing the current process, and check
1446                          * that for the new executable as the first
1447                          * place to look
1448                          */
1449
1450                         prog = g_strdup_printf ("%s/%s", curdir, token);
1451                         g_free (curdir);
1452
1453                         /* I assume X_OK is the criterion to use,
1454                          * rather than F_OK
1455                          *
1456                          * X_OK is too strict *if* the target is a CLR binary
1457                          */
1458                         if (!is_readable_or_executable (prog)) {
1459                                 g_free (prog);
1460                                 prog = g_find_program_in_path (token);
1461                                 if (prog == NULL) {
1462                                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s", __func__, token);
1463
1464                                         g_free (token);
1465                                         SetLastError (ERROR_FILE_NOT_FOUND);
1466                                         goto free_strings;
1467                                 }
1468                         }
1469                 }
1470
1471                 g_free (token);
1472         }
1473
1474         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Exec prog [%s] args [%s]",
1475                 __func__, prog, args_after_prog);
1476
1477         /* Check for CLR binaries; if found, we will try to invoke
1478          * them using the same mono binary that started us.
1479          */
1480         if (is_managed_binary (prog)) {
1481                 gunichar2 *newapp, *newcmd;
1482                 gsize bytes_ignored;
1483
1484                 newapp = mono_unicode_from_external (cli_launcher ? cli_launcher : "mono", &bytes_ignored);
1485                 if (newapp) {
1486                         if (appname)
1487                                 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, NULL);
1488                         else
1489                                 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, NULL);
1490
1491                         g_free (newapp);
1492
1493                         if (newcmd) {
1494                                 ret = process_create (NULL, newcmd, new_environ, cwd, startup_handles, process_info);
1495
1496                                 g_free (newcmd);
1497
1498                                 goto free_strings;
1499                         }
1500                 }
1501         } else {
1502                 if (!is_executable (prog)) {
1503                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Executable permisson not set on %s", __func__, prog);
1504                         SetLastError (ERROR_ACCESS_DENIED);
1505                         goto free_strings;
1506                 }
1507         }
1508
1509         if (args_after_prog != NULL && *args_after_prog) {
1510                 char *qprog;
1511
1512                 qprog = g_shell_quote (prog);
1513                 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
1514                 g_free (qprog);
1515         } else {
1516                 full_prog = g_shell_quote (prog);
1517         }
1518
1519         ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
1520         if (ret == FALSE) {
1521                 g_message ("process_create: %s\n", gerr->message);
1522                 g_error_free (gerr);
1523                 gerr = NULL;
1524                 goto free_strings;
1525         }
1526
1527         if (startup_handles) {
1528                 in_fd = GPOINTER_TO_UINT (startup_handles->input);
1529                 out_fd = GPOINTER_TO_UINT (startup_handles->output);
1530                 err_fd = GPOINTER_TO_UINT (startup_handles->error);
1531         } else {
1532                 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
1533                 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
1534                 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
1535         }
1536
1537         /* new_environ is a block of NULL-terminated strings, which
1538          * is itself NULL-terminated. Of course, passing an array of
1539          * string pointers would have made things too easy :-(
1540          *
1541          * If new_environ is not NULL it specifies the entire set of
1542          * environment variables in the new process.  Otherwise the
1543          * new process inherits the same environment.
1544          */
1545         if (new_environ) {
1546                 gunichar2 *new_environp;
1547
1548                 /* Count the number of strings */
1549                 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1550                         env_count++;
1551                         while (*new_environp)
1552                                 new_environp++;
1553                 }
1554
1555                 /* +2: one for the process handle value, and the last
1556                  * one is NULL
1557                  */
1558                 env_strings = g_new0 (char *, env_count + 2);
1559
1560                 /* Copy each environ string into 'strings' turning it
1561                  * into utf8 (or the requested encoding) at the same
1562                  * time
1563                  */
1564                 env_count = 0;
1565                 for (new_environp = (gunichar2 *)new_environ; *new_environp; new_environp++) {
1566                         env_strings[env_count] = mono_unicode_to_external (new_environp);
1567                         env_count++;
1568                         while (*new_environp) {
1569                                 new_environp++;
1570                         }
1571                 }
1572         } else {
1573                 for (i = 0; environ[i] != NULL; i++)
1574                         env_count++;
1575
1576                 /* +2: one for the process handle value, and the last
1577                  * one is NULL
1578                  */
1579                 env_strings = g_new0 (char *, env_count + 2);
1580
1581                 /* Copy each environ string into 'strings' turning it
1582                  * into utf8 (or the requested encoding) at the same
1583                  * time
1584                  */
1585                 env_count = 0;
1586                 for (i = 0; environ[i] != NULL; i++) {
1587                         env_strings[env_count] = g_strdup (environ[i]);
1588                         env_count++;
1589                 }
1590         }
1591
1592         /* Create a pipe to make sure the child doesn't exit before
1593          * we can add the process to the linked list of processes */
1594         if (pipe (startup_pipe) == -1) {
1595                 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1596                  * This is just for a very hard to hit race condition in the first place */
1597                 startup_pipe [0] = startup_pipe [1] = -1;
1598                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: new process startup not synchronized. We may not notice if the newly created process exits immediately.", __func__);
1599         }
1600
1601 #if HAVE_SIGACTION
1602         /* FIXME: block SIGCHLD */
1603 #endif
1604
1605         switch (pid = fork ()) {
1606         case -1: /* Error */ {
1607                 SetLastError (ERROR_OUTOFMEMORY);
1608                 ret = FALSE;
1609                 break;
1610         }
1611         case 0: /* Child */ {
1612                 if (startup_pipe [0] != -1) {
1613                         /* Wait until the parent has updated it's internal data */
1614                         ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1);
1615                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: child: parent has completed its setup", __func__);
1616                         close (startup_pipe [0]);
1617                         close (startup_pipe [1]);
1618                 }
1619
1620                 /* should we detach from the process group? */
1621
1622                 /* Connect stdin, stdout and stderr */
1623                 dup2 (in_fd, 0);
1624                 dup2 (out_fd, 1);
1625                 dup2 (err_fd, 2);
1626
1627                 /* Close all file descriptors */
1628                 for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
1629                         close (i);
1630
1631 #ifdef DEBUG_ENABLED
1632                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1633                            dir == NULL?".":dir);
1634                 for (i = 0; argv[i] != NULL; i++)
1635                         g_message ("arg %d: [%s]", i, argv[i]);
1636
1637                 for (i = 0; env_strings[i] != NULL; i++)
1638                         g_message ("env %d: [%s]", i, env_strings[i]);
1639 #endif
1640
1641                 /* set cwd */
1642                 if (dir != NULL && chdir (dir) == -1) {
1643                         /* set error */
1644                         _exit (-1);
1645                 }
1646
1647                 /* exec */
1648                 execve (argv[0], argv, env_strings);
1649
1650                 /* set error */
1651                 _exit (-1);
1652
1653                 break;
1654         }
1655         default: /* Parent */ {
1656                 MonoW32HandleProcess process_handle;
1657
1658                 memset (&process_handle, 0, sizeof (process_handle));
1659                 process_handle.id = pid;
1660                 process_handle.proc_name = g_strdup (prog);
1661                 process_set_defaults (&process_handle);
1662
1663                 /* Add our mono_process into the linked list of processes */
1664                 mono_process = (MonoProcess *) g_malloc0 (sizeof (MonoProcess));
1665                 mono_process->pid = pid;
1666                 mono_process->handle_count = 1;
1667                 mono_os_sem_init (&mono_process->exit_sem, 0);
1668
1669                 process_handle.mono_process = mono_process;
1670
1671                 handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
1672                 if (handle == INVALID_HANDLE_VALUE) {
1673                         g_warning ("%s: error creating process handle", __func__);
1674
1675                         mono_os_sem_destroy (&mono_process->exit_sem);
1676                         g_free (mono_process);
1677
1678                         SetLastError (ERROR_OUTOFMEMORY);
1679                         ret = FALSE;
1680                         break;
1681                 }
1682
1683                 /* Keep the process handle artificially alive until the process
1684                  * exits so that the information in the handle isn't lost. */
1685                 mono_w32handle_ref (handle);
1686                 mono_process->handle = handle;
1687
1688                 mono_os_mutex_lock (&processes_mutex);
1689                 mono_process->next = processes;
1690                 processes = mono_process;
1691                 mono_os_mutex_unlock (&processes_mutex);
1692
1693                 if (process_info != NULL) {
1694                         process_info->process_handle = handle;
1695                         process_info->pid = pid;
1696
1697                         /* FIXME: we might need to handle the thread info some day */
1698                         process_info->thread_handle = INVALID_HANDLE_VALUE;
1699                         process_info->tid = 0;
1700                 }
1701
1702                 break;
1703         }
1704         }
1705
1706 #if HAVE_SIGACTION
1707         /* FIXME: unblock SIGCHLD */
1708 #endif
1709
1710         if (startup_pipe [1] != -1) {
1711                 /* Write 1 byte, doesn't matter what */
1712                 ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
1713                 close (startup_pipe [0]);
1714                 close (startup_pipe [1]);
1715         }
1716
1717 free_strings:
1718         if (cmd)
1719                 g_free (cmd);
1720         if (full_prog)
1721                 g_free (full_prog);
1722         if (prog)
1723                 g_free (prog);
1724         if (args)
1725                 g_free (args);
1726         if (dir)
1727                 g_free (dir);
1728         if (env_strings)
1729                 g_strfreev (env_strings);
1730         if (argv)
1731                 g_strfreev (argv);
1732
1733         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p for pid %d", __func__, handle, pid);
1734
1735         /* Check if something needs to be cleaned up. */
1736         processes_cleanup ();
1737
1738         return ret;
1739 #else
1740         SetLastError (ERROR_NOT_SUPPORTED);
1741         return FALSE;
1742 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1743 }
1744
1745 MonoBoolean
1746 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
1747 {
1748         const gunichar2 *lpFile;
1749         const gunichar2 *lpParameters;
1750         const gunichar2 *lpDirectory;
1751         gunichar2 *args;
1752         gboolean ret;
1753
1754         if (!proc_start_info->filename) {
1755                 /* w2k returns TRUE for this, for some reason. */
1756                 ret = TRUE;
1757                 goto done;
1758         }
1759
1760         lpFile = proc_start_info->filename ? mono_string_chars (proc_start_info->filename) : NULL;
1761         lpParameters = proc_start_info->arguments ? mono_string_chars (proc_start_info->arguments) : NULL;
1762         lpDirectory = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) != 0 ?
1763                 mono_string_chars (proc_start_info->working_directory) : NULL;
1764
1765         /* Put both executable and parameters into the second argument
1766          * to process_create (), so it searches $PATH.  The conversion
1767          * into and back out of utf8 is because there is no
1768          * g_strdup_printf () equivalent for gunichar2 :-(
1769          */
1770         args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1771         if (args == NULL) {
1772                 SetLastError (ERROR_INVALID_DATA);
1773                 ret = FALSE;
1774                 goto done;
1775         }
1776         ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1777         g_free (args);
1778
1779         if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
1780                 goto done;
1781
1782         if (!ret) {
1783                 static char *handler;
1784                 static gunichar2 *handler_utf16;
1785
1786                 if (handler_utf16 == (gunichar2 *)-1) {
1787                         ret = FALSE;
1788                         goto done;
1789                 }
1790
1791 #ifdef PLATFORM_MACOSX
1792                 handler = g_strdup ("/usr/bin/open");
1793 #else
1794                 /*
1795                  * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
1796                  * if that fails, try to use gnome-open, then kfmclient
1797                  */
1798                 handler = g_find_program_in_path ("xdg-open");
1799                 if (handler == NULL){
1800                         handler = g_find_program_in_path ("gnome-open");
1801                         if (handler == NULL){
1802                                 handler = g_find_program_in_path ("kfmclient");
1803                                 if (handler == NULL){
1804                                         handler_utf16 = (gunichar2 *) -1;
1805                                         ret = FALSE;
1806                                         goto done;
1807                                 } else {
1808                                         /* kfmclient needs exec argument */
1809                                         char *old = handler;
1810                                         handler = g_strconcat (old, " exec",
1811                                                                NULL);
1812                                         g_free (old);
1813                                 }
1814                         }
1815                 }
1816 #endif
1817                 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
1818                 g_free (handler);
1819
1820                 /* Put quotes around the filename, in case it's a url
1821                  * that contains #'s (process_create() calls
1822                  * g_shell_parse_argv(), which deliberately throws
1823                  * away anything after an unquoted #).  Fixes bug
1824                  * 371567.
1825                  */
1826                 args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
1827                         lpParameters == NULL ? NULL : utf16_space, lpParameters, NULL);
1828                 if (args == NULL) {
1829                         SetLastError (ERROR_INVALID_DATA);
1830                         ret = FALSE;
1831                         goto done;
1832                 }
1833                 ret = process_create (NULL, args, NULL, lpDirectory, NULL, process_info);
1834                 g_free (args);
1835                 if (!ret) {
1836                         if (GetLastError () != ERROR_OUTOFMEMORY)
1837                                 SetLastError (ERROR_INVALID_DATA);
1838                         ret = FALSE;
1839                         goto done;
1840                 }
1841                 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
1842                 CloseHandle (process_info->process_handle);
1843                 process_info->process_handle = NULL;
1844         }
1845
1846 done:
1847         if (ret == FALSE) {
1848                 process_info->pid = -GetLastError ();
1849         } else {
1850                 process_info->thread_handle = NULL;
1851 #if !defined(MONO_CROSS_COMPILE)
1852                 process_info->pid = mono_w32process_get_pid (process_info->process_handle);
1853 #else
1854                 process_info->pid = 0;
1855 #endif
1856                 process_info->tid = 0;
1857         }
1858
1859         return ret;
1860 }
1861
1862 /* Only used when UseShellExecute is false */
1863 static gboolean
1864 process_get_complete_path (const gunichar2 *appname, gchar **completed)
1865 {
1866         gchar *utf8app;
1867         gchar *found;
1868
1869         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
1870
1871         if (g_path_is_absolute (utf8app)) {
1872                 *completed = g_shell_quote (utf8app);
1873                 g_free (utf8app);
1874                 return TRUE;
1875         }
1876
1877         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
1878                 *completed = g_shell_quote (utf8app);
1879                 g_free (utf8app);
1880                 return TRUE;
1881         }
1882         
1883         found = g_find_program_in_path (utf8app);
1884         if (found == NULL) {
1885                 *completed = NULL;
1886                 g_free (utf8app);
1887                 return FALSE;
1888         }
1889
1890         *completed = g_shell_quote (found);
1891         g_free (found);
1892         g_free (utf8app);
1893         return TRUE;
1894 }
1895
1896 static gboolean
1897 process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
1898 {
1899         gchar *spath = NULL;
1900
1901         *shell_path = NULL;
1902         *cmd = proc_start_info->arguments;
1903
1904         process_get_complete_path (mono_string_chars (proc_start_info->filename), &spath);
1905         if (spath != NULL) {
1906                 *shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
1907                 g_free (spath);
1908         }
1909
1910         return (*shell_path != NULL) ? TRUE : FALSE;
1911 }
1912
1913 MonoBoolean
1914 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info,
1915         HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info)
1916 {
1917         gboolean ret;
1918         gunichar2 *dir;
1919         StartupHandles startup_handles;
1920         gunichar2 *shell_path = NULL;
1921         gchar *env_vars = NULL;
1922         MonoString *cmd = NULL;
1923
1924         memset (&startup_handles, 0, sizeof (startup_handles));
1925         startup_handles.input = stdin_handle;
1926         startup_handles.output = stdout_handle;
1927         startup_handles.error = stderr_handle;
1928
1929         if (process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
1930                 process_info->pid = -ERROR_FILE_NOT_FOUND;
1931                 return FALSE;
1932         }
1933
1934         if (process_info->env_keys) {
1935                 gint i, len; 
1936                 MonoString *ms;
1937                 MonoString *key, *value;
1938                 gunichar2 *str, *ptr;
1939                 gunichar2 *equals16;
1940
1941                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
1942                         ms = mono_array_get (process_info->env_values, MonoString *, i);
1943                         if (ms == NULL)
1944                                 continue;
1945
1946                         len += mono_string_length (ms) * sizeof (gunichar2);
1947                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
1948                         len += mono_string_length (ms) * sizeof (gunichar2);
1949                         len += 2 * sizeof (gunichar2);
1950                 }
1951
1952                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
1953                 ptr = str = g_new0 (gunichar2, len + 1);
1954                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
1955                         value = mono_array_get (process_info->env_values, MonoString *, i);
1956                         if (value == NULL)
1957                                 continue;
1958
1959                         key = mono_array_get (process_info->env_keys, MonoString *, i);
1960                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
1961                         ptr += mono_string_length (key);
1962
1963                         memcpy (ptr, equals16, sizeof (gunichar2));
1964                         ptr++;
1965
1966                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
1967                         ptr += mono_string_length (value);
1968                         ptr++;
1969                 }
1970
1971                 g_free (equals16);
1972                 env_vars = (gchar *) str;
1973         }
1974         
1975         /* The default dir name is "".  Turn that into NULL to mean "current directory" */
1976         dir = proc_start_info->working_directory && mono_string_length (proc_start_info->working_directory) > 0 ?
1977                         mono_string_chars (proc_start_info->working_directory) : NULL;
1978
1979         ret = process_create (shell_path, cmd ? mono_string_chars (cmd): NULL, env_vars, dir, &startup_handles, process_info);
1980
1981         g_free (env_vars);
1982         if (shell_path != NULL)
1983                 g_free (shell_path);
1984
1985         if (!ret)
1986                 process_info->pid = -GetLastError ();
1987
1988         return ret;
1989 }
1990
1991 /* Returns an array of pids */
1992 MonoArray *
1993 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
1994 {
1995         MonoError error;
1996         MonoArray *procs;
1997         gpointer *pidarray;
1998         int i, count;
1999
2000         pidarray = mono_process_list (&count);
2001         if (!pidarray) {
2002                 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
2003                 return NULL;
2004         }
2005         procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error);
2006         if (mono_error_set_pending_exception (&error)) {
2007                 g_free (pidarray);
2008                 return NULL;
2009         }
2010         if (sizeof (guint32) == sizeof (gpointer)) {
2011                 memcpy (mono_array_addr (procs, guint32, 0), pidarray, count * sizeof (gint32));
2012         } else {
2013                 for (i = 0; i < count; ++i)
2014                         *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]);
2015         }
2016         g_free (pidarray);
2017
2018         return procs;
2019 }
2020
2021 void
2022 mono_w32process_set_cli_launcher (gchar *path)
2023 {
2024         g_free (cli_launcher);
2025         cli_launcher = g_strdup (path);
2026 }
2027
2028 gpointer
2029 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2030 {
2031         mono_w32handle_ref (current_process);
2032         return current_process;
2033 }
2034
2035 MonoBoolean
2036 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
2037 {
2038         MonoW32HandleProcess *process_handle;
2039         guint32 pid;
2040         gboolean res;
2041
2042         if (!exitcode)
2043                 return FALSE;
2044
2045         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2046                 pid = WAPI_HANDLE_TO_PID (handle);
2047                 /* This is a pseudo handle, so we don't know what the exit
2048                  * code was, but we can check whether it's alive or not */
2049                 if (is_pid_valid (pid)) {
2050                         *exitcode = STILL_ACTIVE;
2051                         return TRUE;
2052                 } else {
2053                         *exitcode = -1;
2054                         return TRUE;
2055                 }
2056         }
2057
2058         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2059         if (!res) {
2060                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2061                 return FALSE;
2062         }
2063
2064         if (process_handle->id == wapi_getpid ()) {
2065                 *exitcode = STILL_ACTIVE;
2066                 return TRUE;
2067         }
2068
2069         /* A process handle is only signalled if the process has exited
2070          * and has been waited for. Make sure any process exit has been
2071          * noticed before checking if the process is signalled.
2072          * Fixes bug 325463. */
2073         mono_w32handle_wait_one (handle, 0, TRUE);
2074
2075         *exitcode = mono_w32handle_issignalled (handle) ? process_handle->exitstatus : STILL_ACTIVE;
2076         return TRUE;
2077 }
2078
2079 MonoBoolean
2080 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
2081 {
2082         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2083                 return TRUE;
2084         return CloseHandle (handle);
2085 }
2086
2087 MonoBoolean
2088 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
2089 {
2090 #ifdef HAVE_KILL
2091         MonoW32HandleProcess *process_handle;
2092         int ret;
2093         pid_t pid;
2094
2095         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2096                 /* This is a pseudo handle */
2097                 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2098         } else {
2099                 gboolean res;
2100
2101                 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2102                 if (!res) {
2103                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2104                         SetLastError (ERROR_INVALID_HANDLE);
2105                         return FALSE;
2106                 }
2107
2108                 pid = process_handle->id;
2109         }
2110
2111         ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
2112         if (ret == 0)
2113                 return TRUE;
2114
2115         switch (errno) {
2116         case EINVAL: SetLastError (ERROR_INVALID_PARAMETER); break;
2117         case EPERM:  SetLastError (ERROR_ACCESS_DENIED);     break;
2118         case ESRCH:  SetLastError (ERROR_PROC_NOT_FOUND);    break;
2119         default:     SetLastError (ERROR_GEN_FAILURE);       break;
2120         }
2121
2122         return FALSE;
2123 #else
2124         g_error ("kill() is not supported by this platform");
2125 #endif
2126 }
2127
2128 MonoBoolean
2129 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
2130 {
2131         MonoW32HandleProcess *process_handle;
2132         gboolean res;
2133
2134         if (!min || !max)
2135                 return FALSE;
2136
2137         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2138                 return FALSE;
2139
2140         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2141         if (!res) {
2142                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2143                 return FALSE;
2144         }
2145
2146         *min = process_handle->min_working_set;
2147         *max = process_handle->max_working_set;
2148         return TRUE;
2149 }
2150
2151 MonoBoolean
2152 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
2153 {
2154         MonoW32HandleProcess *process_handle;
2155         gboolean res;
2156
2157         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
2158                 return FALSE;
2159
2160         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2161         if (!res) {
2162                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2163                 return FALSE;
2164         }
2165
2166         process_handle->min_working_set = min;
2167         process_handle->max_working_set = max;
2168         return TRUE;
2169 }
2170
2171 gint32
2172 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
2173 {
2174 #ifdef HAVE_GETPRIORITY
2175         MonoW32HandleProcess *process_handle;
2176         gint ret;
2177         pid_t pid;
2178
2179         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2180                 /* This is a pseudo handle */
2181                 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2182         } else {
2183                 gboolean res;
2184
2185                 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2186                 if (!res) {
2187                         SetLastError (ERROR_INVALID_HANDLE);
2188                         return 0;
2189                 }
2190
2191                 pid = process_handle->id;
2192         }
2193
2194         errno = 0;
2195         ret = getpriority (PRIO_PROCESS, pid);
2196         if (ret == -1 && errno != 0) {
2197                 switch (errno) {
2198                 case EPERM:
2199                 case EACCES:
2200                         SetLastError (ERROR_ACCESS_DENIED);
2201                         break;
2202                 case ESRCH:
2203                         SetLastError (ERROR_PROC_NOT_FOUND);
2204                         break;
2205                 default:
2206                         SetLastError (ERROR_GEN_FAILURE);
2207                 }
2208                 return 0;
2209         }
2210
2211         if (ret == 0)
2212                 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2213         else if (ret < -15)
2214                 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME;
2215         else if (ret < -10)
2216                 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH;
2217         else if (ret < 0)
2218                 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2219         else if (ret > 10)
2220                 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE;
2221         else if (ret > 0)
2222                 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2223
2224         return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2225 #else
2226         SetLastError (ERROR_NOT_SUPPORTED);
2227         return 0;
2228 #endif
2229 }
2230
2231 MonoBoolean
2232 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
2233 {
2234 #ifdef HAVE_SETPRIORITY
2235         MonoW32HandleProcess *process_handle;
2236         int ret;
2237         int prio;
2238         pid_t pid;
2239
2240         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2241                 /* This is a pseudo handle */
2242                 pid = (pid_t)WAPI_HANDLE_TO_PID (handle);
2243         } else {
2244                 gboolean res;
2245
2246                 res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2247                 if (!res) {
2248                         SetLastError (ERROR_INVALID_HANDLE);
2249                         return FALSE;
2250                 }
2251
2252                 pid = process_handle->id;
2253         }
2254
2255         switch (priorityClass) {
2256         case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
2257                 prio = 19;
2258                 break;
2259         case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
2260                 prio = 10;
2261                 break;
2262         case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL:
2263                 prio = 0;
2264                 break;
2265         case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
2266                 prio = -5;
2267                 break;
2268         case MONO_W32PROCESS_PRIORITY_CLASS_HIGH:
2269                 prio = -11;
2270                 break;
2271         case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME:
2272                 prio = -20;
2273                 break;
2274         default:
2275                 SetLastError (ERROR_INVALID_PARAMETER);
2276                 return FALSE;
2277         }
2278
2279         ret = setpriority (PRIO_PROCESS, pid, prio);
2280         if (ret == -1) {
2281                 switch (errno) {
2282                 case EPERM:
2283                 case EACCES:
2284                         SetLastError (ERROR_ACCESS_DENIED);
2285                         break;
2286                 case ESRCH:
2287                         SetLastError (ERROR_PROC_NOT_FOUND);
2288                         break;
2289                 default:
2290                         SetLastError (ERROR_GEN_FAILURE);
2291                 }
2292         }
2293
2294         return ret == 0;
2295 #else
2296         SetLastError (ERROR_NOT_SUPPORTED);
2297         return FALSE;
2298 #endif
2299 }
2300
2301 static void
2302 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
2303 {
2304         processtime->lowDateTime = ticks & 0xFFFFFFFF;
2305         processtime->highDateTime = ticks >> 32;
2306 }
2307
2308 static void
2309 wapifiletime_to_processtime (WapiFileTime wapi_filetime, ProcessTime *processtime)
2310 {
2311         processtime->lowDateTime = wapi_filetime.dwLowDateTime;
2312         processtime->highDateTime = wapi_filetime.dwHighDateTime;
2313 }
2314
2315 MonoBoolean
2316 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
2317 {
2318         MonoW32HandleProcess *process_handle;
2319         ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime;
2320         gboolean res;
2321
2322         if (!creation_time || !exit_time || !kernel_time || !user_time) {
2323                 /* Not sure if w32 allows NULLs here or not */
2324                 return FALSE;
2325         }
2326
2327         creation_processtime = (ProcessTime*) creation_time;
2328         exit_processtime = (ProcessTime*) exit_time;
2329         kernel_processtime = (ProcessTime*) kernel_time;
2330         user_processtime = (ProcessTime*) user_time;
2331
2332         memset (creation_processtime, 0, sizeof (ProcessTime));
2333         memset (exit_processtime, 0, sizeof (ProcessTime));
2334         memset (kernel_processtime, 0, sizeof (ProcessTime));
2335         memset (user_processtime, 0, sizeof (ProcessTime));
2336
2337         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle)) {
2338                 gint64 start_ticks, user_ticks, kernel_ticks;
2339
2340                 mono_process_get_times (GINT_TO_POINTER (WAPI_HANDLE_TO_PID (handle)),
2341                         &start_ticks, &user_ticks, &kernel_ticks);
2342
2343                 ticks_to_processtime (start_ticks, creation_processtime);
2344                 ticks_to_processtime (user_ticks, kernel_processtime);
2345                 ticks_to_processtime (kernel_ticks, user_processtime);
2346                 return TRUE;
2347         }
2348
2349         res = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS, (gpointer*) &process_handle);
2350         if (!res) {
2351                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, handle);
2352                 return FALSE;
2353         }
2354
2355         wapifiletime_to_processtime (process_handle->create_time, creation_processtime);
2356
2357         /* A process handle is only signalled if the process has
2358          * exited, otherwise exit_processtime isn't set */
2359         if (mono_w32handle_issignalled (handle))
2360                 wapifiletime_to_processtime (process_handle->exit_time, exit_processtime);
2361
2362 #ifdef HAVE_GETRUSAGE
2363         if (process_handle->id == getpid ()) {
2364                 struct rusage time_data;
2365                 if (getrusage (RUSAGE_SELF, &time_data) == 0) {
2366                         ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime);
2367                         ticks_to_processtime ((guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10, kernel_processtime);
2368                 }
2369         }
2370 #endif
2371
2372         return TRUE;
2373 }