Merge pull request #3328 from BrzVlad/finalizer-thread-exited2
[mono.git] / mono / io-layer / processes.c
1 /*
2  * processes.c:  Process handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002-2011 Novell, Inc.
8  * Copyright 2011 Xamarin 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 #include <stdio.h>
15 #include <string.h>
16 #include <pthread.h>
17 #include <sched.h>
18 #include <sys/time.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #ifdef HAVE_SIGNAL_H
24 #include <signal.h>
25 #endif
26 #include <sys/time.h>
27 #include <fcntl.h>
28 #ifdef HAVE_SYS_PARAM_H
29 #include <sys/param.h>
30 #endif
31 #include <ctype.h>
32
33 #ifdef HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif
36 #ifdef HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
38 #endif
39
40 #ifdef HAVE_SYS_MKDEV_H
41 #include <sys/mkdev.h>
42 #endif
43
44 #ifdef HAVE_UTIME_H
45 #include <utime.h>
46 #endif
47
48 /* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
49 #ifdef __APPLE__
50 #include <TargetConditionals.h>
51 #include <sys/resource.h>
52 #ifdef HAVE_LIBPROC_H
53 /* proc_name */
54 #include <libproc.h>
55 #endif
56 #endif
57
58 #if defined(PLATFORM_MACOSX)
59 #define USE_OSX_LOADER
60 #endif
61
62 #if ( defined(__OpenBSD__) || defined(__FreeBSD__) ) && defined(HAVE_LINK_H)
63 #define USE_BSD_LOADER
64 #endif
65
66 #if defined(__HAIKU__)
67 #define USE_HAIKU_LOADER
68 #endif
69
70 #if defined(USE_OSX_LOADER) || defined(USE_BSD_LOADER)
71 #include <sys/proc.h>
72 #include <sys/sysctl.h>
73 #  if !defined(__OpenBSD__)
74 #    include <sys/utsname.h>
75 #  endif
76 #  if defined(__FreeBSD__)
77 #    include <sys/user.h>  /* struct kinfo_proc */
78 #  endif
79 #endif
80
81 #ifdef PLATFORM_SOLARIS
82 /* procfs.h cannot be included if this define is set, but it seems to work fine if it is undefined */
83 #if _FILE_OFFSET_BITS == 64
84 #undef _FILE_OFFSET_BITS
85 #include <procfs.h>
86 #define _FILE_OFFSET_BITS 64
87 #else
88 #include <procfs.h>
89 #endif
90 #endif
91
92 #ifdef __HAIKU__
93 #include <KernelKit.h>
94 #endif
95
96 #include <mono/io-layer/wapi.h>
97 #include <mono/io-layer/wapi-private.h>
98 #include <mono/io-layer/process-private.h>
99 #include <mono/io-layer/threads.h>
100 #include <mono/io-layer/io-trace.h>
101 #include <mono/utils/strenc.h>
102 #include <mono/utils/mono-path.h>
103 #include <mono/io-layer/timefuncs-private.h>
104 #include <mono/utils/mono-time.h>
105 #include <mono/utils/mono-membar.h>
106 #include <mono/utils/mono-os-mutex.h>
107 #include <mono/utils/mono-signal-handler.h>
108 #include <mono/utils/mono-proclib.h>
109 #include <mono/utils/mono-once.h>
110 #include <mono/utils/mono-logger-internals.h>
111 #include <mono/utils/w32handle.h>
112
113 /* The process' environment strings */
114 #if defined(__APPLE__)
115 #if defined (TARGET_OSX)
116 /* Apple defines this in crt_externs.h but doesn't provide that header for 
117  * arm-apple-darwin9.  We'll manually define the symbol on Apple as it does
118  * in fact exist on all implementations (so far) 
119  */
120 gchar ***_NSGetEnviron(void);
121 #define environ (*_NSGetEnviron())
122 #else
123 static char *mono_environ[1] = { NULL };
124 #define environ mono_environ
125 #endif /* defined (TARGET_OSX) */
126 #else
127 extern char **environ;
128 #endif
129
130 static guint32 process_wait (gpointer handle, guint32 timeout, gboolean alertable);
131 static void process_close (gpointer handle, gpointer data);
132 static void process_details (gpointer data);
133 static const gchar* process_typename (void);
134 static gsize process_typesize (void);
135 static gboolean is_pid_valid (pid_t pid);
136
137 #if !(defined(USE_OSX_LOADER) || defined(USE_BSD_LOADER) || defined(USE_HAIKU_LOADER))
138 static FILE *
139 open_process_map (int pid, const char *mode);
140 #endif
141
142 static MonoW32HandleOps _wapi_process_ops = {
143         process_close,          /* close_shared */
144         NULL,                           /* signal */
145         NULL,                           /* own */
146         NULL,                           /* is_owned */
147         process_wait,                   /* special_wait */
148         NULL,                           /* prewait */
149         process_details,        /* details */
150         process_typename,       /* typename */
151         process_typesize,       /* typesize */
152 };
153
154 #if HAVE_SIGACTION
155 static struct sigaction previous_chld_sa;
156 #endif
157 static mono_once_t process_sig_chld_once = MONO_ONCE_INIT;
158 static void process_add_sigchld_handler (void);
159
160 /* The signal-safe logic to use mono_processes goes like this:
161  * - The list must be safe to traverse for the signal handler at all times.
162  *   It's safe to: prepend an entry (which is a single store to 'mono_processes'),
163  *   unlink an entry (assuming the unlinked entry isn't freed and doesn't 
164  *   change its 'next' pointer so that it can still be traversed).
165  * When cleaning up we first unlink an entry, then we verify that
166  * the read lock isn't locked. Then we can free the entry, since
167  * we know that nobody is using the old version of the list (including
168  * the unlinked entry).
169  * We also need to lock when adding and cleaning up so that those two
170  * operations don't mess with eachother. (This lock is not used in the
171  * signal handler)
172  */
173 static struct MonoProcess *mono_processes = NULL;
174 static volatile gint32 mono_processes_cleaning_up = 0;
175 static mono_mutex_t mono_processes_mutex;
176 static void mono_processes_cleanup (void);
177
178 static gpointer current_process;
179 static char *cli_launcher;
180
181 static WapiHandle_process *
182 lookup_process_handle (gpointer handle)
183 {
184         WapiHandle_process *process_data;
185         gboolean ret;
186
187         ret = mono_w32handle_lookup (handle, MONO_W32HANDLE_PROCESS,
188                                                            (gpointer *)&process_data);
189         if (!ret)
190                 return NULL;
191         return process_data;
192 }
193
194 /* Check if a pid is valid - i.e. if a process exists with this pid. */
195 static gboolean
196 is_pid_valid (pid_t pid)
197 {
198         gboolean result = FALSE;
199
200 #if defined(HOST_WATCHOS)
201         result = TRUE; // TODO: Rewrite using sysctl
202 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
203         if (((kill(pid, 0) == 0) || (errno == EPERM)) && pid != 0)
204                 result = TRUE;
205 #elif defined(__HAIKU__)
206         team_info teamInfo;
207         if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
208                 result = TRUE;
209 #else
210         char *dir = g_strdup_printf ("/proc/%d", pid);
211         if (!access (dir, F_OK))
212                 result = TRUE;
213         g_free (dir);
214 #endif
215         
216         return result;
217 }
218
219 static void
220 process_set_defaults (WapiHandle_process *process_handle)
221 {
222         /* These seem to be the defaults on w2k */
223         process_handle->min_working_set = 204800;
224         process_handle->max_working_set = 1413120;
225         
226         _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
227 }
228
229 static int
230 len16 (const gunichar2 *str)
231 {
232         int len = 0;
233         
234         while (*str++ != 0)
235                 len++;
236
237         return len;
238 }
239
240 static gunichar2 *
241 utf16_concat (const gunichar2 *first, ...)
242 {
243         va_list args;
244         int total = 0, i;
245         const gunichar2 *s;
246         gunichar2 *ret;
247
248         va_start (args, first);
249         total += len16 (first);
250         for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *)){
251                 total += len16 (s);
252         }
253         va_end (args);
254
255         ret = g_new (gunichar2, total + 1);
256         if (ret == NULL)
257                 return NULL;
258
259         ret [total] = 0;
260         i = 0;
261         for (s = first; *s != 0; s++)
262                 ret [i++] = *s;
263         va_start (args, first);
264         for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
265                 const gunichar2 *p;
266                 
267                 for (p = s; *p != 0; p++)
268                         ret [i++] = *p;
269         }
270         va_end (args);
271         
272         return ret;
273 }
274
275 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
276 static const gunichar2 *utf16_space = utf16_space_bytes; 
277 static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
278 static const gunichar2 *utf16_quote = utf16_quote_bytes;
279
280 #ifdef DEBUG_ENABLED
281 /* Useful in gdb */
282 void
283 print_utf16 (gunichar2 *str)
284 {
285         char *res;
286
287         res = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL);
288         g_print ("%s\n", res);
289         g_free (res);
290 }
291 #endif
292
293 /* Implemented as just a wrapper around CreateProcess () */
294 gboolean
295 ShellExecuteEx (WapiShellExecuteInfo *sei)
296 {
297         gboolean ret;
298         WapiProcessInformation process_info;
299         gunichar2 *args;
300         
301         if (sei == NULL) {
302                 /* w2k just segfaults here, but we can do better than
303                  * that
304                  */
305                 SetLastError (ERROR_INVALID_PARAMETER);
306                 return FALSE;
307         }
308
309         if (sei->lpFile == NULL)
310                 /* w2k returns TRUE for this, for some reason. */
311                 return TRUE;
312         
313         /* Put both executable and parameters into the second argument
314          * to CreateProcess (), so it searches $PATH.  The conversion
315          * into and back out of utf8 is because there is no
316          * g_strdup_printf () equivalent for gunichar2 :-(
317          */
318         args = utf16_concat (utf16_quote, sei->lpFile, utf16_quote, sei->lpParameters == NULL ? NULL : utf16_space, sei->lpParameters, NULL);
319         if (args == NULL) {
320                 SetLastError (ERROR_INVALID_DATA);
321                 return FALSE;
322         }
323         ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
324                              CREATE_UNICODE_ENVIRONMENT, NULL,
325                              sei->lpDirectory, NULL, &process_info);
326         g_free (args);
327
328         if (!ret && GetLastError () == ERROR_OUTOFMEMORY)
329                 return ret;
330         
331         if (!ret) {
332                 static char *handler;
333                 static gunichar2 *handler_utf16;
334                 
335                 if (handler_utf16 == (gunichar2 *)-1)
336                         return FALSE;
337
338 #ifdef PLATFORM_MACOSX
339                 handler = g_strdup ("/usr/bin/open");
340 #else
341                 /*
342                  * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
343                  * if that fails, try to use gnome-open, then kfmclient
344                  */
345                 handler = g_find_program_in_path ("xdg-open");
346                 if (handler == NULL){
347                         handler = g_find_program_in_path ("gnome-open");
348                         if (handler == NULL){
349                                 handler = g_find_program_in_path ("kfmclient");
350                                 if (handler == NULL){
351                                         handler_utf16 = (gunichar2 *) -1;
352                                         return FALSE;
353                                 } else {
354                                         /* kfmclient needs exec argument */
355                                         char *old = handler;
356                                         handler = g_strconcat (old, " exec",
357                                                                NULL);
358                                         g_free (old);
359                                 }
360                         }
361                 }
362 #endif
363                 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
364                 g_free (handler);
365
366                 /* Put quotes around the filename, in case it's a url
367                  * that contains #'s (CreateProcess() calls
368                  * g_shell_parse_argv(), which deliberately throws
369                  * away anything after an unquoted #).  Fixes bug
370                  * 371567.
371                  */
372                 args = utf16_concat (handler_utf16, utf16_space, utf16_quote,
373                                      sei->lpFile, utf16_quote,
374                                      sei->lpParameters == NULL ? NULL : utf16_space,
375                                      sei->lpParameters, NULL);
376                 if (args == NULL) {
377                         SetLastError (ERROR_INVALID_DATA);
378                         return FALSE;
379                 }
380                 ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
381                                      CREATE_UNICODE_ENVIRONMENT, NULL,
382                                      sei->lpDirectory, NULL, &process_info);
383                 g_free (args);
384                 if (!ret) {
385                         if (GetLastError () != ERROR_OUTOFMEMORY)
386                                 SetLastError (ERROR_INVALID_DATA);
387                         return FALSE;
388                 }
389                 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
390                 CloseHandle (process_info.hProcess);
391                 process_info.hProcess = NULL;
392         }
393         
394         if (sei->fMask & SEE_MASK_NOCLOSEPROCESS)
395                 sei->hProcess = process_info.hProcess;
396         else
397                 CloseHandle (process_info.hProcess);
398         
399         return ret;
400 }
401
402 static gboolean
403 is_managed_binary (const char *filename)
404 {
405         int original_errno = errno;
406 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
407         int file = open (filename, O_RDONLY | O_LARGEFILE);
408 #else
409         int file = open (filename, O_RDONLY);
410 #endif
411         off_t new_offset;
412         unsigned char buffer[8];
413         off_t file_size, optional_header_offset;
414         off_t pe_header_offset;
415         gboolean managed = FALSE;
416         int num_read;
417         guint32 first_word, second_word;
418         
419         /* If we are unable to open the file, then we definitely
420          * can't say that it is managed. The child mono process
421          * probably wouldn't be able to open it anyway.
422          */
423         if (file < 0) {
424                 errno = original_errno;
425                 return FALSE;
426         }
427
428         /* Retrieve the length of the file for future sanity checks. */
429         file_size = lseek (file, 0, SEEK_END);
430         lseek (file, 0, SEEK_SET);
431
432         /* We know we need to read a header field at offset 60. */
433         if (file_size < 64)
434                 goto leave;
435
436         num_read = read (file, buffer, 2);
437
438         if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
439                 goto leave;
440
441         new_offset = lseek (file, 60, SEEK_SET);
442
443         if (new_offset != 60)
444                 goto leave;
445         
446         num_read = read (file, buffer, 4);
447
448         if (num_read != 4)
449                 goto leave;
450         pe_header_offset =  buffer[0]
451                 | (buffer[1] <<  8)
452                 | (buffer[2] << 16)
453                 | (buffer[3] << 24);
454         
455         if (pe_header_offset + 24 > file_size)
456                 goto leave;
457
458         new_offset = lseek (file, pe_header_offset, SEEK_SET);
459
460         if (new_offset != pe_header_offset)
461                 goto leave;
462
463         num_read = read (file, buffer, 4);
464
465         if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
466                 goto leave;
467
468         /*
469          * Verify that the header we want in the optional header data
470          * is present in this binary.
471          */
472         new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
473
474         if (new_offset != pe_header_offset + 20)
475                 goto leave;
476
477         num_read = read (file, buffer, 2);
478
479         if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
480                 goto leave;
481
482         /* Read the CLR header address and size fields. These will be
483          * zero if the binary is not managed.
484          */
485         optional_header_offset = pe_header_offset + 24;
486         new_offset = lseek (file, optional_header_offset + 208, SEEK_SET);
487
488         if (new_offset != optional_header_offset + 208)
489                 goto leave;
490
491         num_read = read (file, buffer, 8);
492         
493         /* We are not concerned with endianness, only with
494          * whether it is zero or not.
495          */
496         first_word = *(guint32 *)&buffer[0];
497         second_word = *(guint32 *)&buffer[4];
498         
499         if ((num_read != 8) || (first_word == 0) || (second_word == 0))
500                 goto leave;
501         
502         managed = TRUE;
503
504 leave:
505         close (file);
506         errno = original_errno;
507         return managed;
508 }
509
510 gboolean
511 CreateProcessWithLogonW (const gunichar2 *username,
512                                                  const gunichar2 *domain,
513                                                  const gunichar2 *password,
514                                                  const guint32 logonFlags,
515                                                  const gunichar2 *appname,
516                                                  const gunichar2 *cmdline,
517                                                  guint32 create_flags,
518                                                  gpointer env,
519                                                  const gunichar2 *cwd,
520                                                  WapiStartupInfo *startup,
521                                                  WapiProcessInformation *process_info)
522 {
523         /* FIXME: use user information */
524         return CreateProcess (appname, cmdline, NULL, NULL, FALSE, create_flags, env, cwd, startup, process_info);
525 }
526
527 static gboolean
528 is_readable_or_executable (const char *prog)
529 {
530         struct stat buf;
531         int a = access (prog, R_OK);
532         int b = access (prog, X_OK);
533         if (a != 0 && b != 0)
534                 return FALSE;
535         if (stat (prog, &buf))
536                 return FALSE;
537         if (S_ISREG (buf.st_mode))
538                 return TRUE;
539         return FALSE;
540 }
541
542 static gboolean
543 is_executable (const char *prog)
544 {
545         struct stat buf;
546         if (access (prog, X_OK) != 0)
547                 return FALSE;
548         if (stat (prog, &buf))
549                 return FALSE;
550         if (S_ISREG (buf.st_mode))
551                 return TRUE;
552         return FALSE;
553 }
554
555 static void
556 switch_dir_separators (char *path)
557 {
558         size_t i, pathLength = strlen(path);
559         
560         /* Turn all the slashes round the right way, except for \' */
561         /* There are probably other characters that need to be excluded as well. */
562         for (i = 0; i < pathLength; i++) {
563                 if (path[i] == '\\' && i < pathLength - 1 && path[i+1] != '\'' )
564                         path[i] = '/';
565         }
566 }
567
568 gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
569                         WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
570                         WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
571                         gboolean inherit_handles, guint32 create_flags,
572                         gpointer new_environ, const gunichar2 *cwd,
573                         WapiStartupInfo *startup,
574                         WapiProcessInformation *process_info)
575 {
576 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
577         char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
578         char *dir = NULL, **env_strings = NULL, **argv = NULL;
579         guint32 i, env_count = 0;
580         gboolean ret = FALSE;
581         gpointer handle = NULL;
582         WapiHandle_process process_handle = {0}, *process_handle_data;
583         GError *gerr = NULL;
584         int in_fd, out_fd, err_fd;
585         pid_t pid = 0;
586         int startup_pipe [2] = {-1, -1};
587         int dummy;
588         struct MonoProcess *mono_process;
589         gboolean fork_failed = FALSE;
590
591         mono_once (&process_sig_chld_once, process_add_sigchld_handler);
592
593         /* appname and cmdline specify the executable and its args:
594          *
595          * If appname is not NULL, it is the name of the executable.
596          * Otherwise the executable is the first token in cmdline.
597          *
598          * Executable searching:
599          *
600          * If appname is not NULL, it can specify the full path and
601          * file name, or else a partial name and the current directory
602          * will be used.  There is no additional searching.
603          *
604          * If appname is NULL, the first whitespace-delimited token in
605          * cmdline is used.  If the name does not contain a full
606          * directory path, the search sequence is:
607          *
608          * 1) The directory containing the current process
609          * 2) The current working directory
610          * 3) The windows system directory  (Ignored)
611          * 4) The windows directory (Ignored)
612          * 5) $PATH
613          *
614          * Just to make things more interesting, tokens can contain
615          * white space if they are surrounded by quotation marks.  I'm
616          * beginning to understand just why windows apps are generally
617          * so crap, with an API like this :-(
618          */
619         if (appname != NULL) {
620                 cmd = mono_unicode_to_external (appname);
621                 if (cmd == NULL) {
622                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL",
623                                    __func__);
624
625                         SetLastError (ERROR_PATH_NOT_FOUND);
626                         goto free_strings;
627                 }
628
629                 switch_dir_separators(cmd);
630         }
631         
632         if (cmdline != NULL) {
633                 args = mono_unicode_to_external (cmdline);
634                 if (args == NULL) {
635                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
636
637                         SetLastError (ERROR_PATH_NOT_FOUND);
638                         goto free_strings;
639                 }
640         }
641
642         if (cwd != NULL) {
643                 dir = mono_unicode_to_external (cwd);
644                 if (dir == NULL) {
645                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: unicode conversion returned NULL", __func__);
646
647                         SetLastError (ERROR_PATH_NOT_FOUND);
648                         goto free_strings;
649                 }
650
651                 /* Turn all the slashes round the right way */
652                 switch_dir_separators(dir);
653         }
654         
655
656         /* We can't put off locating the executable any longer :-( */
657         if (cmd != NULL) {
658                 char *unquoted;
659                 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
660                         /* Strip off the drive letter.  I can't
661                          * believe that CP/M holdover is still
662                          * visible...
663                          */
664                         g_memmove (cmd, cmd+2, strlen (cmd)-2);
665                         cmd[strlen (cmd)-2] = '\0';
666                 }
667
668                 unquoted = g_shell_unquote (cmd, NULL);
669                 if (unquoted[0] == '/') {
670                         /* Assume full path given */
671                         prog = g_strdup (unquoted);
672
673                         /* Executable existing ? */
674                         if (!is_readable_or_executable (prog)) {
675                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
676                                            __func__, prog);
677                                 g_free (unquoted);
678                                 SetLastError (ERROR_FILE_NOT_FOUND);
679                                 goto free_strings;
680                         }
681                 } else {
682                         /* Search for file named by cmd in the current
683                          * directory
684                          */
685                         char *curdir = g_get_current_dir ();
686
687                         prog = g_strdup_printf ("%s/%s", curdir, unquoted);
688                         g_free (curdir);
689
690                         /* And make sure it's readable */
691                         if (!is_readable_or_executable (prog)) {
692                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
693                                            __func__, prog);
694                                 g_free (unquoted);
695                                 SetLastError (ERROR_FILE_NOT_FOUND);
696                                 goto free_strings;
697                         }
698                 }
699                 g_free (unquoted);
700
701                 args_after_prog = args;
702         } else {
703                 char *token = NULL;
704                 char quote;
705                 
706                 /* Dig out the first token from args, taking quotation
707                  * marks into account
708                  */
709
710                 /* First, strip off all leading whitespace */
711                 args = g_strchug (args);
712                 
713                 /* args_after_prog points to the contents of args
714                  * after token has been set (otherwise argv[0] is
715                  * duplicated)
716                  */
717                 args_after_prog = args;
718
719                 /* Assume the opening quote will always be the first
720                  * character
721                  */
722                 if (args[0] == '\"' || args [0] == '\'') {
723                         quote = args [0];
724                         for (i = 1; args[i] != '\0' && args[i] != quote; i++);
725                         if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) {
726                                 /* We found the first token */
727                                 token = g_strndup (args+1, i-1);
728                                 args_after_prog = g_strchug (args + i + 1);
729                         } else {
730                                 /* Quotation mark appeared in the
731                                  * middle of the token.  Just give the
732                                  * whole first token, quotes and all,
733                                  * to exec.
734                                  */
735                         }
736                 }
737                 
738                 if (token == NULL) {
739                         /* No quote mark, or malformed */
740                         for (i = 0; args[i] != '\0'; i++) {
741                                 if (g_ascii_isspace (args[i])) {
742                                         token = g_strndup (args, i);
743                                         args_after_prog = args + i + 1;
744                                         break;
745                                 }
746                         }
747                 }
748
749                 if (token == NULL && args[0] != '\0') {
750                         /* Must be just one token in the string */
751                         token = g_strdup (args);
752                         args_after_prog = NULL;
753                 }
754                 
755                 if (token == NULL) {
756                         /* Give up */
757                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find what to exec", __func__);
758
759                         SetLastError (ERROR_PATH_NOT_FOUND);
760                         goto free_strings;
761                 }
762                 
763                 /* Turn all the slashes round the right way. Only for
764                  * the prg. name
765                  */
766                 switch_dir_separators(token);
767
768                 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
769                         /* Strip off the drive letter.  I can't
770                          * believe that CP/M holdover is still
771                          * visible...
772                          */
773                         g_memmove (token, token+2, strlen (token)-2);
774                         token[strlen (token)-2] = '\0';
775                 }
776
777                 if (token[0] == '/') {
778                         /* Assume full path given */
779                         prog = g_strdup (token);
780                         
781                         /* Executable existing ? */
782                         if (!is_readable_or_executable (prog)) {
783                                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s",
784                                            __func__, token);
785                                 g_free (token);
786                                 SetLastError (ERROR_FILE_NOT_FOUND);
787                                 goto free_strings;
788                         }
789                 } else {
790                         char *curdir = g_get_current_dir ();
791
792                         /* FIXME: Need to record the directory
793                          * containing the current process, and check
794                          * that for the new executable as the first
795                          * place to look
796                          */
797
798                         prog = g_strdup_printf ("%s/%s", curdir, token);
799                         g_free (curdir);
800
801                         /* I assume X_OK is the criterion to use,
802                          * rather than F_OK
803                          *
804                          * X_OK is too strict *if* the target is a CLR binary
805                          */
806                         if (!is_readable_or_executable (prog)) {
807                                 g_free (prog);
808                                 prog = g_find_program_in_path (token);
809                                 if (prog == NULL) {
810                                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Couldn't find executable %s", __func__, token);
811
812                                         g_free (token);
813                                         SetLastError (ERROR_FILE_NOT_FOUND);
814                                         goto free_strings;
815                                 }
816                         }
817                 }
818
819                 g_free (token);
820         }
821
822         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Exec prog [%s] args [%s]", __func__, prog,
823                    args_after_prog);
824         
825         /* Check for CLR binaries; if found, we will try to invoke
826          * them using the same mono binary that started us.
827          */
828         if (is_managed_binary (prog)) {
829                 gunichar2 *newapp = NULL, *newcmd;
830                 gsize bytes_ignored;
831
832                 if (cli_launcher)
833                         newapp = mono_unicode_from_external (cli_launcher, &bytes_ignored);
834                 else
835                         newapp = mono_unicode_from_external ("mono", &bytes_ignored);
836
837                 if (newapp != NULL) {
838                         if (appname != NULL) {
839                                 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space,
840                                                        appname, utf16_space,
841                                                        cmdline, NULL);
842                         } else {
843                                 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space,
844                                                        cmdline, NULL);
845                         }
846                         
847                         g_free ((gunichar2 *)newapp);
848                         
849                         if (newcmd != NULL) {
850                                 ret = CreateProcess (NULL, newcmd,
851                                                      process_attrs,
852                                                      thread_attrs,
853                                                      inherit_handles,
854                                                      create_flags, new_environ,
855                                                      cwd, startup,
856                                                      process_info);
857                                 
858                                 g_free ((gunichar2 *)newcmd);
859                                 
860                                 goto free_strings;
861                         }
862                 }
863         } else {
864                 if (!is_executable (prog)) {
865                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Executable permisson not set on %s", __func__, prog);
866                         SetLastError (ERROR_ACCESS_DENIED);
867                         goto free_strings;
868                 }
869         }
870
871         if (args_after_prog != NULL && *args_after_prog) {
872                 char *qprog;
873
874                 qprog = g_shell_quote (prog);
875                 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
876                 g_free (qprog);
877         } else {
878                 full_prog = g_shell_quote (prog);
879         }
880
881         ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
882         if (ret == FALSE) {
883                 g_message ("CreateProcess: %s\n", gerr->message);
884                 g_error_free (gerr);
885                 gerr = NULL;
886                 goto free_strings;
887         }
888
889         if (startup != NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
890                 in_fd = GPOINTER_TO_UINT (startup->hStdInput);
891                 out_fd = GPOINTER_TO_UINT (startup->hStdOutput);
892                 err_fd = GPOINTER_TO_UINT (startup->hStdError);
893         } else {
894                 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
895                 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
896                 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
897         }
898         
899         process_handle.proc_name = g_strdup (prog);
900
901         process_set_defaults (&process_handle);
902         
903         handle = mono_w32handle_new (MONO_W32HANDLE_PROCESS, &process_handle);
904         if (handle == INVALID_HANDLE_VALUE) {
905                 g_warning ("%s: error creating process handle", __func__);
906
907                 ret = FALSE;
908                 SetLastError (ERROR_OUTOFMEMORY);
909                 goto free_strings;
910         }
911
912         /* new_environ is a block of NULL-terminated strings, which
913          * is itself NULL-terminated. Of course, passing an array of
914          * string pointers would have made things too easy :-(
915          *
916          * If new_environ is not NULL it specifies the entire set of
917          * environment variables in the new process.  Otherwise the
918          * new process inherits the same environment.
919          */
920         if (new_environ) {
921                 gunichar2 *new_environp;
922
923                 /* Count the number of strings */
924                 for (new_environp = (gunichar2 *)new_environ; *new_environp;
925                      new_environp++) {
926                         env_count++;
927                         while (*new_environp) {
928                                 new_environp++;
929                         }
930                 }
931
932                 /* +2: one for the process handle value, and the last
933                  * one is NULL
934                  */
935                 env_strings = g_new0 (char *, env_count + 2);
936                 
937                 /* Copy each environ string into 'strings' turning it
938                  * into utf8 (or the requested encoding) at the same
939                  * time
940                  */
941                 env_count = 0;
942                 for (new_environp = (gunichar2 *)new_environ; *new_environp;
943                      new_environp++) {
944                         env_strings[env_count] = mono_unicode_to_external (new_environp);
945                         env_count++;
946                         while (*new_environp) {
947                                 new_environp++;
948                         }
949                 }
950         } else {
951                 for (i = 0; environ[i] != NULL; i++)
952                         env_count++;
953
954                 /* +2: one for the process handle value, and the last
955                  * one is NULL
956                  */
957                 env_strings = g_new0 (char *, env_count + 2);
958                 
959                 /* Copy each environ string into 'strings' turning it
960                  * into utf8 (or the requested encoding) at the same
961                  * time
962                  */
963                 env_count = 0;
964                 for (i = 0; environ[i] != NULL; i++) {
965                         env_strings[env_count] = g_strdup (environ[i]);
966                         env_count++;
967                 }
968         }
969
970         /* Create a pipe to make sure the child doesn't exit before 
971          * we can add the process to the linked list of mono_processes */
972         if (pipe (startup_pipe) == -1) {
973                 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
974                  * This is just for a very hard to hit race condition in the first place */
975                 startup_pipe [0] = startup_pipe [1] = -1;
976                 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__);
977         }
978
979         switch (pid = fork ()) {
980         case -1: /* Error */ {
981                 SetLastError (ERROR_OUTOFMEMORY);
982                 ret = FALSE;
983                 fork_failed = TRUE;
984                 break;
985         }
986         case 0: /* Child */ {
987                 if (startup_pipe [0] != -1) {
988                         /* Wait until the parent has updated it's internal data */
989                         ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1);
990                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: child: parent has completed its setup", __func__);
991                         close (startup_pipe [0]);
992                         close (startup_pipe [1]);
993                 }
994                 
995                 /* should we detach from the process group? */
996
997                 /* Connect stdin, stdout and stderr */
998                 dup2 (in_fd, 0);
999                 dup2 (out_fd, 1);
1000                 dup2 (err_fd, 2);
1001
1002                 if (inherit_handles != TRUE) {
1003                         /* FIXME: do something here */
1004                 }
1005                 
1006                 /* Close all file descriptors */
1007                 for (i = mono_w32handle_fd_reserve - 1; i > 2; i--)
1008                         close (i);
1009
1010 #ifdef DEBUG_ENABLED
1011                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1012                            dir == NULL?".":dir);
1013                 for (i = 0; argv[i] != NULL; i++)
1014                         g_message ("arg %d: [%s]", i, argv[i]);
1015                 
1016                 for (i = 0; env_strings[i] != NULL; i++)
1017                         g_message ("env %d: [%s]", i, env_strings[i]);
1018 #endif
1019
1020                 /* set cwd */
1021                 if (dir != NULL && chdir (dir) == -1) {
1022                         /* set error */
1023                         _exit (-1);
1024                 }
1025                 
1026                 /* exec */
1027                 execve (argv[0], argv, env_strings);
1028                 
1029                 /* set error */
1030                 _exit (-1);
1031
1032                 break;
1033         }
1034         default: /* Parent */ {
1035                 process_handle_data = lookup_process_handle (handle);
1036                 if (!process_handle_data) {
1037                         g_warning ("%s: error looking up process handle %p", __func__, handle);
1038                         mono_w32handle_unref (handle);
1039                 } else {
1040                         process_handle_data->id = pid;
1041
1042                         /* Add our mono_process into the linked list of mono_processes */
1043                         mono_process = (struct MonoProcess *) g_malloc0 (sizeof (struct MonoProcess));
1044                         mono_process->pid = pid;
1045                         mono_process->handle_count = 1;
1046                         mono_os_sem_init (&mono_process->exit_sem, 0);
1047
1048                         /* Keep the process handle artificially alive until the process
1049                          * exits so that the information in the handle isn't lost. */
1050                         mono_w32handle_ref (handle);
1051                         mono_process->handle = handle;
1052
1053                         process_handle_data->mono_process = mono_process;
1054
1055                         mono_os_mutex_lock (&mono_processes_mutex);
1056                         mono_process->next = mono_processes;
1057                         mono_processes = mono_process;
1058                         mono_os_mutex_unlock (&mono_processes_mutex);
1059
1060                         if (process_info != NULL) {
1061                                 process_info->hProcess = handle;
1062                                 process_info->dwProcessId = pid;
1063
1064                                 /* FIXME: we might need to handle the thread info some day */
1065                                 process_info->hThread = INVALID_HANDLE_VALUE;
1066                                 process_info->dwThreadId = 0;
1067                         }
1068                 }
1069
1070                 break;
1071         }
1072         }
1073
1074         if (fork_failed)
1075                 mono_w32handle_unref (handle);
1076
1077         if (startup_pipe [1] != -1) {
1078                 /* Write 1 byte, doesn't matter what */
1079                 ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
1080                 close (startup_pipe [0]);
1081                 close (startup_pipe [1]);
1082         }
1083
1084 free_strings:
1085         if (cmd)
1086                 g_free (cmd);
1087         if (full_prog)
1088                 g_free (full_prog);
1089         if (prog)
1090                 g_free (prog);
1091         if (args)
1092                 g_free (args);
1093         if (dir)
1094                 g_free (dir);
1095         if (env_strings)
1096                 g_strfreev (env_strings);
1097         if (argv)
1098                 g_strfreev (argv);
1099         
1100         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning handle %p for pid %d", __func__, handle, pid);
1101
1102         /* Check if something needs to be cleaned up. */
1103         mono_processes_cleanup ();
1104         
1105         return ret;
1106 #else
1107         SetLastError (ERROR_NOT_SUPPORTED);
1108         return FALSE;
1109 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1110 }
1111                 
1112 static void
1113 process_set_name (WapiHandle_process *process_handle)
1114 {
1115         char *progname, *utf8_progname, *slash;
1116         
1117         progname = g_get_prgname ();
1118         utf8_progname = mono_utf8_from_external (progname);
1119
1120         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: using [%s] as prog name", __func__, progname);
1121
1122         if (utf8_progname) {
1123                 slash = strrchr (utf8_progname, '/');
1124                 if (slash)
1125                         process_handle->proc_name = g_strdup (slash+1);
1126                 else
1127                         process_handle->proc_name = g_strdup (utf8_progname);
1128                 g_free (utf8_progname);
1129         }
1130 }
1131
1132 void
1133 _wapi_processes_init (void)
1134 {
1135         pid_t pid = _wapi_getpid ();
1136         WapiHandle_process process_handle = {0};
1137
1138         mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS, &_wapi_process_ops);
1139
1140         mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS,
1141                 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
1142         
1143         process_handle.id = pid;
1144
1145         process_set_defaults (&process_handle);
1146         process_set_name (&process_handle);
1147
1148         current_process = mono_w32handle_new (MONO_W32HANDLE_PROCESS,
1149                                             &process_handle);
1150         g_assert (current_process);
1151
1152         mono_os_mutex_init (&mono_processes_mutex);
1153 }
1154
1155 gpointer
1156 _wapi_process_duplicate (void)
1157 {
1158         mono_w32handle_ref (current_process);
1159         
1160         return current_process;
1161 }
1162
1163 /* Returns a pseudo handle that doesn't need to be closed afterwards */
1164 gpointer
1165 GetCurrentProcess (void)
1166 {
1167         return _WAPI_PROCESS_CURRENT;
1168 }
1169
1170 guint32
1171 GetProcessId (gpointer handle)
1172 {
1173         WapiHandle_process *process_handle;
1174
1175         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
1176                 /* This is a pseudo handle */
1177                 return WAPI_HANDLE_TO_PID (handle);
1178         
1179         process_handle = lookup_process_handle (handle);
1180         if (!process_handle) {
1181                 SetLastError (ERROR_INVALID_HANDLE);
1182                 return 0;
1183         }
1184         
1185         return process_handle->id;
1186 }
1187
1188 static gboolean
1189 process_open_compare (gpointer handle, gpointer user_data)
1190 {
1191         pid_t wanted_pid;
1192         WapiHandle_process *process_handle;
1193         pid_t checking_pid;
1194
1195         g_assert (!WAPI_IS_PSEUDO_PROCESS_HANDLE (handle));
1196         
1197         process_handle = lookup_process_handle (handle);
1198         g_assert (process_handle);
1199         
1200         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking at process %d", __func__, process_handle->id);
1201
1202         checking_pid = process_handle->id;
1203
1204         if (checking_pid == 0)
1205                 return FALSE;
1206         
1207         wanted_pid = GPOINTER_TO_UINT (user_data);
1208
1209         /* It's possible to have more than one process handle with the
1210          * same pid, but only the one running process can be
1211          * unsignalled
1212          */
1213         if (checking_pid == wanted_pid &&
1214             !mono_w32handle_issignalled (handle)) {
1215                 /* If the handle is blown away in the window between
1216                  * returning TRUE here and mono_w32handle_search pinging
1217                  * the timestamp, the search will continue
1218                  */
1219                 return TRUE;
1220         } else {
1221                 return FALSE;
1222         }
1223 }
1224
1225 gboolean
1226 CloseProcess (gpointer handle)
1227 {
1228         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (handle))
1229                 return TRUE;
1230         return CloseHandle (handle);
1231 }
1232
1233 /*
1234  * The caller owns the returned handle and must call CloseProcess () on it to clean it up.
1235  */
1236 gpointer
1237 OpenProcess (guint32 req_access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
1238 {
1239         /* Find the process handle that corresponds to pid */
1240         gpointer handle = NULL;
1241         
1242         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: looking for process %d", __func__, pid);
1243
1244         handle = mono_w32handle_search (MONO_W32HANDLE_PROCESS,
1245                                       process_open_compare,
1246                                       GUINT_TO_POINTER (pid), NULL, TRUE);
1247         if (handle == 0) {
1248                 if (is_pid_valid (pid)) {
1249                         /* Return a pseudo handle for processes we
1250                          * don't have handles for
1251                          */
1252                         return WAPI_PID_TO_HANDLE (pid);
1253                 } else {
1254                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find pid %d", __func__, pid);
1255
1256                         SetLastError (ERROR_PROC_NOT_FOUND);
1257         
1258                         return NULL;
1259                 }
1260         }
1261
1262         /* mono_w32handle_search () already added a ref */
1263         return handle;
1264 }
1265
1266 gboolean
1267 GetExitCodeProcess (gpointer process, guint32 *code)
1268 {
1269         WapiHandle_process *process_handle;
1270         guint32 pid = -1;
1271         
1272         if (!code)
1273                 return FALSE;
1274         
1275         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
1276                 pid = WAPI_HANDLE_TO_PID (process);
1277                 /* This is a pseudo handle, so we don't know what the
1278                  * exit code was, but we can check whether it's alive or not
1279                  */
1280                 if (is_pid_valid (pid)) {
1281                         *code = STILL_ACTIVE;
1282                         return TRUE;
1283                 } else {
1284                         *code = -1;
1285                         return TRUE;
1286                 }
1287         }
1288
1289         process_handle = lookup_process_handle (process);
1290         if (!process_handle) {
1291                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
1292                 
1293                 return FALSE;
1294         }
1295
1296         if (process_handle->id == _wapi_getpid ()) {
1297                 *code = STILL_ACTIVE;
1298                 return TRUE;
1299         }
1300
1301         /* A process handle is only signalled if the process has exited
1302          * and has been waited for */
1303
1304         /* Make sure any process exit has been noticed, before
1305          * checking if the process is signalled.  Fixes bug 325463.
1306          */
1307         process_wait (process, 0, TRUE);
1308         
1309         if (mono_w32handle_issignalled (process))
1310                 *code = process_handle->exitstatus;
1311         else
1312                 *code = STILL_ACTIVE;
1313         
1314         return TRUE;
1315 }
1316
1317 gboolean
1318 GetProcessTimes (gpointer process, WapiFileTime *create_time,
1319                                  WapiFileTime *exit_time, WapiFileTime *kernel_time,
1320                                  WapiFileTime *user_time)
1321 {
1322         WapiHandle_process *process_handle;
1323         gboolean ku_times_set = FALSE;
1324         
1325         if (create_time == NULL || exit_time == NULL || kernel_time == NULL ||
1326                 user_time == NULL)
1327                 /* Not sure if w32 allows NULLs here or not */
1328                 return FALSE;
1329         
1330         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
1331                 gpointer pid = GINT_TO_POINTER (WAPI_HANDLE_TO_PID(process));
1332                 gint64 start_ticks, user_ticks, kernel_ticks;
1333
1334                 mono_process_get_times (pid, &start_ticks, &user_ticks, &kernel_ticks);
1335
1336                 _wapi_guint64_to_filetime (start_ticks, create_time);
1337                 _wapi_guint64_to_filetime (user_ticks, kernel_time);
1338                 _wapi_guint64_to_filetime (kernel_ticks, user_time);
1339
1340                 return TRUE;
1341         }
1342
1343         process_handle = lookup_process_handle (process);
1344         if (!process_handle) {
1345                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
1346                 
1347                 return FALSE;
1348         }
1349         
1350         *create_time = process_handle->create_time;
1351
1352         /* A process handle is only signalled if the process has
1353          * exited.  Otherwise exit_time isn't set
1354          */
1355         if (mono_w32handle_issignalled (process))
1356                 *exit_time = process_handle->exit_time;
1357
1358 #ifdef HAVE_GETRUSAGE
1359         if (process_handle->id == getpid ()) {
1360                 struct rusage time_data;
1361                 if (getrusage (RUSAGE_SELF, &time_data) == 0) {
1362                         guint64 tick_val;
1363                         ku_times_set = TRUE;
1364                         tick_val = (guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10;
1365                         _wapi_guint64_to_filetime (tick_val, user_time);
1366                         tick_val = (guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10;
1367                         _wapi_guint64_to_filetime (tick_val, kernel_time);
1368                 }
1369         }
1370 #endif
1371         if (!ku_times_set) {
1372                 memset (kernel_time, 0, sizeof (WapiFileTime));
1373                 memset (user_time, 0, sizeof (WapiFileTime));
1374         }
1375
1376         return TRUE;
1377 }
1378
1379 typedef struct
1380 {
1381         gpointer address_start;
1382         gpointer address_end;
1383         char *perms;
1384         gpointer address_offset;
1385         guint64 device;
1386         guint64 inode;
1387         char *filename;
1388 } WapiProcModule;
1389
1390 static void free_procmodule (WapiProcModule *mod)
1391 {
1392         if (mod->perms != NULL) {
1393                 g_free (mod->perms);
1394         }
1395         if (mod->filename != NULL) {
1396                 g_free (mod->filename);
1397         }
1398         g_free (mod);
1399 }
1400
1401 static gint find_procmodule (gconstpointer a, gconstpointer b)
1402 {
1403         WapiProcModule *want = (WapiProcModule *)a;
1404         WapiProcModule *compare = (WapiProcModule *)b;
1405         
1406         if ((want->device == compare->device) &&
1407             (want->inode == compare->inode)) {
1408                 return(0);
1409         } else {
1410                 return(1);
1411         }
1412 }
1413
1414 #if defined(USE_OSX_LOADER)
1415 #include <mach-o/dyld.h>
1416 #include <mach-o/getsect.h>
1417
1418 static GSList *load_modules (void)
1419 {
1420         GSList *ret = NULL;
1421         WapiProcModule *mod;
1422         uint32_t count = _dyld_image_count ();
1423         int i = 0;
1424
1425         for (i = 0; i < count; i++) {
1426 #if SIZEOF_VOID_P == 8
1427                 const struct mach_header_64 *hdr;
1428                 const struct section_64 *sec;
1429 #else
1430                 const struct mach_header *hdr;
1431                 const struct section *sec;
1432 #endif
1433                 const char *name;
1434
1435                 name = _dyld_get_image_name (i);
1436 #if SIZEOF_VOID_P == 8
1437                 hdr = (const struct mach_header_64*)_dyld_get_image_header (i);
1438                 sec = getsectbynamefromheader_64 (hdr, SEG_DATA, SECT_DATA);
1439 #else
1440                 hdr = _dyld_get_image_header (i);
1441                 sec = getsectbynamefromheader (hdr, SEG_DATA, SECT_DATA);
1442 #endif
1443
1444                 /* Some dynlibs do not have data sections on osx (#533893) */
1445                 if (sec == 0) {
1446                         continue;
1447                 }
1448                         
1449                 mod = g_new0 (WapiProcModule, 1);
1450                 mod->address_start = GINT_TO_POINTER (sec->addr);
1451                 mod->address_end = GINT_TO_POINTER (sec->addr+sec->size);
1452                 mod->perms = g_strdup ("r--p");
1453                 mod->address_offset = 0;
1454                 mod->device = makedev (0, 0);
1455                 mod->inode = i;
1456                 mod->filename = g_strdup (name); 
1457                 
1458                 if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
1459                         ret = g_slist_prepend (ret, mod);
1460                 } else {
1461                         free_procmodule (mod);
1462                 }
1463         }
1464
1465         ret = g_slist_reverse (ret);
1466         
1467         return(ret);
1468 }
1469 #elif defined(USE_BSD_LOADER)
1470 #include <link.h>
1471 static int load_modules_callback (struct dl_phdr_info *info, size_t size, void *ptr)
1472 {
1473         if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
1474             + sizeof (info->dlpi_phnum))
1475                 return (-1);
1476
1477         struct dl_phdr_info *cpy = calloc(1, sizeof(struct dl_phdr_info));
1478         if (!cpy)
1479                 return (-1);
1480
1481         memcpy(cpy, info, sizeof(*info));
1482
1483         g_ptr_array_add ((GPtrArray *)ptr, cpy);
1484
1485         return (0);
1486 }
1487
1488 static GSList *load_modules (void)
1489 {
1490         GSList *ret = NULL;
1491         WapiProcModule *mod;
1492         GPtrArray *dlarray = g_ptr_array_new();
1493         int i;
1494
1495         if (dl_iterate_phdr(load_modules_callback, dlarray) < 0)
1496                 return (ret);
1497
1498         for (i = 0; i < dlarray->len; i++) {
1499                 struct dl_phdr_info *info = g_ptr_array_index (dlarray, i);
1500
1501                 mod = g_new0 (WapiProcModule, 1);
1502                 mod->address_start = (gpointer)(info->dlpi_addr + info->dlpi_phdr[0].p_vaddr);
1503                 mod->address_end = (gpointer)(info->dlpi_addr +
1504                                        info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr);
1505                 mod->perms = g_strdup ("r--p");
1506                 mod->address_offset = 0;
1507                 mod->inode = i;
1508                 mod->filename = g_strdup (info->dlpi_name); 
1509
1510                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: inode=%d, filename=%s, address_start=%p, address_end=%p", __func__,
1511                                    mod->inode, mod->filename, mod->address_start, mod->address_end);
1512
1513                 free(info);
1514
1515                 if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
1516                         ret = g_slist_prepend (ret, mod);
1517                 } else {
1518                         free_procmodule (mod);
1519                 }
1520         }
1521
1522         g_ptr_array_free (dlarray, TRUE);
1523
1524         ret = g_slist_reverse (ret);
1525
1526         return(ret);
1527 }
1528 #elif defined(USE_HAIKU_LOADER)
1529
1530 static GSList *load_modules (void)
1531 {
1532         GSList *ret = NULL;
1533         WapiProcModule *mod;
1534         int32 cookie = 0;
1535         image_info imageInfo;
1536
1537         while (get_next_image_info (B_CURRENT_TEAM, &cookie, &imageInfo) == B_OK) {
1538                 mod = g_new0 (WapiProcModule, 1);
1539                 mod->device = imageInfo.device;
1540                 mod->inode = imageInfo.node;
1541                 mod->filename = g_strdup (imageInfo.name);
1542                 mod->address_start = MIN (imageInfo.text, imageInfo.data);
1543                 mod->address_end = MAX ((uint8_t*)imageInfo.text + imageInfo.text_size,
1544                         (uint8_t*)imageInfo.data + imageInfo.data_size);
1545                 mod->perms = g_strdup ("r--p");
1546                 mod->address_offset = 0;
1547
1548                 if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
1549                         ret = g_slist_prepend (ret, mod);
1550                 } else {
1551                         free_procmodule (mod);
1552                 }
1553         }
1554
1555         ret = g_slist_reverse (ret);
1556
1557         return ret;
1558 }
1559 #else
1560 static GSList *load_modules (FILE *fp)
1561 {
1562         GSList *ret = NULL;
1563         WapiProcModule *mod;
1564         char buf[MAXPATHLEN + 1], *p, *endp;
1565         char *start_start, *end_start, *prot_start, *offset_start;
1566         char *maj_dev_start, *min_dev_start, *inode_start, prot_buf[5];
1567         gpointer address_start, address_end, address_offset;
1568         guint32 maj_dev, min_dev;
1569         guint64 inode;
1570         guint64 device;
1571         
1572         while (fgets (buf, sizeof(buf), fp)) {
1573                 p = buf;
1574                 while (g_ascii_isspace (*p)) ++p;
1575                 start_start = p;
1576                 if (!g_ascii_isxdigit (*start_start)) {
1577                         continue;
1578                 }
1579                 address_start = (gpointer)strtoul (start_start, &endp, 16);
1580                 p = endp;
1581                 if (*p != '-') {
1582                         continue;
1583                 }
1584                 
1585                 ++p;
1586                 end_start = p;
1587                 if (!g_ascii_isxdigit (*end_start)) {
1588                         continue;
1589                 }
1590                 address_end = (gpointer)strtoul (end_start, &endp, 16);
1591                 p = endp;
1592                 if (!g_ascii_isspace (*p)) {
1593                         continue;
1594                 }
1595                 
1596                 while (g_ascii_isspace (*p)) ++p;
1597                 prot_start = p;
1598                 if (*prot_start != 'r' && *prot_start != '-') {
1599                         continue;
1600                 }
1601                 memcpy (prot_buf, prot_start, 4);
1602                 prot_buf[4] = '\0';
1603                 while (!g_ascii_isspace (*p)) ++p;
1604                 
1605                 while (g_ascii_isspace (*p)) ++p;
1606                 offset_start = p;
1607                 if (!g_ascii_isxdigit (*offset_start)) {
1608                         continue;
1609                 }
1610                 address_offset = (gpointer)strtoul (offset_start, &endp, 16);
1611                 p = endp;
1612                 if (!g_ascii_isspace (*p)) {
1613                         continue;
1614                 }
1615                 
1616                 while(g_ascii_isspace (*p)) ++p;
1617                 maj_dev_start = p;
1618                 if (!g_ascii_isxdigit (*maj_dev_start)) {
1619                         continue;
1620                 }
1621                 maj_dev = strtoul (maj_dev_start, &endp, 16);
1622                 p = endp;
1623                 if (*p != ':') {
1624                         continue;
1625                 }
1626                 
1627                 ++p;
1628                 min_dev_start = p;
1629                 if (!g_ascii_isxdigit (*min_dev_start)) {
1630                         continue;
1631                 }
1632                 min_dev = strtoul (min_dev_start, &endp, 16);
1633                 p = endp;
1634                 if (!g_ascii_isspace (*p)) {
1635                         continue;
1636                 }
1637                 
1638                 while (g_ascii_isspace (*p)) ++p;
1639                 inode_start = p;
1640                 if (!g_ascii_isxdigit (*inode_start)) {
1641                         continue;
1642                 }
1643                 inode = (guint64)strtol (inode_start, &endp, 10);
1644                 p = endp;
1645                 if (!g_ascii_isspace (*p)) {
1646                         continue;
1647                 }
1648
1649                 device = makedev ((int)maj_dev, (int)min_dev);
1650                 if ((device == 0) &&
1651                     (inode == 0)) {
1652                         continue;
1653                 }
1654                 
1655                 while(g_ascii_isspace (*p)) ++p;
1656                 /* p now points to the filename */
1657
1658                 mod = g_new0 (WapiProcModule, 1);
1659                 mod->address_start = address_start;
1660                 mod->address_end = address_end;
1661                 mod->perms = g_strdup (prot_buf);
1662                 mod->address_offset = address_offset;
1663                 mod->device = device;
1664                 mod->inode = inode;
1665                 mod->filename = g_strdup (g_strstrip (p));
1666                 
1667                 if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
1668                         ret = g_slist_prepend (ret, mod);
1669                 } else {
1670                         free_procmodule (mod);
1671                 }
1672         }
1673
1674         ret = g_slist_reverse (ret);
1675         
1676         return(ret);
1677 }
1678 #endif
1679
1680 static gboolean match_procname_to_modulename (char *procname, char *modulename)
1681 {
1682         char* lastsep = NULL;
1683         char* lastsep2 = NULL;
1684         char* pname = NULL;
1685         char* mname = NULL;
1686         gboolean result = FALSE;
1687
1688         if (procname == NULL || modulename == NULL)
1689                 return (FALSE);
1690
1691         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename);
1692         pname = mono_path_resolve_symlinks (procname);
1693         mname = mono_path_resolve_symlinks (modulename);
1694
1695         if (!strcmp (pname, mname))
1696                 result = TRUE;
1697
1698         if (!result) {
1699                 lastsep = strrchr (mname, '/');
1700                 if (lastsep)
1701                         if (!strcmp (lastsep+1, pname))
1702                                 result = TRUE;
1703                 if (!result) {
1704                         lastsep2 = strrchr (pname, '/');
1705                         if (lastsep2){
1706                                 if (lastsep) {
1707                                         if (!strcmp (lastsep+1, lastsep2+1))
1708                                                 result = TRUE;
1709                                 } else {
1710                                         if (!strcmp (mname, lastsep2+1))
1711                                                 result = TRUE;
1712                                 }
1713                         }
1714                 }
1715         }
1716
1717         g_free (pname);
1718         g_free (mname);
1719
1720         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: result is %d", __func__, result);
1721         return result;
1722 }
1723
1724 #if !(defined(USE_OSX_LOADER) || defined(USE_BSD_LOADER) || defined(USE_HAIKU_LOADER))
1725 static FILE *
1726 open_process_map (int pid, const char *mode)
1727 {
1728         FILE *fp = NULL;
1729         const char *proc_path[] = {
1730                 "/proc/%d/maps",        /* GNU/Linux */
1731                 "/proc/%d/map",         /* FreeBSD */
1732                 NULL
1733         };
1734         int i;
1735         char *filename;
1736
1737         for (i = 0; fp == NULL && proc_path [i]; i++) {
1738                 filename = g_strdup_printf (proc_path[i], pid);
1739                 fp = fopen (filename, mode);
1740                 g_free (filename);
1741         }
1742
1743         return fp;
1744 }
1745 #endif
1746
1747 static char *get_process_name_from_proc (pid_t pid);
1748
1749 gboolean EnumProcessModules (gpointer process, gpointer *modules,
1750                              guint32 size, guint32 *needed)
1751 {
1752         WapiHandle_process *process_handle;
1753 #if !defined(USE_OSX_LOADER) && !defined(USE_BSD_LOADER)
1754         FILE *fp;
1755 #endif
1756         GSList *mods = NULL;
1757         WapiProcModule *module;
1758         guint32 count, avail = size / sizeof(gpointer);
1759         int i;
1760         pid_t pid;
1761         char *proc_name = NULL;
1762         
1763         /* Store modules in an array of pointers (main module as
1764          * modules[0]), using the load address for each module as a
1765          * token.  (Use 'NULL' as an alternative for the main module
1766          * so that the simple implementation can just return one item
1767          * for now.)  Get the info from /proc/<pid>/maps on linux,
1768          * /proc/<pid>/map on FreeBSD, other systems will have to
1769          * implement /dev/kmem reading or whatever other horrid
1770          * technique is needed.
1771          */
1772         if (size < sizeof(gpointer))
1773                 return FALSE;
1774
1775         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
1776                 pid = WAPI_HANDLE_TO_PID (process);
1777                 proc_name = get_process_name_from_proc (pid);
1778         } else {
1779                 process_handle = lookup_process_handle (process);
1780                 if (!process_handle) {
1781                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
1782                 
1783                         return FALSE;
1784                 }
1785                 pid = process_handle->id;
1786                 proc_name = g_strdup (process_handle->proc_name);
1787         }
1788         
1789 #if defined(USE_OSX_LOADER) || defined(USE_BSD_LOADER) || defined(USE_HAIKU_LOADER)
1790         mods = load_modules ();
1791         if (!proc_name) {
1792                 modules[0] = NULL;
1793                 *needed = sizeof(gpointer);
1794                 return TRUE;
1795         }
1796 #else
1797         fp = open_process_map (pid, "r");
1798         if (!fp) {
1799                 /* No /proc/<pid>/maps so just return the main module
1800                  * shortcut for now
1801                  */
1802                 modules[0] = NULL;
1803                 *needed = sizeof(gpointer);
1804                 g_free (proc_name);
1805                 return TRUE;
1806         }
1807         mods = load_modules (fp);
1808         fclose (fp);
1809 #endif
1810         count = g_slist_length (mods);
1811                 
1812         /* count + 1 to leave slot 0 for the main module */
1813         *needed = sizeof(gpointer) * (count + 1);
1814
1815         /*
1816          * Use the NULL shortcut, as the first line in
1817          * /proc/<pid>/maps isn't the executable, and we need
1818          * that first in the returned list. Check the module name 
1819          * to see if it ends with the proc name and substitute 
1820          * the first entry with it.  FIXME if this turns out to 
1821          * be a problem.
1822          */
1823         modules[0] = NULL;
1824         for (i = 0; i < (avail - 1) && i < count; i++) {
1825                 module = (WapiProcModule *)g_slist_nth_data (mods, i);
1826                 if (modules[0] != NULL)
1827                         modules[i] = module->address_start;
1828                 else if (match_procname_to_modulename (proc_name, module->filename))
1829                         modules[0] = module->address_start;
1830                 else
1831                         modules[i + 1] = module->address_start;
1832         }
1833                 
1834         for (i = 0; i < count; i++) {
1835                 free_procmodule ((WapiProcModule *)g_slist_nth_data (mods, i));
1836         }
1837         g_slist_free (mods);
1838         g_free (proc_name);
1839         
1840         return TRUE;
1841 }
1842
1843 static char *
1844 get_process_name_from_proc (pid_t pid)
1845 {
1846 #if defined(USE_BSD_LOADER)
1847         int mib [6];
1848         size_t size;
1849         struct kinfo_proc *pi;
1850 #elif defined(USE_OSX_LOADER)
1851 #if !(!defined (__mono_ppc__) && defined (TARGET_OSX))
1852         size_t size;
1853         struct kinfo_proc *pi;
1854         int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
1855 #endif
1856 #else
1857         FILE *fp;
1858         char *filename = NULL;
1859 #endif
1860         char buf[256];
1861         char *ret = NULL;
1862
1863 #if defined(PLATFORM_SOLARIS)
1864         filename = g_strdup_printf ("/proc/%d/psinfo", pid);
1865         if ((fp = fopen (filename, "r")) != NULL) {
1866                 struct psinfo info;
1867                 int nread;
1868
1869                 nread = fread (&info, sizeof (info), 1, fp);
1870                 if (nread == 1) {
1871                         ret = g_strdup (info.pr_fname);
1872                 }
1873
1874                 fclose (fp);
1875         }
1876         g_free (filename);
1877 #elif defined(USE_OSX_LOADER)
1878 #if !defined (__mono_ppc__) && defined (TARGET_OSX)
1879         /* No proc name on OSX < 10.5 nor ppc nor iOS */
1880         memset (buf, '\0', sizeof(buf));
1881         proc_name (pid, buf, sizeof(buf));
1882
1883         // Fixes proc_name triming values to 15 characters #32539
1884         if (strlen (buf) >= MAXCOMLEN - 1) {
1885                 char path_buf [PROC_PIDPATHINFO_MAXSIZE];
1886                 char *name_buf;
1887                 int path_len;
1888
1889                 memset (path_buf, '\0', sizeof(path_buf));
1890                 path_len = proc_pidpath (pid, path_buf, sizeof(path_buf));
1891
1892                 if (path_len > 0 && path_len < sizeof(path_buf)) {
1893                         name_buf = path_buf + path_len;
1894                         for(;name_buf > path_buf; name_buf--) {
1895                                 if (name_buf [0] == '/') {
1896                                         name_buf++;
1897                                         break;
1898                                 }
1899                         }
1900
1901                         if (memcmp (buf, name_buf, MAXCOMLEN - 1) == 0)
1902                                 ret = g_strdup (name_buf);
1903                 }
1904         }
1905
1906         if (ret == NULL && strlen (buf) > 0)
1907                 ret = g_strdup (buf);
1908 #else
1909         if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0)
1910                 return(ret);
1911
1912         if ((pi = malloc(size)) == NULL)
1913                 return(ret);
1914
1915         if (sysctl (mib, 4, pi, &size, NULL, 0) < 0) {
1916                 if (errno == ENOMEM) {
1917                         free(pi);
1918                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't allocate enough memory for kproc info", __func__);
1919                 }
1920                 return(ret);
1921         }
1922
1923         if (strlen (pi->kp_proc.p_comm) > 0)
1924                 ret = g_strdup (pi->kp_proc.p_comm);
1925
1926         free(pi);
1927 #endif
1928 #elif defined(USE_BSD_LOADER)
1929 #if defined(__FreeBSD__)
1930         mib [0] = CTL_KERN;
1931         mib [1] = KERN_PROC;
1932         mib [2] = KERN_PROC_PID;
1933         mib [3] = pid;
1934         if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0) {
1935                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: sysctl() failed: %d", __func__, errno);
1936                 return(ret);
1937         }
1938
1939         if ((pi = malloc(size)) == NULL)
1940                 return(ret);
1941
1942         if (sysctl (mib, 4, pi, &size, NULL, 0) < 0) {
1943                 if (errno == ENOMEM) {
1944                         free(pi);
1945                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Didn't allocate enough memory for kproc info", __func__);
1946                 }
1947                 return(ret);
1948         }
1949
1950         if (strlen (pi->ki_comm) > 0)
1951                 ret = g_strdup (pi->ki_comm);
1952         free(pi);
1953 #elif defined(__OpenBSD__)
1954         mib [0] = CTL_KERN;
1955         mib [1] = KERN_PROC;
1956         mib [2] = KERN_PROC_PID;
1957         mib [3] = pid;
1958         mib [4] = sizeof(struct kinfo_proc);
1959         mib [5] = 0;
1960
1961 retry:
1962         if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) {
1963                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: sysctl() failed: %d", __func__, errno);
1964                 return(ret);
1965         }
1966
1967         if ((pi = malloc(size)) == NULL)
1968                 return(ret);
1969
1970         mib[5] = (int)(size / sizeof(struct kinfo_proc));
1971
1972         if ((sysctl (mib, 6, pi, &size, NULL, 0) < 0) ||
1973                 (size != sizeof (struct kinfo_proc))) {
1974                 if (errno == ENOMEM) {
1975                         free(pi);
1976                         goto retry;
1977                 }
1978                 return(ret);
1979         }
1980
1981         if (strlen (pi->p_comm) > 0)
1982                 ret = g_strdup (pi->p_comm);
1983
1984         free(pi);
1985 #endif
1986 #elif defined(USE_HAIKU_LOADER)
1987         image_info imageInfo;
1988         int32 cookie = 0;
1989
1990         if (get_next_image_info ((team_id)pid, &cookie, &imageInfo) == B_OK) {
1991                 ret = g_strdup (imageInfo.name);
1992         }
1993 #else
1994         memset (buf, '\0', sizeof(buf));
1995         filename = g_strdup_printf ("/proc/%d/exe", pid);
1996         if (readlink (filename, buf, 255) > 0) {
1997                 ret = g_strdup (buf);
1998         }
1999         g_free (filename);
2000
2001         if (ret != NULL) {
2002                 return(ret);
2003         }
2004
2005         filename = g_strdup_printf ("/proc/%d/cmdline", pid);
2006         if ((fp = fopen (filename, "r")) != NULL) {
2007                 if (fgets (buf, 256, fp) != NULL) {
2008                         ret = g_strdup (buf);
2009                 }
2010                 
2011                 fclose (fp);
2012         }
2013         g_free (filename);
2014
2015         if (ret != NULL) {
2016                 return(ret);
2017         }
2018         
2019         filename = g_strdup_printf ("/proc/%d/stat", pid);
2020         if ((fp = fopen (filename, "r")) != NULL) {
2021                 if (fgets (buf, 256, fp) != NULL) {
2022                         char *start, *end;
2023                         
2024                         start = strchr (buf, '(');
2025                         if (start != NULL) {
2026                                 end = strchr (start + 1, ')');
2027                                 
2028                                 if (end != NULL) {
2029                                         ret = g_strndup (start + 1,
2030                                                          end - start - 1);
2031                                 }
2032                         }
2033                 }
2034                 
2035                 fclose (fp);
2036         }
2037         g_free (filename);
2038 #endif
2039
2040         return ret;
2041 }
2042
2043 /*
2044  * wapi_process_get_path:
2045  *
2046  *   Return the full path of the executable of the process PID, or NULL if it cannot be determined.
2047  * Returns malloc-ed memory.
2048  */
2049 char*
2050 wapi_process_get_path (pid_t pid)
2051 {
2052 #if defined(PLATFORM_MACOSX) && !defined(__mono_ppc__) && defined(TARGET_OSX)
2053         char buf [PROC_PIDPATHINFO_MAXSIZE];
2054         int res;
2055
2056         res = proc_pidpath (pid, buf, sizeof (buf));
2057         if (res <= 0)
2058                 return NULL;
2059         if (buf [0] == '\0')
2060                 return NULL;
2061         return g_strdup (buf);
2062 #else
2063         return get_process_name_from_proc (pid);
2064 #endif
2065 }
2066
2067 /*
2068  * wapi_process_set_cli_launcher:
2069  *
2070  *   Set the full path of the runtime executable used to launch managed exe's.
2071  */
2072 void
2073 wapi_process_set_cli_launcher (char *path)
2074 {
2075         g_free (cli_launcher);
2076         cli_launcher = path ? g_strdup (path) : NULL;
2077 }
2078
2079 static guint32
2080 get_module_name (gpointer process, gpointer module,
2081                                  gunichar2 *basename, guint32 size,
2082                                  gboolean base)
2083 {
2084         WapiHandle_process *process_handle;
2085         pid_t pid;
2086         gunichar2 *procname;
2087         char *procname_ext = NULL;
2088         glong len;
2089         gsize bytes;
2090 #if !defined(USE_OSX_LOADER) && !defined(USE_BSD_LOADER)
2091         FILE *fp;
2092 #endif
2093         GSList *mods = NULL;
2094         WapiProcModule *found_module;
2095         guint32 count;
2096         int i;
2097         char *proc_name = NULL;
2098         
2099         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module base name, process handle %p module %p",
2100                    __func__, process, module);
2101
2102         size = size * sizeof (gunichar2); /* adjust for unicode characters */
2103
2104         if (basename == NULL || size == 0)
2105                 return 0;
2106         
2107         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
2108                 /* This is a pseudo handle */
2109                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
2110                 proc_name = get_process_name_from_proc (pid);
2111         } else {
2112                 process_handle = lookup_process_handle (process);
2113                 if (!process_handle) {
2114                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__,
2115                                    process);
2116                         
2117                         return 0;
2118                 }
2119                 pid = process_handle->id;
2120                 proc_name = g_strdup (process_handle->proc_name);
2121         }
2122
2123         /* Look up the address in /proc/<pid>/maps */
2124 #if defined(USE_OSX_LOADER) || defined(USE_BSD_LOADER) || defined(USE_HAIKU_LOADER)
2125         mods = load_modules ();
2126 #else
2127         fp = open_process_map (pid, "r");
2128         if (fp == NULL) {
2129                 if (errno == EACCES && module == NULL && base == TRUE) {
2130                         procname_ext = get_process_name_from_proc (pid);
2131                 } else {
2132                         /* No /proc/<pid>/maps, so just return failure
2133                          * for now
2134                          */
2135                         g_free (proc_name);
2136                         return 0;
2137                 }
2138         } else {
2139                 mods = load_modules (fp);
2140                 fclose (fp);
2141         }
2142 #endif
2143         count = g_slist_length (mods);
2144
2145         /* If module != NULL compare the address.
2146          * If module == NULL we are looking for the main module.
2147          * The best we can do for now check it the module name end with the process name.
2148          */
2149         for (i = 0; i < count; i++) {
2150                 found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
2151                 if (procname_ext == NULL &&
2152                         ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
2153                          (module != NULL && found_module->address_start == module))) {
2154                         if (base)
2155                                 procname_ext = g_path_get_basename (found_module->filename);
2156                         else
2157                                 procname_ext = g_strdup (found_module->filename);
2158                 }
2159
2160                 free_procmodule (found_module);
2161         }
2162
2163         if (procname_ext == NULL) {
2164                 /* If it's *still* null, we might have hit the
2165                  * case where reading /proc/$pid/maps gives an
2166                  * empty file for this user.
2167                  */
2168                 procname_ext = get_process_name_from_proc (pid);
2169         }
2170
2171         g_slist_free (mods);
2172         g_free (proc_name);
2173
2174         if (procname_ext) {
2175                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Process name is [%s]", __func__,
2176                            procname_ext);
2177
2178                 procname = mono_unicode_from_external (procname_ext, &bytes);
2179                 if (procname == NULL) {
2180                         /* bugger */
2181                         g_free (procname_ext);
2182                         return 0;
2183                 }
2184                 
2185                 len = (bytes / 2);
2186                 
2187                 /* Add the terminator */
2188                 bytes += 2;
2189                 
2190                 if (size < bytes) {
2191                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
2192
2193                         memcpy (basename, procname, size);
2194                 } else {
2195                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
2196                                    __func__, size, bytes);
2197
2198                         memcpy (basename, procname, bytes);
2199                 }
2200                 
2201                 g_free (procname);
2202                 g_free (procname_ext);
2203                 
2204                 return len;
2205         }
2206         
2207         return 0;
2208 }
2209
2210 static guint32
2211 get_module_filename (gpointer process, gpointer module,
2212                                          gunichar2 *basename, guint32 size)
2213 {
2214         int pid, len;
2215         gsize bytes;
2216         char *path;
2217         gunichar2 *proc_path;
2218         
2219         size *= sizeof (gunichar2); /* adjust for unicode characters */
2220
2221         if (basename == NULL || size == 0)
2222                 return 0;
2223
2224         pid = GetProcessId (process);
2225
2226         path = wapi_process_get_path (pid);
2227         if (path == NULL)
2228                 return 0;
2229
2230         proc_path = mono_unicode_from_external (path, &bytes);
2231         g_free (path);
2232
2233         if (proc_path == NULL)
2234                 return 0;
2235
2236         len = (bytes / 2);
2237         
2238         /* Add the terminator */
2239         bytes += 2;
2240
2241         if (size < bytes) {
2242                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
2243
2244                 memcpy (basename, proc_path, size);
2245         } else {
2246                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Size %d larger than needed (%ld)",
2247                            __func__, size, bytes);
2248
2249                 memcpy (basename, proc_path, bytes);
2250         }
2251
2252         g_free (proc_path);
2253
2254         return len;
2255 }
2256
2257 guint32
2258 GetModuleBaseName (gpointer process, gpointer module,
2259                                    gunichar2 *basename, guint32 size)
2260 {
2261         return get_module_name (process, module, basename, size, TRUE);
2262 }
2263
2264 guint32
2265 GetModuleFileNameEx (gpointer process, gpointer module,
2266                                          gunichar2 *filename, guint32 size)
2267 {
2268         return get_module_filename (process, module, filename, size);
2269 }
2270
2271 gboolean
2272 GetModuleInformation (gpointer process, gpointer module,
2273                                           WapiModuleInfo *modinfo, guint32 size)
2274 {
2275         WapiHandle_process *process_handle;
2276         pid_t pid;
2277 #if !defined(USE_OSX_LOADER) && !defined(USE_BSD_LOADER)
2278         FILE *fp;
2279 #endif
2280         GSList *mods = NULL;
2281         WapiProcModule *found_module;
2282         guint32 count;
2283         int i;
2284         gboolean ret = FALSE;
2285         char *proc_name = NULL;
2286         
2287         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Getting module info, process handle %p module %p",
2288                    __func__, process, module);
2289
2290         if (modinfo == NULL || size < sizeof (WapiModuleInfo))
2291                 return FALSE;
2292         
2293         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
2294                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
2295                 proc_name = get_process_name_from_proc (pid);
2296         } else {
2297                 process_handle = lookup_process_handle (process);
2298                 if (!process_handle) {
2299                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__,
2300                                    process);
2301                         
2302                         return FALSE;
2303                 }
2304                 pid = process_handle->id;
2305                 proc_name = g_strdup (process_handle->proc_name);
2306         }
2307
2308 #if defined(USE_OSX_LOADER) || defined(USE_BSD_LOADER) || defined(USE_HAIKU_LOADER)
2309         mods = load_modules ();
2310 #else
2311         /* Look up the address in /proc/<pid>/maps */
2312         if ((fp = open_process_map (pid, "r")) == NULL) {
2313                 /* No /proc/<pid>/maps, so just return failure
2314                  * for now
2315                  */
2316                 g_free (proc_name);
2317                 return FALSE;
2318         }
2319         mods = load_modules (fp);
2320         fclose (fp);
2321 #endif
2322         count = g_slist_length (mods);
2323
2324         /* If module != NULL compare the address.
2325          * If module == NULL we are looking for the main module.
2326          * The best we can do for now check it the module name end with the process name.
2327          */
2328         for (i = 0; i < count; i++) {
2329                         found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
2330                         if (ret == FALSE &&
2331                                 ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
2332                                  (module != NULL && found_module->address_start == module))) {
2333                                 modinfo->lpBaseOfDll = found_module->address_start;
2334                                 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
2335                                 modinfo->EntryPoint = found_module->address_offset;
2336                                 ret = TRUE;
2337                         }
2338
2339                         free_procmodule (found_module);
2340         }
2341
2342         g_slist_free (mods);
2343         g_free (proc_name);
2344
2345         return ret;
2346 }
2347
2348 gboolean
2349 GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
2350 {
2351         WapiHandle_process *process_handle;
2352         
2353         if (min == NULL || max == NULL)
2354                 /* Not sure if w32 allows NULLs here or not */
2355                 return FALSE;
2356         
2357         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process))
2358                 /* This is a pseudo handle, so just fail for now */
2359                 return FALSE;
2360         
2361         process_handle = lookup_process_handle (process);
2362         if (!process_handle) {
2363                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
2364                 
2365                 return FALSE;
2366         }
2367
2368         *min = process_handle->min_working_set;
2369         *max = process_handle->max_working_set;
2370         
2371         return TRUE;
2372 }
2373
2374 gboolean
2375 SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
2376 {
2377         WapiHandle_process *process_handle;
2378
2379         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process))
2380                 /* This is a pseudo handle, so just fail for now
2381                  */
2382                 return FALSE;
2383
2384         process_handle = lookup_process_handle (process);
2385         if (!process_handle) {
2386                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
2387                 
2388                 return FALSE;
2389         }
2390
2391         process_handle->min_working_set = min;
2392         process_handle->max_working_set = max;
2393         
2394         return TRUE;
2395 }
2396
2397
2398 gboolean
2399 TerminateProcess (gpointer process, gint32 exitCode)
2400 {
2401 #if defined(HAVE_KILL)
2402         WapiHandle_process *process_handle;
2403         int signo;
2404         int ret;
2405         pid_t pid;
2406         
2407         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
2408                 /* This is a pseudo handle */
2409                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
2410         } else {
2411                 process_handle = lookup_process_handle (process);
2412                 if (!process_handle) {
2413                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Can't find process %p", __func__, process);
2414                         SetLastError (ERROR_INVALID_HANDLE);
2415                         return FALSE;
2416                 }
2417                 pid = process_handle->id;
2418         }
2419
2420         signo = (exitCode == -1) ? SIGKILL : SIGTERM;
2421         ret = kill (pid, signo);
2422         if (ret == -1) {
2423                 switch (errno) {
2424                 case EINVAL:
2425                         SetLastError (ERROR_INVALID_PARAMETER);
2426                         break;
2427                 case EPERM:
2428                         SetLastError (ERROR_ACCESS_DENIED);
2429                         break;
2430                 case ESRCH:
2431                         SetLastError (ERROR_PROC_NOT_FOUND);
2432                         break;
2433                 default:
2434                         SetLastError (ERROR_GEN_FAILURE);
2435                 }
2436         }
2437         
2438         return (ret == 0);
2439 #else
2440         g_error ("kill() is not supported by this platform");
2441         return FALSE;
2442 #endif
2443 }
2444
2445 guint32
2446 GetPriorityClass (gpointer process)
2447 {
2448 #ifdef HAVE_GETPRIORITY
2449         WapiHandle_process *process_handle;
2450         int ret;
2451         pid_t pid;
2452         
2453         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
2454                 /* This is a pseudo handle */
2455                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
2456         } else {
2457                 process_handle = lookup_process_handle (process);
2458                 if (!process_handle) {
2459                         SetLastError (ERROR_INVALID_HANDLE);
2460                         return FALSE;
2461                 }
2462                 pid = process_handle->id;
2463         }
2464
2465         errno = 0;
2466         ret = getpriority (PRIO_PROCESS, pid);
2467         if (ret == -1 && errno != 0) {
2468                 switch (errno) {
2469                 case EPERM:
2470                 case EACCES:
2471                         SetLastError (ERROR_ACCESS_DENIED);
2472                         break;
2473                 case ESRCH:
2474                         SetLastError (ERROR_PROC_NOT_FOUND);
2475                         break;
2476                 default:
2477                         SetLastError (ERROR_GEN_FAILURE);
2478                 }
2479                 return FALSE;
2480         }
2481
2482         if (ret == 0)
2483                 return NORMAL_PRIORITY_CLASS;
2484         else if (ret < -15)
2485                 return REALTIME_PRIORITY_CLASS;
2486         else if (ret < -10)
2487                 return HIGH_PRIORITY_CLASS;
2488         else if (ret < 0)
2489                 return ABOVE_NORMAL_PRIORITY_CLASS;
2490         else if (ret > 10)
2491                 return IDLE_PRIORITY_CLASS;
2492         else if (ret > 0)
2493                 return BELOW_NORMAL_PRIORITY_CLASS;
2494
2495         return NORMAL_PRIORITY_CLASS;
2496 #else
2497         SetLastError (ERROR_NOT_SUPPORTED);
2498         return 0;
2499 #endif
2500 }
2501
2502 gboolean
2503 SetPriorityClass (gpointer process, guint32  priority_class)
2504 {
2505 #ifdef HAVE_SETPRIORITY
2506         WapiHandle_process *process_handle;
2507         int ret;
2508         int prio;
2509         pid_t pid;
2510         
2511         if (WAPI_IS_PSEUDO_PROCESS_HANDLE (process)) {
2512                 /* This is a pseudo handle */
2513                 pid = (pid_t)WAPI_HANDLE_TO_PID (process);
2514         } else {
2515                 process_handle = lookup_process_handle (process);
2516                 if (!process_handle) {
2517                         SetLastError (ERROR_INVALID_HANDLE);
2518                         return FALSE;
2519                 }
2520                 pid = process_handle->id;
2521         }
2522
2523         switch (priority_class) {
2524         case IDLE_PRIORITY_CLASS:
2525                 prio = 19;
2526                 break;
2527         case BELOW_NORMAL_PRIORITY_CLASS:
2528                 prio = 10;
2529                 break;
2530         case NORMAL_PRIORITY_CLASS:
2531                 prio = 0;
2532                 break;
2533         case ABOVE_NORMAL_PRIORITY_CLASS:
2534                 prio = -5;
2535                 break;
2536         case HIGH_PRIORITY_CLASS:
2537                 prio = -11;
2538                 break;
2539         case REALTIME_PRIORITY_CLASS:
2540                 prio = -20;
2541                 break;
2542         default:
2543                 SetLastError (ERROR_INVALID_PARAMETER);
2544                 return FALSE;
2545         }
2546
2547         ret = setpriority (PRIO_PROCESS, pid, prio);
2548         if (ret == -1) {
2549                 switch (errno) {
2550                 case EPERM:
2551                 case EACCES:
2552                         SetLastError (ERROR_ACCESS_DENIED);
2553                         break;
2554                 case ESRCH:
2555                         SetLastError (ERROR_PROC_NOT_FOUND);
2556                         break;
2557                 default:
2558                         SetLastError (ERROR_GEN_FAILURE);
2559                 }
2560         }
2561
2562         return ret == 0;
2563 #else
2564         SetLastError (ERROR_NOT_SUPPORTED);
2565         return FALSE;
2566 #endif
2567 }
2568
2569 static void
2570 mono_processes_cleanup (void)
2571 {
2572         struct MonoProcess *mp;
2573         struct MonoProcess *prev = NULL;
2574         GSList *finished = NULL;
2575         GSList *l;
2576         gpointer unref_handle;
2577
2578         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
2579
2580         /* Ensure we're not in here in multiple threads at once, nor recursive. */
2581         if (InterlockedCompareExchange (&mono_processes_cleaning_up, 1, 0) != 0)
2582                 return;
2583
2584         for (mp = mono_processes; mp; mp = mp->next) {
2585                 if (mp->pid == 0 && mp->handle) {
2586                         /* This process has exited and we need to remove the artifical ref
2587                          * on the handle */
2588                         mono_os_mutex_lock (&mono_processes_mutex);
2589                         unref_handle = mp->handle;
2590                         mp->handle = NULL;
2591                         mono_os_mutex_unlock (&mono_processes_mutex);
2592                         if (unref_handle)
2593                                 mono_w32handle_unref (unref_handle);
2594                 }
2595         }
2596
2597         /*
2598          * Remove processes which exited from the mono_processes list.
2599          * We need to synchronize with the sigchld handler here, which runs
2600          * asynchronously. The handler requires that the mono_processes list
2601          * remain valid.
2602          */
2603         mono_os_mutex_lock (&mono_processes_mutex);
2604
2605         mp = mono_processes;
2606         while (mp) {
2607                 if (mp->handle_count == 0 && mp->freeable) {
2608                         /*
2609                          * Unlink the entry.
2610                          * This code can run parallel with the sigchld handler, but the
2611                          * modifications it makes are safe.
2612                          */
2613                         if (mp == mono_processes)
2614                                 mono_processes = mp->next;
2615                         else
2616                                 prev->next = mp->next;
2617                         finished = g_slist_prepend (finished, mp);
2618
2619                         mp = mp->next;
2620                 } else {
2621                         prev = mp;
2622                         mp = mp->next;
2623                 }
2624         }
2625
2626         mono_memory_barrier ();
2627
2628         for (l = finished; l; l = l->next) {
2629                 /*
2630                  * All the entries in the finished list are unlinked from mono_processes, and
2631                  * they have the 'finished' flag set, which means the sigchld handler is done
2632                  * accessing them.
2633                  */
2634                 mp = (MonoProcess *)l->data;
2635                 mono_os_sem_destroy (&mp->exit_sem);
2636                 g_free (mp);
2637         }
2638         g_slist_free (finished);
2639
2640         mono_os_mutex_unlock (&mono_processes_mutex);
2641
2642         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s done", __func__);
2643
2644         InterlockedDecrement (&mono_processes_cleaning_up);
2645 }
2646
2647 static void
2648 process_close (gpointer handle, gpointer data)
2649 {
2650         WapiHandle_process *process_handle;
2651
2652         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s", __func__);
2653
2654         process_handle = (WapiHandle_process *) data;
2655         g_free (process_handle->proc_name);
2656         process_handle->proc_name = NULL;
2657         if (process_handle->mono_process)
2658                 InterlockedDecrement (&process_handle->mono_process->handle_count);
2659         mono_processes_cleanup ();
2660 }
2661
2662 static void process_details (gpointer data)
2663 {
2664         WapiHandle_process *process_handle = (WapiHandle_process *) data;
2665         g_print ("id: %d, exited: %s, exitstatus: %d",
2666                 process_handle->id, process_handle->exited ? "true" : "false", process_handle->exitstatus);
2667 }
2668
2669 static const gchar* process_typename (void)
2670 {
2671         return "Process";
2672 }
2673
2674 static gsize process_typesize (void)
2675 {
2676         return sizeof (WapiHandle_process);
2677 }
2678
2679 #if HAVE_SIGACTION
2680 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
2681 {
2682         int status;
2683         int pid;
2684         struct MonoProcess *p;
2685
2686         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "SIG CHILD handler for pid: %i\n", info->si_pid);
2687
2688         do {
2689                 do {
2690                         pid = waitpid (-1, &status, WNOHANG);
2691                 } while (pid == -1 && errno == EINTR);
2692
2693                 if (pid <= 0)
2694                         break;
2695
2696                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "child ended: %i", pid);
2697
2698                 /*
2699                  * This can run concurrently with the code in the rest of this module.
2700                  */
2701                 for (p = mono_processes; p; p = p->next) {
2702                         if (p->pid == pid) {
2703                                 break;
2704                         }
2705                 }
2706                 if (p) {
2707                         p->pid = 0; /* this pid doesn't exist anymore, clear it */
2708                         p->status = status;
2709                         mono_os_sem_post (&p->exit_sem);
2710                         mono_memory_barrier ();
2711                         /* Mark this as freeable, the pointer becomes invalid afterwards */
2712                         p->freeable = TRUE;
2713                 }
2714         } while (1);
2715
2716         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "SIG CHILD handler: done looping.");
2717 }
2718
2719 #endif
2720
2721 static void
2722 process_add_sigchld_handler (void)
2723 {
2724 #if HAVE_SIGACTION
2725         struct sigaction sa;
2726
2727         sa.sa_sigaction = mono_sigchld_signal_handler;
2728         sigemptyset (&sa.sa_mask);
2729         sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
2730         g_assert (sigaction (SIGCHLD, &sa, &previous_chld_sa) != -1);
2731         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "Added SIGCHLD handler");
2732 #endif
2733 }
2734
2735 static guint32
2736 process_wait (gpointer handle, guint32 timeout, gboolean alertable)
2737 {
2738         WapiHandle_process *process_handle;
2739         pid_t pid G_GNUC_UNUSED, ret;
2740         int status;
2741         gint64 start, now;
2742         struct MonoProcess *mp;
2743
2744         /* FIXME: We can now easily wait on processes that aren't our own children,
2745          * but WaitFor*Object won't call us for pseudo handles. */
2746         g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
2747
2748         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u)", __func__, handle, timeout);
2749
2750         process_handle = lookup_process_handle (handle);
2751         if (!process_handle) {
2752                 g_warning ("%s: error looking up process handle %p", __func__, handle);
2753                 return WAIT_FAILED;
2754         }
2755
2756         if (process_handle->exited) {
2757                 /* We've already done this one */
2758                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Process already exited", __func__, handle, timeout);
2759                 return WAIT_OBJECT_0;
2760         }
2761
2762         pid = process_handle->id;
2763
2764         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): PID: %d", __func__, handle, timeout, pid);
2765
2766         /* We don't need to lock mono_processes here, the entry
2767          * has a handle_count > 0 which means it will not be freed. */
2768         mp = process_handle->mono_process;
2769         if (!mp) {
2770                 pid_t res;
2771
2772                 if (pid == mono_process_current_pid ()) {
2773                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on current process", __func__, handle, timeout);
2774                         return WAIT_TIMEOUT;
2775                 }
2776
2777                 /* This path is used when calling Process.HasExited, so
2778                  * it is only used to poll the state of the process, not
2779                  * to actually wait on it to exit */
2780                 g_assert (timeout == 0);
2781
2782                 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on non-child process", __func__, handle, timeout);
2783
2784                 res = waitpid (pid, &status, WNOHANG);
2785                 if (res == 0) {
2786                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process WAIT_TIMEOUT", __func__, handle, timeout);
2787                         return WAIT_TIMEOUT;
2788                 }
2789                 if (res > 0) {
2790                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): non-child process waited successfully", __func__, handle, timeout);
2791                         return WAIT_OBJECT_0;
2792                 }
2793
2794                 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);
2795                 return WAIT_FAILED;
2796         }
2797
2798         start = mono_msec_ticks ();
2799         now = start;
2800
2801         while (1) {
2802                 if (timeout != INFINITE) {
2803                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore for %li ms...", 
2804                                    __func__, handle, timeout, (timeout - (now - start)));
2805                         ret = mono_os_sem_timedwait (&mp->exit_sem, (timeout - (now - start)), alertable ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
2806                 } else {
2807                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): waiting on semaphore forever...", 
2808                                    __func__, handle, timeout);
2809                         ret = mono_os_sem_wait (&mp->exit_sem, alertable ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
2810                 }
2811
2812                 if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
2813                         /* Success, process has exited */
2814                         mono_os_sem_post (&mp->exit_sem);
2815                         break;
2816                 }
2817
2818                 if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
2819                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): WAIT_TIMEOUT (timeout = 0)", __func__, handle, timeout);
2820                         return WAIT_TIMEOUT;
2821                 }
2822
2823                 now = mono_msec_ticks ();
2824                 if (now - start >= timeout) {
2825                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): WAIT_TIMEOUT", __func__, handle, timeout);
2826                         return WAIT_TIMEOUT;
2827                 }
2828                 
2829                 if (alertable && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
2830                         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): WAIT_IO_COMPLETION", __func__, handle, timeout);
2831                         return WAIT_IO_COMPLETION;
2832                 }
2833         }
2834
2835         /* Process must have exited */
2836         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Waited successfully", __func__, handle, timeout);
2837
2838         status = mp ? mp->status : 0;
2839         if (WIFSIGNALED (status))
2840                 process_handle->exitstatus = 128 + WTERMSIG (status);
2841         else
2842                 process_handle->exitstatus = WEXITSTATUS (status);
2843         _wapi_time_t_to_filetime (time (NULL), &process_handle->exit_time);
2844
2845         process_handle->exited = TRUE;
2846
2847         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s (%p, %u): Setting pid %d signalled, exit status %d",
2848                    __func__, handle, timeout, process_handle->id, process_handle->exitstatus);
2849
2850         mono_w32handle_set_signal_state (handle, TRUE, TRUE);
2851
2852         return WAIT_OBJECT_0;
2853 }
2854
2855 void
2856 wapi_processes_cleanup (void)
2857 {
2858         g_free (cli_launcher);
2859 }