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