Ooops, gen-65 is broken, not test-65.
[mono.git] / mono / io-layer / processes.c
index f861d403202a855382a2bb1a59dd41d9fd05fc5d..dedd4e2f6521c73517becd00f80badd7b42956bc 100644 (file)
@@ -9,7 +9,7 @@
 
 #include <config.h>
 #if HAVE_BOEHM_GC
-#include <gc/gc.h>
+#include <mono/os/gc_wrapper.h>
 #include "mono/utils/mono-hash.h"
 #endif
 #include <glib.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <signal.h>
 
 #include <mono/io-layer/wapi.h>
-#include <mono/io-layer/unicode.h>
 #include <mono/io-layer/wapi-private.h>
 #include <mono/io-layer/handles-private.h>
 #include <mono/io-layer/misc-private.h>
 #include <mono/io-layer/mono-mutex.h>
 #include <mono/io-layer/process-private.h>
 #include <mono/io-layer/threads.h>
+#include <mono/utils/strenc.h>
+
+/* The process' environment strings */
+extern char **environ;
 
 #undef DEBUG
 
@@ -55,7 +59,6 @@ static void process_ops_init (void)
 
 static void process_close_shared (gpointer handle G_GNUC_UNUSED)
 {
-#ifdef DEBUG
        struct _WapiHandle_process *process_handle;
        gboolean ok;
 
@@ -67,28 +70,33 @@ static void process_close_shared (gpointer handle G_GNUC_UNUSED)
                return;
        }
 
+#ifdef DEBUG
        g_message (G_GNUC_PRETTY_FUNCTION
                   ": closing process handle %p with id %d", handle,
                   process_handle->id);
 #endif
+
+       if(process_handle->proc_name!=0) {
+               _wapi_handle_scratch_delete (process_handle->proc_name);
+               process_handle->proc_name=0;
+       }
 }
 
 gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                        WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
                        WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
                        gboolean inherit_handles, guint32 create_flags,
-                       gpointer environ, const gunichar2 *cwd,
+                       gpointer new_environ, const gunichar2 *cwd,
                        WapiStartupInfo *startup,
                        WapiProcessInformation *process_info)
 {
-       gchar *cmd=NULL, *prog, *args=NULL, *dir=NULL;
-       gunichar2 *environp;
-       guint32 env=0, stored_dir=0, stored_prog=0, stored_args=0;
-       guint32 env_count=0, i;
+       gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args=NULL, *args_after_prog=NULL, *dir=NULL;
+       guint32 env=0, stored_dir=0, stored_prog=0, i;
        gboolean ret=FALSE;
        gpointer stdin_handle, stdout_handle, stderr_handle;
        guint32 pid, tid;
        gpointer process_handle, thread_handle;
+       struct _WapiHandle_process *process_handle_data;
        
        mono_once (&process_ops_once, process_ops_init);
        
@@ -119,13 +127,14 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
         * so crap, with an API like this :-(
         */
        if(appname!=NULL) {
-               cmd=_wapi_unicode_to_utf8 (appname);
+               cmd=mono_unicode_to_external (appname);
                if(cmd==NULL) {
 #ifdef DEBUG
                        g_message (G_GNUC_PRETTY_FUNCTION
                                   ": unicode conversion returned NULL");
 #endif
 
+                       SetLastError(ERROR_PATH_NOT_FOUND);
                        goto cleanup;
                }
 
@@ -138,32 +147,27 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
        }
        
        if(cmdline!=NULL) {
-               args=_wapi_unicode_to_utf8 (cmdline);
+               args=mono_unicode_to_external (cmdline);
                if(args==NULL) {
 #ifdef DEBUG
                        g_message (G_GNUC_PRETTY_FUNCTION
                                   ": unicode conversion returned NULL");
 #endif
 
+                       SetLastError(ERROR_PATH_NOT_FOUND);
                        goto cleanup;
                }
-
-               /* Turn all the slashes round the right way */
-               for(i=0; i<strlen (args); i++) {
-                       if(args[i]=='\\') {
-                               args[i]='/';
-                       }
-               }
        }
 
        if(cwd!=NULL) {
-               dir=_wapi_unicode_to_utf8 (cwd);
+               dir=mono_unicode_to_external (cwd);
                if(dir==NULL) {
 #ifdef DEBUG
                        g_message (G_GNUC_PRETTY_FUNCTION
                                   ": unicode conversion returned NULL");
 #endif
 
+                       SetLastError(ERROR_PATH_NOT_FOUND);
                        goto cleanup;
                }
 
@@ -173,28 +177,55 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                                dir[i]='/';
                        }
                }
-               stored_dir=_wapi_handle_scratch_store (dir, strlen (dir));
+       } else {
+               dir=g_get_current_dir ();
        }
+       stored_dir=_wapi_handle_scratch_store (dir, strlen (dir));
        
-       /* environ is a block of NULL-terminated strings, which is
-        * itself NULL-terminated. Of course, passing an array of
+       
+       /* new_environ is a block of NULL-terminated strings, which
+        * is itself NULL-terminated. Of course, passing an array of
         * string pointers would have made things too easy :-(
+        *
+        * If new_environ is not NULL it specifies the entire set of
+        * environment variables in the new process.  Otherwise the
+        * new process inherits the same environment.
         */
-       /* Not sure whether I should turn the w32 env block into
-        * proper env vars, or just leave it to be read back by other
-        * w32 emulation functions.
-        */
-       if(environ!=NULL) {
-               /* env_count counts bytes, not chars */
-               for(environp=(gunichar2 *)environ; *environp;
-                   env_count+=2, environp++) {
-                       while(*environp) {
-                               env_count+=2;
-                               environp++;
+       if(new_environ!=NULL) {
+               gchar **strings;
+               guint32 count=0;
+               gunichar2 *new_environp;
+
+               /* Count the number of strings */
+               for(new_environp=(gunichar2 *)new_environ; *new_environp;
+                   new_environp++) {
+                       count++;
+                       while(*new_environp) {
+                               new_environp++;
+                       }
+               }
+               strings=g_new0 (gchar *, count + 1); /* +1 -> last one is NULL */
+               
+               /* Copy each environ string into 'strings' turning it
+                * into utf8 (or the requested encoding) at the same
+                * time
+                */
+               count=0;
+               for(new_environp=(gunichar2 *)new_environ; *new_environp;
+                   new_environp++) {
+                       strings[count]=mono_unicode_to_external (new_environp);
+                       count++;
+                       while(*new_environp) {
+                               new_environp++;
                        }
                }
 
-               env=_wapi_handle_scratch_store (environ, env_count);
+               env=_wapi_handle_scratch_store_string_array (strings);
+
+               g_strfreev (strings);
+       } else {
+               /* Use the existing environment */
+               env=_wapi_handle_scratch_store_string_array (environ);
        }
 
        /* We can't put off locating the executable any longer :-( */
@@ -211,6 +242,16 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                if(cmd[0]=='/') {
                        /* Assume full path given */
                        prog=g_strdup (cmd);
+
+                       /* Executable existing ? */
+                       if(access (prog, X_OK)!=0) {
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", prog);
+#endif
+                               g_free (prog);
+                               SetLastError (ERROR_FILE_NOT_FOUND);
+                               goto cleanup;
+                       }
                } else {
                        /* Search for file named by cmd in the current
                         * directory
@@ -220,6 +261,8 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                        prog=g_strdup_printf ("%s/%s", curdir, cmd);
                        g_free (curdir);
                }
+
+               args_after_prog=args;
        } else {
                gchar *token=NULL;
                
@@ -227,6 +270,15 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                 * marks into account
                 */
 
+               /* First, strip off all leading whitespace */
+               args=g_strchug (args);
+               
+               /* args_after_prog points to the contents of args
+                * after token has been set (otherwise argv[0] is
+                * duplicated)
+                */
+               args_after_prog=args;
+
                /* Assume the opening quote will always be the first
                 * character
                 */
@@ -235,6 +287,7 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                        if(g_ascii_isspace (args[i+1])) {
                                /* We found the first token */
                                token=g_strndup (args+1, i-1);
+                               args_after_prog=args+i;
                        } else {
                                /* Quotation mark appeared in the
                                 * middle of the token.  Just give the
@@ -249,6 +302,7 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                        for(i=0; args[i]!='\0'; i++) {
                                if(g_ascii_isspace (args[i])) {
                                        token=g_strndup (args, i);
+                                       args_after_prog=args+i+1;
                                        break;
                                }
                        }
@@ -257,6 +311,7 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                if(token==NULL && args[0]!='\0') {
                        /* Must be just one token in the string */
                        token=g_strdup (args);
+                       args_after_prog=NULL;
                }
                
                if(token==NULL) {
@@ -266,9 +321,17 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                                   ": Couldn't find what to exec");
 #endif
 
+                       SetLastError(ERROR_PATH_NOT_FOUND);
                        goto cleanup;
                }
                
+               /* Turn all the slashes round the right way. Only for the prg. name */
+               for(i=0; i < strlen (token); i++) {
+                       if (token[i]=='\\') {
+                               token[i]='/';
+                       }
+               }
+
                if(g_ascii_isalpha (token[0]) && (token[1]==':')) {
                        /* Strip off the drive letter.  I can't
                         * believe that CP/M holdover is still
@@ -281,6 +344,18 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
                if(token[0]=='/') {
                        /* Assume full path given */
                        prog=g_strdup (token);
+                       
+                       /* Executable existing ? */
+                       if(access (prog, X_OK)!=0) {
+                               g_free (prog);
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", token);
+#endif
+                               g_free (token);
+                               SetLastError (ERROR_FILE_NOT_FOUND);
+                               goto cleanup;
+                       }
+
                } else {
                        char *curdir=g_get_current_dir ();
 
@@ -305,6 +380,7 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
 #endif
 
                                        g_free (token);
+                                       SetLastError (ERROR_FILE_NOT_FOUND);
                                        goto cleanup;
                                }
                        }
@@ -314,44 +390,62 @@ gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
        }
 
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": Exec prog [%s] args [%s]",
-                  prog, args);
+       g_message (G_GNUC_PRETTY_FUNCTION ": Exec prog [%s] args [%s]", prog,
+                  args_after_prog);
 #endif
        
-       stored_prog=_wapi_handle_scratch_store (prog, strlen (prog));
-       stored_args=_wapi_handle_scratch_store (args, strlen (args));
-       
-       stdin_handle=GetStdHandle (STD_INPUT_HANDLE);
-       stdout_handle=GetStdHandle (STD_OUTPUT_HANDLE);
-       stderr_handle=GetStdHandle (STD_ERROR_HANDLE);
+       if(args_after_prog!=NULL) {
+               full_prog=g_strconcat (prog, " ", args_after_prog, NULL);
+       } else {
+               full_prog=g_strdup (prog);
+       }
        
-       if(startup!=NULL) {
-               if(startup->dwFlags & STARTF_USESTDHANDLES) {
-                       stdin_handle=startup->hStdInput;
-                       stdout_handle=startup->hStdOutput;
-                       stderr_handle=startup->hStdError;
-               }
+       stored_prog=_wapi_handle_scratch_store (full_prog, strlen (full_prog));
+
+       if(startup!=NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
+               stdin_handle=startup->hStdInput;
+               stdout_handle=startup->hStdOutput;
+               stderr_handle=startup->hStdError;
+       } else {
+               stdin_handle=GetStdHandle (STD_INPUT_HANDLE);
+               stdout_handle=GetStdHandle (STD_OUTPUT_HANDLE);
+               stderr_handle=GetStdHandle (STD_ERROR_HANDLE);
        }
        
-       ret=_wapi_handle_process_fork (stored_prog, stored_args, env,
-                                      stored_dir, inherit_handles,
-                                      create_flags, stdin_handle,
-                                      stdout_handle, stderr_handle,
-                                      &process_handle, &thread_handle, &pid,
-                                      &tid);
+       ret=_wapi_handle_process_fork (stored_prog, env, stored_dir,
+                                      inherit_handles, create_flags,
+                                      stdin_handle, stdout_handle,
+                                      stderr_handle, &process_handle,
+                                      &thread_handle, &pid, &tid);
        
        if(ret==TRUE && process_info!=NULL) {
                process_info->hProcess=process_handle;
                process_info->hThread=thread_handle;
                process_info->dwProcessId=pid;
                process_info->dwThreadId=tid;
+               /* Wait for possible execve failure */
+               if (WaitForSingleObjectEx (process_handle, 500, FALSE) != WAIT_TIMEOUT) {
+                       _wapi_lookup_handle (GUINT_TO_POINTER (process_handle),
+                                            WAPI_HANDLE_PROCESS,
+                                            (gpointer *) &process_handle_data,
+                                            NULL);
+               
+                       if (process_handle_data && process_handle_data->exec_errno != 0) {
+                               ret = FALSE;
+                               SetLastError (ERROR_PATH_NOT_FOUND);
+                       }
+               }
+       } else if (ret==FALSE) {
+               /* FIXME: work out a better error code
+                */
+               SetLastError (ERROR_PATH_NOT_FOUND);
        }
-       
+
 cleanup:
        if(cmd!=NULL) {
                g_free (cmd);
        }
-       if(prog!=NULL) {
+       if(full_prog!=NULL) {
                g_free (prog);
        }
        if(stored_prog!=0) {
@@ -360,9 +454,6 @@ cleanup:
        if(args!=NULL) {
                g_free (args);
        }
-       if(stored_args!=0) {
-               _wapi_handle_scratch_delete (stored_args);
-       }
        if(dir!=NULL) {
                g_free (dir);
        }
@@ -370,46 +461,47 @@ cleanup:
                _wapi_handle_scratch_delete (stored_dir);
        }
        if(env!=0) {
-               _wapi_handle_scratch_delete (env);
+               _wapi_handle_scratch_delete_string_array (env);
        }
        
        return(ret);
 }
-
-static gboolean process_compare (gpointer handle, gpointer user_data)
+               
+static void process_set_name (struct _WapiHandle_process *process_handle)
 {
-       struct _WapiHandle_process *process_handle;
-       gboolean ok;
-       pid_t pid;
+       gchar *progname, *utf8_progname, *slash;
        
-       ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
-                               (gpointer *)&process_handle, NULL);
-       if(ok==FALSE) {
-               g_warning (G_GNUC_PRETTY_FUNCTION
-                          ": error looking up process handle %p", handle);
-               return(FALSE);
-       }
+       progname=g_get_prgname ();
+       utf8_progname=mono_utf8_from_external (progname);
 
-       pid=GPOINTER_TO_UINT (user_data);
-       if(process_handle->id==pid) {
-               return(TRUE);
-       } else {
-               return(FALSE);
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION ": using [%s] as prog name",
+                  progname);
+#endif
+
+       if(utf8_progname!=NULL) {
+               slash=strrchr (utf8_progname, '/');
+               if(slash!=NULL) {
+                       process_handle->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
+               } else {
+                       process_handle->proc_name=_wapi_handle_scratch_store (utf8_progname, strlen (utf8_progname));
+               }
+
+               g_free (utf8_progname);
        }
 }
-       
+
+extern void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime);
+
 static void process_set_current (void)
 {
        struct _WapiHandle_process *process_handle;
        gboolean ok;
        pid_t pid=getpid ();
+       char *handle_env;
        
-       current_process=_wapi_search_handle (WAPI_HANDLE_PROCESS,
-                                            process_compare,
-                                            GUINT_TO_POINTER (pid),
-                                            (gpointer *)&process_handle,
-                                            NULL);
-       if(current_process==0) {
+       handle_env=getenv ("_WAPI_PROCESS_HANDLE");
+       if(handle_env==NULL) {
 #ifdef DEBUG
                g_message (G_GNUC_PRETTY_FUNCTION
                           ": Need to create my own process handle");
@@ -432,10 +524,58 @@ static void process_set_current (void)
                }
 
                process_handle->id=pid;
+               
+               /* These seem to be the defaults on w2k */
+               process_handle->min_working_set=204800;
+               process_handle->max_working_set=1413120;
+
+               _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
+
+               process_set_name (process_handle);
+               
+               /* Make sure the new handle has a reference so it wont go away
+                * until this process exits
+                */
+               _wapi_handle_ref (current_process);
        } else {
+               guchar *procname;
+               
+               current_process=GUINT_TO_POINTER (atoi (handle_env));
+
 #ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION ": Found my process handle");
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          ": Found my process handle: %p", current_process);
 #endif
+
+               ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
+                                       (gpointer *)&process_handle, NULL);
+               if(ok==FALSE) {
+                       g_warning (G_GNUC_PRETTY_FUNCTION
+                                  ": error looking up process handle %p",
+                                  current_process);
+                       return;
+               }
+
+               procname=_wapi_handle_scratch_lookup (process_handle->proc_name);
+               if(procname!=NULL) {
+                       if(!strcmp (procname, "mono")) {
+                               /* Set a better process name */
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION ": Setting better process name");
+#endif
+
+                               _wapi_handle_scratch_delete (process_handle->proc_name);
+                               process_set_name (process_handle);
+                       } else {
+#ifdef DEBUG
+                               g_message (G_GNUC_PRETTY_FUNCTION
+                                          ": Leaving process name: %s",
+                                          procname);
+#endif
+                       }
+                       
+                       g_free (procname);
+               }
        }
 }
 
@@ -449,16 +589,35 @@ gpointer GetCurrentProcess (void)
 
 guint32 GetCurrentProcessId (void)
 {
+       struct _WapiHandle_process *current_process_handle;
+       gboolean ok;
+       
        mono_once (&process_current_once, process_set_current);
                
-       return(getpid ());
+       ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
+                               (gpointer *)&current_process_handle, NULL);
+       if(ok==FALSE) {
+               g_warning (G_GNUC_PRETTY_FUNCTION
+                          ": error looking up current process handle %p",
+                          current_process);
+               /* No failure return is defined.  PID 0 is invalid.
+                * This should only be reached when something else has
+                * gone badly wrong anyway.
+                */
+               return(0);
+       }
+       
+       return(current_process_handle->id);
 }
 
 static gboolean process_enum (gpointer handle, gpointer user_data)
 {
        GPtrArray *processes=user_data;
        
-       g_ptr_array_add (processes, handle);
+       /* Ignore processes that have already exited (ie they are signalled) */
+       if(_wapi_handle_issignalled (handle)==FALSE) {
+               g_ptr_array_add (processes, handle);
+       }
        
        /* Return false to keep searching */
        return(FALSE);
@@ -469,6 +628,8 @@ gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
        GPtrArray *processes=g_ptr_array_new ();
        guint32 fit, i;
        
+       mono_once (&process_current_once, process_set_current);
+       
        _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
                             NULL, NULL);
        
@@ -617,3 +778,196 @@ gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
        
        return(TRUE);
 }
+
+gboolean EnumProcessModules (gpointer process, gpointer *modules,
+                            guint32 size, guint32 *needed)
+{
+       /* 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
+        * so that the simple implementation can just return one item
+        * for now.)  Get the info from /proc/<pid>/maps on linux,
+        * other systems will have to implement /dev/kmem reading or
+        * whatever other horrid technique is needed.
+        */
+       if(size<sizeof(gpointer)) {
+               return(FALSE);
+       }
+       
+#ifdef linux
+       modules[0]=NULL;
+       *needed=sizeof(gpointer);
+#else
+       modules[0]=NULL;
+       *needed=sizeof(gpointer);
+#endif
+       
+       return(TRUE);
+}
+
+guint32 GetModuleBaseName (gpointer process, gpointer module,
+                          gunichar2 *basename, guint32 size)
+{
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       
+       mono_once (&process_current_once, process_set_current);
+
+#ifdef DEBUG
+       g_message (G_GNUC_PRETTY_FUNCTION
+                  ": Getting module base name, process handle %p module %p",
+                  process, module);
+#endif
+
+       if(basename==NULL || size==0) {
+               return(FALSE);
+       }
+       
+       ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                               (gpointer *)&process_handle, NULL);
+       if(ok==FALSE) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
+                          process);
+#endif
+               
+               return(FALSE);
+       }
+
+       if(module==NULL) {
+               /* Shorthand for the main module, which has the
+                * process name recorded in the handle data
+                */
+               pid_t pid;
+               gunichar2 *procname;
+               guchar *procname_utf8;
+               glong len, bytes;
+               
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION
+                          ": Returning main module name");
+#endif
+
+               pid=process_handle->id;
+               procname_utf8=_wapi_handle_scratch_lookup (process_handle->proc_name);
+       
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Process name is [%s]",
+                          procname_utf8);
+#endif
+
+               procname=g_utf8_to_utf16 (procname_utf8, -1, NULL, &len, NULL);
+               if(procname==NULL) {
+                       /* bugger */
+                       g_free (procname_utf8);
+                       return(0);
+               }
+
+               /* Add the terminator, and convert chars to bytes */
+               bytes=(len+1)*2;
+               
+               if(size<bytes) {
+#ifdef DEBUG
+                       g_message (G_GNUC_PRETTY_FUNCTION ": Size %d smaller than needed (%ld); truncating", size, bytes);
+#endif
+
+                       memcpy (basename, procname, size);
+               } else {
+#ifdef DEBUG
+                       g_message (G_GNUC_PRETTY_FUNCTION
+                                  ": Size %d larger than needed (%ld)",
+                                  size, bytes);
+#endif
+
+                       memcpy (basename, procname, bytes);
+               }
+               
+               g_free (procname_utf8);
+               g_free (procname);
+
+               return(len);
+       } else {
+               /* Look up the address in /proc/<pid>/maps */
+       }
+       
+       return(0);
+}
+
+gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
+{
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       
+       mono_once (&process_current_once, process_set_current);
+
+       if(min==NULL || max==NULL) {
+               /* Not sure if w32 allows NULLs here or not */
+               return(FALSE);
+       }
+       
+       ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                               (gpointer *)&process_handle, NULL);
+       if(ok==FALSE) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
+                          process);
+#endif
+               
+               return(FALSE);
+       }
+
+       *min=process_handle->min_working_set;
+       *max=process_handle->max_working_set;
+       
+       return(TRUE);
+}
+
+gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
+{
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+
+       mono_once (&process_current_once, process_set_current);
+
+       ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                               (gpointer *)&process_handle, NULL);
+       if(ok==FALSE) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
+                          process);
+#endif
+               
+               return(FALSE);
+       }
+
+       process_handle->min_working_set=min;
+       process_handle->max_working_set=max;
+       
+       return(TRUE);
+}
+
+
+gboolean
+TerminateProcess (gpointer process, gint32 exitCode)
+{
+       struct _WapiHandle_process *process_handle;
+       gboolean ok;
+       gint signo;
+       gint err;
+
+       ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+                                 (gpointer *) &process_handle, NULL);
+
+       if (ok == FALSE) {
+#ifdef DEBUG
+               g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
+                          process);
+#endif
+               SetLastError (ERROR_INVALID_HANDLE);
+               return FALSE;
+       }
+
+       signo = (exitCode == -1) ? SIGKILL : SIGTERM;
+       return _wapi_handle_process_kill (process_handle->id, signo, &err);
+}
+