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