2 * process.c: System.Diagnostics.Process support
5 * Dick Porter (dick@ximian.com)
7 * Copyright 2002 Ximian, Inc.
8 * Copyright 2002-2006 Novell, Inc.
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include <sys/types.h>
29 #ifdef HAVE_SYS_PARAM_H
30 #include <sys/param.h>
34 #ifdef HAVE_SYS_WAIT_H
37 #ifdef HAVE_SYS_RESOURCE_H
38 #include <sys/resource.h>
41 #ifdef HAVE_SYS_MKDEV_H
42 #include <sys/mkdev.h>
49 #include <mono/metadata/w32process.h>
50 #include <mono/metadata/w32process-internals.h>
51 #include <mono/metadata/w32process-unix-internals.h>
52 #include <mono/metadata/class.h>
53 #include <mono/metadata/class-internals.h>
54 #include <mono/metadata/object.h>
55 #include <mono/metadata/object-internals.h>
56 #include <mono/metadata/metadata.h>
57 #include <mono/metadata/metadata-internals.h>
58 #include <mono/metadata/exception.h>
59 #include <mono/io-layer/io-layer.h>
60 #include <mono/metadata/w32handle.h>
61 #include <mono/utils/mono-membar.h>
62 #include <mono/utils/mono-logger-internals.h>
63 #include <mono/utils/strenc.h>
64 #include <mono/utils/mono-proclib.h>
65 #include <mono/utils/mono-path.h>
66 #include <mono/utils/mono-lazy-init.h>
67 #include <mono/utils/mono-signal-handler.h>
68 #include <mono/utils/mono-time.h>
71 #define MAXPATHLEN 242
74 #define STILL_ACTIVE ((int) 0x00000103)
77 /* define LOGDEBUG(...) g_message(__VA_ARGS__) */
79 /* The process' environment strings */
80 #if defined(__APPLE__)
81 #if defined (TARGET_OSX)
82 /* Apple defines this in crt_externs.h but doesn't provide that header for
83 * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
84 * in fact exist on all implementations (so far)
86 gchar
***_NSGetEnviron(void);
87 #define environ (*_NSGetEnviron())
89 static char *mono_environ
[1] = { NULL
};
90 #define environ mono_environ
91 #endif /* defined (TARGET_OSX) */
93 extern char **environ
;
97 STARTF_USESHOWWINDOW
=0x001,
99 STARTF_USEPOSITION
=0x004,
100 STARTF_USECOUNTCHARS
=0x008,
101 STARTF_USEFILLATTRIBUTE
=0x010,
102 STARTF_RUNFULLSCREEN
=0x020,
103 STARTF_FORCEONFEEDBACK
=0x040,
104 STARTF_FORCEOFFFEEDBACK
=0x080,
105 STARTF_USESTDHANDLES
=0x100
115 #if G_BYTE_ORDER == G_BIG_ENDIAN
116 guint32 highDateTime
;
120 guint32 highDateTime
;
125 * MonoProcess describes processes we create.
126 * It contains a semaphore that can be waited on in order to wait
127 * for process termination. It's accessed in our SIGCHLD handler,
128 * when status is updated (and pid cleared, to not clash with
129 * subsequent processes that may get executed).
131 typedef struct _MonoProcess MonoProcess
;
132 struct _MonoProcess
{
133 pid_t pid
; /* the pid of the process. This value is only valid until the process has exited. */
134 MonoSemType exit_sem
; /* this semaphore will be released when the process exits */
135 int status
; /* the exit status */
136 gint32 handle_count
; /* the number of handles to this mono_process instance */
137 /* we keep a ref to the creating _WapiHandle_process handle until
138 * the process has exited, so that the information there isn't lost.
145 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
150 gpointer main_thread
;
151 WapiFileTime create_time
;
152 WapiFileTime exit_time
;
154 size_t min_working_set
;
155 size_t max_working_set
;
157 MonoProcess
*mono_process
;
158 } MonoW32HandleProcess
;
161 static mono_lazy_init_t process_sig_chld_once
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
164 static gchar
*cli_launcher
;
166 /* The signal-safe logic to use processes goes like this:
167 * - The list must be safe to traverse for the signal handler at all times.
168 * It's safe to: prepend an entry (which is a single store to 'processes'),
169 * unlink an entry (assuming the unlinked entry isn't freed and doesn't
170 * change its 'next' pointer so that it can still be traversed).
171 * When cleaning up we first unlink an entry, then we verify that
172 * the read lock isn't locked. Then we can free the entry, since
173 * we know that nobody is using the old version of the list (including
174 * the unlinked entry).
175 * We also need to lock when adding and cleaning up so that those two
176 * operations don't mess with eachother. (This lock is not used in the
178 static MonoProcess
*processes
;
179 static mono_mutex_t processes_mutex
;
181 static gpointer current_process
;
183 static const gunichar2 utf16_space_bytes
[2] = { 0x20, 0 };
184 static const gunichar2
*utf16_space
= utf16_space_bytes
;
185 static const gunichar2 utf16_quote_bytes
[2] = { 0x22, 0 };
186 static const gunichar2
*utf16_quote
= utf16_quote_bytes
;
188 /* Check if a pid is valid - i.e. if a process exists with this pid. */
190 process_is_alive (pid_t pid
)
192 #if defined(HOST_WATCHOS)
193 return TRUE
; // TODO: Rewrite using sysctl
194 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
197 if (kill (pid
, 0) == 0)
202 #elif defined(__HAIKU__)
204 if (get_team_info ((team_id
)pid
, &teamInfo
) == B_OK
)
208 gchar
*dir
= g_strdup_printf ("/proc/%d", pid
);
209 gboolean result
= access (dir
, F_OK
) == 0;
216 process_details (gpointer data
)
218 MonoW32HandleProcess
*process_handle
= (MonoW32HandleProcess
*) data
;
219 g_print ("id: %d, exited: %s, exitstatus: %d",
220 process_handle
->id
, process_handle
->exited
? "true" : "false", process_handle
->exitstatus
);
224 process_typename (void)
230 process_typesize (void)
232 return sizeof (MonoW32HandleProcess
);
235 static MonoW32HandleWaitRet
236 process_wait (gpointer handle
, guint32 timeout
, gboolean
*alerted
)
238 MonoW32HandleProcess
*process_handle
;
239 pid_t pid G_GNUC_UNUSED
, ret
;
245 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u)", __func__
, handle
, timeout
);
250 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
252 g_warning ("%s: error looking up process handle %p", __func__
, handle
);
253 return MONO_W32HANDLE_WAIT_RET_FAILED
;
256 if (process_handle
->exited
) {
257 /* We've already done this one */
258 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Process already exited", __func__
, handle
, timeout
);
259 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
262 pid
= process_handle
->id
;
264 if (pid
== mono_process_current_pid ()) {
265 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on current process", __func__
, handle
, timeout
);
266 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
269 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): PID: %d", __func__
, handle
, timeout
, pid
);
271 /* We don't need to lock processes here, the entry
272 * has a handle_count > 0 which means it will not be freed. */
273 mp
= process_handle
->mono_process
;
277 /* This path is used when calling Process.HasExited, so
278 * it is only used to poll the state of the process, not
279 * to actually wait on it to exit */
280 g_assert (timeout
== 0);
282 /* We alway create a MonoProcess for a child process */
283 g_assert (!process_handle
->child
);
285 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on non-child process", __func__
, handle
, timeout
);
287 res
= waitpid (pid
, &status
, WNOHANG
);
289 g_error ("%s: a process handle without a mono_process is not a child process", __func__
);
291 if (errno
== ECHILD
) {
292 if (!process_is_alive (pid
)) {
293 /* assume the process had exited */
294 process_handle
->exited
= TRUE
;
295 process_handle
->exitstatus
= -1;
296 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
299 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): non-child process waited successfully (2)", __func__
, handle
, timeout
);
300 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
303 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): non-child process wait failed, error : %s (%d))", __func__
, handle
, timeout
, g_strerror (errno
), errno
);
304 return MONO_W32HANDLE_WAIT_RET_FAILED
;
307 start
= mono_msec_ticks ();
311 if (timeout
!= INFINITE
) {
312 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on semaphore for %li ms...",
313 __func__
, handle
, timeout
, (long)(timeout
- (now
- start
)));
314 ret
= mono_os_sem_timedwait (&mp
->exit_sem
, (timeout
- (now
- start
)), alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
316 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on semaphore forever...",
317 __func__
, handle
, timeout
);
318 ret
= mono_os_sem_wait (&mp
->exit_sem
, alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
321 if (ret
== MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
322 /* Success, process has exited */
323 mono_os_sem_post (&mp
->exit_sem
);
327 if (ret
== MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
) {
328 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait timeout (timeout = 0)", __func__
, handle
, timeout
);
329 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
332 now
= mono_msec_ticks ();
333 if (now
- start
>= timeout
) {
334 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait timeout", __func__
, handle
, timeout
);
335 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
338 if (alerted
&& ret
== MONO_SEM_TIMEDWAIT_RET_ALERTED
) {
339 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait alerted", __func__
, handle
, timeout
);
341 return MONO_W32HANDLE_WAIT_RET_ALERTED
;
345 /* Process must have exited */
346 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Waited successfully", __func__
, handle
, timeout
);
349 if (WIFSIGNALED (status
))
350 process_handle
->exitstatus
= 128 + WTERMSIG (status
);
352 process_handle
->exitstatus
= WEXITSTATUS (status
);
353 _wapi_time_t_to_filetime (time (NULL
), &process_handle
->exit_time
);
355 process_handle
->exited
= TRUE
;
357 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Setting pid %d signalled, exit status %d",
358 __func__
, handle
, timeout
, process_handle
->id
, process_handle
->exitstatus
);
360 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
362 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
366 processes_cleanup (void)
368 static gint32 cleaning_up
;
370 MonoProcess
*prev
= NULL
;
371 GSList
*finished
= NULL
;
373 gpointer unref_handle
;
375 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s", __func__
);
377 /* Ensure we're not in here in multiple threads at once, nor recursive. */
378 if (InterlockedCompareExchange (&cleaning_up
, 1, 0) != 0)
381 for (mp
= processes
; mp
; mp
= mp
->next
) {
382 if (mp
->pid
== 0 && mp
->handle
) {
383 /* This process has exited and we need to remove the artifical ref
385 mono_os_mutex_lock (&processes_mutex
);
386 unref_handle
= mp
->handle
;
388 mono_os_mutex_unlock (&processes_mutex
);
390 mono_w32handle_unref (unref_handle
);
395 * Remove processes which exited from the processes list.
396 * We need to synchronize with the sigchld handler here, which runs
397 * asynchronously. The handler requires that the processes list
400 mono_os_mutex_lock (&processes_mutex
);
404 if (mp
->handle_count
== 0 && mp
->freeable
) {
407 * This code can run parallel with the sigchld handler, but the
408 * modifications it makes are safe.
411 processes
= mp
->next
;
413 prev
->next
= mp
->next
;
414 finished
= g_slist_prepend (finished
, mp
);
423 mono_memory_barrier ();
425 for (l
= finished
; l
; l
= l
->next
) {
427 * All the entries in the finished list are unlinked from processes, and
428 * they have the 'finished' flag set, which means the sigchld handler is done
431 mp
= (MonoProcess
*)l
->data
;
432 mono_os_sem_destroy (&mp
->exit_sem
);
435 g_slist_free (finished
);
437 mono_os_mutex_unlock (&processes_mutex
);
439 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s done", __func__
);
441 InterlockedExchange (&cleaning_up
, 0);
445 process_close (gpointer handle
, gpointer data
)
447 MonoW32HandleProcess
*process_handle
;
449 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s", __func__
);
451 process_handle
= (MonoW32HandleProcess
*) data
;
452 g_free (process_handle
->proc_name
);
453 process_handle
->proc_name
= NULL
;
454 if (process_handle
->mono_process
)
455 InterlockedDecrement (&process_handle
->mono_process
->handle_count
);
456 processes_cleanup ();
459 static MonoW32HandleOps process_ops
= {
460 process_close
, /* close_shared */
464 process_wait
, /* special_wait */
466 process_details
, /* details */
467 process_typename
, /* typename */
468 process_typesize
, /* typesize */
472 process_set_defaults (MonoW32HandleProcess
*process_handle
)
474 /* These seem to be the defaults on w2k */
475 process_handle
->min_working_set
= 204800;
476 process_handle
->max_working_set
= 1413120;
478 _wapi_time_t_to_filetime (time (NULL
), &process_handle
->create_time
);
482 process_set_name (MonoW32HandleProcess
*process_handle
)
484 char *progname
, *utf8_progname
, *slash
;
486 progname
= g_get_prgname ();
487 utf8_progname
= mono_utf8_from_external (progname
);
489 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: using [%s] as prog name", __func__
, progname
);
492 slash
= strrchr (utf8_progname
, '/');
494 process_handle
->proc_name
= g_strdup (slash
+1);
496 process_handle
->proc_name
= g_strdup (utf8_progname
);
497 g_free (utf8_progname
);
502 mono_w32process_init (void)
504 MonoW32HandleProcess process_handle
;
506 mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS
, &process_ops
);
508 mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS
,
509 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SPECIAL_WAIT
));
511 memset (&process_handle
, 0, sizeof (process_handle
));
512 process_handle
.id
= wapi_getpid ();
513 process_set_defaults (&process_handle
);
514 process_set_name (&process_handle
);
516 current_process
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
517 g_assert (current_process
);
519 mono_os_mutex_init (&processes_mutex
);
523 mono_w32process_cleanup (void)
525 g_free (cli_launcher
);
529 len16 (const gunichar2
*str
)
540 utf16_concat (const gunichar2
*first
, ...)
548 va_start (args
, first
);
549 total
+= len16 (first
);
550 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg(args
, gunichar2
*))
554 ret
= g_new (gunichar2
, total
+ 1);
560 for (s
= first
; *s
!= 0; s
++)
562 va_start (args
, first
);
563 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg (args
, gunichar2
*)){
564 for (p
= s
; *p
!= 0; p
++)
573 mono_w32process_get_pid (gpointer handle
)
575 MonoW32HandleProcess
*process_handle
;
578 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
580 SetLastError (ERROR_INVALID_HANDLE
);
584 return process_handle
->id
;
590 } GetProcessForeachData
;
593 get_process_foreach_callback (gpointer handle
, gpointer handle_specific
, gpointer user_data
)
595 GetProcessForeachData
*foreach_data
;
596 MonoW32HandleProcess
*process_handle
;
599 foreach_data
= (GetProcessForeachData
*) user_data
;
601 if (mono_w32handle_get_type (handle
) != MONO_W32HANDLE_PROCESS
)
604 process_handle
= (MonoW32HandleProcess
*) handle_specific
;
606 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: looking at process %d", __func__
, process_handle
->id
);
608 pid
= process_handle
->id
;
612 /* It's possible to have more than one process handle with the
613 * same pid, but only the one running process can be
615 if (foreach_data
->pid
!= pid
)
617 if (mono_w32handle_issignalled (handle
))
620 mono_w32handle_ref (handle
);
621 foreach_data
->handle
= handle
;
626 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid
)
628 GetProcessForeachData foreach_data
;
631 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: looking for process %d", __func__
, pid
);
633 memset (&foreach_data
, 0, sizeof (foreach_data
));
634 foreach_data
.pid
= pid
;
635 mono_w32handle_foreach (get_process_foreach_callback
, &foreach_data
);
636 handle
= foreach_data
.handle
;
638 /* get_process_foreach_callback already added a ref */
642 if (process_is_alive (pid
)) {
643 /* non-child process */
644 MonoW32HandleProcess process_handle
;
646 memset (&process_handle
, 0, sizeof (process_handle
));
647 process_handle
.id
= pid
;
648 process_handle
.proc_name
= mono_w32process_get_name (pid
);
650 handle
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
651 if (handle
== INVALID_HANDLE_VALUE
) {
652 g_warning ("%s: error creating process handle", __func__
);
654 SetLastError (ERROR_OUTOFMEMORY
);
661 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find pid %d", __func__
, pid
);
663 SetLastError (ERROR_PROC_NOT_FOUND
);
668 match_procname_to_modulename (char *procname
, char *modulename
)
670 char* lastsep
= NULL
;
671 char* lastsep2
= NULL
;
674 gboolean result
= FALSE
;
676 if (procname
== NULL
|| modulename
== NULL
)
679 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: procname=\"%s\", modulename=\"%s\"", __func__
, procname
, modulename
);
680 pname
= mono_path_resolve_symlinks (procname
);
681 mname
= mono_path_resolve_symlinks (modulename
);
683 if (!strcmp (pname
, mname
))
687 lastsep
= strrchr (mname
, '/');
689 if (!strcmp (lastsep
+1, pname
))
692 lastsep2
= strrchr (pname
, '/');
695 if (!strcmp (lastsep
+1, lastsep2
+1))
698 if (!strcmp (mname
, lastsep2
+1))
708 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: result is %d", __func__
, result
);
713 mono_w32process_try_get_modules (gpointer process
, gpointer
*modules
, guint32 size
, guint32
*needed
)
715 MonoW32HandleProcess
*process_handle
;
717 MonoW32ProcessModule
*module
;
718 guint32 count
, avail
= size
/ sizeof(gpointer
);
721 char *proc_name
= NULL
;
724 /* Store modules in an array of pointers (main module as
725 * modules[0]), using the load address for each module as a
726 * token. (Use 'NULL' as an alternative for the main module
727 * so that the simple implementation can just return one item
728 * for now.) Get the info from /proc/<pid>/maps on linux,
729 * /proc/<pid>/map on FreeBSD, other systems will have to
730 * implement /dev/kmem reading or whatever other horrid
731 * technique is needed.
733 if (size
< sizeof(gpointer
))
736 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
738 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
742 pid
= process_handle
->id
;
743 proc_name
= g_strdup (process_handle
->proc_name
);
747 *needed
= sizeof(gpointer
);
751 mods
= mono_w32process_get_modules (pid
);
754 *needed
= sizeof(gpointer
);
759 count
= g_slist_length (mods
);
761 /* count + 1 to leave slot 0 for the main module */
762 *needed
= sizeof(gpointer
) * (count
+ 1);
765 * Use the NULL shortcut, as the first line in
766 * /proc/<pid>/maps isn't the executable, and we need
767 * that first in the returned list. Check the module name
768 * to see if it ends with the proc name and substitute
769 * the first entry with it. FIXME if this turns out to
773 for (i
= 0; i
< (avail
- 1) && i
< count
; i
++) {
774 module
= (MonoW32ProcessModule
*)g_slist_nth_data (mods
, i
);
775 if (modules
[0] != NULL
)
776 modules
[i
] = module
->address_start
;
777 else if (match_procname_to_modulename (proc_name
, module
->filename
))
778 modules
[0] = module
->address_start
;
780 modules
[i
+ 1] = module
->address_start
;
783 for (i
= 0; i
< count
; i
++) {
784 mono_w32process_module_free ((MonoW32ProcessModule
*)g_slist_nth_data (mods
, i
));
793 mono_w32process_module_get_filename (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
798 gunichar2
*proc_path
;
800 size
*= sizeof (gunichar2
); /* adjust for unicode characters */
802 if (basename
== NULL
|| size
== 0)
805 pid
= mono_w32process_get_pid (process
);
807 path
= mono_w32process_get_path (pid
);
811 proc_path
= mono_unicode_from_external (path
, &bytes
);
814 if (proc_path
== NULL
)
819 /* Add the terminator */
823 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d smaller than needed (%ld); truncating", __func__
, size
, bytes
);
824 memcpy (basename
, proc_path
, size
);
826 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d larger than needed (%ld)", __func__
, size
, bytes
);
827 memcpy (basename
, proc_path
, bytes
);
836 mono_w32process_module_get_name (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
838 MonoW32HandleProcess
*process_handle
;
841 char *procname_ext
= NULL
;
845 MonoW32ProcessModule
*found_module
;
848 char *proc_name
= NULL
;
851 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Getting module base name, process handle %p module %p",
852 __func__
, process
, module
);
854 size
= size
* sizeof (gunichar2
); /* adjust for unicode characters */
856 if (basename
== NULL
|| size
== 0)
859 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
861 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
865 pid
= process_handle
->id
;
866 proc_name
= g_strdup (process_handle
->proc_name
);
868 mods
= mono_w32process_get_modules (pid
);
874 count
= g_slist_length (mods
);
876 /* If module != NULL compare the address.
877 * If module == NULL we are looking for the main module.
878 * The best we can do for now check it the module name end with the process name.
880 for (i
= 0; i
< count
; i
++) {
881 found_module
= (MonoW32ProcessModule
*)g_slist_nth_data (mods
, i
);
882 if (procname_ext
== NULL
&&
883 ((module
== NULL
&& match_procname_to_modulename (proc_name
, found_module
->filename
)) ||
884 (module
!= NULL
&& found_module
->address_start
== module
))) {
885 procname_ext
= g_path_get_basename (found_module
->filename
);
888 mono_w32process_module_free (found_module
);
891 if (procname_ext
== NULL
) {
892 /* If it's *still* null, we might have hit the
893 * case where reading /proc/$pid/maps gives an
894 * empty file for this user.
896 procname_ext
= mono_w32process_get_name (pid
);
903 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Process name is [%s]", __func__
,
906 procname
= mono_unicode_from_external (procname_ext
, &bytes
);
907 if (procname
== NULL
) {
909 g_free (procname_ext
);
915 /* Add the terminator */
919 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d smaller than needed (%ld); truncating", __func__
, size
, bytes
);
921 memcpy (basename
, procname
, size
);
923 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d larger than needed (%ld)",
924 __func__
, size
, bytes
);
926 memcpy (basename
, procname
, bytes
);
930 g_free (procname_ext
);
939 mono_w32process_module_get_information (gpointer process
, gpointer module
, WapiModuleInfo
*modinfo
, guint32 size
)
941 MonoW32HandleProcess
*process_handle
;
944 MonoW32ProcessModule
*found_module
;
947 gboolean ret
= FALSE
;
948 char *proc_name
= NULL
;
951 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Getting module info, process handle %p module %p",
952 __func__
, process
, module
);
954 if (modinfo
== NULL
|| size
< sizeof (WapiModuleInfo
))
957 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
959 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
963 pid
= process_handle
->id
;
964 proc_name
= g_strdup (process_handle
->proc_name
);
966 mods
= mono_w32process_get_modules (pid
);
972 count
= g_slist_length (mods
);
974 /* If module != NULL compare the address.
975 * If module == NULL we are looking for the main module.
976 * The best we can do for now check it the module name end with the process name.
978 for (i
= 0; i
< count
; i
++) {
979 found_module
= (MonoW32ProcessModule
*)g_slist_nth_data (mods
, i
);
981 ((module
== NULL
&& match_procname_to_modulename (proc_name
, found_module
->filename
)) ||
982 (module
!= NULL
&& found_module
->address_start
== module
))) {
983 modinfo
->lpBaseOfDll
= found_module
->address_start
;
984 modinfo
->SizeOfImage
= (gsize
)(found_module
->address_end
) - (gsize
)(found_module
->address_start
);
985 modinfo
->EntryPoint
= found_module
->address_offset
;
989 mono_w32process_module_free (found_module
);
999 switch_dir_separators (char *path
)
1001 size_t i
, pathLength
= strlen(path
);
1003 /* Turn all the slashes round the right way, except for \' */
1004 /* There are probably other characters that need to be excluded as well. */
1005 for (i
= 0; i
< pathLength
; i
++) {
1006 if (path
[i
] == '\\' && i
< pathLength
- 1 && path
[i
+1] != '\'' )
1013 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler
, (int _dummy
, siginfo_t
*info
, void *context
))
1021 pid
= waitpid (-1, &status
, WNOHANG
);
1022 } while (pid
== -1 && errno
== EINTR
);
1028 * This can run concurrently with the code in the rest of this module.
1030 for (p
= processes
; p
; p
= p
->next
) {
1034 p
->pid
= 0; /* this pid doesn't exist anymore, clear it */
1036 mono_os_sem_post (&p
->exit_sem
);
1037 mono_memory_barrier ();
1038 /* Mark this as freeable, the pointer becomes invalid afterwards */
1046 process_add_sigchld_handler (void)
1048 struct sigaction sa
;
1050 sa
.sa_sigaction
= mono_sigchld_signal_handler
;
1051 sigemptyset (&sa
.sa_mask
);
1052 sa
.sa_flags
= SA_NOCLDSTOP
| SA_SIGINFO
;
1053 g_assert (sigaction (SIGCHLD
, &sa
, NULL
) != -1);
1054 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "Added SIGCHLD handler");
1060 is_readable_or_executable (const char *prog
)
1063 int a
= access (prog
, R_OK
);
1064 int b
= access (prog
, X_OK
);
1065 if (a
!= 0 && b
!= 0)
1067 if (stat (prog
, &buf
))
1069 if (S_ISREG (buf
.st_mode
))
1075 is_executable (const char *prog
)
1078 if (access (prog
, X_OK
) != 0)
1080 if (stat (prog
, &buf
))
1082 if (S_ISREG (buf
.st_mode
))
1088 is_managed_binary (const char *filename
)
1090 int original_errno
= errno
;
1091 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1092 int file
= open (filename
, O_RDONLY
| O_LARGEFILE
);
1094 int file
= open (filename
, O_RDONLY
);
1097 unsigned char buffer
[8];
1098 off_t file_size
, optional_header_offset
;
1099 off_t pe_header_offset
, clr_header_offset
;
1100 gboolean managed
= FALSE
;
1102 guint32 first_word
, second_word
, magic_number
;
1104 /* If we are unable to open the file, then we definitely
1105 * can't say that it is managed. The child mono process
1106 * probably wouldn't be able to open it anyway.
1109 errno
= original_errno
;
1113 /* Retrieve the length of the file for future sanity checks. */
1114 file_size
= lseek (file
, 0, SEEK_END
);
1115 lseek (file
, 0, SEEK_SET
);
1117 /* We know we need to read a header field at offset 60. */
1121 num_read
= read (file
, buffer
, 2);
1123 if ((num_read
!= 2) || (buffer
[0] != 'M') || (buffer
[1] != 'Z'))
1126 new_offset
= lseek (file
, 60, SEEK_SET
);
1128 if (new_offset
!= 60)
1131 num_read
= read (file
, buffer
, 4);
1135 pe_header_offset
= buffer
[0]
1138 | (buffer
[3] << 24);
1140 if (pe_header_offset
+ 24 > file_size
)
1143 new_offset
= lseek (file
, pe_header_offset
, SEEK_SET
);
1145 if (new_offset
!= pe_header_offset
)
1148 num_read
= read (file
, buffer
, 4);
1150 if ((num_read
!= 4) || (buffer
[0] != 'P') || (buffer
[1] != 'E') || (buffer
[2] != 0) || (buffer
[3] != 0))
1154 * Verify that the header we want in the optional header data
1155 * is present in this binary.
1157 new_offset
= lseek (file
, pe_header_offset
+ 20, SEEK_SET
);
1159 if (new_offset
!= pe_header_offset
+ 20)
1162 num_read
= read (file
, buffer
, 2);
1164 if ((num_read
!= 2) || ((buffer
[0] | (buffer
[1] << 8)) < 216))
1167 optional_header_offset
= pe_header_offset
+ 24;
1169 /* Read the PE magic number */
1170 new_offset
= lseek (file
, optional_header_offset
, SEEK_SET
);
1172 if (new_offset
!= optional_header_offset
)
1175 num_read
= read (file
, buffer
, 2);
1180 magic_number
= (buffer
[0] | (buffer
[1] << 8));
1182 if (magic_number
== 0x10B) // PE32
1183 clr_header_offset
= 208;
1184 else if (magic_number
== 0x20B) // PE32+
1185 clr_header_offset
= 224;
1189 /* Read the CLR header address and size fields. These will be
1190 * zero if the binary is not managed.
1192 new_offset
= lseek (file
, optional_header_offset
+ clr_header_offset
, SEEK_SET
);
1194 if (new_offset
!= optional_header_offset
+ clr_header_offset
)
1197 num_read
= read (file
, buffer
, 8);
1199 /* We are not concerned with endianness, only with
1200 * whether it is zero or not.
1202 first_word
= *(guint32
*)&buffer
[0];
1203 second_word
= *(guint32
*)&buffer
[4];
1205 if ((num_read
!= 8) || (first_word
== 0) || (second_word
== 0))
1212 errno
= original_errno
;
1217 process_create (const gunichar2
*appname
, const gunichar2
*cmdline
, gpointer new_environ
,
1218 const gunichar2
*cwd
, StartupHandles
*startup_handles
, MonoW32ProcessInfo
*process_info
)
1220 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1221 char *cmd
= NULL
, *prog
= NULL
, *full_prog
= NULL
, *args
= NULL
, *args_after_prog
= NULL
;
1222 char *dir
= NULL
, **env_strings
= NULL
, **argv
= NULL
;
1223 guint32 i
, env_count
= 0;
1224 gboolean ret
= FALSE
;
1225 gpointer handle
= NULL
;
1226 GError
*gerr
= NULL
;
1227 int in_fd
, out_fd
, err_fd
;
1229 int startup_pipe
[2] = {-1, -1};
1231 MonoProcess
*mono_process
;
1234 mono_lazy_initialize (&process_sig_chld_once
, process_add_sigchld_handler
);
1237 /* appname and cmdline specify the executable and its args:
1239 * If appname is not NULL, it is the name of the executable.
1240 * Otherwise the executable is the first token in cmdline.
1242 * Executable searching:
1244 * If appname is not NULL, it can specify the full path and
1245 * file name, or else a partial name and the current directory
1246 * will be used. There is no additional searching.
1248 * If appname is NULL, the first whitespace-delimited token in
1249 * cmdline is used. If the name does not contain a full
1250 * directory path, the search sequence is:
1252 * 1) The directory containing the current process
1253 * 2) The current working directory
1254 * 3) The windows system directory (Ignored)
1255 * 4) The windows directory (Ignored)
1258 * Just to make things more interesting, tokens can contain
1259 * white space if they are surrounded by quotation marks. I'm
1260 * beginning to understand just why windows apps are generally
1261 * so crap, with an API like this :-(
1263 if (appname
!= NULL
) {
1264 cmd
= mono_unicode_to_external (appname
);
1266 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL",
1269 SetLastError (ERROR_PATH_NOT_FOUND
);
1273 switch_dir_separators(cmd
);
1276 if (cmdline
!= NULL
) {
1277 args
= mono_unicode_to_external (cmdline
);
1279 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
1281 SetLastError (ERROR_PATH_NOT_FOUND
);
1287 dir
= mono_unicode_to_external (cwd
);
1289 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
1291 SetLastError (ERROR_PATH_NOT_FOUND
);
1295 /* Turn all the slashes round the right way */
1296 switch_dir_separators(dir
);
1300 /* We can't put off locating the executable any longer :-( */
1303 if (g_ascii_isalpha (cmd
[0]) && (cmd
[1] == ':')) {
1304 /* Strip off the drive letter. I can't
1305 * believe that CP/M holdover is still
1308 g_memmove (cmd
, cmd
+2, strlen (cmd
)-2);
1309 cmd
[strlen (cmd
)-2] = '\0';
1312 unquoted
= g_shell_unquote (cmd
, NULL
);
1313 if (unquoted
[0] == '/') {
1314 /* Assume full path given */
1315 prog
= g_strdup (unquoted
);
1317 /* Executable existing ? */
1318 if (!is_readable_or_executable (prog
)) {
1319 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1322 SetLastError (ERROR_FILE_NOT_FOUND
);
1326 /* Search for file named by cmd in the current
1329 char *curdir
= g_get_current_dir ();
1331 prog
= g_strdup_printf ("%s/%s", curdir
, unquoted
);
1334 /* And make sure it's readable */
1335 if (!is_readable_or_executable (prog
)) {
1336 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1339 SetLastError (ERROR_FILE_NOT_FOUND
);
1345 args_after_prog
= args
;
1350 /* Dig out the first token from args, taking quotation
1351 * marks into account
1354 /* First, strip off all leading whitespace */
1355 args
= g_strchug (args
);
1357 /* args_after_prog points to the contents of args
1358 * after token has been set (otherwise argv[0] is
1361 args_after_prog
= args
;
1363 /* Assume the opening quote will always be the first
1366 if (args
[0] == '\"' || args
[0] == '\'') {
1368 for (i
= 1; args
[i
] != '\0' && args
[i
] != quote
; i
++);
1369 if (args
[i
+ 1] == '\0' || g_ascii_isspace (args
[i
+1])) {
1370 /* We found the first token */
1371 token
= g_strndup (args
+1, i
-1);
1372 args_after_prog
= g_strchug (args
+ i
+ 1);
1374 /* Quotation mark appeared in the
1375 * middle of the token. Just give the
1376 * whole first token, quotes and all,
1382 if (token
== NULL
) {
1383 /* No quote mark, or malformed */
1384 for (i
= 0; args
[i
] != '\0'; i
++) {
1385 if (g_ascii_isspace (args
[i
])) {
1386 token
= g_strndup (args
, i
);
1387 args_after_prog
= args
+ i
+ 1;
1393 if (token
== NULL
&& args
[0] != '\0') {
1394 /* Must be just one token in the string */
1395 token
= g_strdup (args
);
1396 args_after_prog
= NULL
;
1399 if (token
== NULL
) {
1401 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find what to exec", __func__
);
1403 SetLastError (ERROR_PATH_NOT_FOUND
);
1407 /* Turn all the slashes round the right way. Only for
1410 switch_dir_separators(token
);
1412 if (g_ascii_isalpha (token
[0]) && (token
[1] == ':')) {
1413 /* Strip off the drive letter. I can't
1414 * believe that CP/M holdover is still
1417 g_memmove (token
, token
+2, strlen (token
)-2);
1418 token
[strlen (token
)-2] = '\0';
1421 if (token
[0] == '/') {
1422 /* Assume full path given */
1423 prog
= g_strdup (token
);
1425 /* Executable existing ? */
1426 if (!is_readable_or_executable (prog
)) {
1427 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1430 SetLastError (ERROR_FILE_NOT_FOUND
);
1434 char *curdir
= g_get_current_dir ();
1436 /* FIXME: Need to record the directory
1437 * containing the current process, and check
1438 * that for the new executable as the first
1442 prog
= g_strdup_printf ("%s/%s", curdir
, token
);
1445 /* I assume X_OK is the criterion to use,
1448 * X_OK is too strict *if* the target is a CLR binary
1450 if (!is_readable_or_executable (prog
)) {
1452 prog
= g_find_program_in_path (token
);
1454 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s", __func__
, token
);
1457 SetLastError (ERROR_FILE_NOT_FOUND
);
1466 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Exec prog [%s] args [%s]",
1467 __func__
, prog
, args_after_prog
);
1469 /* Check for CLR binaries; if found, we will try to invoke
1470 * them using the same mono binary that started us.
1472 if (is_managed_binary (prog
)) {
1473 gunichar2
*newapp
, *newcmd
;
1474 gsize bytes_ignored
;
1476 newapp
= mono_unicode_from_external (cli_launcher
? cli_launcher
: "mono", &bytes_ignored
);
1479 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, appname
, utf16_space
, cmdline
, NULL
);
1481 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, cmdline
, NULL
);
1486 ret
= process_create (NULL
, newcmd
, new_environ
, cwd
, startup_handles
, process_info
);
1494 if (!is_executable (prog
)) {
1495 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Executable permisson not set on %s", __func__
, prog
);
1496 SetLastError (ERROR_ACCESS_DENIED
);
1501 if (args_after_prog
!= NULL
&& *args_after_prog
) {
1504 qprog
= g_shell_quote (prog
);
1505 full_prog
= g_strconcat (qprog
, " ", args_after_prog
, NULL
);
1508 full_prog
= g_shell_quote (prog
);
1511 ret
= g_shell_parse_argv (full_prog
, NULL
, &argv
, &gerr
);
1513 g_message ("process_create: %s\n", gerr
->message
);
1514 g_error_free (gerr
);
1519 if (startup_handles
) {
1520 in_fd
= GPOINTER_TO_UINT (startup_handles
->input
);
1521 out_fd
= GPOINTER_TO_UINT (startup_handles
->output
);
1522 err_fd
= GPOINTER_TO_UINT (startup_handles
->error
);
1524 in_fd
= GPOINTER_TO_UINT (GetStdHandle (STD_INPUT_HANDLE
));
1525 out_fd
= GPOINTER_TO_UINT (GetStdHandle (STD_OUTPUT_HANDLE
));
1526 err_fd
= GPOINTER_TO_UINT (GetStdHandle (STD_ERROR_HANDLE
));
1529 /* new_environ is a block of NULL-terminated strings, which
1530 * is itself NULL-terminated. Of course, passing an array of
1531 * string pointers would have made things too easy :-(
1533 * If new_environ is not NULL it specifies the entire set of
1534 * environment variables in the new process. Otherwise the
1535 * new process inherits the same environment.
1538 gunichar2
*new_environp
;
1540 /* Count the number of strings */
1541 for (new_environp
= (gunichar2
*)new_environ
; *new_environp
; new_environp
++) {
1543 while (*new_environp
)
1547 /* +2: one for the process handle value, and the last
1550 env_strings
= g_new0 (char *, env_count
+ 2);
1552 /* Copy each environ string into 'strings' turning it
1553 * into utf8 (or the requested encoding) at the same
1557 for (new_environp
= (gunichar2
*)new_environ
; *new_environp
; new_environp
++) {
1558 env_strings
[env_count
] = mono_unicode_to_external (new_environp
);
1560 while (*new_environp
) {
1565 for (i
= 0; environ
[i
] != NULL
; i
++)
1568 /* +2: one for the process handle value, and the last
1571 env_strings
= g_new0 (char *, env_count
+ 2);
1573 /* Copy each environ string into 'strings' turning it
1574 * into utf8 (or the requested encoding) at the same
1578 for (i
= 0; environ
[i
] != NULL
; i
++) {
1579 env_strings
[env_count
] = g_strdup (environ
[i
]);
1584 /* Create a pipe to make sure the child doesn't exit before
1585 * we can add the process to the linked list of processes */
1586 if (pipe (startup_pipe
) == -1) {
1587 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1588 * This is just for a very hard to hit race condition in the first place */
1589 startup_pipe
[0] = startup_pipe
[1] = -1;
1590 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: new process startup not synchronized. We may not notice if the newly created process exits immediately.", __func__
);
1594 /* FIXME: block SIGCHLD */
1597 switch (pid
= fork ()) {
1598 case -1: /* Error */ {
1599 SetLastError (ERROR_OUTOFMEMORY
);
1603 case 0: /* Child */ {
1604 if (startup_pipe
[0] != -1) {
1605 /* Wait until the parent has updated it's internal data */
1606 ssize_t _i G_GNUC_UNUSED
= read (startup_pipe
[0], &dummy
, 1);
1607 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: child: parent has completed its setup", __func__
);
1608 close (startup_pipe
[0]);
1609 close (startup_pipe
[1]);
1612 /* should we detach from the process group? */
1614 /* Connect stdin, stdout and stderr */
1619 /* Close all file descriptors */
1620 for (i
= mono_w32handle_fd_reserve
- 1; i
> 2; i
--)
1623 #ifdef DEBUG_ENABLED
1624 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: exec()ing [%s] in dir [%s]", __func__
, cmd
,
1625 dir
== NULL
?".":dir
);
1626 for (i
= 0; argv
[i
] != NULL
; i
++)
1627 g_message ("arg %d: [%s]", i
, argv
[i
]);
1629 for (i
= 0; env_strings
[i
] != NULL
; i
++)
1630 g_message ("env %d: [%s]", i
, env_strings
[i
]);
1634 if (dir
!= NULL
&& chdir (dir
) == -1) {
1640 execve (argv
[0], argv
, env_strings
);
1647 default: /* Parent */ {
1648 MonoW32HandleProcess process_handle
;
1650 memset (&process_handle
, 0, sizeof (process_handle
));
1651 process_handle
.id
= pid
;
1652 process_handle
.child
= TRUE
;
1653 process_handle
.proc_name
= g_strdup (prog
);
1654 process_set_defaults (&process_handle
);
1656 /* Add our mono_process into the linked list of processes */
1657 mono_process
= (MonoProcess
*) g_malloc0 (sizeof (MonoProcess
));
1658 mono_process
->pid
= pid
;
1659 mono_process
->handle_count
= 1;
1660 mono_os_sem_init (&mono_process
->exit_sem
, 0);
1662 process_handle
.mono_process
= mono_process
;
1664 handle
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
1665 if (handle
== INVALID_HANDLE_VALUE
) {
1666 g_warning ("%s: error creating process handle", __func__
);
1668 mono_os_sem_destroy (&mono_process
->exit_sem
);
1669 g_free (mono_process
);
1671 SetLastError (ERROR_OUTOFMEMORY
);
1676 /* Keep the process handle artificially alive until the process
1677 * exits so that the information in the handle isn't lost. */
1678 mono_w32handle_ref (handle
);
1679 mono_process
->handle
= handle
;
1681 mono_os_mutex_lock (&processes_mutex
);
1682 mono_process
->next
= processes
;
1683 processes
= mono_process
;
1684 mono_os_mutex_unlock (&processes_mutex
);
1686 if (process_info
!= NULL
) {
1687 process_info
->process_handle
= handle
;
1688 process_info
->pid
= pid
;
1690 /* FIXME: we might need to handle the thread info some day */
1691 process_info
->thread_handle
= INVALID_HANDLE_VALUE
;
1692 process_info
->tid
= 0;
1700 /* FIXME: unblock SIGCHLD */
1703 if (startup_pipe
[1] != -1) {
1704 /* Write 1 byte, doesn't matter what */
1705 ssize_t _i G_GNUC_UNUSED
= write (startup_pipe
[1], startup_pipe
, 1);
1706 close (startup_pipe
[0]);
1707 close (startup_pipe
[1]);
1722 g_strfreev (env_strings
);
1726 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: returning handle %p for pid %d", __func__
, handle
, pid
);
1728 /* Check if something needs to be cleaned up. */
1729 processes_cleanup ();
1733 SetLastError (ERROR_NOT_SUPPORTED
);
1735 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1739 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo
*proc_start_info
, MonoW32ProcessInfo
*process_info
)
1741 const gunichar2
*lpFile
;
1742 const gunichar2
*lpParameters
;
1743 const gunichar2
*lpDirectory
;
1747 if (!proc_start_info
->filename
) {
1748 /* w2k returns TRUE for this, for some reason. */
1753 lpFile
= proc_start_info
->filename
? mono_string_chars (proc_start_info
->filename
) : NULL
;
1754 lpParameters
= proc_start_info
->arguments
? mono_string_chars (proc_start_info
->arguments
) : NULL
;
1755 lpDirectory
= proc_start_info
->working_directory
&& mono_string_length (proc_start_info
->working_directory
) != 0 ?
1756 mono_string_chars (proc_start_info
->working_directory
) : NULL
;
1758 /* Put both executable and parameters into the second argument
1759 * to process_create (), so it searches $PATH. The conversion
1760 * into and back out of utf8 is because there is no
1761 * g_strdup_printf () equivalent for gunichar2 :-(
1763 args
= utf16_concat (utf16_quote
, lpFile
, utf16_quote
, lpParameters
== NULL
? NULL
: utf16_space
, lpParameters
, NULL
);
1765 SetLastError (ERROR_INVALID_DATA
);
1769 ret
= process_create (NULL
, args
, NULL
, lpDirectory
, NULL
, process_info
);
1772 if (!ret
&& GetLastError () == ERROR_OUTOFMEMORY
)
1776 static char *handler
;
1777 static gunichar2
*handler_utf16
;
1779 if (handler_utf16
== (gunichar2
*)-1) {
1784 #ifdef PLATFORM_MACOSX
1785 handler
= g_strdup ("/usr/bin/open");
1788 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
1789 * if that fails, try to use gnome-open, then kfmclient
1791 handler
= g_find_program_in_path ("xdg-open");
1792 if (handler
== NULL
){
1793 handler
= g_find_program_in_path ("gnome-open");
1794 if (handler
== NULL
){
1795 handler
= g_find_program_in_path ("kfmclient");
1796 if (handler
== NULL
){
1797 handler_utf16
= (gunichar2
*) -1;
1801 /* kfmclient needs exec argument */
1802 char *old
= handler
;
1803 handler
= g_strconcat (old
, " exec",
1810 handler_utf16
= g_utf8_to_utf16 (handler
, -1, NULL
, NULL
, NULL
);
1813 /* Put quotes around the filename, in case it's a url
1814 * that contains #'s (process_create() calls
1815 * g_shell_parse_argv(), which deliberately throws
1816 * away anything after an unquoted #). Fixes bug
1819 args
= utf16_concat (handler_utf16
, utf16_space
, utf16_quote
, lpFile
, utf16_quote
,
1820 lpParameters
== NULL
? NULL
: utf16_space
, lpParameters
, NULL
);
1822 SetLastError (ERROR_INVALID_DATA
);
1826 ret
= process_create (NULL
, args
, NULL
, lpDirectory
, NULL
, process_info
);
1829 if (GetLastError () != ERROR_OUTOFMEMORY
)
1830 SetLastError (ERROR_INVALID_DATA
);
1834 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
1835 CloseHandle (process_info
->process_handle
);
1836 process_info
->process_handle
= NULL
;
1841 process_info
->pid
= -GetLastError ();
1843 process_info
->thread_handle
= NULL
;
1844 #if !defined(MONO_CROSS_COMPILE)
1845 process_info
->pid
= mono_w32process_get_pid (process_info
->process_handle
);
1847 process_info
->pid
= 0;
1849 process_info
->tid
= 0;
1855 /* Only used when UseShellExecute is false */
1857 process_get_complete_path (const gunichar2
*appname
, gchar
**completed
)
1862 utf8app
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
1864 if (g_path_is_absolute (utf8app
)) {
1865 *completed
= g_shell_quote (utf8app
);
1870 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
1871 *completed
= g_shell_quote (utf8app
);
1876 found
= g_find_program_in_path (utf8app
);
1877 if (found
== NULL
) {
1883 *completed
= g_shell_quote (found
);
1890 process_get_shell_arguments (MonoW32ProcessStartInfo
*proc_start_info
, gunichar2
**shell_path
, MonoString
**cmd
)
1892 gchar
*spath
= NULL
;
1895 *cmd
= proc_start_info
->arguments
;
1897 process_get_complete_path (mono_string_chars (proc_start_info
->filename
), &spath
);
1898 if (spath
!= NULL
) {
1899 *shell_path
= g_utf8_to_utf16 (spath
, -1, NULL
, NULL
, NULL
);
1903 return (*shell_path
!= NULL
) ? TRUE
: FALSE
;
1907 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo
*proc_start_info
,
1908 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
)
1912 StartupHandles startup_handles
;
1913 gunichar2
*shell_path
= NULL
;
1914 gchar
*env_vars
= NULL
;
1915 MonoString
*cmd
= NULL
;
1917 memset (&startup_handles
, 0, sizeof (startup_handles
));
1918 startup_handles
.input
= stdin_handle
;
1919 startup_handles
.output
= stdout_handle
;
1920 startup_handles
.error
= stderr_handle
;
1922 if (process_get_shell_arguments (proc_start_info
, &shell_path
, &cmd
) == FALSE
) {
1923 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
1927 if (process_info
->env_keys
) {
1930 MonoString
*key
, *value
;
1931 gunichar2
*str
, *ptr
;
1932 gunichar2
*equals16
;
1934 for (len
= 0, i
= 0; i
< mono_array_length (process_info
->env_keys
); i
++) {
1935 ms
= mono_array_get (process_info
->env_values
, MonoString
*, i
);
1939 len
+= mono_string_length (ms
) * sizeof (gunichar2
);
1940 ms
= mono_array_get (process_info
->env_keys
, MonoString
*, i
);
1941 len
+= mono_string_length (ms
) * sizeof (gunichar2
);
1942 len
+= 2 * sizeof (gunichar2
);
1945 equals16
= g_utf8_to_utf16 ("=", 1, NULL
, NULL
, NULL
);
1946 ptr
= str
= g_new0 (gunichar2
, len
+ 1);
1947 for (i
= 0; i
< mono_array_length (process_info
->env_keys
); i
++) {
1948 value
= mono_array_get (process_info
->env_values
, MonoString
*, i
);
1952 key
= mono_array_get (process_info
->env_keys
, MonoString
*, i
);
1953 memcpy (ptr
, mono_string_chars (key
), mono_string_length (key
) * sizeof (gunichar2
));
1954 ptr
+= mono_string_length (key
);
1956 memcpy (ptr
, equals16
, sizeof (gunichar2
));
1959 memcpy (ptr
, mono_string_chars (value
), mono_string_length (value
) * sizeof (gunichar2
));
1960 ptr
+= mono_string_length (value
);
1965 env_vars
= (gchar
*) str
;
1968 /* The default dir name is "". Turn that into NULL to mean "current directory" */
1969 dir
= proc_start_info
->working_directory
&& mono_string_length (proc_start_info
->working_directory
) > 0 ?
1970 mono_string_chars (proc_start_info
->working_directory
) : NULL
;
1972 ret
= process_create (shell_path
, cmd
? mono_string_chars (cmd
): NULL
, env_vars
, dir
, &startup_handles
, process_info
);
1975 if (shell_path
!= NULL
)
1976 g_free (shell_path
);
1979 process_info
->pid
= -GetLastError ();
1984 /* Returns an array of pids */
1986 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
1993 pidarray
= mono_process_list (&count
);
1995 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
1998 procs
= mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count
, &error
);
1999 if (mono_error_set_pending_exception (&error
)) {
2003 if (sizeof (guint32
) == sizeof (gpointer
)) {
2004 memcpy (mono_array_addr (procs
, guint32
, 0), pidarray
, count
* sizeof (gint32
));
2006 for (i
= 0; i
< count
; ++i
)
2007 *(mono_array_addr (procs
, guint32
, i
)) = GPOINTER_TO_UINT (pidarray
[i
]);
2015 mono_w32process_set_cli_launcher (gchar
*path
)
2017 g_free (cli_launcher
);
2018 cli_launcher
= g_strdup (path
);
2022 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2024 mono_w32handle_ref (current_process
);
2025 return current_process
;
2029 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
)
2031 MonoW32HandleProcess
*process_handle
;
2037 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2039 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2043 if (process_handle
->id
== wapi_getpid ()) {
2044 *exitcode
= STILL_ACTIVE
;
2048 /* A process handle is only signalled if the process has exited
2049 * and has been waited for. Make sure any process exit has been
2050 * noticed before checking if the process is signalled.
2051 * Fixes bug 325463. */
2052 mono_w32handle_wait_one (handle
, 0, TRUE
);
2054 *exitcode
= mono_w32handle_issignalled (handle
) ? process_handle
->exitstatus
: STILL_ACTIVE
;
2059 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
)
2061 return CloseHandle (handle
);
2065 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
)
2068 MonoW32HandleProcess
*process_handle
;
2073 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2075 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2076 SetLastError (ERROR_INVALID_HANDLE
);
2080 pid
= process_handle
->id
;
2082 ret
= kill (pid
, exitcode
== -1 ? SIGKILL
: SIGTERM
);
2087 case EINVAL
: SetLastError (ERROR_INVALID_PARAMETER
); break;
2088 case EPERM
: SetLastError (ERROR_ACCESS_DENIED
); break;
2089 case ESRCH
: SetLastError (ERROR_PROC_NOT_FOUND
); break;
2090 default: SetLastError (ERROR_GEN_FAILURE
); break;
2095 g_error ("kill() is not supported by this platform");
2100 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
)
2102 MonoW32HandleProcess
*process_handle
;
2108 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2110 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2114 if (!process_handle
->child
)
2117 *min
= process_handle
->min_working_set
;
2118 *max
= process_handle
->max_working_set
;
2123 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
)
2125 MonoW32HandleProcess
*process_handle
;
2128 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2130 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2134 if (!process_handle
->child
)
2137 process_handle
->min_working_set
= min
;
2138 process_handle
->max_working_set
= max
;
2143 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
)
2145 #ifdef HAVE_GETPRIORITY
2146 MonoW32HandleProcess
*process_handle
;
2151 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2153 SetLastError (ERROR_INVALID_HANDLE
);
2157 pid
= process_handle
->id
;
2160 ret
= getpriority (PRIO_PROCESS
, pid
);
2161 if (ret
== -1 && errno
!= 0) {
2165 SetLastError (ERROR_ACCESS_DENIED
);
2168 SetLastError (ERROR_PROC_NOT_FOUND
);
2171 SetLastError (ERROR_GEN_FAILURE
);
2177 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2179 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
;
2181 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH
;
2183 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
2185 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE
;
2187 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
2189 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2191 SetLastError (ERROR_NOT_SUPPORTED
);
2197 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
)
2199 #ifdef HAVE_SETPRIORITY
2200 MonoW32HandleProcess
*process_handle
;
2206 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2208 SetLastError (ERROR_INVALID_HANDLE
);
2212 pid
= process_handle
->id
;
2214 switch (priorityClass
) {
2215 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE
:
2218 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
:
2221 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
:
2224 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
:
2227 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH
:
2230 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
:
2234 SetLastError (ERROR_INVALID_PARAMETER
);
2238 ret
= setpriority (PRIO_PROCESS
, pid
, prio
);
2243 SetLastError (ERROR_ACCESS_DENIED
);
2246 SetLastError (ERROR_PROC_NOT_FOUND
);
2249 SetLastError (ERROR_GEN_FAILURE
);
2255 SetLastError (ERROR_NOT_SUPPORTED
);
2261 ticks_to_processtime (guint64 ticks
, ProcessTime
*processtime
)
2263 processtime
->lowDateTime
= ticks
& 0xFFFFFFFF;
2264 processtime
->highDateTime
= ticks
>> 32;
2268 wapifiletime_to_processtime (WapiFileTime wapi_filetime
, ProcessTime
*processtime
)
2270 processtime
->lowDateTime
= wapi_filetime
.dwLowDateTime
;
2271 processtime
->highDateTime
= wapi_filetime
.dwHighDateTime
;
2275 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creation_time
, gint64
*exit_time
, gint64
*kernel_time
, gint64
*user_time
)
2277 MonoW32HandleProcess
*process_handle
;
2278 ProcessTime
*creation_processtime
, *exit_processtime
, *kernel_processtime
, *user_processtime
;
2281 if (!creation_time
|| !exit_time
|| !kernel_time
|| !user_time
) {
2282 /* Not sure if w32 allows NULLs here or not */
2286 creation_processtime
= (ProcessTime
*) creation_time
;
2287 exit_processtime
= (ProcessTime
*) exit_time
;
2288 kernel_processtime
= (ProcessTime
*) kernel_time
;
2289 user_processtime
= (ProcessTime
*) user_time
;
2291 memset (creation_processtime
, 0, sizeof (ProcessTime
));
2292 memset (exit_processtime
, 0, sizeof (ProcessTime
));
2293 memset (kernel_processtime
, 0, sizeof (ProcessTime
));
2294 memset (user_processtime
, 0, sizeof (ProcessTime
));
2296 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2298 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2302 if (!process_handle
->child
) {
2303 gint64 start_ticks
, user_ticks
, kernel_ticks
;
2305 mono_process_get_times (GINT_TO_POINTER (process_handle
->id
),
2306 &start_ticks
, &user_ticks
, &kernel_ticks
);
2308 ticks_to_processtime (start_ticks
, creation_processtime
);
2309 ticks_to_processtime (user_ticks
, kernel_processtime
);
2310 ticks_to_processtime (kernel_ticks
, user_processtime
);
2314 wapifiletime_to_processtime (process_handle
->create_time
, creation_processtime
);
2316 /* A process handle is only signalled if the process has
2317 * exited, otherwise exit_processtime isn't set */
2318 if (mono_w32handle_issignalled (handle
))
2319 wapifiletime_to_processtime (process_handle
->exit_time
, exit_processtime
);
2321 #ifdef HAVE_GETRUSAGE
2322 if (process_handle
->id
== getpid ()) {
2323 struct rusage time_data
;
2324 if (getrusage (RUSAGE_SELF
, &time_data
) == 0) {
2325 ticks_to_processtime ((guint64
)time_data
.ru_utime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_utime
.tv_usec
* 10, user_processtime
);
2326 ticks_to_processtime ((guint64
)time_data
.ru_stime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_stime
.tv_usec
* 10, kernel_processtime
);