* mini-s390.c (add_general): Adjust offset calculation to take into account of roundi...
[mono.git] / mono / io-layer / processes.c
blob928105c8f44cc827f27950ac337c0302e066e35e
1 /*
2 * processes.c: Process handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2006 Novell, Inc.
8 */
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 #include <fcntl.h>
23 #include <mono/io-layer/wapi.h>
24 #include <mono/io-layer/wapi-private.h>
25 #include <mono/io-layer/handles-private.h>
26 #include <mono/io-layer/misc-private.h>
27 #include <mono/io-layer/mono-mutex.h>
28 #include <mono/io-layer/process-private.h>
29 #include <mono/io-layer/threads.h>
30 #include <mono/utils/strenc.h>
31 #include <mono/io-layer/timefuncs-private.h>
33 /* The process' environment strings */
34 extern char **environ;
36 #undef DEBUG
38 static guint32 process_wait (gpointer handle, guint32 timeout);
40 struct _WapiHandleOps _wapi_process_ops = {
41 NULL, /* close_shared */
42 NULL, /* signal */
43 NULL, /* own */
44 NULL, /* is_owned */
45 process_wait, /* special_wait */
46 NULL /* prewait */
49 static mono_once_t process_current_once=MONO_ONCE_INIT;
50 static gpointer current_process=NULL;
52 static mono_once_t process_ops_once=MONO_ONCE_INIT;
54 static void process_ops_init (void)
56 _wapi_handle_register_capabilities (WAPI_HANDLE_PROCESS,
57 WAPI_HANDLE_CAP_WAIT |
58 WAPI_HANDLE_CAP_SPECIAL_WAIT);
61 static gboolean process_set_termination_details (gpointer handle, int status)
63 struct _WapiHandle_process *process_handle;
64 gboolean ok;
65 int thr_ret;
67 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
68 (gpointer *)&process_handle);
69 if (ok == FALSE) {
70 g_warning ("%s: error looking up process handle %p",
71 __func__, handle);
72 return(FALSE);
75 thr_ret = _wapi_handle_lock_shared_handles ();
76 g_assert (thr_ret == 0);
78 if (WIFSIGNALED(status)) {
79 process_handle->exitstatus = 128 + WTERMSIG(status);
80 } else {
81 process_handle->exitstatus = WEXITSTATUS(status);
83 _wapi_time_t_to_filetime (time(NULL), &process_handle->exit_time);
85 /* Don't set process_handle->waited here, it needs to only
86 * happen in the parent when wait() has been called.
89 #ifdef DEBUG
90 g_message ("%s: Setting handle %p signalled", __func__, handle);
91 #endif
93 _wapi_shared_handle_set_signal_state (handle, TRUE);
95 _wapi_handle_unlock_shared_handles ();
97 /* Drop the reference we hold so we have somewhere to store
98 * the exit details, now the process has in fact exited
100 _wapi_handle_unref (handle);
102 return (ok);
105 /* See if any child processes have terminated and wait() for them,
106 * updating process handle info. This function is called from the
107 * collection thread every few seconds.
109 static gboolean waitfor_pid (gpointer test, gpointer user_data)
111 struct _WapiHandle_process *process;
112 gboolean ok;
113 int status;
114 pid_t ret;
116 ok = _wapi_lookup_handle (test, WAPI_HANDLE_PROCESS,
117 (gpointer *)&process);
118 if (ok == FALSE) {
119 /* The handle must have been too old and was reaped */
120 return (FALSE);
123 if (process->waited) {
124 /* We've already done this one */
125 return(FALSE);
128 do {
129 ret = waitpid (process->id, &status, WNOHANG);
130 } while (errno == EINTR);
132 if (ret <= 0) {
133 /* Process not ready for wait */
134 #ifdef DEBUG
135 g_message ("%s: Process %d not ready for waiting for: %s",
136 __func__, process->id, g_strerror (errno));
137 #endif
139 return (FALSE);
142 #ifdef DEBUG
143 g_message ("%s: Process %d finished", __func__, ret);
144 #endif
146 process->waited = TRUE;
148 *(int *)user_data = status;
150 return (TRUE);
153 void _wapi_process_reap (void)
155 gpointer proc;
156 int status;
158 #ifdef DEBUG
159 g_message ("%s: Reaping child processes", __func__);
160 #endif
162 do {
163 proc = _wapi_search_handle (WAPI_HANDLE_PROCESS, waitfor_pid,
164 &status, NULL, FALSE);
165 if (proc != NULL) {
166 #ifdef DEBUG
167 g_message ("%s: process handle %p exit code %d",
168 __func__, proc, status);
169 #endif
171 process_set_termination_details (proc, status);
173 /* _wapi_search_handle adds a reference, so
174 * drop it here
176 _wapi_handle_unref (proc);
178 } while (proc != NULL);
181 /* Limitations: This can only wait for processes that are our own
182 * children. Fixing this means resurrecting a daemon helper to manage
183 * processes.
185 static guint32 process_wait (gpointer handle, guint32 timeout)
187 struct _WapiHandle_process *process_handle;
188 gboolean ok;
189 pid_t pid, ret;
190 int status;
192 #ifdef DEBUG
193 g_message ("%s: Waiting for process %p", __func__, handle);
194 #endif
196 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
197 (gpointer *)&process_handle);
198 if (ok == FALSE) {
199 g_warning ("%s: error looking up process handle %p", __func__,
200 handle);
201 return(WAIT_FAILED);
204 if (process_handle->waited) {
205 /* We've already done this one */
206 #ifdef DEBUG
207 g_message ("%s: Process %p already signalled", __func__,
208 handle);
209 #endif
211 return (WAIT_OBJECT_0);
214 pid = process_handle->id;
216 #ifdef DEBUG
217 g_message ("%s: PID is %d, timeout %d", __func__, pid, timeout);
218 #endif
220 if (timeout == INFINITE) {
221 if (pid == _wapi_getpid ()) {
222 do {
223 Sleep (10000);
224 } while(1);
225 } else {
226 while ((ret = waitpid (pid, &status, 0)) != pid) {
227 if (ret == (pid_t)-1 && errno != EINTR) {
228 return(WAIT_FAILED);
232 } else if (timeout == 0) {
233 /* Just poll */
234 ret = waitpid (pid, &status, WNOHANG);
235 if (ret != pid) {
236 return (WAIT_TIMEOUT);
238 } else {
239 /* Poll in a loop */
240 if (pid == _wapi_getpid ()) {
241 Sleep (timeout);
242 return(WAIT_TIMEOUT);
243 } else {
244 do {
245 ret = waitpid (pid, &status, WNOHANG);
246 #ifdef DEBUG
247 g_message ("%s: waitpid returns: %d, timeout is %d", __func__, ret, timeout);
248 #endif
250 if (ret == pid) {
251 break;
252 } else if (ret == (pid_t)-1 &&
253 errno != EINTR) {
254 #ifdef DEBUG
255 g_message ("%s: waitpid failure: %s",
256 __func__,
257 g_strerror (errno));
258 #endif
260 if (errno == ECHILD &&
261 process_handle->waited) {
262 /* The background
263 * process reaper must
264 * have got this one
266 #ifdef DEBUG
267 g_message ("%s: Process %p already reaped", __func__, handle);
268 #endif
270 return(WAIT_OBJECT_0);
271 } else {
272 return(WAIT_FAILED);
276 _wapi_handle_spin (100);
277 timeout -= 100;
278 } while (timeout > 0);
281 if (timeout <= 0) {
282 return(WAIT_TIMEOUT);
286 /* Process must have exited */
287 #ifdef DEBUG
288 g_message ("%s: Wait done, status %d", __func__, status);
289 #endif
291 ok = process_set_termination_details (handle, status);
292 if (ok == FALSE) {
293 SetLastError (ERROR_OUTOFMEMORY);
294 return (WAIT_FAILED);
296 process_handle->waited = TRUE;
298 return(WAIT_OBJECT_0);
301 void _wapi_process_signal_self ()
303 if (current_process != NULL) {
304 process_set_termination_details (current_process, 0);
308 static void process_set_defaults (struct _WapiHandle_process *process_handle)
310 /* These seem to be the defaults on w2k */
311 process_handle->min_working_set = 204800;
312 process_handle->max_working_set = 1413120;
314 process_handle->waited = FALSE;
316 _wapi_time_t_to_filetime (time (NULL), &process_handle->create_time);
319 static int
320 len16 (const gunichar2 *str)
322 int len = 0;
324 while (*str++ != 0)
325 len++;
327 return len;
330 static gunichar2 *
331 utf16_concat (const gunichar2 *first, ...)
333 va_list args;
334 int total = 0, i;
335 const gunichar2 *s;
336 gunichar2 *ret;
338 va_start (args, first);
339 total += len16 (first);
340 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *)){
341 total += len16 (s);
343 va_end (args);
345 ret = g_new (gunichar2, total + 1);
346 if (ret == NULL)
347 return NULL;
349 ret [total] = 0;
350 i = 0;
351 for (s = first; *s != 0; s++)
352 ret [i++] = *s;
353 va_start (args, first);
354 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
355 const gunichar2 *p;
357 for (p = s; *p != 0; p++)
358 ret [i++] = *p;
360 va_end (args);
362 return ret;
365 static const gunichar2 utf16_space_bytes [2] = { 0x20, 0 };
366 static const gunichar2 *utf16_space = utf16_space_bytes;
368 /* Implemented as just a wrapper around CreateProcess () */
369 gboolean ShellExecuteEx (WapiShellExecuteInfo *sei)
371 gboolean ret;
372 WapiProcessInformation process_info;
373 gunichar2 *args;
375 if (sei == NULL) {
376 /* w2k just segfaults here, but we can do better than
377 * that
379 SetLastError (ERROR_INVALID_PARAMETER);
380 return (FALSE);
383 if (sei->lpFile == NULL) {
384 /* w2k returns TRUE for this, for some reason. */
385 return (TRUE);
388 /* Put both executable and parameters into the second argument
389 * to CreateProcess (), so it searches $PATH. The conversion
390 * into and back out of utf8 is because there is no
391 * g_strdup_printf () equivalent for gunichar2 :-(
393 args = utf16_concat (sei->lpFile, sei->lpParameters == NULL ? NULL : utf16_space, sei->lpParameters, NULL);
394 if (args == NULL){
395 SetLastError (ERROR_INVALID_DATA);
396 return (FALSE);
398 ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
399 CREATE_UNICODE_ENVIRONMENT, NULL,
400 sei->lpDirectory, NULL, &process_info);
401 g_free (args);
403 if (!ret) {
404 static char *handler;
405 static gunichar2 *handler_utf16;
407 if (handler_utf16 == (gunichar2 *)-1)
408 return FALSE;
410 #ifdef PLATFORM_MACOSX
411 handler = "/usr/bin/open";
412 #else
414 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
415 * if that fails, try to use gnome-open, then kfmclient
417 handler = g_find_program_in_path ("xdg-open");
418 if (handler == NULL){
419 handler = g_find_program_in_path ("gnome-open");
420 if (handler == NULL){
421 handler = g_find_program_in_path ("kfmclient");
422 if (handler == NULL){
423 handler_utf16 = (gunichar2 *) -1;
424 return (FALSE);
425 } else {
426 /* kfmclient needs exec argument */
427 char *old = handler;
428 handler = g_strconcat (old, " exec",
429 NULL);
430 g_free (old);
434 #endif
435 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
436 g_free (handler);
437 args = utf16_concat (handler_utf16, utf16_space, sei->lpFile,
438 sei->lpParameters == NULL ? NULL : utf16_space,
439 sei->lpParameters, NULL);
440 if (args == NULL){
441 SetLastError (ERROR_INVALID_DATA);
442 return FALSE;
444 ret = CreateProcess (NULL, args, NULL, NULL, TRUE,
445 CREATE_UNICODE_ENVIRONMENT, NULL,
446 sei->lpDirectory, NULL, &process_info);
447 g_free (args);
448 if (!ret){
449 SetLastError (ERROR_INVALID_DATA);
450 return FALSE;
454 if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) {
455 sei->hProcess = process_info.hProcess;
456 } else {
457 CloseHandle (process_info.hProcess);
460 return (ret);
463 static gboolean
464 is_managed_binary (const gchar *filename)
466 int original_errno = errno;
467 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
468 int file = open (filename, O_RDONLY | O_LARGEFILE);
469 #else
470 int file = open (filename, O_RDONLY);
471 #endif
472 off_t new_offset;
473 unsigned char buffer[8];
474 off_t file_size, optional_header_offset;
475 off_t pe_header_offset;
476 gboolean managed = FALSE;
477 int num_read;
478 guint32 first_word, second_word;
480 /* If we are unable to open the file, then we definitely
481 * can't say that it is managed. The child mono process
482 * probably wouldn't be able to open it anyway.
484 if (file < 0) {
485 errno = original_errno;
486 return FALSE;
489 /* Retrieve the length of the file for future sanity checks. */
490 file_size = lseek (file, 0, SEEK_END);
491 lseek (file, 0, SEEK_SET);
493 /* We know we need to read a header field at offset 60. */
494 if (file_size < 64)
495 goto leave;
497 num_read = read (file, buffer, 2);
499 if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
500 goto leave;
502 new_offset = lseek (file, 60, SEEK_SET);
504 if (new_offset != 60)
505 goto leave;
507 num_read = read (file, buffer, 4);
509 if (num_read != 4)
510 goto leave;
511 pe_header_offset = buffer[0]
512 | (buffer[1] << 8)
513 | (buffer[2] << 16)
514 | (buffer[3] << 24);
516 if (pe_header_offset + 24 > file_size)
517 goto leave;
519 new_offset = lseek (file, pe_header_offset, SEEK_SET);
521 if (new_offset != pe_header_offset)
522 goto leave;
524 num_read = read (file, buffer, 4);
526 if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
527 goto leave;
530 * Verify that the header we want in the optional header data
531 * is present in this binary.
533 new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
535 if (new_offset != pe_header_offset + 20)
536 goto leave;
538 num_read = read (file, buffer, 2);
540 if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
541 goto leave;
543 /* Read the CLR header address and size fields. These will be
544 * zero if the binary is not managed.
546 optional_header_offset = pe_header_offset + 24;
547 new_offset = lseek (file, optional_header_offset + 208, SEEK_SET);
549 if (new_offset != optional_header_offset + 208)
550 goto leave;
552 num_read = read (file, buffer, 8);
554 /* We are not concerned with endianness, only with
555 * whether it is zero or not.
557 first_word = *(guint32 *)&buffer[0];
558 second_word = *(guint32 *)&buffer[4];
560 if ((num_read != 8) || (first_word == 0) || (second_word == 0))
561 goto leave;
563 managed = TRUE;
565 leave:
566 close (file);
567 errno = original_errno;
568 return managed;
571 gboolean CreateProcess (const gunichar2 *appname, const gunichar2 *cmdline,
572 WapiSecurityAttributes *process_attrs G_GNUC_UNUSED,
573 WapiSecurityAttributes *thread_attrs G_GNUC_UNUSED,
574 gboolean inherit_handles, guint32 create_flags,
575 gpointer new_environ, const gunichar2 *cwd,
576 WapiStartupInfo *startup,
577 WapiProcessInformation *process_info)
579 gchar *cmd=NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL, *dir = NULL, **env_strings = NULL, **argv = NULL;
580 guint32 i, env_count = 0;
581 gboolean ret = FALSE;
582 gpointer handle;
583 struct _WapiHandle_process process_handle = {0}, *process_handle_data;
584 GError *gerr = NULL;
585 int in_fd, out_fd, err_fd;
586 pid_t pid;
587 int thr_ret;
589 mono_once (&process_ops_once, process_ops_init);
591 /* appname and cmdline specify the executable and its args:
593 * If appname is not NULL, it is the name of the executable.
594 * Otherwise the executable is the first token in cmdline.
596 * Executable searching:
598 * If appname is not NULL, it can specify the full path and
599 * file name, or else a partial name and the current directory
600 * will be used. There is no additional searching.
602 * If appname is NULL, the first whitespace-delimited token in
603 * cmdline is used. If the name does not contain a full
604 * directory path, the search sequence is:
606 * 1) The directory containing the current process
607 * 2) The current working directory
608 * 3) The windows system directory (Ignored)
609 * 4) The windows directory (Ignored)
610 * 5) $PATH
612 * Just to make things more interesting, tokens can contain
613 * white space if they are surrounded by quotation marks. I'm
614 * beginning to understand just why windows apps are generally
615 * so crap, with an API like this :-(
617 if (appname != NULL) {
618 cmd = mono_unicode_to_external (appname);
619 if (cmd == NULL) {
620 #ifdef DEBUG
621 g_message ("%s: unicode conversion returned NULL",
622 __func__);
623 #endif
625 SetLastError (ERROR_PATH_NOT_FOUND);
626 goto cleanup;
629 /* Turn all the slashes round the right way */
630 for (i = 0; i < strlen (cmd); i++) {
631 if (cmd[i] == '\\') {
632 cmd[i] = '/';
637 if (cmdline != NULL) {
638 args = mono_unicode_to_external (cmdline);
639 if (args == NULL) {
640 #ifdef DEBUG
641 g_message ("%s: unicode conversion returned NULL", __func__);
642 #endif
644 SetLastError (ERROR_PATH_NOT_FOUND);
645 goto cleanup;
649 if (cwd != NULL) {
650 dir = mono_unicode_to_external (cwd);
651 if (dir == NULL) {
652 #ifdef DEBUG
653 g_message ("%s: unicode conversion returned NULL", __func__);
654 #endif
656 SetLastError (ERROR_PATH_NOT_FOUND);
657 goto cleanup;
660 /* Turn all the slashes round the right way */
661 for (i = 0; i < strlen (dir); i++) {
662 if (dir[i] == '\\') {
663 dir[i] = '/';
669 /* We can't put off locating the executable any longer :-( */
670 if (cmd != NULL) {
671 gchar *unquoted;
672 if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
673 /* Strip off the drive letter. I can't
674 * believe that CP/M holdover is still
675 * visible...
677 g_memmove (cmd, cmd+2, strlen (cmd)-2);
678 cmd[strlen (cmd)-2] = '\0';
681 unquoted = g_shell_unquote (cmd, NULL);
682 if (unquoted[0] == '/') {
683 /* Assume full path given */
684 prog = g_strdup (unquoted);
686 /* Executable existing ? */
687 if (access (prog, X_OK) != 0) {
688 #ifdef DEBUG
689 g_message ("%s: Couldn't find executable %s",
690 __func__, prog);
691 #endif
692 g_free (unquoted);
693 SetLastError (ERROR_FILE_NOT_FOUND);
694 goto cleanup;
696 } else {
697 /* Search for file named by cmd in the current
698 * directory
700 char *curdir = g_get_current_dir ();
702 prog = g_strdup_printf ("%s/%s", curdir, unquoted);
703 g_free (curdir);
705 /* And make sure it's executable */
706 if (access (prog, X_OK) != 0) {
707 #ifdef DEBUG
708 g_message ("%s: Couldn't find executable %s",
709 __func__, prog);
710 #endif
711 g_free (unquoted);
712 SetLastError (ERROR_FILE_NOT_FOUND);
713 goto cleanup;
716 g_free (unquoted);
718 args_after_prog = args;
719 } else {
720 gchar *token = NULL;
721 char quote;
723 /* Dig out the first token from args, taking quotation
724 * marks into account
727 /* First, strip off all leading whitespace */
728 args = g_strchug (args);
730 /* args_after_prog points to the contents of args
731 * after token has been set (otherwise argv[0] is
732 * duplicated)
734 args_after_prog = args;
736 /* Assume the opening quote will always be the first
737 * character
739 if (args[0] == '\"' || args [0] == '\'') {
740 quote = args [0];
741 for (i = 1; args[i] != '\0' && args[i] != quote; i++);
742 if (g_ascii_isspace (args[i+1])) {
743 /* We found the first token */
744 token = g_strndup (args+1, i-1);
745 args_after_prog = args + i;
746 } else {
747 /* Quotation mark appeared in the
748 * middle of the token. Just give the
749 * whole first token, quotes and all,
750 * to exec.
755 if (token == NULL) {
756 /* No quote mark, or malformed */
757 for (i = 0; args[i] != '\0'; i++) {
758 if (g_ascii_isspace (args[i])) {
759 token = g_strndup (args, i);
760 args_after_prog = args + i + 1;
761 break;
766 if (token == NULL && args[0] != '\0') {
767 /* Must be just one token in the string */
768 token = g_strdup (args);
769 args_after_prog = NULL;
772 if (token == NULL) {
773 /* Give up */
774 #ifdef DEBUG
775 g_message ("%s: Couldn't find what to exec", __func__);
776 #endif
778 SetLastError (ERROR_PATH_NOT_FOUND);
779 goto cleanup;
782 /* Turn all the slashes round the right way. Only for
783 * the prg. name
785 for (i = 0; i < strlen (token); i++) {
786 if (token[i] == '\\') {
787 token[i] = '/';
791 if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
792 /* Strip off the drive letter. I can't
793 * believe that CP/M holdover is still
794 * visible...
796 g_memmove (token, token+2, strlen (token)-2);
797 token[strlen (token)-2] = '\0';
800 if (token[0] == '/') {
801 /* Assume full path given */
802 prog = g_strdup (token);
804 /* Executable existing ? */
805 if (access (prog, X_OK) != 0) {
806 #ifdef DEBUG
807 g_message ("%s: Couldn't find executable %s",
808 __func__, token);
809 #endif
810 g_free (token);
811 SetLastError (ERROR_FILE_NOT_FOUND);
812 goto cleanup;
815 } else {
816 char *curdir = g_get_current_dir ();
818 /* FIXME: Need to record the directory
819 * containing the current process, and check
820 * that for the new executable as the first
821 * place to look
824 prog = g_strdup_printf ("%s/%s", curdir, token);
825 g_free (curdir);
827 /* I assume X_OK is the criterion to use,
828 * rather than F_OK
830 if (access (prog, X_OK) != 0) {
831 g_free (prog);
832 prog = g_find_program_in_path (token);
833 if (prog == NULL) {
834 #ifdef DEBUG
835 g_message ("%s: Couldn't find executable %s", __func__, token);
836 #endif
838 g_free (token);
839 SetLastError (ERROR_FILE_NOT_FOUND);
840 goto cleanup;
845 g_free (token);
848 #ifdef DEBUG
849 g_message ("%s: Exec prog [%s] args [%s]", __func__, prog,
850 args_after_prog);
851 #endif
853 /* Check for CLR binaries; if found, we will try to invoke
854 * them using the same mono binary that started us.
856 if (is_managed_binary (prog) && (appname == NULL)) {
857 gsize bytes_ignored;
859 appname = mono_unicode_from_external ("mono", &bytes_ignored);
861 if (appname != NULL) {
862 cmdline = utf16_concat (appname, utf16_space, cmdline, NULL);
864 g_free ((gunichar2 *)appname);
866 if (cmdline != NULL) {
867 gboolean return_value = CreateProcess (
868 NULL, cmdline, process_attrs,
869 thread_attrs, inherit_handles, create_flags, new_environ,
870 cwd, startup, process_info);
872 g_free ((gunichar2 *)cmdline);
874 return return_value;
879 if (args_after_prog != NULL && *args_after_prog) {
880 gchar *qprog;
882 qprog = g_shell_quote (prog);
883 full_prog = g_strconcat (qprog, " ", args_after_prog, NULL);
884 g_free (qprog);
885 } else {
886 full_prog = g_shell_quote (prog);
889 ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
890 if (ret == FALSE) {
891 /* FIXME: Could do something with the GError here
895 if (startup != NULL && startup->dwFlags & STARTF_USESTDHANDLES) {
896 in_fd = GPOINTER_TO_UINT (startup->hStdInput);
897 out_fd = GPOINTER_TO_UINT (startup->hStdOutput);
898 err_fd = GPOINTER_TO_UINT (startup->hStdError);
899 } else {
900 in_fd = GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE));
901 out_fd = GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE));
902 err_fd = GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE));
905 g_strlcpy (process_handle.proc_name, prog,
906 _WAPI_PROC_NAME_MAX_LEN - 1);
908 process_set_defaults (&process_handle);
910 handle = _wapi_handle_new (WAPI_HANDLE_PROCESS, &process_handle);
911 if (handle == _WAPI_HANDLE_INVALID) {
912 g_warning ("%s: error creating process handle", __func__);
914 SetLastError (ERROR_PATH_NOT_FOUND);
915 goto cleanup;
918 /* Hold another reference so the process has somewhere to
919 * store its exit data even if we drop this handle
921 _wapi_handle_ref (handle);
923 /* new_environ is a block of NULL-terminated strings, which
924 * is itself NULL-terminated. Of course, passing an array of
925 * string pointers would have made things too easy :-(
927 * If new_environ is not NULL it specifies the entire set of
928 * environment variables in the new process. Otherwise the
929 * new process inherits the same environment.
931 if (new_environ != NULL) {
932 gunichar2 *new_environp;
934 /* Count the number of strings */
935 for (new_environp = (gunichar2 *)new_environ; *new_environp;
936 new_environp++) {
937 env_count++;
938 while (*new_environp) {
939 new_environp++;
943 /* +2: one for the process handle value, and the last
944 * one is NULL
946 env_strings = g_new0 (gchar *, env_count + 2);
948 /* Copy each environ string into 'strings' turning it
949 * into utf8 (or the requested encoding) at the same
950 * time
952 env_count = 0;
953 for (new_environp = (gunichar2 *)new_environ; *new_environp;
954 new_environp++) {
955 env_strings[env_count] = mono_unicode_to_external (new_environp);
956 env_count++;
957 while (*new_environp) {
958 new_environp++;
961 } else {
962 for (i = 0; environ[i] != NULL; i++) {
963 env_count++;
966 /* +2: one for the process handle value, and the last
967 * one is NULL
969 env_strings = g_new0 (gchar *, env_count + 2);
971 /* Copy each environ string into 'strings' turning it
972 * into utf8 (or the requested encoding) at the same
973 * time
975 env_count = 0;
976 for (i = 0; environ[i] != NULL; i++) {
977 env_strings[env_count] = g_strdup (environ[i]);
978 env_count++;
981 /* pass process handle info to the child, so it doesn't have
982 * to do an expensive search over the whole list
984 if (env_strings != NULL) {
985 struct _WapiHandleUnshared *handle_data;
986 struct _WapiHandle_shared_ref *ref;
988 handle_data = &_WAPI_PRIVATE_HANDLES(GPOINTER_TO_UINT(handle));
989 ref = &handle_data->u.shared;
991 env_strings[env_count] = g_strdup_printf ("_WAPI_PROCESS_HANDLE_OFFSET=%d", ref->offset);
994 thr_ret = _wapi_handle_lock_shared_handles ();
995 g_assert (thr_ret == 0);
997 pid = fork ();
998 if (pid == -1) {
999 /* Error */
1000 SetLastError (ERROR_OUTOFMEMORY);
1001 _wapi_handle_unref (handle);
1002 goto cleanup;
1003 } else if (pid == 0) {
1004 /* Child */
1006 if (_wapi_shm_disabled == FALSE) {
1007 /* Wait for the parent to finish setting up
1008 * the handle. The semaphore lock is safe
1009 * because the sem_undo structures of a
1010 * semaphore aren't inherited across a fork
1011 * (), but we can't do this if we're not using
1012 * the shared memory
1014 thr_ret = _wapi_handle_lock_shared_handles ();
1015 g_assert (thr_ret == 0);
1017 _wapi_handle_unlock_shared_handles ();
1020 /* should we detach from the process group? */
1022 /* Connect stdin, stdout and stderr */
1023 dup2 (in_fd, 0);
1024 dup2 (out_fd, 1);
1025 dup2 (err_fd, 2);
1027 if (inherit_handles != TRUE) {
1028 /* FIXME: do something here */
1031 /* Close all file descriptors */
1032 for (i = getdtablesize () - 1; i > 2; i--) {
1033 close (i);
1036 #ifdef DEBUG
1037 g_message ("%s: exec()ing [%s] in dir [%s]", __func__, cmd,
1038 dir==NULL?".":dir);
1039 for (i = 0; argv[i] != NULL; i++) {
1040 g_message ("arg %d: [%s]", i, argv[i]);
1043 for (i = 0; env_strings[i] != NULL; i++) {
1044 g_message ("env %d: [%s]", i, env_strings[i]);
1046 #endif
1048 /* set cwd */
1049 if (dir != NULL && chdir (dir) == -1) {
1050 /* set error */
1051 _exit (-1);
1054 /* exec */
1055 execve (argv[0], argv, env_strings);
1057 /* set error */
1058 _exit (-1);
1060 /* parent */
1062 ret = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
1063 (gpointer *)&process_handle_data);
1064 if (ret == FALSE) {
1065 g_warning ("%s: error looking up process handle %p", __func__,
1066 handle);
1067 _wapi_handle_unref (handle);
1068 goto cleanup;
1071 process_handle_data->id = pid;
1073 if (process_info != NULL) {
1074 process_info->hProcess = handle;
1075 process_info->dwProcessId = pid;
1077 /* FIXME: we might need to handle the thread info some
1078 * day
1080 process_info->hThread = INVALID_HANDLE_VALUE;
1081 process_info->dwThreadId = 0;
1084 cleanup:
1085 _wapi_handle_unlock_shared_handles ();
1087 if (cmd != NULL) {
1088 g_free (cmd);
1090 if (full_prog != NULL) {
1091 g_free (full_prog);
1093 if (prog != NULL) {
1094 g_free (prog);
1096 if (args != NULL) {
1097 g_free (args);
1099 if (dir != NULL) {
1100 g_free (dir);
1102 if(env_strings != NULL) {
1103 g_strfreev (env_strings);
1105 if (argv != NULL) {
1106 g_strfreev (argv);
1109 #ifdef DEBUG
1110 g_message ("%s: returning handle %p for pid %d", __func__, handle,
1111 pid);
1112 #endif
1114 return(ret);
1117 static void process_set_name (struct _WapiHandle_process *process_handle)
1119 gchar *progname, *utf8_progname, *slash;
1121 progname=g_get_prgname ();
1122 utf8_progname=mono_utf8_from_external (progname);
1124 #ifdef DEBUG
1125 g_message ("%s: using [%s] as prog name", __func__, progname);
1126 #endif
1128 if(utf8_progname!=NULL) {
1129 slash=strrchr (utf8_progname, '/');
1130 if(slash!=NULL) {
1131 g_strlcpy (process_handle->proc_name, slash+1,
1132 _WAPI_PROC_NAME_MAX_LEN - 1);
1133 } else {
1134 g_strlcpy (process_handle->proc_name, utf8_progname,
1135 _WAPI_PROC_NAME_MAX_LEN - 1);
1138 g_free (utf8_progname);
1142 extern void _wapi_time_t_to_filetime (time_t timeval, WapiFileTime *filetime);
1144 #if !GLIB_CHECK_VERSION (2,4,0)
1145 #define g_setenv(a,b,c) setenv(a,b,c)
1146 #define g_unsetenv(a) unsetenv(a)
1147 #endif
1149 static void process_set_current (void)
1151 pid_t pid = _wapi_getpid ();
1152 const char *handle_env;
1153 struct _WapiHandle_process process_handle = {0};
1155 mono_once (&process_ops_once, process_ops_init);
1157 handle_env = g_getenv ("_WAPI_PROCESS_HANDLE_OFFSET");
1158 g_unsetenv ("_WAPI_PROCESS_HANDLE_OFFSET");
1160 if (handle_env != NULL) {
1161 struct _WapiHandle_process *process_handlep;
1162 gchar *procname = NULL;
1163 gboolean ok;
1165 current_process = _wapi_handle_new_from_offset (WAPI_HANDLE_PROCESS, atoi (handle_env), TRUE);
1167 #ifdef DEBUG
1168 g_message ("%s: Found my process handle: %p (offset %d 0x%x)",
1169 __func__, current_process, atoi (handle_env),
1170 atoi (handle_env));
1171 #endif
1173 ok = _wapi_lookup_handle (current_process, WAPI_HANDLE_PROCESS,
1174 (gpointer *)&process_handlep);
1175 if (ok) {
1176 /* This test will probably break on linuxthreads, but
1177 * that should be ancient history on all distros we
1178 * care about by now
1180 if (process_handlep->id == pid) {
1181 procname = process_handlep->proc_name;
1182 if (!strcmp (procname, "mono")) {
1183 /* Set a better process name */
1184 #ifdef DEBUG
1185 g_message ("%s: Setting better process name", __func__);
1186 #endif
1188 process_set_name (process_handlep);
1189 } else {
1190 #ifdef DEBUG
1191 g_message ("%s: Leaving process name: %s", __func__, procname);
1192 #endif
1195 return;
1198 /* Wrong pid, so drop this handle and fall through to
1199 * create a new one
1201 _wapi_handle_unref (current_process);
1205 /* We get here if the handle wasn't specified in the
1206 * environment, or if the process ID was wrong, or if the
1207 * handle lookup failed (eg if the parent process forked and
1208 * quit immediately, and deleted the shared data before the
1209 * child got a chance to attach it.)
1212 #ifdef DEBUG
1213 g_message ("%s: Need to create my own process handle", __func__);
1214 #endif
1216 process_handle.id = pid;
1218 process_set_defaults (&process_handle);
1219 process_set_name (&process_handle);
1221 current_process = _wapi_handle_new (WAPI_HANDLE_PROCESS,
1222 &process_handle);
1223 if (current_process == _WAPI_HANDLE_INVALID) {
1224 g_warning ("%s: error creating process handle", __func__);
1225 return;
1229 gpointer _wapi_process_duplicate ()
1231 mono_once (&process_current_once, process_set_current);
1233 _wapi_handle_ref (current_process);
1235 return(current_process);
1238 /* Returns a pseudo handle that doesn't need to be closed afterwards */
1239 gpointer GetCurrentProcess (void)
1241 mono_once (&process_current_once, process_set_current);
1243 return(_WAPI_PROCESS_CURRENT);
1246 guint32 GetProcessId (gpointer handle)
1248 struct _WapiHandle_process *process_handle;
1249 gboolean ok;
1251 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
1252 (gpointer *)&process_handle);
1253 if (ok == FALSE) {
1254 SetLastError (ERROR_INVALID_HANDLE);
1255 return (0);
1258 return (process_handle->id);
1261 guint32 GetCurrentProcessId (void)
1263 mono_once (&process_current_once, process_set_current);
1265 return (GetProcessId (current_process));
1268 /* Returns the process id as a convenience to the functions that call this */
1269 static pid_t signal_process_if_gone (gpointer handle)
1271 struct _WapiHandle_process *process_handle;
1272 gboolean ok;
1274 /* Make sure the process is signalled if it has exited - if
1275 * the parent process didn't wait for it then it won't be
1277 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_PROCESS,
1278 (gpointer *)&process_handle);
1279 if (ok == FALSE) {
1280 /* It's possible that the handle has vanished during
1281 * the _wapi_search_handle before it gets here, so
1282 * don't spam the console with warnings.
1284 /* g_warning ("%s: error looking up process handle %p",
1285 __func__, handle);*/
1287 return (0);
1290 #ifdef DEBUG
1291 g_message ("%s: looking at process %d", __func__, process_handle->id);
1292 #endif
1294 if (kill (process_handle->id, 0) == -1 &&
1295 (errno == ESRCH ||
1296 errno == EPERM)) {
1297 /* The process is dead, (EPERM tells us a new process
1298 * has that ID, but as it's owned by someone else it
1299 * can't be the one listed in our shared memory file)
1301 _wapi_shared_handle_set_signal_state (handle, TRUE);
1304 return (process_handle->id);
1307 static gboolean process_enum (gpointer handle, gpointer user_data)
1309 GArray *processes=user_data;
1310 pid_t pid = signal_process_if_gone (handle);
1311 int i;
1313 if (pid == 0) {
1314 return (FALSE);
1317 /* Ignore processes that have already exited (ie they are signalled) */
1318 if (_wapi_handle_issignalled (handle) == FALSE) {
1319 #ifdef DEBUG
1320 g_message ("%s: process %d added to array", __func__, pid);
1321 #endif
1323 /* This ensures that duplicates aren't returned (see
1324 * the comment above _wapi_search_handle () for why
1325 * it's needed
1327 for (i = 0; i < processes->len; i++) {
1328 if (g_array_index (processes, pid_t, i) == pid) {
1329 /* We've already got this one, return
1330 * FALSE to keep searching
1332 return (FALSE);
1336 g_array_append_val (processes, pid);
1339 /* Return false to keep searching */
1340 return(FALSE);
1343 gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
1345 GArray *processes = g_array_new (FALSE, FALSE, sizeof(pid_t));
1346 guint32 fit, i, j;
1348 mono_once (&process_current_once, process_set_current);
1350 _wapi_search_handle (WAPI_HANDLE_PROCESS, process_enum, processes,
1351 NULL, TRUE);
1353 fit=len/sizeof(guint32);
1354 for (i = 0, j = 0; j < fit && i < processes->len; i++) {
1355 pids[j++] = g_array_index (processes, pid_t, i);
1358 g_array_free (processes, TRUE);
1360 *needed = j * sizeof(guint32);
1362 return(TRUE);
1365 static gboolean process_open_compare (gpointer handle, gpointer user_data)
1367 pid_t wanted_pid;
1368 pid_t checking_pid = signal_process_if_gone (handle);
1370 if (checking_pid == 0) {
1371 return(FALSE);
1374 wanted_pid = GPOINTER_TO_UINT (user_data);
1376 /* It's possible to have more than one process handle with the
1377 * same pid, but only the one running process can be
1378 * unsignalled
1380 if (checking_pid == wanted_pid &&
1381 _wapi_handle_issignalled (handle) == FALSE) {
1382 /* If the handle is blown away in the window between
1383 * returning TRUE here and _wapi_search_handle pinging
1384 * the timestamp, the search will continue
1386 return(TRUE);
1387 } else {
1388 return(FALSE);
1392 gpointer OpenProcess (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 pid)
1394 /* Find the process handle that corresponds to pid */
1395 gpointer handle;
1397 mono_once (&process_current_once, process_set_current);
1399 #ifdef DEBUG
1400 g_message ("%s: looking for process %d", __func__, pid);
1401 #endif
1403 handle = _wapi_search_handle (WAPI_HANDLE_PROCESS,
1404 process_open_compare,
1405 GUINT_TO_POINTER (pid), NULL, TRUE);
1406 if (handle == 0) {
1407 #ifdef DEBUG
1408 g_message ("%s: Can't find pid %d", __func__, pid);
1409 #endif
1411 SetLastError (ERROR_PROC_NOT_FOUND);
1413 return(NULL);
1416 _wapi_handle_ref (handle);
1418 return(handle);
1421 gboolean GetExitCodeProcess (gpointer process, guint32 *code)
1423 struct _WapiHandle_process *process_handle;
1424 gboolean ok;
1426 mono_once (&process_current_once, process_set_current);
1428 if(code==NULL) {
1429 return(FALSE);
1432 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1433 (gpointer *)&process_handle);
1434 if(ok==FALSE) {
1435 #ifdef DEBUG
1436 g_message ("%s: Can't find process %p", __func__, process);
1437 #endif
1439 return(FALSE);
1442 /* A process handle is only signalled if the process has exited
1443 * and has been waited for */
1444 if (_wapi_handle_issignalled (process) == TRUE ||
1445 process_wait (process, 0) == WAIT_OBJECT_0) {
1446 *code = process_handle->exitstatus;
1447 } else {
1448 *code = STILL_ACTIVE;
1451 return(TRUE);
1454 gboolean GetProcessTimes (gpointer process, WapiFileTime *create_time,
1455 WapiFileTime *exit_time, WapiFileTime *kernel_time,
1456 WapiFileTime *user_time)
1458 struct _WapiHandle_process *process_handle;
1459 gboolean ok;
1461 mono_once (&process_current_once, process_set_current);
1463 if(create_time==NULL || exit_time==NULL || kernel_time==NULL ||
1464 user_time==NULL) {
1465 /* Not sure if w32 allows NULLs here or not */
1466 return(FALSE);
1469 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1470 (gpointer *)&process_handle);
1471 if(ok==FALSE) {
1472 #ifdef DEBUG
1473 g_message ("%s: Can't find process %p", __func__, process);
1474 #endif
1476 return(FALSE);
1479 *create_time=process_handle->create_time;
1481 /* A process handle is only signalled if the process has
1482 * exited. Otherwise exit_time isn't set
1484 if(_wapi_handle_issignalled (process)==TRUE) {
1485 *exit_time=process_handle->exit_time;
1488 return(TRUE);
1491 gboolean EnumProcessModules (gpointer process, gpointer *modules,
1492 guint32 size, guint32 *needed)
1494 /* Store modules in an array of pointers (main module as
1495 * modules[0]), using the load address for each module as a
1496 * token. (Use 'NULL' as an alternative for the main module
1497 * so that the simple implementation can just return one item
1498 * for now.) Get the info from /proc/<pid>/maps on linux,
1499 * other systems will have to implement /dev/kmem reading or
1500 * whatever other horrid technique is needed.
1502 if(size<sizeof(gpointer)) {
1503 return(FALSE);
1506 #ifdef linux
1507 modules[0]=NULL;
1508 *needed=sizeof(gpointer);
1509 #else
1510 modules[0]=NULL;
1511 *needed=sizeof(gpointer);
1512 #endif
1514 return(TRUE);
1517 guint32 GetModuleBaseName (gpointer process, gpointer module,
1518 gunichar2 *basename, guint32 size)
1520 struct _WapiHandle_process *process_handle;
1521 gboolean ok;
1523 mono_once (&process_current_once, process_set_current);
1525 #ifdef DEBUG
1526 g_message ("%s: Getting module base name, process handle %p module %p",
1527 __func__, process, module);
1528 #endif
1530 if(basename==NULL || size==0) {
1531 return(FALSE);
1534 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1535 (gpointer *)&process_handle);
1536 if(ok==FALSE) {
1537 #ifdef DEBUG
1538 g_message ("%s: Can't find process %p", __func__, process);
1539 #endif
1541 return(FALSE);
1544 if(module==NULL) {
1545 /* Shorthand for the main module, which has the
1546 * process name recorded in the handle data
1548 pid_t pid;
1549 gunichar2 *procname;
1550 gchar *procname_utf8 = NULL;
1551 glong len, bytes;
1553 #ifdef DEBUG
1554 g_message ("%s: Returning main module name", __func__);
1555 #endif
1557 pid=process_handle->id;
1558 procname_utf8 = process_handle->proc_name;
1560 #ifdef DEBUG
1561 g_message ("%s: Process name is [%s]", __func__,
1562 procname_utf8);
1563 #endif
1565 procname = g_utf8_to_utf16 (procname_utf8, -1, NULL, &len,
1566 NULL);
1567 if (procname == NULL) {
1568 /* bugger */
1569 return(0);
1572 /* Add the terminator, and convert chars to bytes */
1573 bytes = (len + 1) * 2;
1575 if (size < bytes) {
1576 #ifdef DEBUG
1577 g_message ("%s: Size %d smaller than needed (%ld); truncating", __func__, size, bytes);
1578 #endif
1580 memcpy (basename, procname, size);
1581 } else {
1582 #ifdef DEBUG
1583 g_message ("%s: Size %d larger than needed (%ld)",
1584 __func__, size, bytes);
1585 #endif
1587 memcpy (basename, procname, bytes);
1590 g_free (procname);
1592 return(len);
1593 } else {
1594 /* Look up the address in /proc/<pid>/maps */
1597 return(0);
1600 gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
1602 struct _WapiHandle_process *process_handle;
1603 gboolean ok;
1605 mono_once (&process_current_once, process_set_current);
1607 if(min==NULL || max==NULL) {
1608 /* Not sure if w32 allows NULLs here or not */
1609 return(FALSE);
1612 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1613 (gpointer *)&process_handle);
1614 if(ok==FALSE) {
1615 #ifdef DEBUG
1616 g_message ("%s: Can't find process %p", __func__, process);
1617 #endif
1619 return(FALSE);
1622 *min=process_handle->min_working_set;
1623 *max=process_handle->max_working_set;
1625 return(TRUE);
1628 gboolean SetProcessWorkingSetSize (gpointer process, size_t min, size_t max)
1630 struct _WapiHandle_process *process_handle;
1631 gboolean ok;
1633 mono_once (&process_current_once, process_set_current);
1635 ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1636 (gpointer *)&process_handle);
1637 if(ok==FALSE) {
1638 #ifdef DEBUG
1639 g_message ("%s: Can't find process %p", __func__, process);
1640 #endif
1642 return(FALSE);
1645 process_handle->min_working_set=min;
1646 process_handle->max_working_set=max;
1648 return(TRUE);
1652 gboolean
1653 TerminateProcess (gpointer process, gint32 exitCode)
1655 struct _WapiHandle_process *process_handle;
1656 gboolean ok;
1657 int signo;
1658 int ret;
1660 ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
1661 (gpointer *) &process_handle);
1663 if (ok == FALSE) {
1664 #ifdef DEBUG
1665 g_message ("%s: Can't find process %p", __func__, process);
1666 #endif
1667 SetLastError (ERROR_INVALID_HANDLE);
1668 return FALSE;
1671 signo = (exitCode == -1) ? SIGKILL : SIGTERM;
1672 ret = kill (process_handle->id, signo);
1673 if (ret == -1) {
1674 switch (errno) {
1675 case EINVAL:
1676 SetLastError (ERROR_INVALID_PARAMETER);
1677 break;
1678 case EPERM:
1679 SetLastError (ERROR_ACCESS_DENIED);
1680 break;
1681 case ESRCH:
1682 SetLastError (ERROR_PROC_NOT_FOUND);
1683 break;
1684 default:
1685 SetLastError (ERROR_GEN_FAILURE);
1689 return (ret == 0);