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