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