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