2009-05-22 Miguel de Icaza <miguel@novell.com>
[mono.git] / mono / io-layer / processes.c
1 /*
2  * processes.c:  Process handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002-2006 Novell, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <pthread.h>
15 #include <sched.h>
16 #include <sys/time.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <sys/wait.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <fcntl.h>
26 #include <sys/param.h>
27 #include <ctype.h>
28
29 #ifdef HAVE_SYS_MKDEV_H
30 #include <sys/mkdev.h>
31 #endif
32
33 /* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
34 #ifdef __APPLE__
35 #include <sys/resource.h>
36 #endif
37
38 #include <mono/io-layer/wapi.h>
39 #include <mono/io-layer/wapi-private.h>
40 #include <mono/io-layer/handles-private.h>
41 #include <mono/io-layer/misc-private.h>
42 #include <mono/io-layer/mono-mutex.h>
43 #include <mono/io-layer/process-private.h>
44 #include <mono/io-layer/threads.h>
45 #include <mono/utils/strenc.h>
46 #include <mono/io-layer/timefuncs-private.h>
47
48 /* The process' environment strings */
49 #ifdef __APPLE__
50 /* Apple defines this in crt_externs.h but doesn't provide that header for 
51  * arm-apple-darwin9.  We'll manually define the symbol on Apple as it does
52  * in fact exist on all implementations (so far) 
53  */
54 gchar ***_NSGetEnviron();
55 #define environ (*_NSGetEnviron())
56 #else
57 extern char **environ;
58 #endif
59
60 #undef DEBUG
61
62 static guint32 process_wait (gpointer handle, guint32 timeout);
63
64 struct _WapiHandleOps _wapi_process_ops = {
65         NULL,                           /* close_shared */
66         NULL,                           /* signal */
67         NULL,                           /* own */
68         NULL,                           /* is_owned */
69         process_wait,                   /* special_wait */
70         NULL                            /* prewait */   
71 };
72
73 static mono_once_t process_current_once=MONO_ONCE_INIT;
74 static gpointer current_process=NULL;
75
76 static mono_once_t process_ops_once=MONO_ONCE_INIT;
77
78 static void process_ops_init (void)
79 {
80         _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS,
81                                             WAPI_HANDLE_CAP_WAIT |
82                                             WAPI_HANDLE_CAP_SPECIAL_WAIT);
83 }
84
85 static gboolean process_set_termination_details (gpointer handle, int status)
86 {
87         struct _WapiHandle_process *process_handle;
88         gboolean ok;
89         int thr_ret;
90         
91         g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
92         
93         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
94                                   (gpointer *)&process_handle);
95         if (ok == FALSE) {
96                 g_warning ("%s: error looking up process handle %p",
97                            __func__, handle);
98                 return(FALSE);
99         }
100         
101         thr_ret = _wapi_handle_lock_shared_handles ();
102         g_assert (thr_ret == 0);
103
104         if (WIFSIGNALED(status)) {
105                 process_handle->exitstatus = 128 + WTERMSIG(status);
106         } else {
107                 process_handle->exitstatus = WEXITSTATUS(status);
108         }
109         _wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time);
110
111         /* Don't set process_handle->waited here, it needs to only
112          * happen in the parent when wait() has been called.
113          */
114         
115 #ifdef DEBUG
116         g_message ("%s: Setting handle %p pid %d signalled, exit status %d",
117                    __func__, handle, process_handle->id,
118                    process_handle->exitstatus);
119 #endif
120
121         _wapi_shared_handle_set_signal_state (handle, TRUE);
122
123         _wapi_handle_unlock_shared_handles ();
124
125         /* Drop the reference we hold so we have somewhere to store
126          * the exit details, now the process has in fact exited
127          */
128         _wapi_handle_unref (handle);
129         
130         return (ok);
131 }
132
133 /* See if any child processes have terminated and wait() for them,
134  * updating process handle info.  This function is called from the
135  * collection thread every few seconds.
136  */
137 static gboolean waitfor_pid (gpointer test, gpointer user_data)
138 {
139         struct _WapiHandle_process *process;
140         gboolean ok;
141         int status;
142         pid_t ret;
143         
144         g_assert ((GPOINTER_TO_UINT (test) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
145         
146         ok = _wapi_lookup_handle (test, WAPI_HANDLE_PROCESS,
147                                   (gpointer *)&process);
148         if (ok == FALSE) {
149                 /* The handle must have been too old and was reaped */
150                 return (FALSE);
151         }
152
153         if (process->waited) {
154                 /* We've already done this one */
155                 return(FALSE);
156         }
157         
158         do {
159                 ret = waitpid (process->id, &status, WNOHANG);
160         } while (errno == EINTR);
161         
162         if (ret <= 0) {
163                 /* Process not ready for wait */
164 #ifdef DEBUG
165                 g_message ("%s: Process %d not ready for waiting for: %s",
166                            __func__, process->id, g_strerror (errno));
167 #endif
168
169                 return (FALSE);
170         }
171         
172 #ifdef DEBUG
173         g_message ("%s: Process %d finished", __func__, ret);
174 #endif
175
176         process->waited = TRUE;
177         
178         *(int *)user_data = status;
179         
180         return (TRUE);
181 }
182
183 void _wapi_process_reap (void)
184 {
185         gpointer proc;
186         int status;
187         
188 #ifdef DEBUG
189         g_message ("%s: Reaping child processes", __func__);
190 #endif
191
192         do {
193                 proc = _wapi_search_handle (WAPI_HANDLE_PROCESS, waitfor_pid,
194                                             &status, NULL, FALSE);
195                 if (proc != NULL) {
196 #ifdef DEBUG
197                         g_message ("%s: process handle %p exit code %d",
198                                    __func__, proc, status);
199 #endif
200                         
201                         process_set_termination_details (proc, status);
202
203                         /* _wapi_search_handle adds a reference, so
204                          * drop it here
205                          */
206                         _wapi_handle_unref (proc);
207                 }
208         } while (proc != NULL);
209 }
210
211 /* Limitations: This can only wait for processes that are our own
212  * children.  Fixing this means resurrecting a daemon helper to manage
213  * processes.
214  */
215 static guint32 process_wait (gpointer handle, guint32 timeout)
216 {
217         struct _WapiHandle_process *process_handle;
218         gboolean ok;
219         pid_t pid, ret;
220         int status;
221         
222         g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
223         
224 #ifdef DEBUG
225         g_message ("%s: Waiting for process %p", __func__, handle);
226 #endif
227
228         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
229                                   (gpointer *)&process_handle);
230         if (ok == FALSE) {
231                 g_warning ("%s: error looking up process handle %p", __func__,
232                            handle);
233                 return(WAIT_FAILED);
234         }
235         
236         if (process_handle->waited) {
237                 /* We've already done this one */
238 #ifdef DEBUG
239                 g_message ("%s: Process %p already signalled", __func__,
240                            handle);
241 #endif
242
243                 return (WAIT_OBJECT_0);
244         }
245         
246         pid = process_handle->id;
247         
248 #ifdef DEBUG
249         g_message ("%s: PID is %d, timeout %d", __func__, pid, timeout);
250 #endif
251
252         if (timeout == INFINITE) {
253                 if (pid == _wapi_getpid ()) {
254                         do {
255                                 Sleep (10000);
256                         } while(1);
257                 } else {
258                         while ((ret = waitpid (pid, &status, 0)) != pid) {
259                                 if (ret == (pid_t)-1 && errno != EINTR) {
260                                         return(WAIT_FAILED);
261                                 }
262                         }
263                 }
264         } else if (timeout == 0) {
265                 /* Just poll */
266                 ret = waitpid (pid, &status, WNOHANG);
267                 if (ret != pid) {
268                         return (WAIT_TIMEOUT);
269                 }
270         } else {
271                 /* Poll in a loop */
272                 if (pid == _wapi_getpid ()) {
273                         Sleep (timeout);
274                         return(WAIT_TIMEOUT);
275                 } else {
276                         do {
277                                 ret = waitpid (pid, &status, WNOHANG);
278 #ifdef DEBUG
279                                 g_message ("%s: waitpid returns: %d, timeout is %d", __func__, ret, timeout);
280 #endif
281                                 
282                                 if (ret == pid) {
283                                         break;
284                                 } else if (ret == (pid_t)-1 &&
285                                            errno != EINTR) {
286 #ifdef DEBUG
287                                         g_message ("%s: waitpid failure: %s",
288                                                    __func__,
289                                                    g_strerror (errno));
290 #endif
291
292                                         if (errno == ECHILD &&
293                                             process_handle->waited) {
294                                                 /* The background
295                                                  * process reaper must
296                                                  * have got this one
297                                                  */
298 #ifdef DEBUG
299                                                 g_message ("%s: Process %p already reaped", __func__, handle);
300 #endif
301
302                                                 return(WAIT_OBJECT_0);
303                                         } else {
304                                                 return(WAIT_FAILED);
305                                         }
306                                 }
307
308                                 _wapi_handle_spin (100);
309                                 timeout -= 100;
310                         } while (timeout > 0);
311                 }
312                 
313                 if (timeout <= 0) {
314                         return(WAIT_TIMEOUT);
315                 }
316         }
317
318         /* Process must have exited */
319 #ifdef DEBUG
320         g_message ("%s: Wait done, status %d", __func__, status);
321 #endif
322
323         ok = process_set_termination_details (handle, status);
324         if (ok == FALSE) {
325                 SetLastError (ERROR_OUTOFMEMORY);
326                 return (WAIT_FAILED);
327         }
328         process_handle->waited = TRUE;
329         
330         return(WAIT_OBJECT_0);
331 }
332
333 void _wapi_process_signal_self ()
334 {
335         if (current_process != NULL) {
336                 process_set_termination_details (current_process, 0);
337         }
338 }
339         
340 static void process_set_defaults (struct _WapiHandle_process *process_handle)
341 {
342         /* These seem to be the defaults on w2k */
343         process_handle->min_working_set = 204800;
344         process_handle->max_working_set = 1413120;
345
346         process_handle->waited = FALSE;
347         
348         _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
349 }
350
351 static int
352 len16 (const gunichar2 *str)
353 {
354         int len = 0;
355         
356         while (*str++ != 0)
357                 len++;
358
359         return len;
360 }
361
362 static gunichar2 *
363 utf16_concat (const gunichar2 *first, ...)
364 {
365         va_list args;
366         int total = 0, i;
367         const gunichar2 *s;
368         gunichar2 *ret;
369
370         va_start (args, first);
371         total += len16 (first);
372         for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *)){
373                 total += len16 (s);
374         }
375         va_end (args);
376
377         ret = g_new (gunichar2, total + 1);
378         if (ret == NULL)
379                 return NULL;
380
381         ret [total] = 0;
382         i = 0;
383         for (s = first; *s != 0; s++)
384                 ret [i++] = *s;
385         va_start (args, first);
386         for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
387                 const gunichar2 *p;
388                 
389                 for (p = s; *p != 0; p++)
390                         ret [i++] = *p;
391         }
392         va_end (args);
393         
394         return ret;
395 }
396
397 #ifdef PLATFORM_MACOSX
398 #include <sys/utsname.h>
399
400 /* 0 = no detection; -1 = not 10.5 or higher;  1 = 10.5 or higher */
401 static int osx_10_5_or_higher;
402
403 static void
404 detect_osx_10_5_or_higher ()
405 {
406         struct utsname u;
407         char *p;
408         int v;
409         
410         if (uname (&u) != 0){
411                 osx_10_5_or_higher = 1;
412                 return;
413         }
414
415         p = u.release;
416         v = atoi (p);
417         
418         if (v < 9)
419                 osx_10_5_or_higher = -1;
420         else 
421                 osx_10_5_or_higher = 1;
422 }
423
424 static gboolean
425 is_macos_10_5_or_higher ()
426 {
427         if (osx_10_5_or_higher == 0)
428                 detect_osx_10_5_or_higher ();
429         
430         return (osx_10_5_or_higher == 1);
431 }
432 #endif
433
434 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
435 static const gunichar2 *utf16_space = utf16_space_bytes; 
436 static const gunichar2 utf16_quote_bytes [2] = { 0x22, 0 };
437 static const gunichar2 *utf16_quote = utf16_quote_bytes;
438
439 /* Implemented as just a wrapper around CreateProcess () */
440 gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
441 {
442         gboolean ret;
443         WapiProcessInformation process_info;
444         gunichar2 *args;
445         
446         if (sei == NULL) {
447                 /* w2k just segfaults here, but we can do better than
448                  * that
449                  */
450                 SetLastError (ERROR_INVALID_PARAMETER);
451                 return (FALSE);
452         }
453
454         if (sei->lpFile == NULL) {
455                 /* w2k returns TRUE for this, for some reason. */
456                 return (TRUE);
457         }
458         
459         /* Put both executable and parameters into the second argument
460          * to CreateProcess (), so it searches $PATH.  The conversion
461          * into and back out of utf8 is because there is no
462          * g_strdup_printf () equivalent for gunichar2 :-(
463          */
464         args = utf16_concat (sei->lpFile, sei->lpParameters == NULL ? NULL : utf16_space, sei->lpParameters, NULL);
465         if (args == NULL){
466                 SetLastError (ERROR_INVALID_DATA);
467                 return (FALSE);
468         }
469         ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
470                              CREATE_UNICODE_ENVIRONMENT, NULL,
471                              sei->lpDirectory, NULL, &process_info);
472         g_free (args);
473         
474         if (!ret) {
475                 static char *handler;
476                 static gunichar2 *handler_utf16;
477                 
478                 if (handler_utf16 == (gunichar2 *)-1)
479                         return FALSE;
480
481 #ifdef PLATFORM_MACOSX
482                 if (is_macos_10_5_or_higher ())
483                         handler = g_strdup ("/usr/bin/open -W");
484                 else
485                         handler = g_strdup ("/usr/bin/open");
486 #else
487                 /*
488                  * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
489                  * if that fails, try to use gnome-open, then kfmclient
490                  */
491                 handler = g_find_program_in_path ("xdg-open");
492                 if (handler == NULL){
493                         handler = g_find_program_in_path ("gnome-open");
494                         if (handler == NULL){
495                                 handler = g_find_program_in_path ("kfmclient");
496                                 if (handler == NULL){
497                                         handler_utf16 = (gunichar2 *) -1;
498                                         return (FALSE);
499                                 } else {
500                                         /* kfmclient needs exec argument */
501                                         char *old = handler;
502                                         handler = g_strconcat (old, " exec",
503                                                                NULL);
504                                         g_free (old);
505                                 }
506                         }
507                 }
508 #endif
509                 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
510                 g_free (handler);
511
512                 /* Put quotes around the filename, in case it's a url
513                  * that contains #'s (CreateProcess() calls
514                  * g_shell_parse_argv(), which deliberately throws
515                  * away anything after an unquoted #).  Fixes bug
516                  * 371567.
517                  */
518                 args = utf16_concat (handler_utf16, utf16_space, utf16_quote,
519                                      sei->lpFile, utf16_quote,
520                                      sei->lpParameters == NULL ? NULL : utf16_space,
521                                      sei->lpParameters, NULL);
522                 if (args == NULL){
523                         SetLastError (ERROR_INVALID_DATA);
524                         return FALSE;
525                 }
526                 ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
527                                      CREATE_UNICODE_ENVIRONMENT, NULL,
528                                      sei->lpDirectory, NULL, &process_info);
529                 g_free (args);
530                 if (!ret){
531                         SetLastError (ERROR_INVALID_DATA);
532                         return FALSE;
533                 }
534         }
535         
536         if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) {
537                 sei->hProcess = process_info.hProcess;
538         } else {
539                 CloseHandle (process_info.hProcess);
540         }
541         
542         return (ret);
543 }
544
545 static gboolean
546 is_managed_binary (const gchar *filename)
547 {
548         int original_errno = errno;
549 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
550         int file = open (filename, O_RDONLY | O_LARGEFILE);
551 #else
552         int file = open (filename, O_RDONLY);
553 #endif
554         off_t new_offset;
555         unsigned char buffer[8];
556         off_t file_size, optional_header_offset;
557         off_t pe_header_offset;
558         gboolean managed = FALSE;
559         int num_read;
560         guint32 first_word, second_word;
561         
562         /* If we are unable to open the file, then we definitely
563          * can't say that it is managed. The child mono process
564          * probably wouldn't be able to open it anyway.
565          */
566         if (file < 0) {
567                 errno = original_errno;
568                 return FALSE;
569         }
570
571         /* Retrieve the length of the file for future sanity checks. */
572         file_size = lseek (file, 0, SEEK_END);
573         lseek (file, 0, SEEK_SET);
574
575         /* We know we need to read a header field at offset 60. */
576         if (file_size < 64)
577                 goto leave;
578
579         num_read = read (file, buffer, 2);
580
581         if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
582                 goto leave;
583
584         new_offset = lseek (file, 60, SEEK_SET);
585
586         if (new_offset != 60)
587                 goto leave;
588         
589         num_read = read (file, buffer, 4);
590
591         if (num_read != 4)
592                 goto leave;
593         pe_header_offset =  buffer[0]
594                 | (buffer[1] <<  8)
595                 | (buffer[2] << 16)
596                 | (buffer[3] << 24);
597         
598         if (pe_header_offset + 24 > file_size)
599                 goto leave;
600
601         new_offset = lseek (file, pe_header_offset, SEEK_SET);
602
603         if (new_offset != pe_header_offset)
604                 goto leave;
605
606         num_read = read (file, buffer, 4);
607
608         if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
609                 goto leave;
610
611         /*
612          * Verify that the header we want in the optional header data
613          * is present in this binary.
614          */
615         new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
616
617         if (new_offset != pe_header_offset + 20)
618                 goto leave;
619
620         num_read = read (file, buffer, 2);
621
622         if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
623                 goto leave;
624
625         /* Read the CLR header address and size fields. These will be
626          * zero if the binary is not managed.
627          */
628         optional_header_offset = pe_header_offset + 24;
629         new_offset = lseek (file, optional_header_offset + 208, SEEK_SET);
630
631         if (new_offset != optional_header_offset + 208)
632                 goto leave;
633
634         num_read = read (file, buffer, 8);
635         
636         /* We are not concerned with endianness, only with
637          * whether it is zero or not.
638          */
639         first_word = *(guint32 *)&buffer[0];
640         second_word = *(guint32 *)&buffer[4];
641         
642         if ((num_read != 8) || (first_word == 0) || (second_word == 0))
643                 goto leave;
644         
645         managed = TRUE;
646
647 leave:
648         close (file);
649         errno = original_errno;
650         return managed;
651 }
652
653 gboolean CreateProcessWithLogonW (const gunichar2 *username,
654                                   const gunichar2 *domain,
655                                   const gunichar2 *password,
656                                   const guint32 logonFlags,
657                                   const gunichar2 *appname,
658                                   const gunichar2 *cmdline,
659                                   guint32 create_flags,
660                                   gpointer env,
661                                   const gunichar2 *cwd,
662                                   WapiStartupInfo *startup,
663                                   WapiProcessInformation *process_info)
664 {
665         /* FIXME: use user information */
666         return CreateProcess (appname, cmdline, NULL, NULL, FALSE, create_flags, env, cwd, startup, process_info);
667 }
668
669 static gboolean
670 is_executable (const char *prog)
671 {
672         struct stat buf;
673         if (access (prog, X_OK) != 0)
674                 return FALSE;
675         if (stat (prog, &buf))
676                 return FALSE;
677         if (S_ISREG (buf.st_mode))
678                 return TRUE;
679         return FALSE;
680 }
681
682 gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
683                         WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
684                         WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
685                         gboolean inherit_handles, guint32 create_flags,
686                         gpointer new_environ, const gunichar2 *cwd,
687                         WapiStartupInfo *startup,
688                         WapiProcessInformation *process_info)
689 {
690         gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv = NULL;
691         guint32 i, env_count = 0;
692         gboolean ret = FALSE;
693         gpointer handle;
694         struct _WapiHandle_process process_handle = {0}, *process_handle_data;
695         GError *gerr = NULL;
696         int in_fd, out_fd, err_fd;
697         pid_t pid;
698         int thr_ret;
699         
700         mono_once (&process_ops_once, process_ops_init);
701         
702         /* appname and cmdline specify the executable and its args:
703          *
704          * If appname is not NULL, it is the name of the executable.
705          * Otherwise the executable is the first token in cmdline.
706          *
707          * Executable searching:
708          *
709          * If appname is not NULL, it can specify the full path and
710          * file name, or else a partial name and the current directory
711          * will be used.  There is no additional searching.
712          *
713          * If appname is NULL, the first whitespace-delimited token in
714          * cmdline is used.  If the name does not contain a full
715          * directory path, the search sequence is:
716          *
717          * 1) The directory containing the current process
718          * 2) The current working directory
719          * 3) The windows system directory  (Ignored)
720          * 4) The windows directory (Ignored)
721          * 5) $PATH
722          *
723          * Just to make things more interesting, tokens can contain
724          * white space if they are surrounded by quotation marks.  I'm
725          * beginning to understand just why windows apps are generally
726          * so crap, with an API like this :-(
727          */
728         if (appname != NULL) {
729                 cmd = mono_unicode_to_external (appname);
730                 if (cmd == NULL) {
731 #ifdef DEBUG
732                         g_message ("%s: unicode conversion returned NULL",
733                                    __func__);
734 #endif
735
736                         SetLastError (ERROR_PATH_NOT_FOUND);
737                         goto free_strings;
738                 }
739
740                 /* Turn all the slashes round the right way */
741                 for (i = 0; i < strlen (cmd); i++) {
742                         if (cmd[i] == '\\') {
743                                 cmd[i] = '/';
744                         }
745                 }
746         }
747         
748         if (cmdline != NULL) {
749                 args = mono_unicode_to_external (cmdline);
750                 if (args == NULL) {
751 #ifdef DEBUG
752                         g_message ("%s: unicode conversion returned NULL", __func__);
753 #endif
754
755                         SetLastError (ERROR_PATH_NOT_FOUND);
756                         goto free_strings;
757                 }
758         }
759
760         if (cwd != NULL) {
761                 dir = mono_unicode_to_external (cwd);
762                 if (dir == NULL) {
763 #ifdef DEBUG
764                         g_message ("%s: unicode conversion returned NULL", __func__);
765 #endif
766
767                         SetLastError (ERROR_PATH_NOT_FOUND);
768                         goto free_strings;
769                 }
770
771                 /* Turn all the slashes round the right way */
772                 for (i = 0; i < strlen (dir); i++) {
773                         if (dir[i] == '\\') {
774                                 dir[i] = '/';
775                         }
776                 }
777         }
778         
779
780         /* We can't put off locating the executable any longer :-( */
781         if (cmd != NULL) {
782                 gchar *unquoted;
783                 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
784                         /* Strip off the drive letter.  I can't
785                          * believe that CP/M holdover is still
786                          * visible...
787                          */
788                         g_memmove (cmd, cmd+2, strlen (cmd)-2);
789                         cmd[strlen (cmd)-2] = '\0';
790                 }
791
792                 unquoted = g_shell_unquote (cmd, NULL);
793                 if (unquoted[0] == '/') {
794                         /* Assume full path given */
795                         prog = g_strdup (unquoted);
796
797                         /* Executable existing ? */
798                         if (!is_executable (prog)) {
799 #ifdef DEBUG
800                                 g_message ("%s: Couldn't find executable %s",
801                                            __func__, prog);
802 #endif
803                                 g_free (unquoted);
804                                 SetLastError (ERROR_FILE_NOT_FOUND);
805                                 goto free_strings;
806                         }
807                 } else {
808                         /* Search for file named by cmd in the current
809                          * directory
810                          */
811                         char *curdir = g_get_current_dir ();
812
813                         prog = g_strdup_printf ("%s/%s", curdir, unquoted);
814                         g_free (curdir);
815
816                         /* And make sure it's executable */
817                         if (!is_executable (prog)) {
818 #ifdef DEBUG
819                                 g_message ("%s: Couldn't find executable %s",
820                                            __func__, prog);
821 #endif
822                                 g_free (unquoted);
823                                 SetLastError (ERROR_FILE_NOT_FOUND);
824                                 goto free_strings;
825                         }
826                 }
827                 g_free (unquoted);
828
829                 args_after_prog = args;
830         } else {
831                 gchar *token = NULL;
832                 char quote;
833                 
834                 /* Dig out the first token from args, taking quotation
835                  * marks into account
836                  */
837
838                 /* First, strip off all leading whitespace */
839                 args = g_strchug (args);
840                 
841                 /* args_after_prog points to the contents of args
842                  * after token has been set (otherwise argv[0] is
843                  * duplicated)
844                  */
845                 args_after_prog = args;
846
847                 /* Assume the opening quote will always be the first
848                  * character
849                  */
850                 if (args[0] == '\"' || args [0] == '\'') {
851                         quote = args [0];
852                         for (i = 1; args[i] != '\0' && args[i] != quote; i++);
853                         if (g_ascii_isspace (args[i+1])) {
854                                 /* We found the first token */
855                                 token = g_strndup (args+1, i-1);
856                                 args_after_prog = args + i;
857                         } else {
858                                 /* Quotation mark appeared in the
859                                  * middle of the token.  Just give the
860                                  * whole first token, quotes and all,
861                                  * to exec.
862                                  */
863                         }
864                 }
865                 
866                 if (token == NULL) {
867                         /* No quote mark, or malformed */
868                         for (i = 0; args[i] != '\0'; i++) {
869                                 if (g_ascii_isspace (args[i])) {
870                                         token = g_strndup (args, i);
871                                         args_after_prog = args + i + 1;
872                                         break;
873                                 }
874                         }
875                 }
876
877                 if (token == NULL && args[0] != '\0') {
878                         /* Must be just one token in the string */
879                         token = g_strdup (args);
880                         args_after_prog = NULL;
881                 }
882                 
883                 if (token == NULL) {
884                         /* Give up */
885 #ifdef DEBUG
886                         g_message ("%s: Couldn't find what to exec", __func__);
887 #endif
888
889                         SetLastError (ERROR_PATH_NOT_FOUND);
890                         goto free_strings;
891                 }
892                 
893                 /* Turn all the slashes round the right way. Only for
894                  * the prg. name
895                  */
896                 for (i = 0; i < strlen (token); i++) {
897                         if (token[i] == '\\') {
898                                 token[i] = '/';
899                         }
900                 }
901
902                 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
903                         /* Strip off the drive letter.  I can't
904                          * believe that CP/M holdover is still
905                          * visible...
906                          */
907                         g_memmove (token, token+2, strlen (token)-2);
908                         token[strlen (token)-2] = '\0';
909                 }
910
911                 if (token[0] == '/') {
912                         /* Assume full path given */
913                         prog = g_strdup (token);
914                         
915                         /* Executable existing ? */
916                         if (!is_executable (prog)) {
917 #ifdef DEBUG
918                                 g_message ("%s: Couldn't find executable %s",
919                                            __func__, token);
920 #endif
921                                 g_free (token);
922                                 SetLastError (ERROR_FILE_NOT_FOUND);
923                                 goto free_strings;
924                         }
925
926                 } else {
927                         char *curdir = g_get_current_dir ();
928
929                         /* FIXME: Need to record the directory
930                          * containing the current process, and check
931                          * that for the new executable as the first
932                          * place to look
933                          */
934
935                         prog = g_strdup_printf ("%s/%s", curdir, token);
936                         g_free (curdir);
937
938                         /* I assume X_OK is the criterion to use,
939                          * rather than F_OK
940                          */
941                         if (!is_executable (prog)) {
942                                 g_free (prog);
943                                 prog = g_find_program_in_path (token);
944                                 if (prog == NULL) {
945 #ifdef DEBUG
946                                         g_message ("%s: Couldn't find executable %s", __func__, token);
947 #endif
948
949                                         g_free (token);
950                                         SetLastError (ERROR_FILE_NOT_FOUND);
951                                         goto free_strings;
952                                 }
953                         }
954                 }
955
956                 g_free (token);
957         }
958
959 #ifdef DEBUG
960         g_message ("%s: Exec prog [%s] args [%s]", __func__, prog,
961                    args_after_prog);
962 #endif
963         
964         /* Check for CLR binaries; if found, we will try to invoke
965          * them using the same mono binary that started us.
966          */
967         if (is_managed_binary (prog)) {
968                 gunichar2 *newapp, *newcmd;
969                 gsize bytes_ignored;
970
971                 newapp = mono_unicode_from_external ("mono", &bytes_ignored);
972
973                 if (newapp != NULL) {
974                         if (appname != NULL) {
975                                 newcmd = utf16_concat (newapp, utf16_space,
976                                                        appname, utf16_space,
977                                                        cmdline, NULL);
978                         } else {
979                                 newcmd = utf16_concat (newapp, utf16_space,
980                                                        cmdline, NULL);
981                         }
982                         
983                         g_free ((gunichar2 *)newapp);
984                         
985                         if (newcmd != NULL) {
986                                 ret = CreateProcess (NULL, newcmd,
987                                                      process_attrs,
988                                                      thread_attrs,
989                                                      inherit_handles,
990                                                      create_flags, new_environ,
991                                                      cwd, startup,
992                                                      process_info);
993                                 
994                                 g_free ((gunichar2 *)newcmd);
995                                 
996                                 goto free_strings;
997                         }
998                 }
999         }
1000
1001         if (args_after_prog != NULL && *args_after_prog) {
1002                 gchar *qprog;
1003
1004                 qprog = g_shell_quote (prog);
1005                 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
1006                 g_free (qprog);
1007         } else {
1008                 full_prog = g_shell_quote (prog);
1009         }
1010
1011         ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
1012         if (ret == FALSE) {
1013                 /* FIXME: Could do something with the GError here
1014                  */
1015         }
1016
1017         if (startup != NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
1018                 in_fd = GPOINTER_TO_UINT (startup->hStdInput);
1019                 out_fd = GPOINTER_TO_UINT (startup->hStdOutput);
1020                 err_fd = GPOINTER_TO_UINT (startup->hStdError);
1021         } else {
1022                 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
1023                 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
1024                 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
1025         }
1026         
1027         g_strlcpy (process_handle.proc_name, prog,
1028                    _WAPI_PROC_NAME_MAX_LEN - 1);
1029
1030         process_set_defaults (&process_handle);
1031         
1032         handle = _wapi_handle_new (WAPI_HANDLE_PROCESS, &process_handle);
1033         if (handle == _WAPI_HANDLE_INVALID) {
1034                 g_warning ("%s: error creating process handle", __func__);
1035
1036                 SetLastError (ERROR_PATH_NOT_FOUND);
1037                 goto free_strings;
1038         }
1039
1040         /* Hold another reference so the process has somewhere to
1041          * store its exit data even if we drop this handle
1042          */
1043         _wapi_handle_ref (handle);
1044         
1045         /* new_environ is a block of NULL-terminated strings, which
1046          * is itself NULL-terminated. Of course, passing an array of
1047          * string pointers would have made things too easy :-(
1048          *
1049          * If new_environ is not NULL it specifies the entire set of
1050          * environment variables in the new process.  Otherwise the
1051          * new process inherits the same environment.
1052          */
1053         if (new_environ != NULL) {
1054                 gunichar2 *new_environp;
1055
1056                 /* Count the number of strings */
1057                 for (new_environp = (gunichar2 *)new_environ; *new_environp;
1058                      new_environp++) {
1059                         env_count++;
1060                         while (*new_environp) {
1061                                 new_environp++;
1062                         }
1063                 }
1064
1065                 /* +2: one for the process handle value, and the last
1066                  * one is NULL
1067                  */
1068                 env_strings = g_new0 (gchar *, env_count + 2);
1069                 
1070                 /* Copy each environ string into 'strings' turning it
1071                  * into utf8 (or the requested encoding) at the same
1072                  * time
1073                  */
1074                 env_count = 0;
1075                 for (new_environp = (gunichar2 *)new_environ; *new_environp;
1076                      new_environp++) {
1077                         env_strings[env_count] = mono_unicode_to_external (new_environp);
1078                         env_count++;
1079                         while (*new_environp) {
1080                                 new_environp++;
1081                         }
1082                 }
1083         } else {
1084                 for (i = 0; environ[i] != NULL; i++) {
1085                         env_count++;
1086                 }
1087
1088                 /* +2: one for the process handle value, and the last
1089                  * one is NULL
1090                  */
1091                 env_strings = g_new0 (gchar *, env_count + 2);
1092                 
1093                 /* Copy each environ string into 'strings' turning it
1094                  * into utf8 (or the requested encoding) at the same
1095                  * time
1096                  */
1097                 env_count = 0;
1098                 for (i = 0; environ[i] != NULL; i++) {
1099                         env_strings[env_count] = g_strdup (environ[i]);
1100                         env_count++;
1101                 }
1102         }
1103         /* pass process handle info to the child, so it doesn't have
1104          * to do an expensive search over the whole list
1105          */
1106         if (env_strings != NULL) {
1107                 struct _WapiHandleUnshared *handle_data;
1108                 struct _WapiHandle_shared_ref *ref;
1109                 
1110                 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(handle));
1111                 ref = &handle_data->u.shared;
1112                 
1113                 env_strings[env_count] = g_strdup_printf ("_WAPI_PROCESS_HANDLE_OFFSET=%d", ref->offset);
1114         }
1115
1116         thr_ret = _wapi_handle_lock_shared_handles ();
1117         g_assert (thr_ret == 0);
1118         
1119         pid = fork ();
1120         if (pid == -1) {
1121                 /* Error */
1122                 SetLastError (ERROR_OUTOFMEMORY);
1123                 _wapi_handle_unref (handle);
1124                 goto cleanup;
1125         } else if (pid == 0) {
1126                 /* Child */
1127                 
1128                 if (_wapi_shm_disabled == FALSE) {
1129                         /* Wait for the parent to finish setting up
1130                          * the handle.  The semaphore lock is safe
1131                          * because the sem_undo structures of a
1132                          * semaphore aren't inherited across a fork
1133                          * (), but we can't do this if we're not using
1134                          * the shared memory
1135                          */
1136                         thr_ret = _wapi_handle_lock_shared_handles ();
1137                         g_assert (thr_ret == 0);
1138         
1139                         _wapi_handle_unlock_shared_handles ();
1140                 }
1141                 
1142                 /* should we detach from the process group? */
1143
1144                 /* Connect stdin, stdout and stderr */
1145                 dup2 (in_fd, 0);
1146                 dup2 (out_fd, 1);
1147                 dup2 (err_fd, 2);
1148
1149                 if (inherit_handles != TRUE) {
1150                         /* FIXME: do something here */
1151                 }
1152                 
1153                 /* Close all file descriptors */
1154                 for (i = getdtablesize () - 1; i > 2; i--) {
1155                         close (i);
1156                 }
1157
1158 #ifdef DEBUG
1159                 g_message ("%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1160                            dir==NULL?".":dir);
1161                 for (i = 0; argv[i] != NULL; i++) {
1162                         g_message ("arg %d: [%s]", i, argv[i]);
1163                 }
1164                 
1165                 for (i = 0; env_strings[i] != NULL; i++) {
1166                         g_message ("env %d: [%s]", i, env_strings[i]);
1167                 }
1168 #endif
1169
1170                 /* set cwd */
1171                 if (dir != NULL && chdir (dir) == -1) {
1172                         /* set error */
1173                         _exit (-1);
1174                 }
1175                 
1176                 /* exec */
1177                 execve (argv[0], argv, env_strings);
1178                 
1179                 /* set error */
1180                 _exit (-1);
1181         }
1182         /* parent */
1183         
1184         ret = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
1185                                    (gpointer *)&process_handle_data);
1186         if (ret == FALSE) {
1187                 g_warning ("%s: error looking up process handle %p", __func__,
1188                            handle);
1189                 _wapi_handle_unref (handle);
1190                 goto cleanup;
1191         }
1192         
1193         process_handle_data->id = pid;
1194         
1195         if (process_info != NULL) {
1196                 process_info->hProcess = handle;
1197                 process_info->dwProcessId = pid;
1198
1199                 /* FIXME: we might need to handle the thread info some
1200                  * day
1201                  */
1202                 process_info->hThread = INVALID_HANDLE_VALUE;
1203                 process_info->dwThreadId = 0;
1204         }
1205
1206 cleanup:
1207         _wapi_handle_unlock_shared_handles ();
1208
1209 free_strings:
1210         if (cmd != NULL) {
1211                 g_free (cmd);
1212         }
1213         if (full_prog != NULL) {
1214                 g_free (full_prog);
1215         }
1216         if (prog != NULL) {
1217                 g_free (prog);
1218         }
1219         if (args != NULL) {
1220                 g_free (args);
1221         }
1222         if (dir != NULL) {
1223                 g_free (dir);
1224         }
1225         if(env_strings != NULL) {
1226                 g_strfreev (env_strings);
1227         }
1228         if (argv != NULL) {
1229                 g_strfreev (argv);
1230         }
1231         
1232 #ifdef DEBUG
1233         g_message ("%s: returning handle %p for pid %d", __func__, handle,
1234                    pid);
1235 #endif
1236
1237         return(ret);
1238 }
1239                 
1240 static void process_set_name (struct _WapiHandle_process *process_handle)
1241 {
1242         gchar *progname, *utf8_progname, *slash;
1243         
1244         progname=g_get_prgname ();
1245         utf8_progname=mono_utf8_from_external (progname);
1246
1247 #ifdef DEBUG
1248         g_message ("%s: using [%s] as prog name", __func__, progname);
1249 #endif
1250
1251         if(utf8_progname!=NULL) {
1252                 slash=strrchr (utf8_progname, '/');
1253                 if(slash!=NULL) {
1254                         g_strlcpy (process_handle->proc_name, slash+1,
1255                                    _WAPI_PROC_NAME_MAX_LEN - 1);
1256                 } else {
1257                         g_strlcpy (process_handle->proc_name, utf8_progname,
1258                                    _WAPI_PROC_NAME_MAX_LEN - 1);
1259                 }
1260
1261                 g_free (utf8_progname);
1262         }
1263 }
1264
1265 extern void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime);
1266
1267 #if !GLIB_CHECK_VERSION (2,4,0)
1268 #define g_setenv(a,b,c) setenv(a,b,c)
1269 #define g_unsetenv(a) unsetenv(a)
1270 #endif
1271
1272 static void process_set_current (void)
1273 {
1274         pid_t pid = _wapi_getpid ();
1275         const char *handle_env;
1276         struct _WapiHandle_process process_handle = {0};
1277         
1278         mono_once (&process_ops_once, process_ops_init);
1279         
1280         handle_env = g_getenv ("_WAPI_PROCESS_HANDLE_OFFSET");
1281         g_unsetenv ("_WAPI_PROCESS_HANDLE_OFFSET");
1282         
1283         if (handle_env != NULL) {
1284                 struct _WapiHandle_process *process_handlep;
1285                 gchar *procname = NULL;
1286                 gboolean ok;
1287                 
1288                 current_process = _wapi_handle_new_from_offset (WAPI_HANDLE_PROCESS, atoi (handle_env), TRUE);
1289                 
1290 #ifdef DEBUG
1291                 g_message ("%s: Found my process handle: %p (offset %d 0x%x)",
1292                            __func__, current_process, atoi (handle_env),
1293                            atoi (handle_env));
1294 #endif
1295
1296                 ok = _wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
1297                                           (gpointer *)&process_handlep);
1298                 if (ok) {
1299                         /* This test will probably break on linuxthreads, but
1300                          * that should be ancient history on all distros we
1301                          * care about by now
1302                          */
1303                         if (process_handlep->id == pid) {
1304                                 procname = process_handlep->proc_name;
1305                                 if (!strcmp (procname, "mono")) {
1306                                         /* Set a better process name */
1307 #ifdef DEBUG
1308                                         g_message ("%s: Setting better process name", __func__);
1309 #endif
1310                                         
1311                                         process_set_name (process_handlep);
1312                                 } else {
1313 #ifdef DEBUG
1314                                         g_message ("%s: Leaving process name: %s", __func__, procname);
1315 #endif
1316                                 }
1317
1318                                 return;
1319                         }
1320
1321                         /* Wrong pid, so drop this handle and fall through to
1322                          * create a new one
1323                          */
1324                         _wapi_handle_unref (current_process);
1325                 }
1326         }
1327
1328         /* We get here if the handle wasn't specified in the
1329          * environment, or if the process ID was wrong, or if the
1330          * handle lookup failed (eg if the parent process forked and
1331          * quit immediately, and deleted the shared data before the
1332          * child got a chance to attach it.)
1333          */
1334
1335 #ifdef DEBUG
1336         g_message ("%s: Need to create my own process handle", __func__);
1337 #endif
1338
1339         process_handle.id = pid;
1340
1341         process_set_defaults (&process_handle);
1342         process_set_name (&process_handle);
1343
1344         current_process = _wapi_handle_new (WAPI_HANDLE_PROCESS,
1345                                             &process_handle);
1346         if (current_process == _WAPI_HANDLE_INVALID) {
1347                 g_warning ("%s: error creating process handle", __func__);
1348                 return;
1349         }
1350 }
1351
1352 gpointer _wapi_process_duplicate ()
1353 {
1354         mono_once (&process_current_once, process_set_current);
1355         
1356         _wapi_handle_ref (current_process);
1357         
1358         return(current_process);
1359 }
1360
1361 /* Returns a pseudo handle that doesn't need to be closed afterwards */
1362 gpointer GetCurrentProcess (void)
1363 {
1364         mono_once (&process_current_once, process_set_current);
1365                 
1366         return(_WAPI_PROCESS_CURRENT);
1367 }
1368
1369 guint32 GetProcessId (gpointer handle)
1370 {
1371         struct _WapiHandle_process *process_handle;
1372         gboolean ok;
1373
1374         if ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
1375                 /* This is a pseudo handle */
1376                 return(GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
1377         }
1378         
1379         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
1380                                   (gpointer *)&process_handle);
1381         if (ok == FALSE) {
1382                 SetLastError (ERROR_INVALID_HANDLE);
1383                 return (0);
1384         }
1385         
1386         return (process_handle->id);
1387 }
1388
1389 guint32 GetCurrentProcessId (void)
1390 {
1391         mono_once (&process_current_once, process_set_current);
1392                 
1393         return (GetProcessId (current_process));
1394 }
1395
1396 /* Returns the process id as a convenience to the functions that call this */
1397 static pid_t signal_process_if_gone (gpointer handle)
1398 {
1399         struct _WapiHandle_process *process_handle;
1400         gboolean ok;
1401         
1402         g_assert ((GPOINTER_TO_UINT (handle) & _WAPI_PROCESS_UNHANDLED) != _WAPI_PROCESS_UNHANDLED);
1403         
1404         /* Make sure the process is signalled if it has exited - if
1405          * the parent process didn't wait for it then it won't be
1406          */
1407         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
1408                                   (gpointer *)&process_handle);
1409         if (ok == FALSE) {
1410                 /* It's possible that the handle has vanished during
1411                  * the _wapi_search_handle before it gets here, so
1412                  * don't spam the console with warnings.
1413                  */
1414 /*              g_warning ("%s: error looking up process handle %p",
1415   __func__, handle);*/
1416                 
1417                 return (0);
1418         }
1419         
1420 #ifdef DEBUG
1421         g_message ("%s: looking at process %d", __func__, process_handle->id);
1422 #endif
1423
1424         if (kill (process_handle->id, 0) == -1 &&
1425             (errno == ESRCH ||
1426              errno == EPERM)) {
1427                 /* The process is dead, (EPERM tells us a new process
1428                  * has that ID, but as it's owned by someone else it
1429                  * can't be the one listed in our shared memory file)
1430                  */
1431                 _wapi_shared_handle_set_signal_state (handle, TRUE);
1432         }
1433
1434         return (process_handle->id);
1435 }
1436
1437 #ifdef UNUSED_CODE
1438 static gboolean process_enum (gpointer handle, gpointer user_data)
1439 {
1440         GArray *processes=user_data;
1441         pid_t pid = signal_process_if_gone (handle);
1442         int i;
1443         
1444         if (pid == 0) {
1445                 return (FALSE);
1446         }
1447         
1448         /* Ignore processes that have already exited (ie they are signalled) */
1449         if (_wapi_handle_issignalled (handle) == FALSE) {
1450 #ifdef DEBUG
1451                 g_message ("%s: process %d added to array", __func__, pid);
1452 #endif
1453
1454                 /* This ensures that duplicates aren't returned (see
1455                  * the comment above _wapi_search_handle () for why
1456                  * it's needed
1457                  */
1458                 for (i = 0; i < processes->len; i++) {
1459                         if (g_array_index (processes, pid_t, i) == pid) {
1460                                 /* We've already got this one, return
1461                                  * FALSE to keep searching
1462                                  */
1463                                 return (FALSE);
1464                         }
1465                 }
1466                 
1467                 g_array_append_val (processes, pid);
1468         }
1469         
1470         /* Return false to keep searching */
1471         return(FALSE);
1472 }
1473 #endif /* UNUSED_CODE */
1474
1475 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
1476 {
1477         GArray *processes = g_array_new (FALSE, FALSE, sizeof(pid_t));
1478         guint32 fit, i, j;
1479         DIR *dir;
1480         struct dirent *entry;
1481         
1482         mono_once (&process_current_once, process_set_current);
1483
1484         dir = opendir ("/proc");
1485         if (dir == NULL) {
1486                 return(FALSE);
1487         }
1488         while((entry = readdir (dir)) != NULL) {
1489                 if (isdigit (entry->d_name[0])) {
1490                         char *endptr;
1491                         pid_t pid = (pid_t)strtol (entry->d_name, &endptr, 10);
1492
1493                         if (*endptr == '\0') {
1494                                 /* Name was entirely numeric, so was a
1495                                  * process ID
1496                                  */
1497                                 g_array_append_val (processes, pid);
1498                         }
1499                 }
1500         }
1501         closedir (dir);
1502
1503         fit=len/sizeof(guint32);
1504         for (i = 0, j = 0; j < fit && i < processes->len; i++) {
1505                 pids[j++] = g_array_index (processes, pid_t, i);
1506         }
1507
1508         g_array_free (processes, TRUE);
1509         
1510         *needed = j * sizeof(guint32);
1511         
1512         return(TRUE);
1513 }
1514
1515 static gboolean process_open_compare (gpointer handle, gpointer user_data)
1516 {
1517         pid_t wanted_pid;
1518         pid_t checking_pid = signal_process_if_gone (handle);
1519
1520         if (checking_pid == 0) {
1521                 return(FALSE);
1522         }
1523         
1524         wanted_pid = GPOINTER_TO_UINT (user_data);
1525
1526         /* It's possible to have more than one process handle with the
1527          * same pid, but only the one running process can be
1528          * unsignalled
1529          */
1530         if (checking_pid == wanted_pid &&
1531             _wapi_handle_issignalled (handle) == FALSE) {
1532                 /* If the handle is blown away in the window between
1533                  * returning TRUE here and _wapi_search_handle pinging
1534                  * the timestamp, the search will continue
1535                  */
1536                 return(TRUE);
1537         } else {
1538                 return(FALSE);
1539         }
1540 }
1541
1542 gpointer OpenProcess (guint32 req_access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
1543 {
1544         /* Find the process handle that corresponds to pid */
1545         gpointer handle;
1546         
1547         mono_once (&process_current_once, process_set_current);
1548
1549 #ifdef DEBUG
1550         g_message ("%s: looking for process %d", __func__, pid);
1551 #endif
1552
1553         handle = _wapi_search_handle (WAPI_HANDLE_PROCESS,
1554                                       process_open_compare,
1555                                       GUINT_TO_POINTER (pid), NULL, TRUE);
1556         if (handle == 0) {
1557                 gchar *dir = g_strdup_printf ("/proc/%d", pid);
1558                 if (!access (dir, F_OK)) {
1559                         /* Return a pseudo handle for processes we
1560                          * don't have handles for
1561                          */
1562                         return((gpointer)(_WAPI_PROCESS_UNHANDLED + pid));
1563                 } else {
1564 #ifdef DEBUG
1565                         g_message ("%s: Can't find pid %d", __func__, pid);
1566 #endif
1567
1568                         SetLastError (ERROR_PROC_NOT_FOUND);
1569         
1570                         return(NULL);
1571                 }
1572         }
1573
1574         _wapi_handle_ref (handle);
1575         
1576         return(handle);
1577 }
1578
1579 gboolean GetExitCodeProcess (gpointer process, guint32 *code)
1580 {
1581         struct _WapiHandle_process *process_handle;
1582         gboolean ok;
1583         
1584         mono_once (&process_current_once, process_set_current);
1585
1586         if(code==NULL) {
1587                 return(FALSE);
1588         }
1589         
1590         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
1591                 /* This is a pseudo handle, so we don't know what the
1592                  * exit code was
1593                  */
1594                 return(FALSE);
1595         }
1596         
1597         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1598                                 (gpointer *)&process_handle);
1599         if(ok==FALSE) {
1600 #ifdef DEBUG
1601                 g_message ("%s: Can't find process %p", __func__, process);
1602 #endif
1603                 
1604                 return(FALSE);
1605         }
1606         
1607         /* A process handle is only signalled if the process has exited
1608          * and has been waited for */
1609
1610         /* Make sure any process exit has been noticed, before
1611          * checking if the process is signalled.  Fixes bug 325463.
1612          */
1613         process_wait (process, 0);
1614         
1615         if (_wapi_handle_issignalled (process) == TRUE) {
1616                 *code = process_handle->exitstatus;
1617         } else {
1618                 *code = STILL_ACTIVE;
1619         }
1620         
1621         return(TRUE);
1622 }
1623
1624 gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
1625                           WapiFileTime *exit_time, WapiFileTime *kernel_time,
1626                           WapiFileTime *user_time)
1627 {
1628         struct _WapiHandle_process *process_handle;
1629         gboolean ok;
1630         gboolean ku_times_set = FALSE;
1631         
1632         mono_once (&process_current_once, process_set_current);
1633
1634         if(create_time==NULL || exit_time==NULL || kernel_time==NULL ||
1635            user_time==NULL) {
1636                 /* Not sure if w32 allows NULLs here or not */
1637                 return(FALSE);
1638         }
1639         
1640         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
1641                 /* This is a pseudo handle, so just fail for now
1642                  */
1643                 return(FALSE);
1644         }
1645         
1646         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1647                                 (gpointer *)&process_handle);
1648         if(ok==FALSE) {
1649 #ifdef DEBUG
1650                 g_message ("%s: Can't find process %p", __func__, process);
1651 #endif
1652                 
1653                 return(FALSE);
1654         }
1655         
1656         *create_time=process_handle->create_time;
1657
1658         /* A process handle is only signalled if the process has
1659          * exited.  Otherwise exit_time isn't set
1660          */
1661         if(_wapi_handle_issignalled (process)==TRUE) {
1662                 *exit_time=process_handle->exit_time;
1663         }
1664
1665 #ifdef HAVE_GETRUSAGE
1666         if (process_handle->id == getpid ()) {
1667                 struct rusage time_data;
1668                 if (getrusage (RUSAGE_SELF, &time_data) == 0) {
1669                         gint64 tick_val;
1670                         gint64 *tick_val_ptr;
1671                         ku_times_set = TRUE;
1672                         tick_val = time_data.ru_utime.tv_sec * 10000000 + time_data.ru_utime.tv_usec * 10;
1673                         tick_val_ptr = (gint64*)user_time;
1674                         *tick_val_ptr = tick_val;
1675                         tick_val = time_data.ru_stime.tv_sec * 10000000 + time_data.ru_stime.tv_usec * 10;
1676                         tick_val_ptr = (gint64*)kernel_time;
1677                         *tick_val_ptr = tick_val;
1678                 }
1679         }
1680 #endif
1681         if (!ku_times_set) {
1682                 memset (kernel_time, 0, sizeof (WapiFileTime));
1683                 memset (user_time, 0, sizeof (WapiFileTime));
1684         }
1685
1686         return(TRUE);
1687 }
1688
1689 typedef struct
1690 {
1691         gpointer address_start;
1692         gpointer address_end;
1693         gchar *perms;
1694         gpointer address_offset;
1695         dev_t device;
1696         ino_t inode;
1697         gchar *filename;
1698 } WapiProcModule;
1699
1700 static void free_procmodule (WapiProcModule *mod)
1701 {
1702         if (mod->perms != NULL) {
1703                 g_free (mod->perms);
1704         }
1705         if (mod->filename != NULL) {
1706                 g_free (mod->filename);
1707         }
1708         g_free (mod);
1709 }
1710
1711 static gint find_procmodule (gconstpointer a, gconstpointer b)
1712 {
1713         WapiProcModule *want = (WapiProcModule *)a;
1714         WapiProcModule *compare = (WapiProcModule *)b;
1715         
1716         if ((want->device == compare->device) &&
1717             (want->inode == compare->inode)) {
1718                 return(0);
1719         } else {
1720                 return(1);
1721         }
1722 }
1723
1724 #ifdef PLATFORM_MACOSX
1725 #include <mach-o/dyld.h>
1726
1727 static GSList *load_modules ()
1728 {
1729         GSList *ret = NULL;
1730         WapiProcModule *mod;
1731         uint32_t count = _dyld_image_count ();
1732         int i = 0;
1733
1734         for (i = 0; i < count; i++) {
1735                 const struct mach_header *hdr;
1736                 const struct section *sec;
1737                 const char *name;
1738                 intptr_t slide;
1739
1740                 slide = _dyld_get_image_vmaddr_slide (i);
1741                 name = _dyld_get_image_name (i);
1742                 hdr = _dyld_get_image_header (i);
1743                 sec = getsectbynamefromheader (hdr, SEG_DATA, SECT_DATA);
1744
1745                 mod = g_new0 (WapiProcModule, 1);
1746                 mod->address_start = sec->addr;
1747                 mod->address_end = sec->addr+sec->size;
1748                 mod->perms = g_strdup ("r--p");
1749                 mod->address_offset = 0;
1750                 mod->device = makedev (0, 0);
1751                 mod->inode = (ino_t) i;
1752                 mod->filename = g_strdup (name); 
1753                 
1754                 if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
1755                         ret = g_slist_prepend (ret, mod);
1756                 } else {
1757                         free_procmodule (mod);
1758                 }
1759         }
1760
1761         ret = g_slist_reverse (ret);
1762         
1763         return(ret);
1764 }
1765 #else
1766 static GSList *load_modules (FILE *fp)
1767 {
1768         GSList *ret = NULL;
1769         WapiProcModule *mod;
1770         gchar buf[MAXPATHLEN + 1], *p, *endp;
1771         gchar *start_start, *end_start, *prot_start, *offset_start;
1772         gchar *maj_dev_start, *min_dev_start, *inode_start, prot_buf[5];
1773         gpointer address_start, address_end, address_offset;
1774         guint32 maj_dev, min_dev;
1775         ino_t inode;
1776         dev_t device;
1777         
1778         while (fgets (buf, sizeof(buf), fp)) {
1779                 p = buf;
1780                 while (g_ascii_isspace (*p)) ++p;
1781                 start_start = p;
1782                 if (!g_ascii_isxdigit (*start_start)) {
1783                         continue;
1784                 }
1785                 address_start = (gpointer)strtoul (start_start, &endp, 16);
1786                 p = endp;
1787                 if (*p != '-') {
1788                         continue;
1789                 }
1790                 
1791                 ++p;
1792                 end_start = p;
1793                 if (!g_ascii_isxdigit (*end_start)) {
1794                         continue;
1795                 }
1796                 address_end = (gpointer)strtoul (end_start, &endp, 16);
1797                 p = endp;
1798                 if (!g_ascii_isspace (*p)) {
1799                         continue;
1800                 }
1801                 
1802                 while (g_ascii_isspace (*p)) ++p;
1803                 prot_start = p;
1804                 if (*prot_start != 'r' && *prot_start != '-') {
1805                         continue;
1806                 }
1807                 memcpy (prot_buf, prot_start, 4);
1808                 prot_buf[4] = '\0';
1809                 while (!g_ascii_isspace (*p)) ++p;
1810                 
1811                 while (g_ascii_isspace (*p)) ++p;
1812                 offset_start = p;
1813                 if (!g_ascii_isxdigit (*offset_start)) {
1814                         continue;
1815                 }
1816                 address_offset = (gpointer)strtoul (offset_start, &endp, 16);
1817                 p = endp;
1818                 if (!g_ascii_isspace (*p)) {
1819                         continue;
1820                 }
1821                 
1822                 while(g_ascii_isspace (*p)) ++p;
1823                 maj_dev_start = p;
1824                 if (!g_ascii_isxdigit (*maj_dev_start)) {
1825                         continue;
1826                 }
1827                 maj_dev = strtoul (maj_dev_start, &endp, 16);
1828                 p = endp;
1829                 if (*p != ':') {
1830                         continue;
1831                 }
1832                 
1833                 ++p;
1834                 min_dev_start = p;
1835                 if (!g_ascii_isxdigit (*min_dev_start)) {
1836                         continue;
1837                 }
1838                 min_dev = strtoul (min_dev_start, &endp, 16);
1839                 p = endp;
1840                 if (!g_ascii_isspace (*p)) {
1841                         continue;
1842                 }
1843                 
1844                 while (g_ascii_isspace (*p)) ++p;
1845                 inode_start = p;
1846                 if (!g_ascii_isxdigit (*inode_start)) {
1847                         continue;
1848                 }
1849                 inode = (ino_t)strtol (inode_start, &endp, 10);
1850                 p = endp;
1851                 if (!g_ascii_isspace (*p)) {
1852                         continue;
1853                 }
1854
1855                 device = makedev ((int)maj_dev, (int)min_dev);
1856                 if ((device == 0) &&
1857                     (inode == 0)) {
1858                         continue;
1859                 }
1860                 
1861                 while(g_ascii_isspace (*p)) ++p;
1862                 /* p now points to the filename */
1863
1864                 mod = g_new0 (WapiProcModule, 1);
1865                 mod->address_start = address_start;
1866                 mod->address_end = address_end;
1867                 mod->perms = g_strdup (prot_buf);
1868                 mod->address_offset = address_offset;
1869                 mod->device = device;
1870                 mod->inode = inode;
1871                 mod->filename = g_strdup (g_strstrip (p));
1872                 
1873                 if (g_slist_find_custom (ret, mod, find_procmodule) == NULL) {
1874                         ret = g_slist_prepend (ret, mod);
1875                 } else {
1876                         free_procmodule (mod);
1877                 }
1878         }
1879
1880         ret = g_slist_reverse (ret);
1881         
1882         return(ret);
1883 }
1884 #endif
1885
1886 static gboolean match_procname_to_modulename (gchar *procname, gchar *modulename)
1887 {
1888         char* lastsep = NULL;
1889
1890         if (procname == NULL || modulename == NULL)
1891                 return (FALSE);
1892
1893         if (!strcmp (procname, modulename))
1894                 return (TRUE);
1895
1896         lastsep = strrchr (modulename, '/');
1897         if (lastsep) {
1898                 if (!strcmp (lastsep+1, procname))
1899                         return (TRUE);
1900                 return (FALSE);
1901         }
1902
1903         return (FALSE);
1904 }
1905
1906 gboolean EnumProcessModules (gpointer process, gpointer *modules,
1907                              guint32 size, guint32 *needed)
1908 {
1909         struct _WapiHandle_process *process_handle;
1910         gboolean ok;
1911         gchar *filename = NULL;
1912         FILE *fp;
1913         GSList *mods = NULL;
1914         WapiProcModule *module;
1915         guint32 count, avail = size / sizeof(gpointer);
1916         int i;
1917         pid_t pid;
1918         gchar *proc_name = NULL;
1919         
1920         /* Store modules in an array of pointers (main module as
1921          * modules[0]), using the load address for each module as a
1922          * token.  (Use 'NULL' as an alternative for the main module
1923          * so that the simple implementation can just return one item
1924          * for now.)  Get the info from /proc/<pid>/maps on linux,
1925          * other systems will have to implement /dev/kmem reading or
1926          * whatever other horrid technique is needed.
1927          */
1928         if (size < sizeof(gpointer)) {
1929                 return(FALSE);
1930         }
1931
1932         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
1933                 /* This is a pseudo handle */
1934                 pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
1935         } else {
1936                 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1937                                           (gpointer *)&process_handle);
1938                 if (ok == FALSE) {
1939 #ifdef DEBUG
1940                         g_message ("%s: Can't find process %p", __func__, process);
1941 #endif
1942                 
1943                         return(FALSE);
1944                 }
1945                 pid = process_handle->id;
1946                 proc_name = process_handle->proc_name;
1947         }
1948         
1949 #ifdef PLATFORM_MACOSX
1950         {
1951                 mods = load_modules ();
1952 #else
1953         filename = g_strdup_printf ("/proc/%d/maps", pid);
1954         if ((fp = fopen (filename, "r")) == NULL) {
1955                 /* No /proc/<pid>/maps so just return the main module
1956                  * shortcut for now
1957                  */
1958                 modules[0] = NULL;
1959                 *needed = sizeof(gpointer);
1960         } else {
1961                 mods = load_modules (fp);
1962                 fclose (fp);
1963 #endif
1964                 count = g_slist_length (mods);
1965                 
1966                 /* count + 1 to leave slot 0 for the main module */
1967                 *needed = sizeof(gpointer) * (count + 1);
1968
1969                 /* Use the NULL shortcut, as the first line in
1970                  * /proc/<pid>/maps isn't the executable, and we need
1971                  * that first in the returned list. Check the module name 
1972                  * to see if it ends with the proc name and substitute 
1973                  * the first entry with it.  FIXME if this turns out to 
1974                  * be a problem.
1975                  */
1976                 modules[0] = NULL;
1977                 for (i = 0; i < (avail - 1) && i < count; i++) {
1978                         module = (WapiProcModule *)g_slist_nth_data (mods, i);
1979                         if (modules[0] != NULL)
1980                                 modules[i] = module->address_start;
1981                         else if (match_procname_to_modulename (proc_name, module->filename))
1982                                 modules[0] = module->address_start;
1983                         else
1984                                 modules[i + 1] = module->address_start;
1985                 }
1986                 
1987                 for (i = 0; i < count; i++) {
1988                         free_procmodule (g_slist_nth_data (mods, i));
1989                 }
1990                 g_slist_free (mods);
1991         }
1992
1993         g_free (filename);
1994         
1995         return(TRUE);
1996 }
1997
1998 static gchar *get_process_name_from_proc (pid_t pid)
1999 {
2000         gchar *filename = NULL;
2001         gchar *ret = NULL;
2002         gchar buf[256];
2003         FILE *fp;
2004         
2005         memset (buf, '\0', sizeof(buf));
2006         
2007         filename = g_strdup_printf ("/proc/%d/exe", pid);
2008         if (readlink (filename, buf, 255) > 0) {
2009                 ret = g_strdup (buf);
2010         }
2011         g_free (filename);
2012
2013         if (ret != NULL) {
2014                 return(ret);
2015         }
2016         
2017         filename = g_strdup_printf ("/proc/%d/cmdline", pid);
2018         if ((fp = fopen (filename, "r")) != NULL) {
2019                 if (fgets (buf, 256, fp) != NULL) {
2020                         ret = g_strdup (buf);
2021                 }
2022                 
2023                 fclose (fp);
2024         }
2025         g_free (filename);
2026
2027         if (ret != NULL) {
2028                 return(ret);
2029         }
2030         
2031         filename = g_strdup_printf ("/proc/%d/stat", pid);
2032         if ((fp = fopen (filename, "r")) != NULL) {
2033                 if (fgets (buf, 256, fp) != NULL) {
2034                         gchar *start, *end;
2035                         
2036                         start = strchr (buf, '(');
2037                         if (start != NULL) {
2038                                 end = strchr (start + 1, ')');
2039                                 
2040                                 if (end != NULL) {
2041                                         ret = g_strndup (start + 1,
2042                                                          end - start - 1);
2043                                 }
2044                         }
2045                 }
2046                 
2047                 fclose (fp);
2048         }
2049         g_free (filename);
2050
2051         if (ret != NULL) {
2052                 return(ret);
2053         }
2054
2055         return(NULL);
2056 }
2057
2058 static guint32 get_module_name (gpointer process, gpointer module,
2059                                 gunichar2 *basename, guint32 size,
2060                                 gboolean base)
2061 {
2062         struct _WapiHandle_process *process_handle;
2063         gboolean ok;
2064         pid_t pid;
2065         gunichar2 *procname;
2066         gchar *procname_ext = NULL;
2067         glong len;
2068         gsize bytes;
2069         gchar *filename = NULL;
2070         FILE *fp;
2071         GSList *mods = NULL;
2072         WapiProcModule *found_module;
2073         guint32 count;
2074         int i;
2075         gchar *proc_name = NULL;
2076         
2077         mono_once (&process_current_once, process_set_current);
2078
2079 #ifdef DEBUG
2080         g_message ("%s: Getting module base name, process handle %p module %p",
2081                    __func__, process, module);
2082 #endif
2083
2084         size = size*sizeof(gunichar2); /* adjust for unicode characters */
2085
2086         if (basename == NULL || size == 0) {
2087                 return(0);
2088         }
2089         
2090         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2091                 /* This is a pseudo handle */
2092                 pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
2093                 proc_name = get_process_name_from_proc (pid);
2094         } else {
2095                 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2096                                           (gpointer *)&process_handle);
2097                 if (ok == FALSE) {
2098 #ifdef DEBUG
2099                         g_message ("%s: Can't find process %p", __func__,
2100                                    process);
2101 #endif
2102                         
2103                         return(0);
2104                 }
2105                 pid = process_handle->id;
2106                 proc_name = g_strdup (process_handle->proc_name);
2107         }
2108
2109         /* Look up the address in /proc/<pid>/maps */
2110 #ifdef PLATFORM_MACOSX
2111         {
2112                 mods = load_modules ();
2113 #else
2114         filename = g_strdup_printf ("/proc/%d/maps", pid);
2115         if ((fp = fopen (filename, "r")) == NULL) {
2116                 if (errno == EACCES && module == NULL && base == TRUE) {
2117                         procname_ext = get_process_name_from_proc (pid);
2118                 } else {
2119                         /* No /proc/<pid>/maps, so just return failure
2120                          * for now
2121                          */
2122                         g_free (proc_name);
2123                         g_free (filename);
2124                         return(0);
2125                 }
2126         } else {
2127                 mods = load_modules (fp);
2128                 fclose (fp);
2129 #endif
2130                 count = g_slist_length (mods);
2131
2132                 /* If module != NULL compare the address.
2133                  * If module == NULL we are looking for the main module.
2134                  * The best we can do for now check it the module name end with the process name.
2135                  */
2136                 for (i = 0; i < count; i++) {
2137                         found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
2138                         if (procname_ext == NULL &&
2139                             ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||    
2140                              (module != NULL && found_module->address_start == module))) {
2141                                 if (base) {
2142                                         procname_ext = g_path_get_basename (found_module->filename);
2143                                 } else {
2144                                         procname_ext = g_strdup (found_module->filename);
2145                                 }
2146                         }
2147
2148                         free_procmodule (found_module);
2149                 }
2150
2151                 if (procname_ext == NULL)
2152                 {
2153                         /* If it's *still* null, we might have hit the
2154                          * case where reading /proc/$pid/maps gives an
2155                          * empty file for this user.
2156                          */
2157                         procname_ext = get_process_name_from_proc (pid);
2158                 }
2159
2160                 g_slist_free (mods);
2161                 g_free (filename);
2162                 g_free (proc_name);
2163         }
2164
2165         if (procname_ext != NULL) {
2166 #ifdef DEBUG
2167                 g_message ("%s: Process name is [%s]", __func__,
2168                            procname_ext);
2169 #endif
2170
2171                 procname = mono_unicode_from_external (procname_ext, &bytes);
2172                 if (procname == NULL) {
2173                         /* bugger */
2174                         g_free (procname_ext);
2175                         return(0);
2176                 }
2177                 
2178                 len = (bytes / 2);
2179                 
2180                 /* Add the terminator */
2181                 bytes += 2;
2182                 
2183                 if (size < bytes) {
2184 #ifdef DEBUG
2185                         g_message ("%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
2186 #endif
2187
2188                         memcpy (basename, procname, size);
2189                 } else {
2190 #ifdef DEBUG
2191                         g_message ("%s: Size %d larger than needed (%ld)",
2192                                    __func__, size, bytes);
2193 #endif
2194
2195                         memcpy (basename, procname, bytes);
2196                 }
2197                 
2198                 g_free (procname);
2199                 g_free (procname_ext);
2200                 
2201                 return(len);
2202         }
2203         
2204         return(0);
2205 }
2206
2207 guint32 GetModuleBaseName (gpointer process, gpointer module,
2208                            gunichar2 *basename, guint32 size)
2209 {
2210         return(get_module_name (process, module, basename, size, TRUE));
2211 }
2212
2213 guint32 GetModuleFileNameEx (gpointer process, gpointer module,
2214                              gunichar2 *filename, guint32 size)
2215 {
2216         return(get_module_name (process, module, filename, size, FALSE));
2217 }
2218
2219 gboolean GetModuleInformation (gpointer process, gpointer module,
2220                                WapiModuleInfo *modinfo, guint32 size)
2221 {
2222         struct _WapiHandle_process *process_handle;
2223         gboolean ok;
2224         pid_t pid;
2225         gchar *filename = NULL;
2226         FILE *fp;
2227         GSList *mods = NULL;
2228         WapiProcModule *found_module;
2229         guint32 count;
2230         int i;
2231         gboolean ret = FALSE;
2232         gchar *proc_name = NULL;
2233         
2234         mono_once (&process_current_once, process_set_current);
2235         
2236 #ifdef DEBUG
2237         g_message ("%s: Getting module info, process handle %p module %p",
2238                    __func__, process, module);
2239 #endif
2240
2241         if (modinfo == NULL || size < sizeof(WapiModuleInfo)) {
2242                 return(FALSE);
2243         }
2244         
2245         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2246                 /* This is a pseudo handle */
2247                 pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
2248                 proc_name = get_process_name_from_proc (pid);
2249         } else {
2250                 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2251                                           (gpointer *)&process_handle);
2252                 if (ok == FALSE) {
2253 #ifdef DEBUG
2254                         g_message ("%s: Can't find process %p", __func__,
2255                                    process);
2256 #endif
2257                         
2258                         return(FALSE);
2259                 }
2260                 pid = process_handle->id;
2261                 proc_name = g_strdup (process_handle->proc_name);
2262         }
2263
2264 #ifdef PLATFORM_MACOSX
2265         {
2266                 mods = load_modules ();
2267 #else
2268         /* Look up the address in /proc/<pid>/maps */
2269         filename = g_strdup_printf ("/proc/%d/maps", pid);
2270         if ((fp = fopen (filename, "r")) == NULL) {
2271                 /* No /proc/<pid>/maps, so just return failure
2272                  * for now
2273                  */
2274                 g_free (proc_name);
2275                 g_free (filename);
2276                 return(FALSE);
2277         } else {
2278                 mods = load_modules (fp);
2279                 fclose (fp);
2280 #endif
2281                 count = g_slist_length (mods);
2282
2283                 /* If module != NULL compare the address.
2284                  * If module == NULL we are looking for the main module.
2285                  * The best we can do for now check it the module name end with the process name.
2286                  */
2287                 for (i = 0; i < count; i++) {
2288                         found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
2289                         if ( ret == FALSE &&
2290                              ((module == NULL && match_procname_to_modulename (proc_name, found_module->filename)) ||
2291                               (module != NULL && found_module->address_start == module))) {
2292                                 modinfo->lpBaseOfDll = found_module->address_start;
2293                                 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
2294                                 modinfo->EntryPoint = found_module->address_offset;
2295                                 ret = TRUE;
2296                         }
2297
2298                         free_procmodule (found_module);
2299                 }
2300
2301                 g_slist_free (mods);
2302                 g_free (filename);
2303                 g_free (proc_name);
2304         }
2305
2306         return(ret);
2307 }
2308
2309 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
2310 {
2311         struct _WapiHandle_process *process_handle;
2312         gboolean ok;
2313         
2314         mono_once (&process_current_once, process_set_current);
2315
2316         if(min==NULL || max==NULL) {
2317                 /* Not sure if w32 allows NULLs here or not */
2318                 return(FALSE);
2319         }
2320         
2321         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2322                 /* This is a pseudo handle, so just fail for now
2323                  */
2324                 return(FALSE);
2325         }
2326         
2327         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2328                                 (gpointer *)&process_handle);
2329         if(ok==FALSE) {
2330 #ifdef DEBUG
2331                 g_message ("%s: Can't find process %p", __func__, process);
2332 #endif
2333                 
2334                 return(FALSE);
2335         }
2336
2337         *min=process_handle->min_working_set;
2338         *max=process_handle->max_working_set;
2339         
2340         return(TRUE);
2341 }
2342
2343 gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
2344 {
2345         struct _WapiHandle_process *process_handle;
2346         gboolean ok;
2347
2348         mono_once (&process_current_once, process_set_current);
2349         
2350         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2351                 /* This is a pseudo handle, so just fail for now
2352                  */
2353                 return(FALSE);
2354         }
2355
2356         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2357                                 (gpointer *)&process_handle);
2358         if(ok==FALSE) {
2359 #ifdef DEBUG
2360                 g_message ("%s: Can't find process %p", __func__, process);
2361 #endif
2362                 
2363                 return(FALSE);
2364         }
2365
2366         process_handle->min_working_set=min;
2367         process_handle->max_working_set=max;
2368         
2369         return(TRUE);
2370 }
2371
2372
2373 gboolean
2374 TerminateProcess (gpointer process, gint32 exitCode)
2375 {
2376         struct _WapiHandle_process *process_handle;
2377         gboolean ok;
2378         int signo;
2379         int ret;
2380         pid_t pid;
2381         
2382         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2383                 /* This is a pseudo handle */
2384                 pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
2385         } else {
2386                 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2387                                           (gpointer *) &process_handle);
2388
2389                 if (ok == FALSE) {
2390 #ifdef DEBUG
2391                         g_message ("%s: Can't find process %p", __func__,
2392                                    process);
2393 #endif
2394                         SetLastError (ERROR_INVALID_HANDLE);
2395                         return FALSE;
2396                 }
2397                 pid = process_handle->id;
2398         }
2399
2400         signo = (exitCode == -1) ? SIGKILL : SIGTERM;
2401         ret = kill (pid, signo);
2402         if (ret == -1) {
2403                 switch (errno) {
2404                 case EINVAL:
2405                         SetLastError (ERROR_INVALID_PARAMETER);
2406                         break;
2407                 case EPERM:
2408                         SetLastError (ERROR_ACCESS_DENIED);
2409                         break;
2410                 case ESRCH:
2411                         SetLastError (ERROR_PROC_NOT_FOUND);
2412                         break;
2413                 default:
2414                         SetLastError (ERROR_GEN_FAILURE);
2415                 }
2416         }
2417         
2418         return (ret == 0);
2419 }
2420
2421 guint32
2422 GetPriorityClass (gpointer process)
2423 {
2424 #ifdef HAVE_GETPRIORITY
2425         struct _WapiHandle_process *process_handle;
2426         gboolean ok;
2427         int ret;
2428         pid_t pid;
2429         
2430         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2431                 /* This is a pseudo handle */
2432                 pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
2433         } else {
2434                 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2435                                           (gpointer *) &process_handle);
2436
2437                 if (!ok) {
2438                         SetLastError (ERROR_INVALID_HANDLE);
2439                         return FALSE;
2440                 }
2441                 pid = process_handle->id;
2442         }
2443
2444         errno = 0;
2445         ret = getpriority (PRIO_PROCESS, pid);
2446         if (ret == -1 && errno != 0) {
2447                 switch (errno) {
2448                 case EPERM:
2449                 case EACCES:
2450                         SetLastError (ERROR_ACCESS_DENIED);
2451                         break;
2452                 case ESRCH:
2453                         SetLastError (ERROR_PROC_NOT_FOUND);
2454                         break;
2455                 default:
2456                         SetLastError (ERROR_GEN_FAILURE);
2457                 }
2458                 return FALSE;
2459         }
2460
2461         if (ret == 0)
2462                 return NORMAL_PRIORITY_CLASS;
2463         else if (ret < -15)
2464                 return REALTIME_PRIORITY_CLASS;
2465         else if (ret < -10)
2466                 return HIGH_PRIORITY_CLASS;
2467         else if (ret < 0)
2468                 return ABOVE_NORMAL_PRIORITY_CLASS;
2469         else if (ret > 10)
2470                 return IDLE_PRIORITY_CLASS;
2471         else if (ret > 0)
2472                 return BELOW_NORMAL_PRIORITY_CLASS;
2473
2474         return NORMAL_PRIORITY_CLASS;
2475 #else
2476         SetLastError (ERROR_NOT_SUPPORTED);
2477         return 0;
2478 #endif
2479 }
2480
2481 gboolean
2482 SetPriorityClass (gpointer process, guint32  priority_class)
2483 {
2484 #ifdef HAVE_SETPRIORITY
2485         struct _WapiHandle_process *process_handle;
2486         gboolean ok;
2487         int ret;
2488         int prio;
2489         pid_t pid;
2490         
2491         if ((GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED) == _WAPI_PROCESS_UNHANDLED) {
2492                 /* This is a pseudo handle */
2493                 pid = (pid_t)(GPOINTER_TO_UINT (process) & _WAPI_PROCESS_UNHANDLED_PID_MASK);
2494         } else {
2495                 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
2496                                           (gpointer *) &process_handle);
2497
2498                 if (!ok) {
2499                         SetLastError (ERROR_INVALID_HANDLE);
2500                         return FALSE;
2501                 }
2502                 pid = process_handle->id;
2503         }
2504
2505         switch (priority_class) {
2506         case IDLE_PRIORITY_CLASS:
2507                 prio = 19;
2508                 break;
2509         case BELOW_NORMAL_PRIORITY_CLASS:
2510                 prio = 10;
2511                 break;
2512         case NORMAL_PRIORITY_CLASS:
2513                 prio = 0;
2514                 break;
2515         case ABOVE_NORMAL_PRIORITY_CLASS:
2516                 prio = -5;
2517                 break;
2518         case HIGH_PRIORITY_CLASS:
2519                 prio = -11;
2520                 break;
2521         case REALTIME_PRIORITY_CLASS:
2522                 prio = -20;
2523                 break;
2524         default:
2525                 SetLastError (ERROR_INVALID_PARAMETER);
2526                 return FALSE;
2527         }
2528
2529         ret = setpriority (PRIO_PROCESS, pid, prio);
2530         if (ret == -1) {
2531                 switch (errno) {
2532                 case EPERM:
2533                 case EACCES:
2534                         SetLastError (ERROR_ACCESS_DENIED);
2535                         break;
2536                 case ESRCH:
2537                         SetLastError (ERROR_PROC_NOT_FOUND);
2538                         break;
2539                 default:
2540                         SetLastError (ERROR_GEN_FAILURE);
2541                 }
2542         }
2543
2544         return ret == 0;
2545 #else
2546         SetLastError (ERROR_NOT_SUPPORTED);
2547         return FALSE;
2548 #endif
2549 }