2003-01-12 Alp Toker <alp@atoker.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, *full_prog, *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                 } else {
248                         /* Search for file named by cmd in the current
249                          * directory
250                          */
251                         char *curdir=g_get_current_dir ();
252
253                         prog=g_strdup_printf ("%s/%s", curdir, cmd);
254                         g_free (curdir);
255                 }
256
257                 args_after_prog=args;
258         } else {
259                 gchar *token=NULL;
260                 
261                 /* Dig out the first token from args, taking quotation
262                  * marks into account
263                  */
264
265                 /* First, strip off all leading whitespace */
266                 args=g_strchug (args);
267                 
268                 /* args_after_prog points to the contents of args
269                  * after token has been set (otherwise argv[0] is
270                  * duplicated)
271                  */
272                 args_after_prog=args;
273
274                 /* Assume the opening quote will always be the first
275                  * character
276                  */
277                 if(args[0]=='\"') {
278                         for(i=1; args[i]!='\0' && args[i]!='\"'; i++);
279                         if(g_ascii_isspace (args[i+1])) {
280                                 /* We found the first token */
281                                 token=g_strndup (args+1, i-1);
282                                 args_after_prog=args+i;
283                         } else {
284                                 /* Quotation mark appeared in the
285                                  * middle of the token.  Just give the
286                                  * whole first token, quotes and all,
287                                  * to exec.
288                                  */
289                         }
290                 }
291                 
292                 if(token==NULL) {
293                         /* No quote mark, or malformed */
294                         for(i=0; args[i]!='\0'; i++) {
295                                 if(g_ascii_isspace (args[i])) {
296                                         token=g_strndup (args, i);
297                                         args_after_prog=args+i;
298                                         break;
299                                 }
300                         }
301                 }
302
303                 if(token==NULL && args[0]!='\0') {
304                         /* Must be just one token in the string */
305                         token=g_strdup (args);
306                         args_after_prog=NULL;
307                 }
308                 
309                 if(token==NULL) {
310                         /* Give up */
311 #ifdef DEBUG
312                         g_message (G_GNUC_PRETTY_FUNCTION
313                                    ": Couldn't find what to exec");
314 #endif
315
316                         goto cleanup;
317                 }
318                 
319                 if(g_ascii_isalpha (token[0]) && (token[1]==':')) {
320                         /* Strip off the drive letter.  I can't
321                          * believe that CP/M holdover is still
322                          * visible...
323                          */
324                         memmove (token, token+2, strlen (token)-2);
325                         token[strlen (token)-2]='\0';
326                 }
327
328                 if(token[0]=='/') {
329                         /* Assume full path given */
330                         prog=g_strdup (token);
331                 } else {
332                         char *curdir=g_get_current_dir ();
333
334                         /* FIXME: Need to record the directory
335                          * containing the current process, and check
336                          * that for the new executable as the first
337                          * place to look
338                          */
339
340                         prog=g_strdup_printf ("%s/%s", curdir, token);
341                         g_free (curdir);
342
343                         /* I assume X_OK is the criterion to use,
344                          * rather than F_OK
345                          */
346                         if(access (prog, X_OK)!=0) {
347                                 g_free (prog);
348                                 prog=g_find_program_in_path (token);
349                                 if(prog==NULL) {
350 #ifdef DEBUG
351                                         g_message (G_GNUC_PRETTY_FUNCTION ": Couldn't find executable %s", token);
352 #endif
353
354                                         g_free (token);
355                                         goto cleanup;
356                                 }
357                         }
358                 }
359
360                 g_free (token);
361         }
362
363 #ifdef DEBUG
364         g_message (G_GNUC_PRETTY_FUNCTION ": Exec prog [%s] args [%s]", prog,
365                    args_after_prog);
366 #endif
367         
368         if(args_after_prog!=NULL) {
369                 full_prog=g_strconcat (prog, " ", args_after_prog, NULL);
370         } else {
371                 full_prog=g_strdup (prog);
372         }
373         
374         stored_prog=_wapi_handle_scratch_store (full_prog, strlen (full_prog));
375
376         if(startup!=NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
377                 stdin_handle=startup->hStdInput;
378                 stdout_handle=startup->hStdOutput;
379                 stderr_handle=startup->hStdError;
380         } else {
381                 stdin_handle=GetStdHandle (STD_INPUT_HANDLE);
382                 stdout_handle=GetStdHandle (STD_OUTPUT_HANDLE);
383                 stderr_handle=GetStdHandle (STD_ERROR_HANDLE);
384         }
385         
386         ret=_wapi_handle_process_fork (stored_prog, env, stored_dir,
387                                        inherit_handles, create_flags,
388                                        stdin_handle, stdout_handle,
389                                        stderr_handle, &process_handle,
390                                        &thread_handle, &pid, &tid);
391         
392         if(ret==TRUE && process_info!=NULL) {
393                 process_info->hProcess=process_handle;
394                 process_info->hThread=thread_handle;
395                 process_info->dwProcessId=pid;
396                 process_info->dwThreadId=tid;
397         }
398         
399 cleanup:
400         if(cmd!=NULL) {
401                 g_free (cmd);
402         }
403         if(full_prog!=NULL) {
404                 g_free (prog);
405         }
406         if(stored_prog!=0) {
407                 _wapi_handle_scratch_delete (stored_prog);
408         }
409         if(args!=NULL) {
410                 g_free (args);
411         }
412         if(dir!=NULL) {
413                 g_free (dir);
414         }
415         if(stored_dir!=0) {
416                 _wapi_handle_scratch_delete (stored_dir);
417         }
418         if(env!=0) {
419                 _wapi_handle_scratch_delete_string_array (env);
420         }
421         
422         return(ret);
423 }
424
425 static gboolean process_compare (gpointer handle, gpointer user_data)
426 {
427         struct _WapiHandle_process *process_handle;
428         gboolean ok;
429         pid_t pid;
430         
431         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
432                                 (gpointer *)&process_handle, NULL);
433         if(ok==FALSE) {
434                 g_warning (G_GNUC_PRETTY_FUNCTION
435                            ": error looking up process handle %p", handle);
436                 return(FALSE);
437         }
438
439         pid=GPOINTER_TO_UINT (user_data);
440         if(process_handle->id==pid) {
441                 return(TRUE);
442         } else {
443                 return(FALSE);
444         }
445 }
446         
447 static void process_set_current (void)
448 {
449         struct _WapiHandle_process *process_handle;
450         gboolean ok;
451         pid_t pid=getpid ();
452         
453         current_process=_wapi_search_handle (WAPI_HANDLE_PROCESS,
454                                              process_compare,
455                                              GUINT_TO_POINTER (pid),
456                                              (gpointer *)&process_handle,
457                                              NULL);
458         if(current_process==0) {
459                 gchar *progname, *slash;
460                 
461 #ifdef DEBUG
462                 g_message (G_GNUC_PRETTY_FUNCTION
463                            ": Need to create my own process handle");
464 #endif
465
466                 current_process=_wapi_handle_new (WAPI_HANDLE_PROCESS);
467                 if(current_process==_WAPI_HANDLE_INVALID) {
468                         g_warning (G_GNUC_PRETTY_FUNCTION
469                                    ": error creating process handle");
470                         return;
471                 }
472
473                 ok=_wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
474                                         (gpointer *)&process_handle, NULL);
475                 if(ok==FALSE) {
476                         g_warning (G_GNUC_PRETTY_FUNCTION
477                                    ": error looking up process handle %p",
478                                    current_process);
479                         return;
480                 }
481
482                 process_handle->id=pid;
483                 
484                 /* These seem to be the defaults on w2k */
485                 process_handle->min_working_set=204800;
486                 process_handle->max_working_set=1413120;
487                 
488                 progname=g_get_prgname ();
489
490 #ifdef DEBUG
491                 g_message (G_GNUC_PRETTY_FUNCTION ": using [%s] as prog name",
492                            progname);
493 #endif
494
495                 if(progname!=NULL) {
496                         slash=strrchr (progname, '/');
497                         if(slash!=NULL) {
498                                 process_handle->proc_name=_wapi_handle_scratch_store (slash+1, strlen (slash+1));
499                         } else {
500                                 process_handle->proc_name=_wapi_handle_scratch_store (progname, strlen (progname));
501                         }
502                 }
503
504                 /* Make sure the new handle has a reference so it wont go away
505                  * until this process exits
506                  */
507                 _wapi_handle_ref (current_process);
508         } else {
509 #ifdef DEBUG
510                 g_message (G_GNUC_PRETTY_FUNCTION ": Found my process handle");
511 #endif
512         }
513 }
514
515 /* Returns a pseudo handle that doesn't need to be closed afterwards */
516 gpointer GetCurrentProcess (void)
517 {
518         mono_once (&process_current_once, process_set_current);
519                 
520         return((gpointer)-1);
521 }
522
523 guint32 GetCurrentProcessId (void)
524 {
525         mono_once (&process_current_once, process_set_current);
526                 
527         return(getpid ());
528 }
529
530 static gboolean process_enum (gpointer handle, gpointer user_data)
531 {
532         GPtrArray *processes=user_data;
533         
534         /* Ignore processes that have already exited (ie they are signalled) */
535         if(_wapi_handle_issignalled (handle)==FALSE) {
536                 g_ptr_array_add (processes, handle);
537         }
538         
539         /* Return false to keep searching */
540         return(FALSE);
541 }
542
543 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
544 {
545         GPtrArray *processes=g_ptr_array_new ();
546         guint32 fit, i;
547         
548         mono_once (&process_current_once, process_set_current);
549         
550         _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
551                              NULL, NULL);
552         
553         fit=len/sizeof(guint32);
554         for(i=0; i<fit && i<processes->len; i++) {
555                 struct _WapiHandle_process *process_handle;
556                 gboolean ok;
557
558                 ok=_wapi_lookup_handle (g_ptr_array_index (processes, i),
559                                         WAPI_HANDLE_PROCESS,
560                                         (gpointer *)&process_handle, NULL);
561                 if(ok==FALSE) {
562                         g_warning (G_GNUC_PRETTY_FUNCTION ": error looking up process handle %p", g_ptr_array_index (processes, i));
563                         g_ptr_array_free (processes, FALSE);
564                         return(FALSE);
565                 }
566
567                 pids[i]=process_handle->id;
568         }
569
570         g_ptr_array_free (processes, FALSE);
571         
572         *needed=i*sizeof(guint32);
573         
574         return(TRUE);
575 }
576
577 static gboolean process_open_compare (gpointer handle, gpointer user_data)
578 {
579         struct _WapiHandle_process *process_handle;
580         gboolean ok;
581         pid_t pid;
582         
583         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
584                                 (gpointer *)&process_handle, NULL);
585         if(ok==FALSE) {
586                 g_warning (G_GNUC_PRETTY_FUNCTION
587                            ": error looking up process handle %p", handle);
588                 return(FALSE);
589         }
590
591         pid=GPOINTER_TO_UINT (user_data);
592
593         /* It's possible to have more than one process handle with the
594          * same pid, but only the one running process can be
595          * unsignalled
596          */
597         if(process_handle->id==pid &&
598            _wapi_handle_issignalled (handle)==FALSE) {
599                 return(TRUE);
600         } else {
601                 return(FALSE);
602         }
603 }
604
605 gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
606 {
607         /* Find the process handle that corresponds to pid */
608         gpointer handle;
609         
610         mono_once (&process_current_once, process_set_current);
611
612         handle=_wapi_search_handle (WAPI_HANDLE_PROCESS, process_open_compare,
613                                     GUINT_TO_POINTER (pid), NULL, NULL);
614         if(handle==0) {
615 #ifdef DEBUG
616                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find pid %d", pid);
617 #endif
618
619                 /* Set an error code */
620         
621                 return(NULL);
622         }
623
624         _wapi_handle_ref (handle);
625         
626         return(handle);
627 }
628
629 gboolean GetExitCodeProcess (gpointer process, guint32 *code)
630 {
631         struct _WapiHandle_process *process_handle;
632         gboolean ok;
633         
634         mono_once (&process_current_once, process_set_current);
635
636         if(code==NULL) {
637                 return(FALSE);
638         }
639         
640         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
641                                 (gpointer *)&process_handle, NULL);
642         if(ok==FALSE) {
643 #ifdef DEBUG
644                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
645                            process);
646 #endif
647                 
648                 return(FALSE);
649         }
650         
651         /* A process handle is only signalled if the process has exited */
652         if(_wapi_handle_issignalled (process)==TRUE) {
653                 *code=process_handle->exitstatus;
654         } else {
655                 *code=STILL_ACTIVE;
656         }
657         
658         return(TRUE);
659 }
660
661 gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
662                           WapiFileTime *exit_time, WapiFileTime *kernel_time,
663                           WapiFileTime *user_time)
664 {
665         struct _WapiHandle_process *process_handle;
666         gboolean ok;
667         
668         mono_once (&process_current_once, process_set_current);
669
670         if(create_time==NULL || exit_time==NULL || kernel_time==NULL ||
671            user_time==NULL) {
672                 /* Not sure if w32 allows NULLs here or not */
673                 return(FALSE);
674         }
675         
676         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
677                                 (gpointer *)&process_handle, NULL);
678         if(ok==FALSE) {
679 #ifdef DEBUG
680                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
681                            process);
682 #endif
683                 
684                 return(FALSE);
685         }
686         
687         *create_time=process_handle->create_time;
688
689         /* A process handle is only signalled if the process has
690          * exited.  Otherwise exit_time isn't set
691          */
692         if(_wapi_handle_issignalled (process)==TRUE) {
693                 *exit_time=process_handle->exit_time;
694         }
695         
696         return(TRUE);
697 }
698
699 gboolean EnumProcessModules (gpointer process, gpointer *modules,
700                              guint32 size, guint32 *needed)
701 {
702         /* Store modules in an array of pointers (main module as
703          * modules[0]), using the load address for each module as a
704          * token.  (Use 'NULL' as an alternative for the main module
705          * so that the simple implementation can just return one item
706          * for now.)  Get the info from /proc/<pid>/maps on linux,
707          * other systems will have to implement /dev/kmem reading or
708          * whatever other horrid technique is needed.
709          */
710         if(size<sizeof(gpointer)) {
711                 return(FALSE);
712         }
713         
714 #ifdef linux
715         modules[0]=NULL;
716         *needed=sizeof(gpointer);
717 #else
718         modules[0]=NULL;
719         *needed=sizeof(gpointer);
720 #endif
721         
722         return(TRUE);
723 }
724
725 guint32 GetModuleBaseName (gpointer process, gpointer module,
726                            gunichar2 *basename, guint32 size)
727 {
728         struct _WapiHandle_process *process_handle;
729         gboolean ok;
730         
731         mono_once (&process_current_once, process_set_current);
732
733 #ifdef DEBUG
734         g_message (G_GNUC_PRETTY_FUNCTION
735                    ": Getting module base name, process handle %p module %p",
736                    process, module);
737 #endif
738
739         if(basename==NULL || size==0) {
740                 return(FALSE);
741         }
742         
743         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
744                                 (gpointer *)&process_handle, NULL);
745         if(ok==FALSE) {
746 #ifdef DEBUG
747                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
748                            process);
749 #endif
750                 
751                 return(FALSE);
752         }
753
754         if(module==NULL) {
755                 /* Shorthand for the main module, which has the
756                  * process name recorded in the handle data
757                  */
758                 pid_t pid;
759                 gunichar2 *procname;
760                 guchar *procname_utf8;
761                 glong len, bytes;
762                 
763 #ifdef DEBUG
764                 g_message (G_GNUC_PRETTY_FUNCTION
765                            ": Returning main module name");
766 #endif
767
768                 pid=process_handle->id;
769                 procname_utf8=_wapi_handle_scratch_lookup_as_string (process_handle->proc_name);
770         
771 #ifdef DEBUG
772                 g_message (G_GNUC_PRETTY_FUNCTION ": Process name is [%s]",
773                            procname_utf8);
774 #endif
775
776                 procname=g_utf8_to_utf16 (procname_utf8, -1, NULL, &len, NULL);
777                 if(procname==NULL) {
778                         /* bugger */
779                         g_free (procname_utf8);
780                         return(0);
781                 }
782
783                 /* Add the terminator, and convert chars to bytes */
784                 bytes=(len+1)*2;
785                 
786                 if(size<bytes) {
787 #ifdef DEBUG
788                         g_message (G_GNUC_PRETTY_FUNCTION ": Size %d smaller than needed (%ld); truncating", size, bytes);
789 #endif
790
791                         memcpy (basename, procname, size);
792                 } else {
793 #ifdef DEBUG
794                         g_message (G_GNUC_PRETTY_FUNCTION
795                                    ": Size %d larger than needed (%ld)",
796                                    size, bytes);
797 #endif
798
799                         memcpy (basename, procname, bytes);
800                 }
801                 
802                 g_free (procname_utf8);
803                 g_free (procname);
804
805                 return(len);
806         } else {
807                 /* Look up the address in /proc/<pid>/maps */
808         }
809         
810         return(0);
811 }
812
813 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
814 {
815         struct _WapiHandle_process *process_handle;
816         gboolean ok;
817         
818         mono_once (&process_current_once, process_set_current);
819
820         if(min==NULL || max==NULL) {
821                 /* Not sure if w32 allows NULLs here or not */
822                 return(FALSE);
823         }
824         
825         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
826                                 (gpointer *)&process_handle, NULL);
827         if(ok==FALSE) {
828 #ifdef DEBUG
829                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
830                            process);
831 #endif
832                 
833                 return(FALSE);
834         }
835
836         *min=process_handle->min_working_set;
837         *max=process_handle->max_working_set;
838         
839         return(TRUE);
840 }
841
842 gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
843 {
844         struct _WapiHandle_process *process_handle;
845         gboolean ok;
846
847         mono_once (&process_current_once, process_set_current);
848
849         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
850                                 (gpointer *)&process_handle, NULL);
851         if(ok==FALSE) {
852 #ifdef DEBUG
853                 g_message (G_GNUC_PRETTY_FUNCTION ": Can't find process %p",
854                            process);
855 #endif
856                 
857                 return(FALSE);
858         }
859
860         process_handle->min_working_set=min;
861         process_handle->max_working_set=max;
862         
863         return(TRUE);
864 }