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