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