2003-03-13 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mono / io-layer / processes.c
1 /*
2  * processes.c:  Process handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #if HAVE_BOEHM_GC
12 #include <mono/os/gc_wrapper.h>
13 #include "mono/utils/mono-hash.h"
14 #endif
15 #include <glib.h>
16 #include <string.h>
17 #include <pthread.h>
18 #include <sched.h>
19 #include <sys/time.h>
20 #include <errno.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <mono/io-layer/wapi.h>
25 #include <mono/io-layer/unicode.h>
26 #include <mono/io-layer/wapi-private.h>
27 #include <mono/io-layer/handles-private.h>
28 #include <mono/io-layer/misc-private.h>
29 #include <mono/io-layer/mono-mutex.h>
30 #include <mono/io-layer/process-private.h>
31 #include <mono/io-layer/threads.h>
32
33 /* The process' environment strings */
34 extern char **environ;
35
36 #undef DEBUG
37
38 static void process_close_shared (gpointer handle);
39
40 struct _WapiHandleOps _wapi_process_ops = {
41         process_close_shared,           /* close_shared */
42         NULL,                           /* close_private */
43         NULL,                           /* signal */
44         NULL,                           /* own */
45         NULL,                           /* is_owned */
46 };
47
48 static mono_once_t process_current_once=MONO_ONCE_INIT;
49 static gpointer current_process=NULL;
50
51 static mono_once_t process_ops_once=MONO_ONCE_INIT;
52
53 static void process_ops_init (void)
54 {
55         _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS,
56                                             WAPI_HANDLE_CAP_WAIT);
57 }
58
59 static void process_close_shared (gpointer handle G_GNUC_UNUSED)
60 {
61         struct _WapiHandle_process *process_handle;
62         gboolean ok;
63
64         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
65                                 (gpointer *)&process_handle, NULL);
66         if(ok==FALSE) {
67                 g_warning (G_GNUC_PRETTY_FUNCTION
68                            ": error looking up process handle %p", handle);
69                 return;
70         }
71
72 #ifdef DEBUG
73         g_message (G_GNUC_PRETTY_FUNCTION
74                    ": closing process handle %p with id %d", handle,
75                    process_handle->id);
76 #endif
77
78         if(process_handle->proc_name!=0) {
79                 _wapi_handle_scratch_delete (process_handle->proc_name);
80                 process_handle->proc_name=0;
81         }
82 }
83
84 gboolean CreateProcess (const gunichar2 *appname, gunichar2 *cmdline,
85                         WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
86                         WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
87                         gboolean inherit_handles, guint32 create_flags,
88                         gpointer new_environ, const gunichar2 *cwd,
89                         WapiStartupInfo *startup,
90                         WapiProcessInformation *process_info)
91 {
92         gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args=NULL, *args_after_prog=NULL, *dir=NULL;
93         guint32 env=0, stored_dir=0, stored_prog=0, i;
94         gboolean ret=FALSE;
95         gpointer stdin_handle, stdout_handle, stderr_handle;
96         guint32 pid, tid;
97         gpointer process_handle, thread_handle;
98         
99         mono_once (&process_ops_once, process_ops_init);
100         
101         /* appname and cmdline specify the executable and its args:
102          *
103          * If appname is not NULL, it is the name of the executable.
104          * Otherwise the executable is the first token in cmdline.
105          *
106          * Executable searching:
107          *
108          * If appname is not NULL, it can specify the full path and
109          * file name, or else a partial name and the current directory
110          * will be used.  There is no additional searching.
111          *
112          * If appname is NULL, the first whitespace-delimited token in
113          * cmdline is used.  If the name does not contain a full
114          * directory path, the search sequence is:
115          *
116          * 1) The directory containing the current process
117          * 2) The current working directory
118          * 3) The windows system directory  (Ignored)
119          * 4) The windows directory (Ignored)
120          * 5) $PATH
121          *
122          * Just to make things more interesting, tokens can contain
123          * white space if they are surrounded by quotation marks.  I'm
124          * beginning to understand just why windows apps are generally
125          * so crap, with an API like this :-(
126          */
127         if(appname!=NULL) {
128                 cmd=_wapi_unicode_to_utf8 (appname);
129                 if(cmd==NULL) {
130 #ifdef DEBUG
131                         g_message (G_GNUC_PRETTY_FUNCTION
132                                    ": unicode conversion returned NULL");
133 #endif
134
135                         goto cleanup;
136                 }
137
138                 /* Turn all the slashes round the right way */
139                 for(i=0; i<strlen (cmd); i++) {
140                         if(cmd[i]=='\\') {
141                                 cmd[i]='/';
142                         }
143                 }
144         }
145         
146         if(cmdline!=NULL) {
147                 args=_wapi_unicode_to_utf8 (cmdline);
148                 if(args==NULL) {
149 #ifdef DEBUG
150                         g_message (G_GNUC_PRETTY_FUNCTION
151                                    ": unicode conversion returned NULL");
152 #endif
153
154                         goto cleanup;
155                 }
156
157                 /* Turn all the slashes round the right way */
158                 for(i=0; i<strlen (args); i++) {
159                         if(args[i]=='\\') {
160                                 args[i]='/';
161                         }
162                 }
163         }
164
165         if(cwd!=NULL) {
166                 dir=_wapi_unicode_to_utf8 (cwd);
167                 if(dir==NULL) {
168 #ifdef DEBUG
169                         g_message (G_GNUC_PRETTY_FUNCTION
170                                    ": unicode conversion returned NULL");
171 #endif
172
173                         goto cleanup;
174                 }
175
176                 /* Turn all the slashes round the right way */
177                 for(i=0; i<strlen (dir); i++) {
178                         if(dir[i]=='\\') {
179                                 dir[i]='/';
180                         }
181                 }
182         } else {
183                 dir=g_get_current_dir ();
184         }
185         stored_dir=_wapi_handle_scratch_store (dir, strlen (dir));
186         
187         
188         /* new_environ is a block of NULL-terminated strings, which
189          * is itself NULL-terminated. Of course, passing an array of
190          * string pointers would have made things too easy :-(
191          *
192          * If new_environ is not NULL it specifies the entire set of
193          * environment variables in the new process.  Otherwise the
194          * new process inherits the same environment.
195          */
196         if(new_environ!=NULL) {
197                 gchar **strings;
198                 guint32 count;
199                 gunichar2 *new_environp;
200
201                 /* Count the number of strings */
202                 for(new_environp=(gunichar2 *)new_environ; *new_environp;
203                     new_environp++) {
204                         count++;
205                         while(*new_environp) {
206                                 new_environp++;
207                         }
208                 }
209                 strings=g_new0 (gchar *, count);
210                 
211                 /* Copy each environ string into 'strings' turning it
212                  * into utf8 at the same time
213                  */
214                 count=0;
215                 for(new_environp=(gunichar2 *)new_environ; *new_environp;
216                     new_environp++) {
217                         strings[count]=g_utf16_to_utf8 (new_environp, -1, NULL,
218                                                         NULL, NULL);
219                         count++;
220                         while(*new_environp) {
221                                 new_environp++;
222                         }
223                 }
224
225                 env=_wapi_handle_scratch_store_string_array (strings);
226
227                 g_strfreev (strings);
228         } else {
229                 /* Use the existing environment */
230                 env=_wapi_handle_scratch_store_string_array (environ);
231         }
232
233         /* We can't put off locating the executable any longer :-( */
234         if(cmd!=NULL) {
235                 if(g_ascii_isalpha (cmd[0]) && (cmd[1]==':')) {
236                         /* Strip off the drive letter.  I can't
237                          * believe that CP/M holdover is still
238                          * visible...
239                          */
240                         memmove (cmd, cmd+2, strlen (cmd)-2);
241                         cmd[strlen (cmd)-2]='\0';
242                 }
243
244                 if(cmd[0]=='/') {
245                         /* Assume full path given */
246                         prog=g_strdup (cmd);
247
248                         /* Executable existing ? */
249                         if(access (prog, X_OK)!=0) {
250                                 g_free (prog);
251 #ifdef DEBUG
252                                 g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", token);
253 #endif
254                                 SetLastError (ERROR_FILE_NOT_FOUND);
255                                 goto cleanup;
256                         }
257                 } else {
258                         /* Search for file named by cmd in the current
259                          * directory
260                          */
261                         char *curdir=g_get_current_dir ();
262
263                         prog=g_strdup_printf ("%s/%s", curdir, cmd);
264                         g_free (curdir);
265                 }
266
267                 args_after_prog=args;
268         } else {
269                 gchar *token=NULL;
270                 
271                 /* Dig out the first token from args, taking quotation
272                  * marks into account
273                  */
274
275                 /* First, strip off all leading whitespace */
276                 args=g_strchug (args);
277                 
278                 /* args_after_prog points to the contents of args
279                  * after token has been set (otherwise argv[0] is
280                  * duplicated)
281                  */
282                 args_after_prog=args;
283
284                 /* Assume the opening quote will always be the first
285                  * character
286                  */
287                 if(args[0]=='\"') {
288                         for(i=1; args[i]!='\0' && args[i]!='\"'; i++);
289                         if(g_ascii_isspace (args[i+1])) {
290                                 /* We found the first token */
291                                 token=g_strndup (args+1, i-1);
292                                 args_after_prog=args+i;
293                         } else {
294                                 /* Quotation mark appeared in the
295                                  * middle of the token.  Just give the
296                                  * whole first token, quotes and all,
297                                  * to exec.
298                                  */
299                         }
300                 }
301                 
302                 if(token==NULL) {
303                         /* No quote mark, or malformed */
304                         for(i=0; args[i]!='\0'; i++) {
305                                 if(g_ascii_isspace (args[i])) {
306                                         token=g_strndup (args, i);
307                                         args_after_prog=args+i;
308                                         break;
309                                 }
310                         }
311                 }
312
313                 if(token==NULL && args[0]!='\0') {
314                         /* Must be just one token in the string */
315                         token=g_strdup (args);
316                         args_after_prog=NULL;
317                 }
318                 
319                 if(token==NULL) {
320                         /* Give up */
321 #ifdef DEBUG
322                         g_message (G_GNUC_PRETTY_FUNCTION
323                                    ": Couldn't find what to exec");
324 #endif
325
326                         goto cleanup;
327                 }
328                 
329                 if(g_ascii_isalpha (token[0]) && (token[1]==':')) {
330                         /* Strip off the drive letter.  I can't
331                          * believe that CP/M holdover is still
332                          * visible...
333                          */
334                         memmove (token, token+2, strlen (token)-2);
335                         token[strlen (token)-2]='\0';
336                 }
337
338                 if(token[0]=='/') {
339                         /* Assume full path given */
340                         prog=g_strdup (token);
341                         
342                         /* Executable existing ? */
343                         if(access (prog, X_OK)!=0) {
344                                 g_free (prog);
345 #ifdef DEBUG
346                                 g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", token);
347 #endif
348                                 g_free (token);
349                                 SetLastError (ERROR_FILE_NOT_FOUND);
350                                 goto cleanup;
351                         }
352
353                 } else {
354                         char *curdir=g_get_current_dir ();
355
356                         /* FIXME: Need to record the directory
357                          * containing the current process, and check
358                          * that for the new executable as the first
359                          * place to look
360                          */
361
362                         prog=g_strdup_printf ("%s/%s", curdir, token);
363                         g_free (curdir);
364
365                         /* I assume X_OK is the criterion to use,
366                          * rather than F_OK
367                          */
368                         if(access (prog, X_OK)!=0) {
369                                 g_free (prog);
370                                 prog=g_find_program_in_path (token);
371                                 if(prog==NULL) {
372 #ifdef DEBUG
373                                         g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", token);
374 #endif
375
376                                         g_free (token);
377                                         SetLastError (ERROR_FILE_NOT_FOUND);
378                                         goto cleanup;
379                                 }
380                         }
381                 }
382
383                 g_free (token);
384         }
385
386 #ifdef DEBUG
387         g_message (G_GNUC_PRETTY_FUNCTION ": Exec prog [%s] args [%s]", prog,
388                    args_after_prog);
389 #endif
390         
391         if(args_after_prog!=NULL) {
392                 full_prog=g_strconcat (prog, " ", args_after_prog, NULL);
393         } else {
394                 full_prog=g_strdup (prog);
395         }
396         
397         stored_prog=_wapi_handle_scratch_store (full_prog, strlen (full_prog));
398
399         if(startup!=NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
400                 stdin_handle=startup->hStdInput;
401                 stdout_handle=startup->hStdOutput;
402                 stderr_handle=startup->hStdError;
403         } else {
404                 stdin_handle=GetStdHandle (STD_INPUT_HANDLE);
405                 stdout_handle=GetStdHandle (STD_OUTPUT_HANDLE);
406                 stderr_handle=GetStdHandle (STD_ERROR_HANDLE);
407         }
408         
409         ret=_wapi_handle_process_fork (stored_prog, env, stored_dir,
410                                        inherit_handles, create_flags,
411                                        stdin_handle, stdout_handle,
412                                        stderr_handle, &process_handle,
413                                        &thread_handle, &pid, &tid);
414         
415         if(ret==TRUE && process_info!=NULL) {
416                 process_info->hProcess=process_handle;
417                 process_info->hThread=thread_handle;
418                 process_info->dwProcessId=pid;
419                 process_info->dwThreadId=tid;
420         }
421         
422 cleanup:
423         if(cmd!=NULL) {
424                 g_free (cmd);
425         }
426         if(full_prog!=NULL) {
427                 g_free (prog);
428         }
429         if(stored_prog!=0) {
430                 _wapi_handle_scratch_delete (stored_prog);
431         }
432         if(args!=NULL) {
433                 g_free (args);
434         }
435         if(dir!=NULL) {
436                 g_free (dir);
437         }
438         if(stored_dir!=0) {
439                 _wapi_handle_scratch_delete (stored_dir);
440         }
441         if(env!=0) {
442                 _wapi_handle_scratch_delete_string_array (env);
443         }
444         
445         return(ret);
446 }
447
448 static gboolean process_compare (gpointer handle, gpointer user_data)
449 {
450         struct _WapiHandle_process *process_handle;
451         gboolean ok;
452         pid_t pid;
453         
454         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
455                                 (gpointer *)&process_handle, NULL);
456         if(ok==FALSE) {
457                 g_warning (G_GNUC_PRETTY_FUNCTION
458                            ": error looking up process handle %p", handle);
459                 return(FALSE);
460         }
461
462         pid=GPOINTER_TO_UINT (user_data);
463         if(process_handle->id==pid) {
464                 return(TRUE);
465         } else {
466                 return(FALSE);
467         }
468 }
469         
470 static void process_set_current (void)
471 {
472         struct _WapiHandle_process *process_handle;
473         gboolean ok;
474         pid_t pid=getpid ();
475         
476         current_process=_wapi_search_handle (WAPI_HANDLE_PROCESS,
477                                              process_compare,
478                                              GUINT_TO_POINTER (pid),
479                                              (gpointer *)&process_handle,
480                                              NULL);
481         if(current_process==0) {
482                 gchar *progname, *slash;
483                 
484 #ifdef DEBUG
485                 g_message (G_GNUC_PRETTY_FUNCTION
486                            ": Need to create my own process handle");
487 #endif
488
489                 current_process=_wapi_handle_new (WAPI_HANDLE_PROCESS);
490                 if(current_process==_WAPI_HANDLE_INVALID) {
491                         g_warning (G_GNUC_PRETTY_FUNCTION
492                                    ": error creating process handle");
493                         return;
494                 }
495
496                 ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
497                                         (gpointer *)&process_handle, NULL);
498                 if(ok==FALSE) {
499                         g_warning (G_GNUC_PRETTY_FUNCTION
500                                    ": error looking up process handle %p",
501                                    current_process);
502                         return;
503                 }
504
505                 process_handle->id=pid;
506                 
507                 /* These seem to be the defaults on w2k */
508                 process_handle->min_working_set=204800;
509                 process_handle->max_working_set=1413120;
510                 
511                 progname=g_get_prgname ();
512
513 #ifdef DEBUG
514                 g_message (G_GNUC_PRETTY_FUNCTION ": using [%s] as prog name",
515                            progname);
516 #endif
517
518                 if(progname!=NULL) {
519                         slash=strrchr (progname, '/');
520                         if(slash!=NULL) {
521                                 process_handle->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
522                         } else {
523                                 process_handle->proc_name=_wapi_handle_scratch_store (progname, strlen (progname));
524                         }
525                 }
526
527                 /* Make sure the new handle has a reference so it wont go away
528                  * until this process exits
529                  */
530                 _wapi_handle_ref (current_process);
531         } else {
532 #ifdef DEBUG
533                 g_message (G_GNUC_PRETTY_FUNCTION ": Found my process handle");
534 #endif
535         }
536 }
537
538 /* Returns a pseudo handle that doesn't need to be closed afterwards */
539 gpointer GetCurrentProcess (void)
540 {
541         mono_once (&process_current_once, process_set_current);
542                 
543         return((gpointer)-1);
544 }
545
546 guint32 GetCurrentProcessId (void)
547 {
548         struct _WapiHandle_process *current_process_handle;
549         gboolean ok;
550         
551         mono_once (&process_current_once, process_set_current);
552                 
553         ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
554                                 (gpointer *)&current_process_handle, NULL);
555         if(ok==FALSE) {
556                 g_warning (G_GNUC_PRETTY_FUNCTION
557                            ": error looking up current process handle %p",
558                            current_process);
559                 /* No failure return is defined.  PID 0 is invalid.
560                  * This should only be reached when something else has
561                  * gone badly wrong anyway.
562                  */
563                 return(0);
564         }
565         
566         return(current_process_handle->id);
567 }
568
569 static gboolean process_enum (gpointer handle, gpointer user_data)
570 {
571         GPtrArray *processes=user_data;
572         
573         /* Ignore processes that have already exited (ie they are signalled) */
574         if(_wapi_handle_issignalled (handle)==FALSE) {
575                 g_ptr_array_add (processes, handle);
576         }
577         
578         /* Return false to keep searching */
579         return(FALSE);
580 }
581
582 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
583 {
584         GPtrArray *processes=g_ptr_array_new ();
585         guint32 fit, i;
586         
587         mono_once (&process_current_once, process_set_current);
588         
589         _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
590                              NULL, NULL);
591         
592         fit=len/sizeof(guint32);
593         for(i=0; i<fit && i<processes->len; i++) {
594                 struct _WapiHandle_process *process_handle;
595                 gboolean ok;
596
597                 ok=_wapi_lookup_handle (g_ptr_array_index (processes, i),
598                                         WAPI_HANDLE_PROCESS,
599                                         (gpointer *)&process_handle, NULL);
600                 if(ok==FALSE) {
601                         g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", g_ptr_array_index (processes, i));
602                         g_ptr_array_free (processes, FALSE);
603                         return(FALSE);
604                 }
605
606                 pids[i]=process_handle->id;
607         }
608
609         g_ptr_array_free (processes, FALSE);
610         
611         *needed=i*sizeof(guint32);
612         
613         return(TRUE);
614 }
615
616 static gboolean process_open_compare (gpointer handle, gpointer user_data)
617 {
618         struct _WapiHandle_process *process_handle;
619         gboolean ok;
620         pid_t pid;
621         
622         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
623                                 (gpointer *)&process_handle, NULL);
624         if(ok==FALSE) {
625                 g_warning (G_GNUC_PRETTY_FUNCTION
626                            ": error looking up process handle %p", handle);
627                 return(FALSE);
628         }
629
630         pid=GPOINTER_TO_UINT (user_data);
631
632         /* It's possible to have more than one process handle with the
633          * same pid, but only the one running process can be
634          * unsignalled
635          */
636         if(process_handle->id==pid &&
637            _wapi_handle_issignalled (handle)==FALSE) {
638                 return(TRUE);
639         } else {
640                 return(FALSE);
641         }
642 }
643
644 gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
645 {
646         /* Find the process handle that corresponds to pid */
647         gpointer handle;
648         
649         mono_once (&process_current_once, process_set_current);
650
651         handle=_wapi_search_handle (WAPI_HANDLE_PROCESS, process_open_compare,
652                                     GUINT_TO_POINTER (pid), NULL, NULL);
653         if(handle==0) {
654 #ifdef DEBUG
655                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find pid %d", pid);
656 #endif
657
658                 /* Set an error code */
659         
660                 return(NULL);
661         }
662
663         _wapi_handle_ref (handle);
664         
665         return(handle);
666 }
667
668 gboolean GetExitCodeProcess (gpointer process, guint32 *code)
669 {
670         struct _WapiHandle_process *process_handle;
671         gboolean ok;
672         
673         mono_once (&process_current_once, process_set_current);
674
675         if(code==NULL) {
676                 return(FALSE);
677         }
678         
679         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
680                                 (gpointer *)&process_handle, NULL);
681         if(ok==FALSE) {
682 #ifdef DEBUG
683                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
684                            process);
685 #endif
686                 
687                 return(FALSE);
688         }
689         
690         /* A process handle is only signalled if the process has exited */
691         if(_wapi_handle_issignalled (process)==TRUE) {
692                 *code=process_handle->exitstatus;
693         } else {
694                 *code=STILL_ACTIVE;
695         }
696         
697         return(TRUE);
698 }
699
700 gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
701                           WapiFileTime *exit_time, WapiFileTime *kernel_time,
702                           WapiFileTime *user_time)
703 {
704         struct _WapiHandle_process *process_handle;
705         gboolean ok;
706         
707         mono_once (&process_current_once, process_set_current);
708
709         if(create_time==NULL || exit_time==NULL || kernel_time==NULL ||
710            user_time==NULL) {
711                 /* Not sure if w32 allows NULLs here or not */
712                 return(FALSE);
713         }
714         
715         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
716                                 (gpointer *)&process_handle, NULL);
717         if(ok==FALSE) {
718 #ifdef DEBUG
719                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
720                            process);
721 #endif
722                 
723                 return(FALSE);
724         }
725         
726         *create_time=process_handle->create_time;
727
728         /* A process handle is only signalled if the process has
729          * exited.  Otherwise exit_time isn't set
730          */
731         if(_wapi_handle_issignalled (process)==TRUE) {
732                 *exit_time=process_handle->exit_time;
733         }
734         
735         return(TRUE);
736 }
737
738 gboolean EnumProcessModules (gpointer process, gpointer *modules,
739                              guint32 size, guint32 *needed)
740 {
741         /* Store modules in an array of pointers (main module as
742          * modules[0]), using the load address for each module as a
743          * token.  (Use 'NULL' as an alternative for the main module
744          * so that the simple implementation can just return one item
745          * for now.)  Get the info from /proc/<pid>/maps on linux,
746          * other systems will have to implement /dev/kmem reading or
747          * whatever other horrid technique is needed.
748          */
749         if(size<sizeof(gpointer)) {
750                 return(FALSE);
751         }
752         
753 #ifdef linux
754         modules[0]=NULL;
755         *needed=sizeof(gpointer);
756 #else
757         modules[0]=NULL;
758         *needed=sizeof(gpointer);
759 #endif
760         
761         return(TRUE);
762 }
763
764 guint32 GetModuleBaseName (gpointer process, gpointer module,
765                            gunichar2 *basename, guint32 size)
766 {
767         struct _WapiHandle_process *process_handle;
768         gboolean ok;
769         
770         mono_once (&process_current_once, process_set_current);
771
772 #ifdef DEBUG
773         g_message (G_GNUC_PRETTY_FUNCTION
774                    ": Getting module base name, process handle %p module %p",
775                    process, module);
776 #endif
777
778         if(basename==NULL || size==0) {
779                 return(FALSE);
780         }
781         
782         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
783                                 (gpointer *)&process_handle, NULL);
784         if(ok==FALSE) {
785 #ifdef DEBUG
786                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
787                            process);
788 #endif
789                 
790                 return(FALSE);
791         }
792
793         if(module==NULL) {
794                 /* Shorthand for the main module, which has the
795                  * process name recorded in the handle data
796                  */
797                 pid_t pid;
798                 gunichar2 *procname;
799                 guchar *procname_utf8;
800                 glong len, bytes;
801                 
802 #ifdef DEBUG
803                 g_message (G_GNUC_PRETTY_FUNCTION
804                            ": Returning main module name");
805 #endif
806
807                 pid=process_handle->id;
808                 procname_utf8=_wapi_handle_scratch_lookup_as_string (process_handle->proc_name);
809         
810 #ifdef DEBUG
811                 g_message (G_GNUC_PRETTY_FUNCTION ": Process name is [%s]",
812                            procname_utf8);
813 #endif
814
815                 procname=g_utf8_to_utf16 (procname_utf8, -1, NULL, &len, NULL);
816                 if(procname==NULL) {
817                         /* bugger */
818                         g_free (procname_utf8);
819                         return(0);
820                 }
821
822                 /* Add the terminator, and convert chars to bytes */
823                 bytes=(len+1)*2;
824                 
825                 if(size<bytes) {
826 #ifdef DEBUG
827                         g_message (G_GNUC_PRETTY_FUNCTION ": Size %d smaller than needed (%ld); truncating", size, bytes);
828 #endif
829
830                         memcpy (basename, procname, size);
831                 } else {
832 #ifdef DEBUG
833                         g_message (G_GNUC_PRETTY_FUNCTION
834                                    ": Size %d larger than needed (%ld)",
835                                    size, bytes);
836 #endif
837
838                         memcpy (basename, procname, bytes);
839                 }
840                 
841                 g_free (procname_utf8);
842                 g_free (procname);
843
844                 return(len);
845         } else {
846                 /* Look up the address in /proc/<pid>/maps */
847         }
848         
849         return(0);
850 }
851
852 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
853 {
854         struct _WapiHandle_process *process_handle;
855         gboolean ok;
856         
857         mono_once (&process_current_once, process_set_current);
858
859         if(min==NULL || max==NULL) {
860                 /* Not sure if w32 allows NULLs here or not */
861                 return(FALSE);
862         }
863         
864         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
865                                 (gpointer *)&process_handle, NULL);
866         if(ok==FALSE) {
867 #ifdef DEBUG
868                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
869                            process);
870 #endif
871                 
872                 return(FALSE);
873         }
874
875         *min=process_handle->min_working_set;
876         *max=process_handle->max_working_set;
877         
878         return(TRUE);
879 }
880
881 gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
882 {
883         struct _WapiHandle_process *process_handle;
884         gboolean ok;
885
886         mono_once (&process_current_once, process_set_current);
887
888         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
889                                 (gpointer *)&process_handle, NULL);
890         if(ok==FALSE) {
891 #ifdef DEBUG
892                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
893                            process);
894 #endif
895                 
896                 return(FALSE);
897         }
898
899         process_handle->min_working_set=min;
900         process_handle->max_working_set=max;
901         
902         return(TRUE);
903 }