2006-03-15 Dick Porter <dick@ximian.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 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <string.h>
13 #include <pthread.h>
14 #include <sched.h>
15 #include <sys/time.h>
16 #include <errno.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <sys/wait.h>
21
22 #include <mono/io-layer/wapi.h>
23 #include <mono/io-layer/wapi-private.h>
24 #include <mono/io-layer/handles-private.h>
25 #include <mono/io-layer/misc-private.h>
26 #include <mono/io-layer/mono-mutex.h>
27 #include <mono/io-layer/process-private.h>
28 #include <mono/io-layer/threads.h>
29 #include <mono/utils/strenc.h>
30 #include <mono/io-layer/timefuncs-private.h>
31
32 /* The process' environment strings */
33 extern char **environ;
34
35 #undef DEBUG
36
37 static guint32 process_wait (gpointer handle, guint32 timeout);
38
39 struct _WapiHandleOps _wapi_process_ops = {
40         NULL,                           /* close_shared */
41         NULL,                           /* signal */
42         NULL,                           /* own */
43         NULL,                           /* is_owned */
44         process_wait,                   /* special_wait */
45         NULL                            /* prewait */   
46 };
47
48 static mono_once_t process_current_once=MONO_ONCE_INIT;
49 static gpointer current_process=NULL;
50
51 static mono_once_t process_ops_once=MONO_ONCE_INIT;
52
53 static void process_ops_init (void)
54 {
55         _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS,
56                                             WAPI_HANDLE_CAP_WAIT |
57                                             WAPI_HANDLE_CAP_SPECIAL_WAIT);
58 }
59
60 static gboolean process_set_termination_details (gpointer handle, int status)
61 {
62         struct _WapiHandle_process *process_handle;
63         gboolean ok;
64         int thr_ret;
65         
66         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
67                                   (gpointer *)&process_handle);
68         if (ok == FALSE) {
69                 g_warning ("%s: error looking up process handle %p",
70                            __func__, handle);
71                 return(FALSE);
72         }
73         
74         thr_ret = _wapi_handle_lock_shared_handles ();
75         g_assert (thr_ret == 0);
76
77         if (WIFSIGNALED(status)) {
78                 process_handle->exitstatus = 128 + WTERMSIG(status);
79         } else {
80                 process_handle->exitstatus = WEXITSTATUS(status);
81         }
82         _wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time);
83         
84         _wapi_shared_handle_set_signal_state (handle, TRUE);
85
86         _wapi_handle_unlock_shared_handles ();
87
88         return (ok);
89 }
90
91 /* See if any child processes have terminated and wait() for them,
92  * updating process handle info.  This function is called from the
93  * collection thread every few seconds.
94  */
95 static gboolean waitfor_pid (gpointer test, gpointer user_data G_GNUC_UNUSED)
96 {
97         struct _WapiHandle_process *process;
98         gboolean ok;
99         int status;
100         pid_t ret;
101         
102         if (_wapi_handle_issignalled (test)) {
103                 /* We've already done this one */
104                 return (FALSE);
105         }
106         
107         ok = _wapi_lookup_handle (test, WAPI_HANDLE_PROCESS,
108                                   (gpointer *)&process);
109         if (ok == FALSE) {
110                 /* The handle must have been too old and was reaped */
111                 return (FALSE);
112         }
113         
114         do {
115                 ret = waitpid (process->id, &status, WNOHANG);
116         } while (errno == EINTR);
117         
118         if (ret <= 0) {
119                 /* Process not ready for wait */
120 #ifdef DEBUG
121                 g_message ("%s: Process %d not ready for waiting for: %s",
122                            __func__, process->id, g_strerror (errno));
123 #endif
124
125                 return (FALSE);
126         }
127         
128 #ifdef DEBUG
129         g_message ("%s: Process %d finished", __func__, ret);
130 #endif
131
132         process_set_termination_details (test, status);
133         
134         /* return FALSE to keep searching */
135         return (FALSE);
136 }
137
138 void _wapi_process_reap (void)
139 {
140 #ifdef DEBUG
141         g_message ("%s: Reaping child processes", __func__);
142 #endif
143
144         _wapi_search_handle (WAPI_HANDLE_PROCESS, waitfor_pid, NULL, NULL);
145 }
146
147 /* Limitations: This can only wait for processes that are our own
148  * children.  Fixing this means resurrecting a daemon helper to manage
149  * processes.
150  */
151 static guint32 process_wait (gpointer handle, guint32 timeout)
152 {
153         struct _WapiHandle_process *process_handle;
154         gboolean ok;
155         pid_t pid, ret;
156         int status;
157         
158 #ifdef DEBUG
159         g_message ("%s: Waiting for process %p", __func__, handle);
160 #endif
161
162         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
163                                   (gpointer *)&process_handle);
164         if (ok == FALSE) {
165                 g_warning ("%s: error looking up process handle %p", __func__,
166                            handle);
167                 return(WAIT_FAILED);
168         }
169         
170         pid = process_handle->id;
171         
172 #ifdef DEBUG
173         g_message ("%s: PID is %d", __func__, pid);
174 #endif
175
176         if (timeout == INFINITE) {
177                 while ((ret = waitpid (pid, &status, 0)) != pid) {
178                         if (ret == (pid_t)-1 && errno != EINTR) {
179                                 return(WAIT_FAILED);
180                         }
181                 }
182         } else if (timeout == 0) {
183                 /* Just poll */
184                 ret = waitpid (pid, &status, WNOHANG);
185                 if (ret != pid) {
186                         return (WAIT_TIMEOUT);
187                 }
188         } else {
189                 /* Poll in a loop */
190                 do {
191                         ret = waitpid (pid, &status, WNOHANG);
192                         if (ret == pid) {
193                                 break;
194                         } else if (ret == (pid_t)-1 && errno != EINTR) {
195                                 return(WAIT_FAILED);
196                         }
197
198                         _wapi_handle_spin (100);
199                         timeout -= 100;
200                 } while (timeout > 0);
201
202                 if (timeout <= 0) {
203                         return(WAIT_TIMEOUT);
204                 }
205         }
206
207         /* Process must have exited */
208 #ifdef DEBUG
209         g_message ("%s: Wait done", __func__);
210 #endif
211
212         ok = process_set_termination_details (handle, status);
213         if (ok == FALSE) {
214                 SetLastError (ERROR_OUTOFMEMORY);
215                 return (WAIT_FAILED);
216         }
217
218         return(WAIT_OBJECT_0);
219 }
220         
221 static void process_set_defaults (struct _WapiHandle_process *process_handle)
222 {
223         /* These seem to be the defaults on w2k */
224         process_handle->min_working_set = 204800;
225         process_handle->max_working_set = 1413120;
226
227         _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
228 }
229
230 /* Implemented as just a wrapper around CreateProcess () */
231 gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
232 {
233         gboolean ret;
234         WapiProcessInformation process_info;
235         gunichar2 *args;
236         gchar *u8file, *u8params, *u8args;
237         
238         if (sei == NULL) {
239                 /* w2k just segfaults here, but we can do better than
240                  * that
241                  */
242                 SetLastError (ERROR_INVALID_PARAMETER);
243                 return (FALSE);
244         }
245
246         if (sei->lpFile == NULL) {
247                 /* w2k returns TRUE for this, for some reason. */
248                 return (TRUE);
249         }
250         
251         /* Put both executable and parameters into the second argument
252          * to CreateProcess (), so it searches $PATH.  The conversion
253          * into and back out of utf8 is because there is no
254          * g_strdup_printf () equivalent for gunichar2 :-(
255          */
256         u8file = g_utf16_to_utf8 (sei->lpFile, -1, NULL, NULL, NULL);
257         if (u8file == NULL) {
258                 SetLastError (ERROR_INVALID_DATA);
259                 return (FALSE);
260         }
261         
262         if (sei->lpParameters != NULL) {
263                 u8params = g_utf16_to_utf8 (sei->lpParameters, -1, NULL, NULL, NULL);
264                 if (u8params == NULL) {
265                         SetLastError (ERROR_INVALID_DATA);
266                         g_free (u8file);
267                         return (FALSE);
268                 }
269         
270                 u8args = g_strdup_printf ("%s %s", u8file, u8params);
271                 if (u8args == NULL) {
272                         SetLastError (ERROR_INVALID_DATA);
273                         g_free (u8params);
274                         g_free (u8file);
275                         return (FALSE);
276                 }
277         
278                 args = g_utf8_to_utf16 (u8args, -1, NULL, NULL, NULL);
279         
280                 g_free (u8file);
281                 g_free (u8params);
282                 g_free (u8args);
283         } else {
284                 args = g_utf8_to_utf16 (u8file, -1, NULL, NULL, NULL);
285         }
286                 
287         if (args == NULL) {
288                 SetLastError (ERROR_INVALID_DATA);
289                 return (FALSE);
290         }
291         
292         ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
293                              CREATE_UNICODE_ENVIRONMENT, NULL,
294                              sei->lpDirectory, NULL, &process_info);
295         g_free (args);
296         
297         if (!ret) {
298                 return (FALSE);
299         }
300         
301         if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) {
302                 sei->hProcess = process_info.hProcess;
303         } else {
304                 CloseHandle (process_info.hProcess);
305         }
306         
307         return (ret);
308 }
309
310 gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
311                         WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
312                         WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
313                         gboolean inherit_handles, guint32 create_flags,
314                         gpointer new_environ, const gunichar2 *cwd,
315                         WapiStartupInfo *startup,
316                         WapiProcessInformation *process_info)
317 {
318         gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv;
319         guint32 i, env_count = 0;
320         gboolean ret = FALSE;
321         gpointer handle;
322         struct _WapiHandle_process process_handle = {0}, *process_handle_data;
323         GError *gerr = NULL;
324         int in_fd, out_fd, err_fd;
325         pid_t pid;
326         int thr_ret;
327         
328         mono_once (&process_ops_once, process_ops_init);
329         
330         /* appname and cmdline specify the executable and its args:
331          *
332          * If appname is not NULL, it is the name of the executable.
333          * Otherwise the executable is the first token in cmdline.
334          *
335          * Executable searching:
336          *
337          * If appname is not NULL, it can specify the full path and
338          * file name, or else a partial name and the current directory
339          * will be used.  There is no additional searching.
340          *
341          * If appname is NULL, the first whitespace-delimited token in
342          * cmdline is used.  If the name does not contain a full
343          * directory path, the search sequence is:
344          *
345          * 1) The directory containing the current process
346          * 2) The current working directory
347          * 3) The windows system directory  (Ignored)
348          * 4) The windows directory (Ignored)
349          * 5) $PATH
350          *
351          * Just to make things more interesting, tokens can contain
352          * white space if they are surrounded by quotation marks.  I'm
353          * beginning to understand just why windows apps are generally
354          * so crap, with an API like this :-(
355          */
356         if (appname != NULL) {
357                 cmd = mono_unicode_to_external (appname);
358                 if (cmd == NULL) {
359 #ifdef DEBUG
360                         g_message ("%s: unicode conversion returned NULL",
361                                    __func__);
362 #endif
363
364                         SetLastError (ERROR_PATH_NOT_FOUND);
365                         goto cleanup;
366                 }
367
368                 /* Turn all the slashes round the right way */
369                 for (i = 0; i < strlen (cmd); i++) {
370                         if (cmd[i] == '\\') {
371                                 cmd[i] = '/';
372                         }
373                 }
374         }
375         
376         if (cmdline != NULL) {
377                 args = mono_unicode_to_external (cmdline);
378                 if (args == NULL) {
379 #ifdef DEBUG
380                         g_message ("%s: unicode conversion returned NULL", __func__);
381 #endif
382
383                         SetLastError (ERROR_PATH_NOT_FOUND);
384                         goto cleanup;
385                 }
386         }
387
388         if (cwd != NULL) {
389                 dir = mono_unicode_to_external (cwd);
390                 if (dir == NULL) {
391 #ifdef DEBUG
392                         g_message ("%s: unicode conversion returned NULL", __func__);
393 #endif
394
395                         SetLastError (ERROR_PATH_NOT_FOUND);
396                         goto cleanup;
397                 }
398
399                 /* Turn all the slashes round the right way */
400                 for (i = 0; i < strlen (dir); i++) {
401                         if (dir[i] == '\\') {
402                                 dir[i] = '/';
403                         }
404                 }
405         } else {
406                 dir = g_get_current_dir ();
407         }
408         
409
410         /* We can't put off locating the executable any longer :-( */
411         if (cmd != NULL) {
412                 gchar *unquoted;
413                 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
414                         /* Strip off the drive letter.  I can't
415                          * believe that CP/M holdover is still
416                          * visible...
417                          */
418                         g_memmove (cmd, cmd+2, strlen (cmd)-2);
419                         cmd[strlen (cmd)-2] = '\0';
420                 }
421
422                 unquoted = g_shell_unquote (cmd, NULL);
423                 if (unquoted[0] == '/') {
424                         /* Assume full path given */
425                         prog = g_strdup (unquoted);
426
427                         /* Executable existing ? */
428                         if (access (prog, X_OK) != 0) {
429 #ifdef DEBUG
430                                 g_message ("%s: Couldn't find executable %s",
431                                            __func__, prog);
432 #endif
433                                 g_free (prog);
434                                 g_free (unquoted);
435                                 SetLastError (ERROR_FILE_NOT_FOUND);
436                                 goto cleanup;
437                         }
438                 } else {
439                         /* Search for file named by cmd in the current
440                          * directory
441                          */
442                         char *curdir = g_get_current_dir ();
443
444                         prog = g_strdup_printf ("%s/%s", curdir, unquoted);
445                         g_free (unquoted);
446                         g_free (curdir);
447
448                         /* And make sure it's executable */
449                         if (access (prog, X_OK) != 0) {
450 #ifdef DEBUG
451                                 g_message ("%s: Couldn't find executable %s",
452                                            __func__, prog);
453 #endif
454                                 g_free (prog);
455                                 SetLastError (ERROR_FILE_NOT_FOUND);
456                                 goto cleanup;
457                         }
458                 }
459
460                 args_after_prog = args;
461         } else {
462                 gchar *token = NULL;
463                 char quote;
464                 
465                 /* Dig out the first token from args, taking quotation
466                  * marks into account
467                  */
468
469                 /* First, strip off all leading whitespace */
470                 args = g_strchug (args);
471                 
472                 /* args_after_prog points to the contents of args
473                  * after token has been set (otherwise argv[0] is
474                  * duplicated)
475                  */
476                 args_after_prog = args;
477
478                 /* Assume the opening quote will always be the first
479                  * character
480                  */
481                 if (args[0] == '\"' || args [0] == '\'') {
482                         quote = args [0];
483                         for (i = 1; args[i] != '\0' && args[i] != quote; i++);
484                         if (g_ascii_isspace (args[i+1])) {
485                                 /* We found the first token */
486                                 token = g_strndup (args+1, i-1);
487                                 args_after_prog = args + i;
488                         } else {
489                                 /* Quotation mark appeared in the
490                                  * middle of the token.  Just give the
491                                  * whole first token, quotes and all,
492                                  * to exec.
493                                  */
494                         }
495                 }
496                 
497                 if (token == NULL) {
498                         /* No quote mark, or malformed */
499                         for (i = 0; args[i] != '\0'; i++) {
500                                 if (g_ascii_isspace (args[i])) {
501                                         token = g_strndup (args, i);
502                                         args_after_prog = args + i + 1;
503                                         break;
504                                 }
505                         }
506                 }
507
508                 if (token == NULL && args[0] != '\0') {
509                         /* Must be just one token in the string */
510                         token = g_strdup (args);
511                         args_after_prog = NULL;
512                 }
513                 
514                 if (token == NULL) {
515                         /* Give up */
516 #ifdef DEBUG
517                         g_message ("%s: Couldn't find what to exec", __func__);
518 #endif
519
520                         SetLastError (ERROR_PATH_NOT_FOUND);
521                         goto cleanup;
522                 }
523                 
524                 /* Turn all the slashes round the right way. Only for
525                  * the prg. name
526                  */
527                 for (i = 0; i < strlen (token); i++) {
528                         if (token[i] == '\\') {
529                                 token[i] = '/';
530                         }
531                 }
532
533                 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
534                         /* Strip off the drive letter.  I can't
535                          * believe that CP/M holdover is still
536                          * visible...
537                          */
538                         g_memmove (token, token+2, strlen (token)-2);
539                         token[strlen (token)-2] = '\0';
540                 }
541
542                 if (token[0] == '/') {
543                         /* Assume full path given */
544                         prog = g_strdup (token);
545                         
546                         /* Executable existing ? */
547                         if (access (prog, X_OK) != 0) {
548                                 g_free (prog);
549 #ifdef DEBUG
550                                 g_message ("%s: Couldn't find executable %s",
551                                            __func__, token);
552 #endif
553                                 g_free (token);
554                                 SetLastError (ERROR_FILE_NOT_FOUND);
555                                 goto cleanup;
556                         }
557
558                 } else {
559                         char *curdir = g_get_current_dir ();
560
561                         /* FIXME: Need to record the directory
562                          * containing the current process, and check
563                          * that for the new executable as the first
564                          * place to look
565                          */
566
567                         prog = g_strdup_printf ("%s/%s", curdir, token);
568                         g_free (curdir);
569
570                         /* I assume X_OK is the criterion to use,
571                          * rather than F_OK
572                          */
573                         if (access (prog, X_OK) != 0) {
574                                 g_free (prog);
575                                 prog = g_find_program_in_path (token);
576                                 if (prog == NULL) {
577 #ifdef DEBUG
578                                         g_message ("%s: Couldn't find executable %s", __func__, token);
579 #endif
580
581                                         g_free (token);
582                                         SetLastError (ERROR_FILE_NOT_FOUND);
583                                         goto cleanup;
584                                 }
585                         }
586                 }
587
588                 g_free (token);
589         }
590
591 #ifdef DEBUG
592         g_message ("%s: Exec prog [%s] args [%s]", __func__, prog,
593                    args_after_prog);
594 #endif
595         
596         if (args_after_prog != NULL && *args_after_prog) {
597                 gchar *qprog;
598
599                 qprog = g_shell_quote (prog);
600                 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
601                 g_free (qprog);
602         } else {
603                 full_prog = g_shell_quote (prog);
604         }
605
606         ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
607         if (ret == FALSE) {
608                 /* FIXME: Could do something with the GError here
609                  */
610         }
611
612         if (startup != NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
613                 in_fd = GPOINTER_TO_UINT (startup->hStdInput);
614                 out_fd = GPOINTER_TO_UINT (startup->hStdOutput);
615                 err_fd = GPOINTER_TO_UINT (startup->hStdError);
616         } else {
617                 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
618                 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
619                 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
620         }
621         
622         g_strlcpy (process_handle.proc_name, prog,
623                    _WAPI_PROC_NAME_MAX_LEN - 1);
624
625         process_set_defaults (&process_handle);
626         
627         handle = _wapi_handle_new (WAPI_HANDLE_PROCESS, &process_handle);
628         if (handle == _WAPI_HANDLE_INVALID) {
629                 g_warning ("%s: error creating process handle", __func__);
630
631                 SetLastError (ERROR_PATH_NOT_FOUND);
632                 goto cleanup;
633         }
634         
635         /* new_environ is a block of NULL-terminated strings, which
636          * is itself NULL-terminated. Of course, passing an array of
637          * string pointers would have made things too easy :-(
638          *
639          * If new_environ is not NULL it specifies the entire set of
640          * environment variables in the new process.  Otherwise the
641          * new process inherits the same environment.
642          */
643         if (new_environ != NULL) {
644                 gunichar2 *new_environp;
645
646                 /* Count the number of strings */
647                 for (new_environp = (gunichar2 *)new_environ; *new_environp;
648                      new_environp++) {
649                         env_count++;
650                         while (*new_environp) {
651                                 new_environp++;
652                         }
653                 }
654
655                 /* +2: one for the process handle value, and the last
656                  * one is NULL
657                  */
658                 env_strings = g_new0 (gchar *, env_count + 2);
659                 
660                 /* Copy each environ string into 'strings' turning it
661                  * into utf8 (or the requested encoding) at the same
662                  * time
663                  */
664                 env_count = 0;
665                 for (new_environp = (gunichar2 *)new_environ; *new_environp;
666                      new_environp++) {
667                         env_strings[env_count] = mono_unicode_to_external (new_environp);
668                         env_count++;
669                         while (*new_environp) {
670                                 new_environp++;
671                         }
672                 }
673         } else {
674                 for (i = 0; environ[i] != NULL; i++) {
675                         env_count++;
676                 }
677
678                 /* +2: one for the process handle value, and the last
679                  * one is NULL
680                  */
681                 env_strings = g_new0 (gchar *, env_count + 2);
682                 
683                 /* Copy each environ string into 'strings' turning it
684                  * into utf8 (or the requested encoding) at the same
685                  * time
686                  */
687                 env_count = 0;
688                 for (i = 0; environ[i] != NULL; i++) {
689                         env_strings[env_count] = g_strdup (environ[i]);
690                         env_count++;
691                 }
692         }
693         /* pass process handle info to the child, so it doesn't have
694          * to do an expensive search over the whole list
695          */
696         if (env_strings != NULL) {
697                 struct _WapiHandleUnshared *handle_data;
698                 struct _WapiHandle_shared_ref *ref;
699                 
700                 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(handle));
701                 ref = &handle_data->u.shared;
702                 
703                 env_strings[env_count] = g_strdup_printf ("_WAPI_PROCESS_HANDLE_OFFSET=%d", ref->offset);
704         }
705
706         thr_ret = _wapi_handle_lock_shared_handles ();
707         g_assert (thr_ret == 0);
708         
709         pid = fork ();
710         if (pid == -1) {
711                 /* Error */
712                 SetLastError (ERROR_OUTOFMEMORY);
713                 _wapi_handle_unref (handle);
714                 goto cleanup;
715         } else if (pid == 0) {
716                 /* Child */
717                 
718                 /* Wait for the parent to finish setting up the
719                  * handle.  The semaphore lock is safe because the
720                  * sem_undo structures of a semaphore aren't inherited
721                  * across a fork ()
722                  */
723                 thr_ret = _wapi_handle_lock_shared_handles ();
724                 g_assert (thr_ret == 0);
725         
726                 _wapi_handle_unlock_shared_handles ();
727                 
728                 /* should we detach from the process group? */
729
730                 /* Connect stdin, stdout and stderr */
731                 dup2 (in_fd, 0);
732                 dup2 (out_fd, 1);
733                 dup2 (err_fd, 2);
734
735                 if (inherit_handles != TRUE) {
736                         /* FIXME: do something here */
737                 }
738                 
739                 /* Close all file descriptors */
740                 for (i = getdtablesize () - 1; i > 2; i--) {
741                         close (i);
742                 }
743
744 #ifdef DEBUG
745                 g_message ("%s: exec()ing [%s] in dir [%s]", __func__, cmd,
746                            dir);
747                 for (i = 0; argv[i] != NULL; i++) {
748                         g_message ("arg %d: [%s]", i, argv[i]);
749                 }
750                 
751                 for (i = 0; env_strings[i] != NULL; i++) {
752                         g_message ("env %d: [%s]", i, env_strings[i]);
753                 }
754 #endif
755
756                 /* set cwd */
757                 if (chdir (dir) == -1) {
758                         /* set error */
759                         exit (-1);
760                 }
761                 
762                 /* exec */
763                 execve (argv[0], argv, env_strings);
764                 
765                 /* set error */
766                 exit (-1);
767         }
768         /* parent */
769         
770         ret = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
771                                    (gpointer *)&process_handle_data);
772         if (ret == FALSE) {
773                 g_warning ("%s: error looking up process handle %p", __func__,
774                            handle);
775                 _wapi_handle_unref (handle);
776                 goto cleanup;
777         }
778         
779         process_handle_data->id = pid;
780         
781         if (process_info != NULL) {
782                 process_info->hProcess = handle;
783                 process_info->dwProcessId = pid;
784
785                 /* FIXME: we might need to handle the thread info some
786                  * day
787                  */
788                 process_info->hThread = NULL;
789                 process_info->dwThreadId = 0;
790         }
791
792 cleanup:
793         _wapi_handle_unlock_shared_handles ();
794
795         if (cmd != NULL) {
796                 g_free (cmd);
797         }
798         if (full_prog != NULL) {
799                 g_free (prog);
800         }
801         if (args != NULL) {
802                 g_free (args);
803         }
804         if (dir != NULL) {
805                 g_free (dir);
806         }
807         if(env_strings != NULL) {
808                 g_strfreev (env_strings);
809         }
810         
811         return(ret);
812 }
813                 
814 static void process_set_name (struct _WapiHandle_process *process_handle)
815 {
816         gchar *progname, *utf8_progname, *slash;
817         
818         progname=g_get_prgname ();
819         utf8_progname=mono_utf8_from_external (progname);
820
821 #ifdef DEBUG
822         g_message ("%s: using [%s] as prog name", __func__, progname);
823 #endif
824
825         if(utf8_progname!=NULL) {
826                 slash=strrchr (utf8_progname, '/');
827                 if(slash!=NULL) {
828                         g_strlcpy (process_handle->proc_name, slash+1,
829                                    _WAPI_PROC_NAME_MAX_LEN - 1);
830                 } else {
831                         g_strlcpy (process_handle->proc_name, utf8_progname,
832                                    _WAPI_PROC_NAME_MAX_LEN - 1);
833                 }
834
835                 g_free (utf8_progname);
836         }
837 }
838
839 extern void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime);
840
841 static void process_set_current (void)
842 {
843         pid_t pid = _wapi_getpid ();
844         char *handle_env;
845         struct _WapiHandle_process process_handle = {0};
846         
847         handle_env = getenv ("_WAPI_PROCESS_HANDLE_OFFSET");
848         if (handle_env != NULL) {
849                 struct _WapiHandle_process *process_handlep;
850                 guchar *procname = NULL;
851                 gboolean ok;
852                 
853                 current_process = _wapi_handle_new_from_offset (WAPI_HANDLE_PROCESS, atoi (handle_env), TRUE);
854                 
855 #ifdef DEBUG
856                 g_message ("%s: Found my process handle: %p (offset %d)",
857                            __func__, current_process, atoi (handle_env));
858 #endif
859
860                 ok = _wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
861                                           (gpointer *)&process_handlep);
862                 if (ok == FALSE) {
863                         g_warning ("%s: error looking up process handle %p",
864                                    __func__, current_process);
865                         return;
866                 }
867
868                 /* This test will probably break on linuxthreads, but
869                  * that should be ancient history on all distros we
870                  * care about by now
871                  */
872                 if (process_handlep->id == pid) {
873                         procname = process_handlep->proc_name;
874                         if (!strcmp (procname, "mono")) {
875                                 /* Set a better process name */
876 #ifdef DEBUG
877                                 g_message ("%s: Setting better process name",
878                                            __func__);
879 #endif
880
881                                 process_set_name (process_handlep);
882                         } else {
883 #ifdef DEBUG
884                                 g_message ("%s: Leaving process name: %s",
885                                            __func__, procname);
886 #endif
887                         }
888
889                         return;
890                 }
891
892                 /* Wrong pid, so drop this handle and fall through to
893                  * create a new one
894                  */
895                 _wapi_handle_unref (current_process);
896         }
897
898 #ifdef DEBUG
899         g_message ("%s: Need to create my own process handle", __func__);
900 #endif
901
902         process_handle.id = pid;
903
904         process_set_defaults (&process_handle);
905         process_set_name (&process_handle);
906
907         current_process = _wapi_handle_new (WAPI_HANDLE_PROCESS,
908                                             &process_handle);
909         if (current_process == _WAPI_HANDLE_INVALID) {
910                 g_warning ("%s: error creating process handle", __func__);
911                 return;
912         }
913                 
914         /* Make sure the new handle has a reference so it wont go away
915          * until this process exits
916          */
917         _wapi_handle_ref (current_process);
918 }
919
920 /* Returns a pseudo handle that doesn't need to be closed afterwards */
921 gpointer GetCurrentProcess (void)
922 {
923         mono_once (&process_current_once, process_set_current);
924                 
925         return((gpointer)-1);
926 }
927
928 guint32 GetProcessId (gpointer handle)
929 {
930         struct _WapiHandle_process *process_handle;
931         gboolean ok;
932         
933         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
934                                   (gpointer *)&process_handle);
935         if (ok == FALSE) {
936                 SetLastError (ERROR_INVALID_HANDLE);
937                 return (0);
938         }
939         
940         return (process_handle->id);
941 }
942
943 guint32 GetCurrentProcessId (void)
944 {
945         mono_once (&process_current_once, process_set_current);
946                 
947         return (GetProcessId (current_process));
948 }
949
950 /* Returns the process id as a convenience to the functions that call this */
951 static pid_t signal_process_if_gone (gpointer handle)
952 {
953         struct _WapiHandle_process *process_handle;
954         gboolean ok;
955         
956         /* Make sure the process is signalled if it has exited - if
957          * the parent process didn't wait for it then it won't be
958          */
959         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
960                                   (gpointer *)&process_handle);
961         if (ok == FALSE) {
962                 /* It's possible that the handle has vanished during
963                  * the _wapi_search_handle before it gets here, so
964                  * don't spam the console with warnings.
965                  */
966 /*              g_warning ("%s: error looking up process handle %p",
967   __func__, handle);*/
968                 
969                 return (0);
970         }
971         
972 #ifdef DEBUG
973         g_message ("%s: looking at process %d", __func__, process_handle->id);
974 #endif
975
976         if (kill (process_handle->id, 0) == -1 &&
977             (errno == ESRCH ||
978              errno == EPERM)) {
979                 /* The process is dead, (EPERM tells us a new process
980                  * has that ID, but as it's owned by someone else it
981                  * can't be the one listed in our shared memory file)
982                  */
983                 _wapi_shared_handle_set_signal_state (handle, TRUE);
984         }
985
986         return (process_handle->id);
987 }
988
989 static gboolean process_enum (gpointer handle, gpointer user_data)
990 {
991         GArray *processes=user_data;
992         pid_t pid = signal_process_if_gone (handle);
993         int i;
994         
995         if (pid == 0) {
996                 return (FALSE);
997         }
998         
999         /* Ignore processes that have already exited (ie they are signalled) */
1000         if (_wapi_handle_issignalled (handle) == FALSE) {
1001 #ifdef DEBUG
1002                 g_message ("%s: process %d added to array", __func__, pid);
1003 #endif
1004
1005                 /* This ensures that duplicates aren't returned (see
1006                  * the comment above _wapi_search_handle () for why
1007                  * it's needed
1008                  */
1009                 for (i = 0; i < processes->len; i++) {
1010                         if (g_array_index (processes, pid_t, i) == pid) {
1011                                 /* We've already got this one, return
1012                                  * FALSE to keep searching
1013                                  */
1014                                 return (FALSE);
1015                         }
1016                 }
1017                 
1018                 g_array_append_val (processes, pid);
1019         }
1020         
1021         /* Return false to keep searching */
1022         return(FALSE);
1023 }
1024
1025 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
1026 {
1027         GArray *processes = g_array_new (FALSE, FALSE, sizeof(pid_t));
1028         guint32 fit, i, j;
1029         
1030         mono_once (&process_current_once, process_set_current);
1031         
1032         _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
1033                              NULL);
1034         
1035         fit=len/sizeof(guint32);
1036         for (i = 0, j = 0; j < fit && i < processes->len; i++) {
1037                 pids[j++] = g_array_index (processes, pid_t, i);
1038         }
1039
1040         g_array_free (processes, TRUE);
1041         
1042         *needed = j * sizeof(guint32);
1043         
1044         return(TRUE);
1045 }
1046
1047 static gboolean process_open_compare (gpointer handle, gpointer user_data)
1048 {
1049         pid_t wanted_pid;
1050         pid_t checking_pid = signal_process_if_gone (handle);
1051
1052         if (checking_pid == 0) {
1053                 return(FALSE);
1054         }
1055         
1056         wanted_pid = GPOINTER_TO_UINT (user_data);
1057
1058         /* It's possible to have more than one process handle with the
1059          * same pid, but only the one running process can be
1060          * unsignalled
1061          */
1062         if (checking_pid == wanted_pid &&
1063             _wapi_handle_issignalled (handle) == FALSE) {
1064                 /* If the handle is blown away in the window between
1065                  * returning TRUE here and _wapi_search_handle pinging
1066                  * the timestamp, the search will continue
1067                  */
1068                 return(TRUE);
1069         } else {
1070                 return(FALSE);
1071         }
1072 }
1073
1074 gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
1075 {
1076         /* Find the process handle that corresponds to pid */
1077         gpointer handle;
1078         
1079         mono_once (&process_current_once, process_set_current);
1080
1081 #ifdef DEBUG
1082         g_message ("%s: looking for process %d", __func__, pid);
1083 #endif
1084
1085         handle = _wapi_search_handle (WAPI_HANDLE_PROCESS,
1086                                       process_open_compare,
1087                                       GUINT_TO_POINTER (pid), NULL);
1088         if (handle == 0) {
1089 #ifdef DEBUG
1090                 g_message ("%s: Can't find pid %d", __func__, pid);
1091 #endif
1092
1093                 SetLastError (ERROR_PROC_NOT_FOUND);
1094         
1095                 return(NULL);
1096         }
1097
1098         _wapi_handle_ref (handle);
1099         
1100         return(handle);
1101 }
1102
1103 gboolean GetExitCodeProcess (gpointer process, guint32 *code)
1104 {
1105         struct _WapiHandle_process *process_handle;
1106         gboolean ok;
1107         
1108         mono_once (&process_current_once, process_set_current);
1109
1110         if(code==NULL) {
1111                 return(FALSE);
1112         }
1113         
1114         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1115                                 (gpointer *)&process_handle);
1116         if(ok==FALSE) {
1117 #ifdef DEBUG
1118                 g_message ("%s: Can't find process %p", __func__, process);
1119 #endif
1120                 
1121                 return(FALSE);
1122         }
1123         
1124         /* A process handle is only signalled if the process has exited
1125          * and has been waited for */
1126         if (_wapi_handle_issignalled (process) == TRUE ||
1127             process_wait (process, 0) == WAIT_OBJECT_0) {
1128                 *code = process_handle->exitstatus;
1129         } else {
1130                 *code = STILL_ACTIVE;
1131         }
1132         
1133         return(TRUE);
1134 }
1135
1136 gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
1137                           WapiFileTime *exit_time, WapiFileTime *kernel_time,
1138                           WapiFileTime *user_time)
1139 {
1140         struct _WapiHandle_process *process_handle;
1141         gboolean ok;
1142         
1143         mono_once (&process_current_once, process_set_current);
1144
1145         if(create_time==NULL || exit_time==NULL || kernel_time==NULL ||
1146            user_time==NULL) {
1147                 /* Not sure if w32 allows NULLs here or not */
1148                 return(FALSE);
1149         }
1150         
1151         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1152                                 (gpointer *)&process_handle);
1153         if(ok==FALSE) {
1154 #ifdef DEBUG
1155                 g_message ("%s: Can't find process %p", __func__, process);
1156 #endif
1157                 
1158                 return(FALSE);
1159         }
1160         
1161         *create_time=process_handle->create_time;
1162
1163         /* A process handle is only signalled if the process has
1164          * exited.  Otherwise exit_time isn't set
1165          */
1166         if(_wapi_handle_issignalled (process)==TRUE) {
1167                 *exit_time=process_handle->exit_time;
1168         }
1169         
1170         return(TRUE);
1171 }
1172
1173 gboolean EnumProcessModules (gpointer process, gpointer *modules,
1174                              guint32 size, guint32 *needed)
1175 {
1176         /* Store modules in an array of pointers (main module as
1177          * modules[0]), using the load address for each module as a
1178          * token.  (Use 'NULL' as an alternative for the main module
1179          * so that the simple implementation can just return one item
1180          * for now.)  Get the info from /proc/<pid>/maps on linux,
1181          * other systems will have to implement /dev/kmem reading or
1182          * whatever other horrid technique is needed.
1183          */
1184         if(size<sizeof(gpointer)) {
1185                 return(FALSE);
1186         }
1187         
1188 #ifdef linux
1189         modules[0]=NULL;
1190         *needed=sizeof(gpointer);
1191 #else
1192         modules[0]=NULL;
1193         *needed=sizeof(gpointer);
1194 #endif
1195         
1196         return(TRUE);
1197 }
1198
1199 guint32 GetModuleBaseName (gpointer process, gpointer module,
1200                            gunichar2 *basename, guint32 size)
1201 {
1202         struct _WapiHandle_process *process_handle;
1203         gboolean ok;
1204         
1205         mono_once (&process_current_once, process_set_current);
1206
1207 #ifdef DEBUG
1208         g_message ("%s: Getting module base name, process handle %p module %p",
1209                    __func__, process, module);
1210 #endif
1211
1212         if(basename==NULL || size==0) {
1213                 return(FALSE);
1214         }
1215         
1216         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1217                                 (gpointer *)&process_handle);
1218         if(ok==FALSE) {
1219 #ifdef DEBUG
1220                 g_message ("%s: Can't find process %p", __func__, process);
1221 #endif
1222                 
1223                 return(FALSE);
1224         }
1225
1226         if(module==NULL) {
1227                 /* Shorthand for the main module, which has the
1228                  * process name recorded in the handle data
1229                  */
1230                 pid_t pid;
1231                 gunichar2 *procname;
1232                 guchar *procname_utf8 = NULL;
1233                 glong len, bytes;
1234                 
1235 #ifdef DEBUG
1236                 g_message ("%s: Returning main module name", __func__);
1237 #endif
1238
1239                 pid=process_handle->id;
1240                 procname_utf8 = process_handle->proc_name;
1241         
1242 #ifdef DEBUG
1243                 g_message ("%s: Process name is [%s]", __func__,
1244                            procname_utf8);
1245 #endif
1246
1247                 procname = g_utf8_to_utf16 (procname_utf8, -1, NULL, &len,
1248                                             NULL);
1249                 if (procname == NULL) {
1250                         /* bugger */
1251                         return(0);
1252                 }
1253
1254                 /* Add the terminator, and convert chars to bytes */
1255                 bytes = (len + 1) * 2;
1256                 
1257                 if (size < bytes) {
1258 #ifdef DEBUG
1259                         g_message ("%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
1260 #endif
1261
1262                         memcpy (basename, procname, size);
1263                 } else {
1264 #ifdef DEBUG
1265                         g_message ("%s: Size %d larger than needed (%ld)",
1266                                    __func__, size, bytes);
1267 #endif
1268
1269                         memcpy (basename, procname, bytes);
1270                 }
1271                 
1272                 g_free (procname);
1273
1274                 return(len);
1275         } else {
1276                 /* Look up the address in /proc/<pid>/maps */
1277         }
1278         
1279         return(0);
1280 }
1281
1282 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
1283 {
1284         struct _WapiHandle_process *process_handle;
1285         gboolean ok;
1286         
1287         mono_once (&process_current_once, process_set_current);
1288
1289         if(min==NULL || max==NULL) {
1290                 /* Not sure if w32 allows NULLs here or not */
1291                 return(FALSE);
1292         }
1293         
1294         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1295                                 (gpointer *)&process_handle);
1296         if(ok==FALSE) {
1297 #ifdef DEBUG
1298                 g_message ("%s: Can't find process %p", __func__, process);
1299 #endif
1300                 
1301                 return(FALSE);
1302         }
1303
1304         *min=process_handle->min_working_set;
1305         *max=process_handle->max_working_set;
1306         
1307         return(TRUE);
1308 }
1309
1310 gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
1311 {
1312         struct _WapiHandle_process *process_handle;
1313         gboolean ok;
1314
1315         mono_once (&process_current_once, process_set_current);
1316
1317         ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1318                                 (gpointer *)&process_handle);
1319         if(ok==FALSE) {
1320 #ifdef DEBUG
1321                 g_message ("%s: Can't find process %p", __func__, process);
1322 #endif
1323                 
1324                 return(FALSE);
1325         }
1326
1327         process_handle->min_working_set=min;
1328         process_handle->max_working_set=max;
1329         
1330         return(TRUE);
1331 }
1332
1333
1334 gboolean
1335 TerminateProcess (gpointer process, gint32 exitCode)
1336 {
1337         struct _WapiHandle_process *process_handle;
1338         gboolean ok;
1339         int signo;
1340         int ret;
1341
1342         ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1343                                   (gpointer *) &process_handle);
1344
1345         if (ok == FALSE) {
1346 #ifdef DEBUG
1347                 g_message ("%s: Can't find process %p", __func__, process);
1348 #endif
1349                 SetLastError (ERROR_INVALID_HANDLE);
1350                 return FALSE;
1351         }
1352
1353         signo = (exitCode == -1) ? SIGKILL : SIGTERM;
1354         ret = kill (process_handle->id, signo);
1355         if (ret == -1) {
1356                 switch (errno) {
1357                 case EINVAL:
1358                         SetLastError (ERROR_INVALID_PARAMETER);
1359                         break;
1360                 case EPERM:
1361                         SetLastError (ERROR_ACCESS_DENIED);
1362                         break;
1363                 case ESRCH:
1364                         SetLastError (ERROR_PROC_NOT_FOUND);
1365                         break;
1366                 default:
1367                         SetLastError (ERROR_GEN_FAILURE);
1368                 }
1369         }
1370         
1371         return (ret == 0);
1372 }
1373