2008-08-19 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / io-layer / processes.c
index 9c70516a9b40f534ba99771ba8f5f727ec20b3ff..a163857e0a31ed03cc4e35d4348316f508ced52f 100644 (file)
@@ -9,15 +9,31 @@
 
 #include <config.h>
 #include <glib.h>
+#include <stdio.h>
 #include <string.h>
 #include <pthread.h>
 #include <sched.h>
 #include <sys/time.h>
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <signal.h>
 #include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+/* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
+#ifdef __APPLE__
+#include <sys/resource.h>
+#endif
 
 #include <mono/io-layer/wapi.h>
 #include <mono/io-layer/wapi-private.h>
 #include <mono/io-layer/timefuncs-private.h>
 
 /* The process' environment strings */
+#ifdef __APPLE__
+/* Apple defines this in crt_externs.h but doesn't provide that header for 
+ * arm-apple-darwin9.  We'll manually define the symbol on Apple as it does
+ * in fact exist on all implementations (so far) 
+ */
+gchar ***_NSGetEnviron();
+#define environ (*_NSGetEnviron())
+#else
 extern char **environ;
+#endif
 
 #undef DEBUG
 
@@ -63,6 +88,8 @@ static gboolean process_set_termination_details (gpointer handle, int status)
        gboolean ok;
        int thr_ret;
        
+       g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+       
        ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
                                  (gpointer *)&process_handle);
        if (ok == FALSE) {
@@ -80,9 +107,15 @@ static gboolean process_set_termination_details (gpointer handle, int status)
                process_handle->exitstatus = WEXITSTATUS(status);
        }
        _wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time);
+
+       /* Don't set process_handle->waited here, it needs to only
+        * happen in the parent when wait() has been called.
+        */
        
 #ifdef DEBUG
-       g_message ("%s: Setting handle %p signalled", __func__, handle);
+       g_message ("%s: Setting handle %p pid %d signalled, exit status %d",
+                  __func__, handle, process_handle->id,
+                  process_handle->exitstatus);
 #endif
 
        _wapi_shared_handle_set_signal_state (handle, TRUE);
@@ -108,10 +141,7 @@ static gboolean waitfor_pid (gpointer test, gpointer user_data)
        int status;
        pid_t ret;
        
-       if (_wapi_handle_issignalled (test)) {
-               /* We've already done this one */
-               return (FALSE);
-       }
+       g_assert ((GPOINTER_TO_UINT (test) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
        
        ok = _wapi_lookup_handle (test, WAPI_HANDLE_PROCESS,
                                  (gpointer *)&process);
@@ -119,6 +149,11 @@ static gboolean waitfor_pid (gpointer test, gpointer user_data)
                /* The handle must have been too old and was reaped */
                return (FALSE);
        }
+
+       if (process->waited) {
+               /* We've already done this one */
+               return(FALSE);
+       }
        
        do {
                ret = waitpid (process->id, &status, WNOHANG);
@@ -138,6 +173,8 @@ static gboolean waitfor_pid (gpointer test, gpointer user_data)
        g_message ("%s: Process %d finished", __func__, ret);
 #endif
 
+       process->waited = TRUE;
+       
        *(int *)user_data = status;
        
        return (TRUE);
@@ -182,11 +219,21 @@ static guint32 process_wait (gpointer handle, guint32 timeout)
        pid_t pid, ret;
        int status;
        
+       g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+       
 #ifdef DEBUG
        g_message ("%s: Waiting for process %p", __func__, handle);
 #endif
+
+       ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
+                                 (gpointer *)&process_handle);
+       if (ok == FALSE) {
+               g_warning ("%s: error looking up process handle %p", __func__,
+                          handle);
+               return(WAIT_FAILED);
+       }
        
-       if (_wapi_handle_issignalled (handle)) {
+       if (process_handle->waited) {
                /* We've already done this one */
 #ifdef DEBUG
                g_message ("%s: Process %p already signalled", __func__,
@@ -195,14 +242,6 @@ static guint32 process_wait (gpointer handle, guint32 timeout)
 
                return (WAIT_OBJECT_0);
        }
-
-       ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
-                                 (gpointer *)&process_handle);
-       if (ok == FALSE) {
-               g_warning ("%s: error looking up process handle %p", __func__,
-                          handle);
-               return(WAIT_FAILED);
-       }
        
        pid = process_handle->id;
        
@@ -211,9 +250,15 @@ static guint32 process_wait (gpointer handle, guint32 timeout)
 #endif
 
        if (timeout == INFINITE) {
-               while ((ret = waitpid (pid, &status, 0)) != pid) {
-                       if (ret == (pid_t)-1 && errno != EINTR) {
-                               return(WAIT_FAILED);
+               if (pid == _wapi_getpid ()) {
+                       do {
+                               Sleep (10000);
+                       } while(1);
+               } else {
+                       while ((ret = waitpid (pid, &status, 0)) != pid) {
+                               if (ret == (pid_t)-1 && errno != EINTR) {
+                                       return(WAIT_FAILED);
+                               }
                        }
                }
        } else if (timeout == 0) {
@@ -224,18 +269,47 @@ static guint32 process_wait (gpointer handle, guint32 timeout)
                }
        } else {
                /* Poll in a loop */
-               do {
-                       ret = waitpid (pid, &status, WNOHANG);
-                       if (ret == pid) {
-                               break;
-                       } else if (ret == (pid_t)-1 && errno != EINTR) {
-                               return(WAIT_FAILED);
-                       }
+               if (pid == _wapi_getpid ()) {
+                       Sleep (timeout);
+                       return(WAIT_TIMEOUT);
+               } else {
+                       do {
+                               ret = waitpid (pid, &status, WNOHANG);
+#ifdef DEBUG
+                               g_message ("%s: waitpid returns: %d, timeout is %d", __func__, ret, timeout);
+#endif
+                               
+                               if (ret == pid) {
+                                       break;
+                               } else if (ret == (pid_t)-1 &&
+                                          errno != EINTR) {
+#ifdef DEBUG
+                                       g_message ("%s: waitpid failure: %s",
+                                                  __func__,
+                                                  g_strerror (errno));
+#endif
 
-                       _wapi_handle_spin (100);
-                       timeout -= 100;
-               } while (timeout > 0);
+                                       if (errno == ECHILD &&
+                                           process_handle->waited) {
+                                               /* The background
+                                                * process reaper must
+                                                * have got this one
+                                                */
+#ifdef DEBUG
+                                               g_message ("%s: Process %p already reaped", __func__, handle);
+#endif
 
+                                               return(WAIT_OBJECT_0);
+                                       } else {
+                                               return(WAIT_FAILED);
+                                       }
+                               }
+
+                               _wapi_handle_spin (100);
+                               timeout -= 100;
+                       } while (timeout > 0);
+               }
+               
                if (timeout <= 0) {
                        return(WAIT_TIMEOUT);
                }
@@ -251,7 +325,8 @@ static guint32 process_wait (gpointer handle, guint32 timeout)
                SetLastError (ERROR_OUTOFMEMORY);
                return (WAIT_FAILED);
        }
-
+       process_handle->waited = TRUE;
+       
        return(WAIT_OBJECT_0);
 }
 
@@ -268,16 +343,68 @@ static void process_set_defaults (struct _WapiHandle_process *process_handle)
        process_handle->min_working_set = 204800;
        process_handle->max_working_set = 1413120;
 
+       process_handle->waited = FALSE;
+       
        _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
 }
 
+static int
+len16 (const gunichar2 *str)
+{
+       int len = 0;
+       
+       while (*str++ != 0)
+               len++;
+
+       return len;
+}
+
+static gunichar2 *
+utf16_concat (const gunichar2 *first, ...)
+{
+       va_list args;
+       int total = 0, i;
+       const gunichar2 *s;
+       gunichar2 *ret;
+
+       va_start (args, first);
+       total += len16 (first);
+        for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *)){
+               total += len16 (s);
+        }
+       va_end (args);
+
+       ret = g_new (gunichar2, total + 1);
+       if (ret == NULL)
+               return NULL;
+
+       ret [total] = 0;
+       i = 0;
+       for (s = first; *s != 0; s++)
+               ret [i++] = *s;
+       va_start (args, first);
+       for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
+               const gunichar2 *p;
+               
+               for (p = s; *p != 0; p++)
+                       ret [i++] = *p;
+       }
+       va_end (args);
+       
+       return ret;
+}
+
+static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
+static const gunichar2 *utf16_space = utf16_space_bytes; 
+static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
+static const gunichar2 *utf16_quote = utf16_quote_bytes;
+
 /* Implemented as just a wrapper around CreateProcess () */
 gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
 {
        gboolean ret;
        WapiProcessInformation process_info;
        gunichar2 *args;
-       gchar *u8file, *u8params, *u8args;
        
        if (sei == NULL) {
                /* w2k just segfaults here, but we can do better than
@@ -297,49 +424,73 @@ gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
         * into and back out of utf8 is because there is no
         * g_strdup_printf () equivalent for gunichar2 :-(
         */
-       u8file = g_utf16_to_utf8 (sei->lpFile, -1, NULL, NULL, NULL);
-       if (u8file == NULL) {
+       args = utf16_concat (sei->lpFile, sei->lpParameters == NULL ? NULL : utf16_space, sei->lpParameters, NULL);
+       if (args == NULL){
                SetLastError (ERROR_INVALID_DATA);
                return (FALSE);
        }
-       
-       if (sei->lpParameters != NULL) {
-               u8params = g_utf16_to_utf8 (sei->lpParameters, -1, NULL, NULL, NULL);
-               if (u8params == NULL) {
-                       SetLastError (ERROR_INVALID_DATA);
-                       g_free (u8file);
-                       return (FALSE);
-               }
-       
-               u8args = g_strdup_printf ("%s %s", u8file, u8params);
-               if (u8args == NULL) {
-                       SetLastError (ERROR_INVALID_DATA);
-                       g_free (u8params);
-                       g_free (u8file);
-                       return (FALSE);
-               }
-       
-               args = g_utf8_to_utf16 (u8args, -1, NULL, NULL, NULL);
-       
-               g_free (u8file);
-               g_free (u8params);
-               g_free (u8args);
-       } else {
-               args = g_utf8_to_utf16 (u8file, -1, NULL, NULL, NULL);
-       }
-               
-       if (args == NULL) {
-               SetLastError (ERROR_INVALID_DATA);
-               return (FALSE);
-       }
-       
        ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
                             CREATE_UNICODE_ENVIRONMENT, NULL,
                             sei->lpDirectory, NULL, &process_info);
        g_free (args);
        
        if (!ret) {
-               return (FALSE);
+               static char *handler;
+               static gunichar2 *handler_utf16;
+               
+               if (handler_utf16 == (gunichar2 *)-1)
+                       return FALSE;
+
+#ifdef PLATFORM_MACOSX
+               handler = "/usr/bin/open";
+#else
+               /*
+                * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
+                * if that fails, try to use gnome-open, then kfmclient
+                */
+               handler = g_find_program_in_path ("xdg-open");
+               if (handler == NULL){
+                       handler = g_find_program_in_path ("gnome-open");
+                       if (handler == NULL){
+                               handler = g_find_program_in_path ("kfmclient");
+                               if (handler == NULL){
+                                       handler_utf16 = (gunichar2 *) -1;
+                                       return (FALSE);
+                               } else {
+                                       /* kfmclient needs exec argument */
+                                       char *old = handler;
+                                       handler = g_strconcat (old, " exec",
+                                                              NULL);
+                                       g_free (old);
+                               }
+                       }
+               }
+#endif
+               handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
+               g_free (handler);
+
+               /* Put quotes around the filename, in case it's a url
+                * that contains #'s (CreateProcess() calls
+                * g_shell_parse_argv(), which deliberately throws
+                * away anything after an unquoted #).  Fixes bug
+                * 371567.
+                */
+               args = utf16_concat (handler_utf16, utf16_space, utf16_quote,
+                                    sei->lpFile, utf16_quote,
+                                    sei->lpParameters == NULL ? NULL : utf16_space,
+                                    sei->lpParameters, NULL);
+               if (args == NULL){
+                       SetLastError (ERROR_INVALID_DATA);
+                       return FALSE;
+               }
+               ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
+                                    CREATE_UNICODE_ENVIRONMENT, NULL,
+                                    sei->lpDirectory, NULL, &process_info);
+               g_free (args);
+               if (!ret){
+                       SetLastError (ERROR_INVALID_DATA);
+                       return FALSE;
+               }
        }
        
        if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) {
@@ -351,6 +502,143 @@ gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
        return (ret);
 }
 
+static gboolean
+is_managed_binary (const gchar *filename)
+{
+       int original_errno = errno;
+#if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
+       int file = open (filename, O_RDONLY | O_LARGEFILE);
+#else
+       int file = open (filename, O_RDONLY);
+#endif
+       off_t new_offset;
+       unsigned char buffer[8];
+       off_t file_size, optional_header_offset;
+       off_t pe_header_offset;
+       gboolean managed = FALSE;
+       int num_read;
+       guint32 first_word, second_word;
+       
+       /* If we are unable to open the file, then we definitely
+        * can't say that it is managed. The child mono process
+        * probably wouldn't be able to open it anyway.
+        */
+       if (file < 0) {
+               errno = original_errno;
+               return FALSE;
+       }
+
+       /* Retrieve the length of the file for future sanity checks. */
+       file_size = lseek (file, 0, SEEK_END);
+       lseek (file, 0, SEEK_SET);
+
+       /* We know we need to read a header field at offset 60. */
+       if (file_size < 64)
+               goto leave;
+
+       num_read = read (file, buffer, 2);
+
+       if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
+               goto leave;
+
+       new_offset = lseek (file, 60, SEEK_SET);
+
+       if (new_offset != 60)
+               goto leave;
+       
+       num_read = read (file, buffer, 4);
+
+       if (num_read != 4)
+               goto leave;
+       pe_header_offset =  buffer[0]
+               | (buffer[1] <<  8)
+               | (buffer[2] << 16)
+               | (buffer[3] << 24);
+       
+       if (pe_header_offset + 24 > file_size)
+               goto leave;
+
+       new_offset = lseek (file, pe_header_offset, SEEK_SET);
+
+       if (new_offset != pe_header_offset)
+               goto leave;
+
+       num_read = read (file, buffer, 4);
+
+       if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
+               goto leave;
+
+       /*
+        * Verify that the header we want in the optional header data
+        * is present in this binary.
+        */
+       new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
+
+       if (new_offset != pe_header_offset + 20)
+               goto leave;
+
+       num_read = read (file, buffer, 2);
+
+       if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
+               goto leave;
+
+       /* Read the CLR header address and size fields. These will be
+        * zero if the binary is not managed.
+        */
+       optional_header_offset = pe_header_offset + 24;
+       new_offset = lseek (file, optional_header_offset + 208, SEEK_SET);
+
+       if (new_offset != optional_header_offset + 208)
+               goto leave;
+
+       num_read = read (file, buffer, 8);
+       
+       /* We are not concerned with endianness, only with
+        * whether it is zero or not.
+        */
+       first_word = *(guint32 *)&buffer[0];
+       second_word = *(guint32 *)&buffer[4];
+       
+       if ((num_read != 8) || (first_word == 0) || (second_word == 0))
+               goto leave;
+       
+       managed = TRUE;
+
+leave:
+       close (file);
+       errno = original_errno;
+       return managed;
+}
+
+gboolean CreateProcessWithLogonW (const gunichar2 *username,
+                                 const gunichar2 *domain,
+                                 const gunichar2 *password,
+                                 const guint32 logonFlags,
+                                 const gunichar2 *appname,
+                                 const gunichar2 *cmdline,
+                                 guint32 create_flags,
+                                 gpointer env,
+                                 const gunichar2 *cwd,
+                                 WapiStartupInfo *startup,
+                                 WapiProcessInformation *process_info)
+{
+       /* FIXME: use user information */
+       return CreateProcess (appname, cmdline, NULL, NULL, FALSE, create_flags, env, cwd, startup, process_info);
+}
+
+static gboolean
+is_executable (const char *prog)
+{
+       struct stat buf;
+       if (access (prog, X_OK) != 0)
+               return FALSE;
+       if (stat (prog, &buf))
+               return FALSE;
+       if (S_ISREG (buf.st_mode))
+               return TRUE;
+       return FALSE;
+}
+
 gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
                        WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
@@ -359,7 +647,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        WapiStartupInfo *startup,
                        WapiProcessInformation *process_info)
 {
-       gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv;
+       gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv = NULL;
        guint32 i, env_count = 0;
        gboolean ret = FALSE;
        gpointer handle;
@@ -406,7 +694,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 #endif
 
                        SetLastError (ERROR_PATH_NOT_FOUND);
-                       goto cleanup;
+                       goto free_strings;
                }
 
                /* Turn all the slashes round the right way */
@@ -425,7 +713,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 #endif
 
                        SetLastError (ERROR_PATH_NOT_FOUND);
-                       goto cleanup;
+                       goto free_strings;
                }
        }
 
@@ -437,7 +725,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 #endif
 
                        SetLastError (ERROR_PATH_NOT_FOUND);
-                       goto cleanup;
+                       goto free_strings;
                }
 
                /* Turn all the slashes round the right way */
@@ -467,15 +755,14 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        prog = g_strdup (unquoted);
 
                        /* Executable existing ? */
-                       if (access (prog, X_OK) != 0) {
+                       if (!is_executable (prog)) {
 #ifdef DEBUG
                                g_message ("%s: Couldn't find executable %s",
                                           __func__, prog);
 #endif
-                               g_free (prog);
                                g_free (unquoted);
                                SetLastError (ERROR_FILE_NOT_FOUND);
-                               goto cleanup;
+                               goto free_strings;
                        }
                } else {
                        /* Search for file named by cmd in the current
@@ -484,20 +771,20 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        char *curdir = g_get_current_dir ();
 
                        prog = g_strdup_printf ("%s/%s", curdir, unquoted);
-                       g_free (unquoted);
                        g_free (curdir);
 
                        /* And make sure it's executable */
-                       if (access (prog, X_OK) != 0) {
+                       if (!is_executable (prog)) {
 #ifdef DEBUG
                                g_message ("%s: Couldn't find executable %s",
                                           __func__, prog);
 #endif
-                               g_free (prog);
+                               g_free (unquoted);
                                SetLastError (ERROR_FILE_NOT_FOUND);
-                               goto cleanup;
+                               goto free_strings;
                        }
                }
+               g_free (unquoted);
 
                args_after_prog = args;
        } else {
@@ -560,7 +847,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 #endif
 
                        SetLastError (ERROR_PATH_NOT_FOUND);
-                       goto cleanup;
+                       goto free_strings;
                }
                
                /* Turn all the slashes round the right way. Only for
@@ -586,15 +873,14 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        prog = g_strdup (token);
                        
                        /* Executable existing ? */
-                       if (access (prog, X_OK) != 0) {
-                               g_free (prog);
+                       if (!is_executable (prog)) {
 #ifdef DEBUG
                                g_message ("%s: Couldn't find executable %s",
                                           __func__, token);
 #endif
                                g_free (token);
                                SetLastError (ERROR_FILE_NOT_FOUND);
-                               goto cleanup;
+                               goto free_strings;
                        }
 
                } else {
@@ -612,7 +898,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                        /* I assume X_OK is the criterion to use,
                         * rather than F_OK
                         */
-                       if (access (prog, X_OK) != 0) {
+                       if (!is_executable (prog)) {
                                g_free (prog);
                                prog = g_find_program_in_path (token);
                                if (prog == NULL) {
@@ -622,7 +908,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 
                                        g_free (token);
                                        SetLastError (ERROR_FILE_NOT_FOUND);
-                                       goto cleanup;
+                                       goto free_strings;
                                }
                        }
                }
@@ -635,6 +921,43 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                   args_after_prog);
 #endif
        
+       /* Check for CLR binaries; if found, we will try to invoke
+        * them using the same mono binary that started us.
+        */
+       if (is_managed_binary (prog)) {
+               gunichar2 *newapp, *newcmd;
+               gsize bytes_ignored;
+
+               newapp = mono_unicode_from_external ("mono", &bytes_ignored);
+
+               if (newapp != NULL) {
+                       if (appname != NULL) {
+                               newcmd = utf16_concat (newapp, utf16_space,
+                                                      appname, utf16_space,
+                                                      cmdline, NULL);
+                       } else {
+                               newcmd = utf16_concat (newapp, utf16_space,
+                                                      cmdline, NULL);
+                       }
+                       
+                       g_free ((gunichar2 *)newapp);
+                       
+                       if (newcmd != NULL) {
+                               ret = CreateProcess (NULL, newcmd,
+                                                    process_attrs,
+                                                    thread_attrs,
+                                                    inherit_handles,
+                                                    create_flags, new_environ,
+                                                    cwd, startup,
+                                                    process_info);
+                               
+                               g_free ((gunichar2 *)newcmd);
+                               
+                               goto free_strings;
+                       }
+               }
+       }
+
        if (args_after_prog != NULL && *args_after_prog) {
                gchar *qprog;
 
@@ -671,7 +994,7 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
                g_warning ("%s: error creating process handle", __func__);
 
                SetLastError (ERROR_PATH_NOT_FOUND);
-               goto cleanup;
+               goto free_strings;
        }
 
        /* Hold another reference so the process has somewhere to
@@ -762,15 +1085,19 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
        } else if (pid == 0) {
                /* Child */
                
-               /* Wait for the parent to finish setting up the
-                * handle.  The semaphore lock is safe because the
-                * sem_undo structures of a semaphore aren't inherited
-                * across a fork ()
-                */
-               thr_ret = _wapi_handle_lock_shared_handles ();
-               g_assert (thr_ret == 0);
+               if (_wapi_shm_disabled == FALSE) {
+                       /* Wait for the parent to finish setting up
+                        * the handle.  The semaphore lock is safe
+                        * because the sem_undo structures of a
+                        * semaphore aren't inherited across a fork
+                        * (), but we can't do this if we're not using
+                        * the shared memory
+                        */
+                       thr_ret = _wapi_handle_lock_shared_handles ();
+                       g_assert (thr_ret == 0);
        
-               _wapi_handle_unlock_shared_handles ();
+                       _wapi_handle_unlock_shared_handles ();
+               }
                
                /* should we detach from the process group? */
 
@@ -839,10 +1166,14 @@ gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
 cleanup:
        _wapi_handle_unlock_shared_handles ();
 
+free_strings:
        if (cmd != NULL) {
                g_free (cmd);
        }
        if (full_prog != NULL) {
+               g_free (full_prog);
+       }
+       if (prog != NULL) {
                g_free (prog);
        }
        if (args != NULL) {
@@ -854,7 +1185,15 @@ cleanup:
        if(env_strings != NULL) {
                g_strfreev (env_strings);
        }
+       if (argv != NULL) {
+               g_strfreev (argv);
+       }
        
+#ifdef DEBUG
+       g_message ("%s: returning handle %p for pid %d", __func__, handle,
+                  pid);
+#endif
+
        return(ret);
 }
                
@@ -896,6 +1235,8 @@ static void process_set_current (void)
        const char *handle_env;
        struct _WapiHandle_process process_handle = {0};
        
+       mono_once (&process_ops_once, process_ops_init);
+       
        handle_env = g_getenv ("_WAPI_PROCESS_HANDLE_OFFSET");
        g_unsetenv ("_WAPI_PROCESS_HANDLE_OFFSET");
        
@@ -989,6 +1330,11 @@ guint32 GetProcessId (gpointer handle)
 {
        struct _WapiHandle_process *process_handle;
        gboolean ok;
+
+       if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               return(GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+       }
        
        ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
                                  (gpointer *)&process_handle);
@@ -1013,6 +1359,8 @@ static pid_t signal_process_if_gone (gpointer handle)
        struct _WapiHandle_process *process_handle;
        gboolean ok;
        
+       g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
+       
        /* Make sure the process is signalled if it has exited - if
         * the parent process didn't wait for it then it won't be
         */
@@ -1046,6 +1394,7 @@ static pid_t signal_process_if_gone (gpointer handle)
        return (process_handle->id);
 }
 
+#ifdef UNUSED_CODE
 static gboolean process_enum (gpointer handle, gpointer user_data)
 {
        GArray *processes=user_data;
@@ -1081,17 +1430,36 @@ static gboolean process_enum (gpointer handle, gpointer user_data)
        /* Return false to keep searching */
        return(FALSE);
 }
+#endif /* UNUSED_CODE */
 
 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
 {
        GArray *processes = g_array_new (FALSE, FALSE, sizeof(pid_t));
        guint32 fit, i, j;
+       DIR *dir;
+       struct dirent *entry;
        
        mono_once (&process_current_once, process_set_current);
-       
-       _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
-                            NULL, TRUE);
-       
+
+       dir = opendir ("/proc");
+       if (dir == NULL) {
+               return(FALSE);
+       }
+       while((entry = readdir (dir)) != NULL) {
+               if (isdigit (entry->d_name[0])) {
+                       char *endptr;
+                       pid_t pid = (pid_t)strtol (entry->d_name, &endptr, 10);
+
+                       if (*endptr == '\0') {
+                               /* Name was entirely numeric, so was a
+                                * process ID
+                                */
+                               g_array_append_val (processes, pid);
+                       }
+               }
+       }
+       closedir (dir);
+
        fit=len/sizeof(guint32);
        for (i = 0, j = 0; j < fit && i < processes->len; i++) {
                pids[j++] = g_array_index (processes, pid_t, i);
@@ -1131,7 +1499,7 @@ static gboolean process_open_compare (gpointer handle, gpointer user_data)
        }
 }
 
-gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
+gpointer OpenProcess (guint32 req_access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
 {
        /* Find the process handle that corresponds to pid */
        gpointer handle;
@@ -1146,13 +1514,21 @@ gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUS
                                      process_open_compare,
                                      GUINT_TO_POINTER (pid), NULL, TRUE);
        if (handle == 0) {
+               gchar *dir = g_strdup_printf ("/proc/%d", pid);
+               if (!access (dir, F_OK)) {
+                       /* Return a pseudo handle for processes we
+                        * don't have handles for
+                        */
+                       return((gpointer)(_WAPI_PROCESS_UNHANDLED + pid));
+               } else {
 #ifdef DEBUG
-               g_message ("%s: Can't find pid %d", __func__, pid);
+                       g_message ("%s: Can't find pid %d", __func__, pid);
 #endif
 
-               SetLastError (ERROR_PROC_NOT_FOUND);
+                       SetLastError (ERROR_PROC_NOT_FOUND);
        
-               return(NULL);
+                       return(NULL);
+               }
        }
 
        _wapi_handle_ref (handle);
@@ -1171,6 +1547,13 @@ gboolean GetExitCodeProcess (gpointer process, guint32 *code)
                return(FALSE);
        }
        
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle, so we don't know what the
+                * exit code was
+                */
+               return(FALSE);
+       }
+       
        ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
                                (gpointer *)&process_handle);
        if(ok==FALSE) {
@@ -1183,8 +1566,13 @@ gboolean GetExitCodeProcess (gpointer process, guint32 *code)
        
        /* A process handle is only signalled if the process has exited
         * and has been waited for */
-       if (_wapi_handle_issignalled (process) == TRUE ||
-           process_wait (process, 0) == WAIT_OBJECT_0) {
+
+       /* Make sure any process exit has been noticed, before
+        * checking if the process is signalled.  Fixes bug 325463.
+        */
+       process_wait (process, 0);
+       
+       if (_wapi_handle_issignalled (process) == TRUE) {
                *code = process_handle->exitstatus;
        } else {
                *code = STILL_ACTIVE;
@@ -1199,6 +1587,7 @@ gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
 {
        struct _WapiHandle_process *process_handle;
        gboolean ok;
+       gboolean ku_times_set = FALSE;
        
        mono_once (&process_current_once, process_set_current);
 
@@ -1208,6 +1597,12 @@ gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
                return(FALSE);
        }
        
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle, so just fail for now
+                */
+               return(FALSE);
+       }
+       
        ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
                                (gpointer *)&process_handle);
        if(ok==FALSE) {
@@ -1226,13 +1621,219 @@ gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
        if(_wapi_handle_issignalled (process)==TRUE) {
                *exit_time=process_handle->exit_time;
        }
-       
+
+#ifdef HAVE_GETRUSAGE
+       if (process_handle->id == getpid ()) {
+               struct rusage time_data;
+               if (getrusage (RUSAGE_SELF, &time_data) == 0) {
+                       gint64 tick_val;
+                       gint64 *tick_val_ptr;
+                       ku_times_set = TRUE;
+                       tick_val = time_data.ru_utime.tv_sec * 10000000 + time_data.ru_utime.tv_usec * 10;
+                       tick_val_ptr = (gint64*)user_time;
+                       *tick_val_ptr = tick_val;
+                       tick_val = time_data.ru_stime.tv_sec * 10000000 + time_data.ru_stime.tv_usec * 10;
+                       tick_val_ptr = (gint64*)kernel_time;
+                       *tick_val_ptr = tick_val;
+               }
+       }
+#endif
+       if (!ku_times_set) {
+               memset (kernel_time, 0, sizeof (WapiFileTime));
+               memset (user_time, 0, sizeof (WapiFileTime));
+       }
+
        return(TRUE);
 }
 
+typedef struct
+{
+       gpointer address_start;
+       gpointer address_end;
+       gchar *perms;
+       gpointer address_offset;
+       dev_t device;
+       ino_t inode;
+       gchar *filename;
+} WapiProcModule;
+
+static void free_procmodule (WapiProcModule *mod)
+{
+       if (mod->perms != NULL) {
+               g_free (mod->perms);
+       }
+       if (mod->filename != NULL) {
+               g_free (mod->filename);
+       }
+       g_free (mod);
+}
+
+static gint find_procmodule (gconstpointer a, gconstpointer b)
+{
+       WapiProcModule *want = (WapiProcModule *)a;
+       WapiProcModule *compare = (WapiProcModule *)b;
+       
+       if ((want->device == compare->device) &&
+           (want->inode == compare->inode)) {
+               return(0);
+       } else {
+               return(1);
+       }
+}
+
+static GSList *load_modules (FILE *fp)
+{
+       GSList *ret = NULL;
+       WapiProcModule *mod;
+       gchar buf[MAXPATHLEN + 1], *p, *endp;
+       gchar *start_start, *end_start, *prot_start, *offset_start;
+       gchar *maj_dev_start, *min_dev_start, *inode_start, prot_buf[5];
+       gpointer address_start, address_end, address_offset;
+       guint32 maj_dev, min_dev;
+       ino_t inode;
+       dev_t device;
+       
+       while (fgets (buf, sizeof(buf), fp)) {
+               p = buf;
+               while (g_ascii_isspace (*p)) ++p;
+               start_start = p;
+               if (!g_ascii_isxdigit (*start_start)) {
+                       continue;
+               }
+               address_start = (gpointer)strtoul (start_start, &endp, 16);
+               p = endp;
+               if (*p != '-') {
+                       continue;
+               }
+               
+               ++p;
+               end_start = p;
+               if (!g_ascii_isxdigit (*end_start)) {
+                       continue;
+               }
+               address_end = (gpointer)strtoul (end_start, &endp, 16);
+               p = endp;
+               if (!g_ascii_isspace (*p)) {
+                       continue;
+               }
+               
+               while (g_ascii_isspace (*p)) ++p;
+               prot_start = p;
+               if (*prot_start != 'r' && *prot_start != '-') {
+                       continue;
+               }
+               memcpy (prot_buf, prot_start, 4);
+               prot_buf[4] = '\0';
+               while (!g_ascii_isspace (*p)) ++p;
+               
+               while (g_ascii_isspace (*p)) ++p;
+               offset_start = p;
+               if (!g_ascii_isxdigit (*offset_start)) {
+                       continue;
+               }
+               address_offset = (gpointer)strtoul (offset_start, &endp, 16);
+               p = endp;
+               if (!g_ascii_isspace (*p)) {
+                       continue;
+               }
+               
+               while(g_ascii_isspace (*p)) ++p;
+               maj_dev_start = p;
+               if (!g_ascii_isxdigit (*maj_dev_start)) {
+                       continue;
+               }
+               maj_dev = strtoul (maj_dev_start, &endp, 16);
+               p = endp;
+               if (*p != ':') {
+                       continue;
+               }
+               
+               ++p;
+               min_dev_start = p;
+               if (!g_ascii_isxdigit (*min_dev_start)) {
+                       continue;
+               }
+               min_dev = strtoul (min_dev_start, &endp, 16);
+               p = endp;
+               if (!g_ascii_isspace (*p)) {
+                       continue;
+               }
+               
+               while (g_ascii_isspace (*p)) ++p;
+               inode_start = p;
+               if (!g_ascii_isxdigit (*inode_start)) {
+                       continue;
+               }
+               inode = (ino_t)strtol (inode_start, &endp, 10);
+               p = endp;
+               if (!g_ascii_isspace (*p)) {
+                       continue;
+               }
+
+               device = makedev ((int)maj_dev, (int)min_dev);
+               if ((device == 0) &&
+                   (inode == 0)) {
+                       continue;
+               }
+               
+               while(g_ascii_isspace (*p)) ++p;
+               /* p now points to the filename */
+
+               mod = g_new0 (WapiProcModule, 1);
+               mod->address_start = address_start;
+               mod->address_end = address_end;
+               mod->perms = g_strdup (prot_buf);
+               mod->address_offset = address_offset;
+               mod->device = device;
+               mod->inode = inode;
+               mod->filename = g_strdup (g_strstrip (p));
+               
+               if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
+                       ret = g_slist_prepend (ret, mod);
+               } else {
+                       free_procmodule (mod);
+               }
+       }
+
+       ret = g_slist_reverse (ret);
+       
+       return(ret);
+}
+
+static gboolean match_procname_to_modulename (gchar *procname, gchar *modulename)
+{
+       char* lastsep = NULL;
+
+       if (procname == NULL || modulename == NULL)
+               return (FALSE);
+
+       if (!strcmp (procname, modulename))
+               return (TRUE);
+
+       lastsep = strrchr (modulename, '/');
+       if (lastsep) {
+               if (!strcmp (lastsep+1, procname))
+                       return (TRUE);
+               return (FALSE);
+       }
+
+       return (FALSE);
+}
+
 gboolean EnumProcessModules (gpointer process, gpointer *modules,
                             guint32 size, guint32 *needed)
 {
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       gchar *filename;
+       FILE *fp;
+       GSList *mods = NULL;
+       WapiProcModule *module;
+       guint32 count, avail = size / sizeof(gpointer);
+       int i;
+       pid_t pid;
+       gchar *proc_name = NULL;
+       
        /* Store modules in an array of pointers (main module as
         * modules[0]), using the load address for each module as a
         * token.  (Use 'NULL' as an alternative for the main module
@@ -1241,26 +1842,149 @@ gboolean EnumProcessModules (gpointer process, gpointer *modules,
         * other systems will have to implement /dev/kmem reading or
         * whatever other horrid technique is needed.
         */
-       if(size<sizeof(gpointer)) {
+       if (size < sizeof(gpointer)) {
                return(FALSE);
        }
-       
-#ifdef linux
-       modules[0]=NULL;
-       *needed=sizeof(gpointer);
-#else
-       modules[0]=NULL;
-       *needed=sizeof(gpointer);
+
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+       } else {
+               ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                         (gpointer *)&process_handle);
+               if (ok == FALSE) {
+#ifdef DEBUG
+                       g_message ("%s: Can't find process %p", __func__, process);
 #endif
+               
+                       return(FALSE);
+               }
+               pid = process_handle->id;
+               proc_name = process_handle->proc_name;
+       }
+       
+       filename = g_strdup_printf ("/proc/%d/maps", pid);
+       if ((fp = fopen (filename, "r")) == NULL) {
+               /* No /proc/<pid>/maps so just return the main module
+                * shortcut for now
+                */
+               modules[0] = NULL;
+               *needed = sizeof(gpointer);
+       } else {
+               mods = load_modules (fp);
+               fclose (fp);
+               count = g_slist_length (mods);
+               
+               /* count + 1 to leave slot 0 for the main module */
+               *needed = sizeof(gpointer) * (count + 1);
+
+               /* Use the NULL shortcut, as the first line in
+                * /proc/<pid>/maps isn't the executable, and we need
+                * that first in the returned list. Check the module name 
+                * to see if it ends with the proc name and substitute 
+                * the first entry with it.  FIXME if this turns out to 
+                * be a problem.
+                */
+               modules[0] = NULL;
+               for (i = 0; i < (avail - 1) && i < count; i++) {
+                       module = (WapiProcModule *)g_slist_nth_data (mods, i);
+                       if (modules[0] != NULL)
+                               modules[i] = module->address_start;
+                       else if (match_procname_to_modulename (proc_name, module->filename))
+                               modules[0] = module->address_start;
+                       else
+                               modules[i + 1] = module->address_start;
+               }
+               
+               for (i = 0; i < count; i++) {
+                       free_procmodule (g_slist_nth_data (mods, i));
+               }
+               g_slist_free (mods);
+       }
+
+       g_free (filename);
        
        return(TRUE);
 }
 
-guint32 GetModuleBaseName (gpointer process, gpointer module,
-                          gunichar2 *basename, guint32 size)
+static gchar *get_process_name_from_proc (pid_t pid)
+{
+       gchar *filename;
+       gchar *ret = NULL;
+       gchar buf[256];
+       FILE *fp;
+       
+       memset (buf, '\0', sizeof(buf));
+       
+       filename = g_strdup_printf ("/proc/%d/exe", pid);
+       if (readlink (filename, buf, 255) > 0) {
+               ret = g_strdup (buf);
+       }
+       g_free (filename);
+
+       if (ret != NULL) {
+               return(ret);
+       }
+       
+       filename = g_strdup_printf ("/proc/%d/cmdline", pid);
+       if ((fp = fopen (filename, "r")) != NULL) {
+               if (fgets (buf, 256, fp) != NULL) {
+                       ret = g_strdup (buf);
+               }
+               
+               fclose (fp);
+       }
+       g_free (filename);
+
+       if (ret != NULL) {
+               return(ret);
+       }
+       
+       filename = g_strdup_printf ("/proc/%d/stat", pid);
+       if ((fp = fopen (filename, "r")) != NULL) {
+               if (fgets (buf, 256, fp) != NULL) {
+                       gchar *start, *end;
+                       
+                       start = strchr (buf, '(');
+                       if (start != NULL) {
+                               end = strchr (start + 1, ')');
+                               
+                               if (end != NULL) {
+                                       ret = g_strndup (start + 1,
+                                                        end - start - 1);
+                               }
+                       }
+               }
+               
+               fclose (fp);
+       }
+       g_free (filename);
+
+       if (ret != NULL) {
+               return(ret);
+       }
+
+       return(NULL);
+}
+
+static guint32 get_module_name (gpointer process, gpointer module,
+                               gunichar2 *basename, guint32 size,
+                               gboolean base)
 {
        struct _WapiHandle_process *process_handle;
        gboolean ok;
+       pid_t pid;
+       gunichar2 *procname;
+       gchar *procname_ext = NULL;
+       glong len;
+       gsize bytes;
+       gchar *filename;
+       FILE *fp;
+       GSList *mods = NULL;
+       WapiProcModule *found_module;
+       guint32 count;
+       int i;
+       gchar *proc_name = NULL;
        
        mono_once (&process_current_once, process_set_current);
 
@@ -1269,50 +1993,97 @@ guint32 GetModuleBaseName (gpointer process, gpointer module,
                   __func__, process, module);
 #endif
 
-       if(basename==NULL || size==0) {
-               return(FALSE);
+       if (basename == NULL || size == 0) {
+               return(0);
        }
        
-       ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
-                               (gpointer *)&process_handle);
-       if(ok==FALSE) {
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+               proc_name = get_process_name_from_proc (pid);
+       } else {
+               ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                         (gpointer *)&process_handle);
+               if (ok == FALSE) {
 #ifdef DEBUG
-               g_message ("%s: Can't find process %p", __func__, process);
+                       g_message ("%s: Can't find process %p", __func__,
+                                  process);
 #endif
-               
-               return(FALSE);
+                       
+                       return(0);
+               }
+               pid = process_handle->id;
+               proc_name = g_strdup (process_handle->proc_name);
        }
 
-       if(module==NULL) {
-               /* Shorthand for the main module, which has the
-                * process name recorded in the handle data
+       /* Look up the address in /proc/<pid>/maps */
+       filename = g_strdup_printf ("/proc/%d/maps", pid);
+       if ((fp = fopen (filename, "r")) == NULL) {
+               if (errno == EACCES && module == NULL && base == TRUE) {
+                       procname_ext = get_process_name_from_proc (pid);
+               } else {
+                       /* No /proc/<pid>/maps, so just return failure
+                        * for now
+                        */
+                       g_free (proc_name);
+                       g_free (filename);
+                       return(0);
+               }
+       } else {
+               mods = load_modules (fp);
+               fclose (fp);
+               count = g_slist_length (mods);
+
+               /* If module != NULL compare the address.
+                * If module == NULL we are looking for the main module.
+                * The best we can do for now check it the module name end with the process name.
                 */
-               pid_t pid;
-               gunichar2 *procname;
-               gchar *procname_utf8 = NULL;
-               glong len, bytes;
-               
-#ifdef DEBUG
-               g_message ("%s: Returning main module name", __func__);
-#endif
+               for (i = 0; i < count; i++) {
+                       found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+                       if (procname_ext == NULL &&
+                           ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||    
+                            (module != NULL && found_module->address_start == module))) {
+                               if (base) {
+                                       procname_ext = g_path_get_basename (found_module->filename);
+                               } else {
+                                       procname_ext = g_strdup (found_module->filename);
+                               }
+                       }
 
-               pid=process_handle->id;
-               procname_utf8 = process_handle->proc_name;
-       
+                       free_procmodule (found_module);
+               }
+
+               if (procname_ext == NULL)
+               {
+                       /* If it's *still* null, we might have hit the
+                        * case where reading /proc/$pid/maps gives an
+                        * empty file for this user.
+                        */
+                       procname_ext = get_process_name_from_proc (pid);
+               }
+
+               g_slist_free (mods);
+               g_free (filename);
+               g_free (proc_name);
+       }
+
+       if (procname_ext != NULL) {
 #ifdef DEBUG
                g_message ("%s: Process name is [%s]", __func__,
-                          procname_utf8);
+                          procname_ext);
 #endif
 
-               procname = g_utf8_to_utf16 (procname_utf8, -1, NULL, &len,
-                                           NULL);
+               procname = mono_unicode_from_external (procname_ext, &bytes);
                if (procname == NULL) {
                        /* bugger */
+                       g_free (procname_ext);
                        return(0);
                }
-
-               /* Add the terminator, and convert chars to bytes */
-               bytes = (len + 1) * 2;
+               
+               len = (bytes / 2);
+               
+               /* Add the terminator */
+               bytes += 2;
                
                if (size < bytes) {
 #ifdef DEBUG
@@ -1330,15 +2101,111 @@ guint32 GetModuleBaseName (gpointer process, gpointer module,
                }
                
                g_free (procname);
-
+               g_free (procname_ext);
+               
                return(len);
-       } else {
-               /* Look up the address in /proc/<pid>/maps */
        }
        
        return(0);
 }
 
+guint32 GetModuleBaseName (gpointer process, gpointer module,
+                          gunichar2 *basename, guint32 size)
+{
+       return(get_module_name (process, module, basename, size, TRUE));
+}
+
+guint32 GetModuleFileNameEx (gpointer process, gpointer module,
+                            gunichar2 *filename, guint32 size)
+{
+       return(get_module_name (process, module, filename, size, FALSE));
+}
+
+gboolean GetModuleInformation (gpointer process, gpointer module,
+                              WapiModuleInfo *modinfo, guint32 size)
+{
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       pid_t pid;
+       gchar *filename;
+       FILE *fp;
+       GSList *mods = NULL;
+       WapiProcModule *found_module;
+       guint32 count;
+       int i;
+       gboolean ret = FALSE;
+       gchar *proc_name = NULL;
+       
+       mono_once (&process_current_once, process_set_current);
+       
+#ifdef DEBUG
+       g_message ("%s: Getting module info, process handle %p module %p",
+                  __func__, process, module);
+#endif
+
+       if (modinfo == NULL || size < sizeof(WapiModuleInfo)) {
+               return(FALSE);
+       }
+       
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+               proc_name = get_process_name_from_proc (pid);
+       } else {
+               ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                         (gpointer *)&process_handle);
+               if (ok == FALSE) {
+#ifdef DEBUG
+                       g_message ("%s: Can't find process %p", __func__,
+                                  process);
+#endif
+                       
+                       return(FALSE);
+               }
+               pid = process_handle->id;
+               proc_name = g_strdup (process_handle->proc_name);
+       }
+
+       /* Look up the address in /proc/<pid>/maps */
+       filename = g_strdup_printf ("/proc/%d/maps", pid);
+       if ((fp = fopen (filename, "r")) == NULL) {
+               /* No /proc/<pid>/maps, so just return failure
+                * for now
+                */
+               g_free (proc_name);
+               g_free (filename);
+               return(FALSE);
+       } else {
+               mods = load_modules (fp);
+               fclose (fp);
+               count = g_slist_length (mods);
+
+               /* If module != NULL compare the address.
+                * If module == NULL we are looking for the main module.
+                * The best we can do for now check it the module name end with the process name.
+                */
+               for (i = 0; i < count; i++) {
+                       found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+                       if ( ret == FALSE &&
+                            ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
+                             (module != NULL && found_module->address_start == module))) {
+                               modinfo->lpBaseOfDll = found_module->address_start;
+                               modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
+                               modinfo->EntryPoint = found_module->address_offset;
+                               ret = TRUE;
+                       }
+
+                       free_procmodule (found_module);
+               }
+
+               g_slist_free (mods);
+               g_free (filename);
+               g_free (proc_name);
+       }
+
+       return(ret);
+}
+
 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
 {
        struct _WapiHandle_process *process_handle;
@@ -1351,6 +2218,12 @@ gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
                return(FALSE);
        }
        
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle, so just fail for now
+                */
+               return(FALSE);
+       }
+       
        ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
                                (gpointer *)&process_handle);
        if(ok==FALSE) {
@@ -1373,6 +2246,12 @@ gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
        gboolean ok;
 
        mono_once (&process_current_once, process_set_current);
+       
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle, so just fail for now
+                */
+               return(FALSE);
+       }
 
        ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
                                (gpointer *)&process_handle);
@@ -1398,20 +2277,28 @@ TerminateProcess (gpointer process, gint32 exitCode)
        gboolean ok;
        int signo;
        int ret;
+       pid_t pid;
+       
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+       } else {
+               ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                         (gpointer *) &process_handle);
 
-       ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
-                                 (gpointer *) &process_handle);
-
-       if (ok == FALSE) {
+               if (ok == FALSE) {
 #ifdef DEBUG
-               g_message ("%s: Can't find process %p", __func__, process);
+                       g_message ("%s: Can't find process %p", __func__,
+                                  process);
 #endif
-               SetLastError (ERROR_INVALID_HANDLE);
-               return FALSE;
+                       SetLastError (ERROR_INVALID_HANDLE);
+                       return FALSE;
+               }
+               pid = process_handle->id;
        }
 
        signo = (exitCode == -1) ? SIGKILL : SIGTERM;
-       ret = kill (process_handle->id, signo);
+       ret = kill (pid, signo);
        if (ret == -1) {
                switch (errno) {
                case EINVAL:
@@ -1431,3 +2318,132 @@ TerminateProcess (gpointer process, gint32 exitCode)
        return (ret == 0);
 }
 
+guint32
+GetPriorityClass (gpointer process)
+{
+#ifdef HAVE_GETPRIORITY
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       int ret;
+       pid_t pid;
+       
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+       } else {
+               ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                         (gpointer *) &process_handle);
+
+               if (!ok) {
+                       SetLastError (ERROR_INVALID_HANDLE);
+                       return FALSE;
+               }
+               pid = process_handle->id;
+       }
+
+       errno = 0;
+       ret = getpriority (PRIO_PROCESS, pid);
+       if (ret == -1 && errno != 0) {
+               switch (errno) {
+               case EPERM:
+               case EACCES:
+                       SetLastError (ERROR_ACCESS_DENIED);
+                       break;
+               case ESRCH:
+                       SetLastError (ERROR_PROC_NOT_FOUND);
+                       break;
+               default:
+                       SetLastError (ERROR_GEN_FAILURE);
+               }
+               return FALSE;
+       }
+
+       if (ret == 0)
+               return NORMAL_PRIORITY_CLASS;
+       else if (ret < -15)
+               return REALTIME_PRIORITY_CLASS;
+       else if (ret < -10)
+               return HIGH_PRIORITY_CLASS;
+       else if (ret < 0)
+               return ABOVE_NORMAL_PRIORITY_CLASS;
+       else if (ret > 10)
+               return IDLE_PRIORITY_CLASS;
+       else if (ret > 0)
+               return BELOW_NORMAL_PRIORITY_CLASS;
+
+       return NORMAL_PRIORITY_CLASS;
+#else
+       SetLastError (ERROR_NOT_SUPPORTED);
+       return 0;
+#endif
+}
+
+gboolean
+SetPriorityClass (gpointer process, guint32  priority_class)
+{
+#ifdef HAVE_SETPRIORITY
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       int ret;
+       int prio;
+       pid_t pid;
+       
+       if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
+               /* This is a pseudo handle */
+               pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
+       } else {
+               ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                         (gpointer *) &process_handle);
+
+               if (!ok) {
+                       SetLastError (ERROR_INVALID_HANDLE);
+                       return FALSE;
+               }
+               pid = process_handle->id;
+       }
+
+       switch (priority_class) {
+       case IDLE_PRIORITY_CLASS:
+               prio = 19;
+               break;
+       case BELOW_NORMAL_PRIORITY_CLASS:
+               prio = 10;
+               break;
+       case NORMAL_PRIORITY_CLASS:
+               prio = 0;
+               break;
+       case ABOVE_NORMAL_PRIORITY_CLASS:
+               prio = -5;
+               break;
+       case HIGH_PRIORITY_CLASS:
+               prio = -11;
+               break;
+       case REALTIME_PRIORITY_CLASS:
+               prio = -20;
+               break;
+       default:
+               SetLastError (ERROR_INVALID_PARAMETER);
+               return FALSE;
+       }
+
+       ret = setpriority (PRIO_PROCESS, pid, prio);
+       if (ret == -1) {
+               switch (errno) {
+               case EPERM:
+               case EACCES:
+                       SetLastError (ERROR_ACCESS_DENIED);
+                       break;
+               case ESRCH:
+                       SetLastError (ERROR_PROC_NOT_FOUND);
+                       break;
+               default:
+                       SetLastError (ERROR_GEN_FAILURE);
+               }
+       }
+
+       return ret == 0;
+#else
+       SetLastError (ERROR_NOT_SUPPORTED);
+       return FALSE;
+#endif
+}