3 * System.Diagnostics.Process support
6 * Dick Porter (dick@ximian.com)
8 * Copyright 2002 Ximian, Inc.
9 * Copyright 2002-2006 Novell, Inc.
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
22 #include <sys/types.h>
30 #ifdef HAVE_SYS_PARAM_H
31 #include <sys/param.h>
35 #ifdef HAVE_SYS_WAIT_H
38 #ifdef HAVE_SYS_RESOURCE_H
39 #include <sys/resource.h>
42 #ifdef HAVE_SYS_MKDEV_H
43 #include <sys/mkdev.h>
53 #elif defined (__FreeBSD__)
54 #include <sys/sysctl.h>
57 #elif defined(__linux__)
61 #include <mono/metadata/object-internals.h>
62 #include <mono/metadata/w32process.h>
63 #include <mono/metadata/w32process-internals.h>
64 #include <mono/metadata/w32process-unix-internals.h>
65 #include <mono/metadata/w32error.h>
66 #include <mono/metadata/class.h>
67 #include <mono/metadata/class-internals.h>
68 #include <mono/metadata/object.h>
69 #include <mono/metadata/metadata.h>
70 #include <mono/metadata/metadata-internals.h>
71 #include <mono/metadata/exception.h>
72 #include <mono/metadata/w32handle.h>
73 #include <mono/metadata/w32file.h>
74 #include <mono/utils/mono-membar.h>
75 #include <mono/utils/mono-logger-internals.h>
76 #include <mono/utils/strenc-internals.h>
77 #include <mono/utils/strenc.h>
78 #include <mono/utils/mono-proclib.h>
79 #include <mono/utils/mono-path.h>
80 #include <mono/utils/mono-lazy-init.h>
81 #include <mono/utils/mono-signal-handler.h>
82 #include <mono/utils/mono-time.h>
83 #include <mono/utils/mono-mmap.h>
84 #include <mono/utils/strenc.h>
85 #include <mono/utils/mono-io-portability.h>
86 #include <mono/utils/w32api.h>
87 #include <mono/utils/mono-errno.h>
88 #include <mono/utils/mono-error-internals.h>
89 #include <mono/utils/mono-threads-coop.h>
90 #include "object-internals.h"
91 #include "icall-decl.h"
93 #ifndef ENABLE_NETCORE
96 #define MAXPATHLEN 242
99 #define STILL_ACTIVE ((int) 0x00000103)
101 #define LOGDEBUG(...)
102 /* define LOGDEBUG(...) g_message(__VA_ARGS__) */
104 /* The process' environment strings */
105 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
106 #if defined(__APPLE__)
107 #if defined (TARGET_OSX)
108 /* Apple defines this in crt_externs.h but doesn't provide that header for
109 * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
110 * in fact exist on all implementations (so far)
113 gchar
***_NSGetEnviron(void);
115 #define environ (*_NSGetEnviron())
117 static char *mono_environ
[1] = { NULL
};
118 #define environ mono_environ
119 #endif /* defined (TARGET_OSX) */
122 extern char **environ
;
128 STARTF_USESHOWWINDOW
=0x001,
129 STARTF_USESIZE
=0x002,
130 STARTF_USEPOSITION
=0x004,
131 STARTF_USECOUNTCHARS
=0x008,
132 STARTF_USEFILLATTRIBUTE
=0x010,
133 STARTF_RUNFULLSCREEN
=0x020,
134 STARTF_FORCEONFEEDBACK
=0x040,
135 STARTF_FORCEOFFFEEDBACK
=0x080,
136 STARTF_USESTDHANDLES
=0x100
146 #if G_BYTE_ORDER == G_BIG_ENDIAN
147 guint32 highDateTime
;
151 guint32 highDateTime
;
156 * Process describes processes we create.
157 * It contains a semaphore that can be waited on in order to wait
158 * for process termination.
160 typedef struct _Process
{
161 pid_t pid
; /* the pid of the process. This value is only valid until the process has exited. */
162 MonoCoopSem exit_sem
; /* this semaphore will be released when the process exits */
163 int status
; /* the exit status */
164 gint32 handle_count
; /* the number of handles to this process instance */
165 /* we keep a ref to the creating _WapiHandle_process handle until
166 * the process has exited, so that the information there isn't lost.
170 struct _Process
*next
;
173 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
178 gpointer main_thread
;
182 size_t min_working_set
;
183 size_t max_working_set
;
186 } MonoW32HandleProcess
;
191 * 2 bytes: Length in bytes (this block, and all child blocks. does _not_ include alignment padding between blocks)
192 * 2 bytes: Length in bytes of VS_FIXEDFILEINFO struct
193 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
194 * Variable length unicode string (null terminated): Key (currently "VS_VERSION_INFO")
195 * Variable length padding to align VS_FIXEDFILEINFO on a 32-bit boundary
196 * VS_FIXEDFILEINFO struct
197 * Variable length padding to align Child struct on a 32-bit boundary
198 * Child struct (zero or one StringFileInfo structs, zero or one VarFileInfo structs)
204 * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
205 * 2 bytes: Value length (always zero)
206 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
207 * Variable length unicode string: Key (currently "StringFileInfo")
208 * Variable length padding to align Child struct on a 32-bit boundary
209 * Child structs ( one or more StringTable structs. Each StringTable struct's Key member indicates the appropriate language and code page for displaying the text in that StringTable struct.)
215 * 2 bytes: Length in bytes (includes this block as well as all Child blocks, but excludes any padding between String blocks)
216 * 2 bytes: Value length (always zero)
217 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
218 * Variable length unicode string: Key. An 8-digit hex number stored as a unicode string. The four most significant digits represent the language identifier. The four least significant digits represent the code page for which the data is formatted.
219 * Variable length padding to align Child struct on a 32-bit boundary
220 * Child structs (an array of one or more String structs (each aligned on a 32-bit boundary)
226 * 2 bytes: Length in bytes (of this block)
227 * 2 bytes: Value length (the length in words of the Value member)
228 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
229 * Variable length unicode string: Key. arbitrary string, identifies data.
230 * Variable length padding to align Value on a 32-bit boundary
231 * Value: Variable length unicode string, holding data.
237 * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
238 * 2 bytes: Value length (always zero)
239 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
240 * Variable length unicode string: Key (currently "VarFileInfo")
241 * Variable length padding to align Child struct on a 32-bit boundary
242 * Child structs (a Var struct)
248 * 2 bytes: Length in bytes of this block
249 * 2 bytes: Value length in bytes of the Value
250 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
251 * Variable length unicode string: Key ("Translation")
252 * Variable length padding to align Value on a 32-bit boundary
253 * Value: an array of one or more 4 byte values that are language and code page identifier pairs, low-order word containing a language identifier, and the high-order word containing a code page number. Either word can be zero, indicating that the file is language or code page independent.
256 #if G_BYTE_ORDER == G_BIG_ENDIAN
257 #define VS_FFI_SIGNATURE 0xbd04effe
258 #define VS_FFI_STRUCVERSION 0x00000100
260 #define VS_FFI_SIGNATURE 0xfeef04bd
261 #define VS_FFI_STRUCVERSION 0x00010000
264 #define VOS_UNKNOWN 0x00000000
265 #define VOS_DOS 0x00010000
266 #define VOS_OS216 0x00020000
267 #define VOS_OS232 0x00030000
268 #define VOS_NT 0x00040000
269 #define VOS__BASE 0x00000000
270 #define VOS__WINDOWS16 0x00000001
271 #define VOS__PM16 0x00000002
272 #define VOS__PM32 0x00000003
273 #define VOS__WINDOWS32 0x00000004
274 /* Should "embrace and extend" here with some entries for linux etc */
276 #define VOS_DOS_WINDOWS16 0x00010001
277 #define VOS_DOS_WINDOWS32 0x00010004
278 #define VOS_OS216_PM16 0x00020002
279 #define VOS_OS232_PM32 0x00030003
280 #define VOS_NT_WINDOWS32 0x00040004
282 #define VFT_UNKNOWN 0x0000
283 #define VFT_APP 0x0001
284 #define VFT_DLL 0x0002
285 #define VFT_DRV 0x0003
286 #define VFT_FONT 0x0004
287 #define VFT_VXD 0x0005
288 #define VFT_STATIC_LIB 0x0007
290 #define VFT2_UNKNOWN 0x0000
291 #define VFT2_DRV_PRINTER 0x0001
292 #define VFT2_DRV_KEYBOARD 0x0002
293 #define VFT2_DRV_LANGUAGE 0x0003
294 #define VFT2_DRV_DISPLAY 0x0004
295 #define VFT2_DRV_MOUSE 0x0005
296 #define VFT2_DRV_NETWORK 0x0006
297 #define VFT2_DRV_SYSTEM 0x0007
298 #define VFT2_DRV_INSTALLABLE 0x0008
299 #define VFT2_DRV_SOUND 0x0009
300 #define VFT2_DRV_COMM 0x000a
301 #define VFT2_DRV_INPUTMETHOD 0x000b
302 #define VFT2_FONT_RASTER 0x0001
303 #define VFT2_FONT_VECTOR 0x0002
304 #define VFT2_FONT_TRUETYPE 0x0003
306 #define MAKELANGID(primary,secondary) ((guint16)((secondary << 10) | (primary)))
308 #define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
311 static mono_lazy_init_t process_sig_chld_once
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
314 static gchar
*cli_launcher
;
316 static Process
*processes
;
317 static MonoCoopMutex processes_mutex
;
319 static pid_t current_pid
;
320 static gpointer current_process
;
322 static const gunichar2 utf16_space
[2] = { 0x20, 0 };
323 static const gunichar2 utf16_quote
[2] = { 0x22, 0 };
326 mono_get_exit_code_process (gpointer handle
, gint32
*exitcode
);
328 /* Check if a pid is valid - i.e. if a process exists with this pid. */
330 process_is_alive (pid_t pid
)
332 #if defined(HOST_WATCHOS)
333 return TRUE
; // TODO: Rewrite using sysctl
334 #elif defined(HOST_DARWIN) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(_AIX)
337 if (kill (pid
, 0) == 0)
342 #elif defined(__HAIKU__)
344 if (get_team_info ((team_id
)pid
, &teamInfo
) == B_OK
)
348 gchar
*dir
= g_strdup_printf ("/proc/%d", pid
);
349 gboolean result
= access (dir
, F_OK
) == 0;
356 process_details (MonoW32Handle
*handle_data
)
358 MonoW32HandleProcess
*process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
359 g_print ("pid: %d, exited: %s, exitstatus: %d",
360 process_handle
->pid
, process_handle
->exited
? "true" : "false", process_handle
->exitstatus
);
364 process_typename (void)
370 process_typesize (void)
372 return sizeof (MonoW32HandleProcess
);
375 static MonoW32HandleWaitRet
376 process_wait (MonoW32Handle
*handle_data
, guint32 timeout
, gboolean
*alerted
)
378 MonoW32HandleProcess
*process_handle
;
379 pid_t pid G_GNUC_UNUSED
, ret
;
384 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
")", __func__
, handle_data
, timeout
);
389 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
391 if (process_handle
->exited
) {
392 /* We've already done this one */
393 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): Process already exited", __func__
, handle_data
, timeout
);
394 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
397 pid
= process_handle
->pid
;
399 if (pid
== mono_process_current_pid ()) {
400 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): waiting on current process", __func__
, handle_data
, timeout
);
401 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
404 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): PID: %d", __func__
, handle_data
, timeout
, pid
);
406 if (!process_handle
->child
) {
407 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): waiting on non-child process", __func__
, handle_data
, timeout
);
409 if (!process_is_alive (pid
)) {
410 /* assume the process has exited */
411 process_handle
->exited
= TRUE
;
412 process_handle
->exitstatus
= -1;
413 mono_w32handle_set_signal_state (handle_data
, TRUE
, TRUE
);
415 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): non-child process is not alive anymore (2)", __func__
, handle_data
, timeout
);
416 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
419 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): non-child process wait failed, error : %s (%d))", __func__
, handle_data
, timeout
, g_strerror (errno
), errno
);
420 return MONO_W32HANDLE_WAIT_RET_FAILED
;
423 /* We don't need to lock processes here, the entry
424 * has a handle_count > 0 which means it will not be freed. */
425 process
= process_handle
->process
;
428 start
= mono_msec_ticks ();
432 if (timeout
!= MONO_INFINITE_WAIT
) {
433 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): waiting on semaphore for %" G_GINT64_FORMAT
" ms...",
434 __func__
, handle_data
, timeout
, timeout
- (now
- start
));
435 ret
= mono_coop_sem_timedwait (&process
->exit_sem
, (timeout
- (now
- start
)), alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
437 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): waiting on semaphore forever...",
438 __func__
, handle_data
, timeout
);
439 ret
= mono_coop_sem_wait (&process
->exit_sem
, alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
442 if (ret
== MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
443 /* Success, process has exited */
444 mono_coop_sem_post (&process
->exit_sem
);
448 if (ret
== MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
) {
449 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): wait timeout (timeout = 0)", __func__
, handle_data
, timeout
);
450 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
453 now
= mono_msec_ticks ();
454 if (now
- start
>= timeout
) {
455 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): wait timeout", __func__
, handle_data
, timeout
);
456 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
459 if (alerted
&& ret
== MONO_SEM_TIMEDWAIT_RET_ALERTED
) {
460 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): wait alerted", __func__
, handle_data
, timeout
);
462 return MONO_W32HANDLE_WAIT_RET_ALERTED
;
466 /* Process must have exited */
467 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): Waited successfully", __func__
, handle_data
, timeout
);
469 status
= process
->status
;
470 if (WIFSIGNALED (status
))
471 process_handle
->exitstatus
= 128 + WTERMSIG (status
);
473 process_handle
->exitstatus
= WEXITSTATUS (status
);
475 process_handle
->exit_time
= mono_100ns_datetime ();
477 process_handle
->exited
= TRUE
;
479 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s (%p, %" G_GUINT32_FORMAT
"): Setting pid %d signalled, exit status %d",
480 __func__
, handle_data
, timeout
, process_handle
->pid
, process_handle
->exitstatus
);
482 mono_w32handle_set_signal_state (handle_data
, TRUE
, TRUE
);
484 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
488 processes_cleanup (void)
490 static gint32 cleaning_up
;
492 Process
*prev
= NULL
;
494 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s", __func__
);
496 /* Ensure we're not in here in multiple threads at once, nor recursive. */
497 if (mono_atomic_cas_i32 (&cleaning_up
, 1, 0) != 0)
501 * This needs to be done outside the lock but atomically, hence the CAS above.
503 for (process
= processes
; process
; process
= process
->next
) {
504 if (process
->signalled
&& process
->handle
) {
505 /* This process has exited and we need to remove the artifical ref
507 mono_w32handle_close (process
->handle
);
508 process
->handle
= NULL
;
512 mono_coop_mutex_lock (&processes_mutex
);
514 for (process
= processes
; process
;) {
515 Process
*next
= process
->next
;
516 if (process
->handle_count
== 0 && process
->signalled
) {
520 if (process
== processes
)
521 processes
= process
->next
;
523 prev
->next
= process
->next
;
525 mono_coop_sem_destroy (&process
->exit_sem
);
533 mono_coop_mutex_unlock (&processes_mutex
);
535 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s done", __func__
);
537 mono_atomic_xchg_i32 (&cleaning_up
, 0);
541 process_close (gpointer data
)
543 MonoW32HandleProcess
*process_handle
;
545 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s", __func__
);
547 process_handle
= (MonoW32HandleProcess
*) data
;
548 g_free (process_handle
->pname
);
549 process_handle
->pname
= NULL
;
550 if (process_handle
->process
)
551 mono_atomic_dec_i32 (&process_handle
->process
->handle_count
);
552 processes_cleanup ();
555 static const MonoW32HandleOps process_ops
= {
556 process_close
, /* close_shared */
560 process_wait
, /* special_wait */
562 process_details
, /* details */
563 process_typename
, /* typename */
564 process_typesize
, /* typesize */
568 process_set_defaults (MonoW32HandleProcess
*process_handle
)
570 /* These seem to be the defaults on w2k */
571 process_handle
->min_working_set
= 204800;
572 process_handle
->max_working_set
= 1413120;
574 process_handle
->create_time
= mono_100ns_datetime ();
578 process_set_name (MonoW32HandleProcess
*process_handle
)
580 char *progname
, *utf8_progname
, *slash
;
582 progname
= g_get_prgname ();
583 utf8_progname
= mono_utf8_from_external (progname
);
585 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: using [%s] as prog name", __func__
, progname
);
588 slash
= strrchr (utf8_progname
, '/');
590 process_handle
->pname
= g_strdup (slash
+1);
592 process_handle
->pname
= g_strdup (utf8_progname
);
593 g_free (utf8_progname
);
598 mono_w32process_init (void)
600 MonoW32HandleProcess process_handle
;
602 mono_w32handle_register_ops (MONO_W32TYPE_PROCESS
, &process_ops
);
604 mono_w32handle_register_capabilities (MONO_W32TYPE_PROCESS
,
605 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SPECIAL_WAIT
));
607 current_pid
= getpid ();
609 memset (&process_handle
, 0, sizeof (process_handle
));
610 process_handle
.pid
= current_pid
;
611 process_set_defaults (&process_handle
);
612 process_set_name (&process_handle
);
614 current_process
= mono_w32handle_new (MONO_W32TYPE_PROCESS
, &process_handle
);
615 g_assert (current_process
!= INVALID_HANDLE_VALUE
);
617 mono_coop_mutex_init (&processes_mutex
);
621 mono_w32process_cleanup (void)
623 g_free (cli_launcher
);
627 len16 (const gunichar2
*str
)
638 utf16_concat (const gunichar2
*first
, ...)
646 va_start (args
, first
);
647 total
+= len16 (first
);
648 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg(args
, gunichar2
*))
652 ret
= g_new (gunichar2
, total
+ 1);
658 for (s
= first
; *s
!= 0; s
++)
660 va_start (args
, first
);
661 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg (args
, gunichar2
*)){
662 for (p
= s
; *p
!= 0; p
++)
671 mono_w32process_get_pid (gpointer handle
)
673 MonoW32Handle
*handle_data
;
676 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
677 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
678 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
682 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
683 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
684 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
685 mono_w32handle_unref (handle_data
);
689 ret
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
691 mono_w32handle_unref (handle_data
);
699 } GetProcessForeachData
;
702 get_process_foreach_callback (MonoW32Handle
*handle_data
, gpointer user_data
)
704 GetProcessForeachData
*foreach_data
;
705 MonoW32HandleProcess
*process_handle
;
708 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
)
711 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
713 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: looking at process %d", __func__
, process_handle
->pid
);
715 pid
= process_handle
->pid
;
719 foreach_data
= (GetProcessForeachData
*) user_data
;
721 /* It's possible to have more than one process handle with the
722 * same pid, but only the one running process can be
724 if (foreach_data
->pid
!= pid
)
726 if (mono_w32handle_issignalled (handle_data
))
729 foreach_data
->handle
= mono_w32handle_duplicate (handle_data
);
734 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid
, MonoError
*error
)
736 GetProcessForeachData foreach_data
;
739 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: looking for process %d", __func__
, pid
);
741 memset (&foreach_data
, 0, sizeof (foreach_data
));
742 foreach_data
.pid
= pid
;
743 mono_w32handle_foreach (get_process_foreach_callback
, &foreach_data
);
744 handle
= foreach_data
.handle
;
746 /* get_process_foreach_callback already added a ref */
750 if (process_is_alive (pid
)) {
751 /* non-child process */
752 MonoW32HandleProcess process_handle
;
754 memset (&process_handle
, 0, sizeof (process_handle
));
755 process_handle
.pid
= pid
;
756 process_handle
.pname
= mono_w32process_get_name (pid
);
758 handle
= mono_w32handle_new (MONO_W32TYPE_PROCESS
, &process_handle
);
759 if (handle
== INVALID_HANDLE_VALUE
) {
760 g_warning ("%s: error creating process handle", __func__
);
762 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
769 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find pid %d", __func__
, pid
);
771 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
776 match_procname_to_modulename (char *procname
, char *modulename
)
778 char* lastsep
= NULL
;
779 char* lastsep2
= NULL
;
782 gboolean result
= FALSE
;
784 if (procname
== NULL
|| modulename
== NULL
)
787 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: procname=\"%s\", modulename=\"%s\"", __func__
, procname
, modulename
);
788 pname
= mono_path_resolve_symlinks (procname
);
789 mname
= mono_path_resolve_symlinks (modulename
);
791 if (!strcmp (pname
, mname
))
795 lastsep
= strrchr (mname
, '/');
797 if (!strcmp (lastsep
+1, pname
))
800 lastsep2
= strrchr (pname
, '/');
803 if (!strcmp (lastsep
+1, lastsep2
+1))
806 if (!strcmp (mname
, lastsep2
+1))
816 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: result is %" G_GINT32_FORMAT
, __func__
, result
);
821 mono_w32process_try_get_modules (gpointer handle
, gpointer
*modules
, guint32 size
, guint32
*needed
)
823 MonoW32Handle
*handle_data
;
824 MonoW32HandleProcess
*process_handle
;
825 GSList
*mods
= NULL
, *mods_iter
;
826 MonoW32ProcessModule
*module
;
827 guint32 count
, avail
= size
/ sizeof(gpointer
);
832 /* Store modules in an array of pointers (main module as
833 * modules[0]), using the load address for each module as a
834 * token. (Use 'NULL' as an alternative for the main module
835 * so that the simple implementation can just return one item
836 * for now.) Get the info from /proc/<pid>/maps on linux,
837 * /proc/<pid>/map on FreeBSD, other systems will have to
838 * implement /dev/kmem reading or whatever other horrid
839 * technique is needed.
841 if (size
< sizeof(gpointer
))
844 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
845 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
846 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
850 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
851 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
852 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
853 mono_w32handle_unref (handle_data
);
857 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
859 pid
= process_handle
->pid
;
860 pname
= g_strdup (process_handle
->pname
);
864 *needed
= sizeof(gpointer
);
865 mono_w32handle_unref (handle_data
);
869 mods
= mono_w32process_get_modules (pid
);
872 *needed
= sizeof(gpointer
);
874 mono_w32handle_unref (handle_data
);
881 * Use the NULL shortcut, as the first line in
882 * /proc/<pid>/maps isn't the executable, and we need
883 * that first in the returned list. Check the module name
884 * to see if it ends with the proc name and substitute
885 * the first entry with it. FIXME if this turns out to
890 for (i
= 0; mods_iter
; i
++) {
892 module
= (MonoW32ProcessModule
*)mods_iter
->data
;
893 if (modules
[0] != NULL
)
894 modules
[i
] = module
->address_start
;
895 else if (match_procname_to_modulename (pname
, module
->filename
))
896 modules
[0] = module
->address_start
;
898 modules
[i
+ 1] = module
->address_start
;
900 mono_w32process_module_free ((MonoW32ProcessModule
*)mods_iter
->data
);
901 mods_iter
= g_slist_next (mods_iter
);
905 /* count + 1 to leave slot 0 for the main module */
906 *needed
= sizeof(gpointer
) * (count
+ 1);
910 mono_w32handle_unref (handle_data
);
915 mono_w32process_module_get_filename (gpointer handle
, gpointer module
, gunichar2
*basename
, guint32 size
)
920 gunichar2
*proc_path
;
922 size
*= sizeof (gunichar2
); /* adjust for unicode characters */
924 if (basename
== NULL
|| size
== 0)
927 pid
= mono_w32process_get_pid (handle
);
929 path
= mono_w32process_get_path (pid
);
933 proc_path
= mono_unicode_from_external (path
, &bytes
);
936 if (proc_path
== NULL
)
941 /* Add the terminator */
945 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Size %" G_GUINT32_FORMAT
" smaller than needed (%zd); truncating", __func__
, size
, bytes
);
946 memcpy (basename
, proc_path
, size
);
948 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Size %" G_GUINT32_FORMAT
" larger than needed (%zd)", __func__
, size
, bytes
);
949 memcpy (basename
, proc_path
, bytes
);
958 mono_w32process_module_get_name (gpointer handle
, gpointer module
, gunichar2
*basename
, guint32 size
)
960 MonoW32Handle
*handle_data
;
961 MonoW32HandleProcess
*process_handle
;
964 char *procname_ext
= NULL
;
967 GSList
*mods
= NULL
, *mods_iter
;
968 MonoW32ProcessModule
*found_module
;
971 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Getting module base name, process handle %p module %p basename %p size %" G_GUINT32_FORMAT
,
972 __func__
, handle
, module
, basename
, size
);
974 size
= size
* sizeof (gunichar2
); /* adjust for unicode characters */
976 if (basename
== NULL
|| size
== 0)
979 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
980 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
981 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
985 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
986 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
987 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
988 mono_w32handle_unref (handle_data
);
992 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
994 pid
= process_handle
->pid
;
995 pname
= g_strdup (process_handle
->pname
);
997 mods
= mono_w32process_get_modules (pid
);
998 if (!mods
&& module
!= NULL
) {
999 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't get modules %p", __func__
, handle
);
1001 mono_w32handle_unref (handle_data
);
1005 /* If module != NULL compare the address.
1006 * If module == NULL we are looking for the main module.
1007 * The best we can do for now check it the module name end with the process name.
1009 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1010 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1011 if (procname_ext
== NULL
&&
1012 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1013 (module
!= NULL
&& found_module
->address_start
== module
))) {
1014 procname_ext
= g_path_get_basename (found_module
->filename
);
1017 mono_w32process_module_free (found_module
);
1020 if (procname_ext
== NULL
) {
1021 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find procname_ext from procmods %p", __func__
, handle
);
1022 /* If it's *still* null, we might have hit the
1023 * case where reading /proc/$pid/maps gives an
1024 * empty file for this user.
1026 procname_ext
= mono_w32process_get_name (pid
);
1028 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find procname_ext from proc_get_name %p pid %d", __func__
, handle
, pid
);
1031 g_slist_free (mods
);
1035 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Process name is [%s]", __func__
,
1038 procname
= mono_unicode_from_external (procname_ext
, &bytes
);
1039 if (procname
== NULL
) {
1040 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't get procname %p", __func__
, handle
);
1042 g_free (procname_ext
);
1043 mono_w32handle_unref (handle_data
);
1049 /* Add the terminator */
1053 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Size %" G_GUINT32_FORMAT
" smaller than needed (%zd); truncating", __func__
, size
, bytes
);
1055 memcpy (basename
, procname
, size
);
1057 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Size %" G_GUINT32_FORMAT
" larger than needed (%zd)",
1058 __func__
, size
, bytes
);
1060 memcpy (basename
, procname
, bytes
);
1064 g_free (procname_ext
);
1066 mono_w32handle_unref (handle_data
);
1070 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find procname_ext %p", __func__
, handle
);
1071 mono_w32handle_unref (handle_data
);
1076 mono_w32process_module_get_information (gpointer handle
, gpointer module
, MODULEINFO
*modinfo
, guint32 size
)
1078 MonoW32Handle
*handle_data
;
1079 MonoW32HandleProcess
*process_handle
;
1081 GSList
*mods
= NULL
, *mods_iter
;
1082 MonoW32ProcessModule
*found_module
;
1083 gboolean ret
= FALSE
;
1086 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Getting module info, process handle %p module %p",
1087 __func__
, handle
, module
);
1089 if (modinfo
== NULL
|| size
< sizeof (MODULEINFO
))
1092 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
1093 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
1094 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
1098 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
1099 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
1100 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
1101 mono_w32handle_unref (handle_data
);
1105 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
1107 pid
= process_handle
->pid
;
1108 pname
= g_strdup (process_handle
->pname
);
1110 mods
= mono_w32process_get_modules (pid
);
1113 mono_w32handle_unref (handle_data
);
1117 /* If module != NULL compare the address.
1118 * If module == NULL we are looking for the main module.
1119 * The best we can do for now check it the module name end with the process name.
1121 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1122 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1124 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1125 (module
!= NULL
&& found_module
->address_start
== module
))) {
1126 modinfo
->lpBaseOfDll
= found_module
->address_start
;
1127 modinfo
->SizeOfImage
= (gsize
)(found_module
->address_end
) - (gsize
)(found_module
->address_start
);
1128 modinfo
->EntryPoint
= found_module
->address_offset
;
1132 mono_w32process_module_free (found_module
);
1135 g_slist_free (mods
);
1137 mono_w32handle_unref (handle_data
);
1142 switch_dir_separators (char *path
)
1144 size_t i
, pathLength
= strlen(path
);
1146 /* Turn all the slashes round the right way, except for \' */
1147 /* There are probably other characters that need to be excluded as well. */
1148 for (i
= 0; i
< pathLength
; i
++) {
1149 if (path
[i
] == '\\' && i
< pathLength
- 1 && path
[i
+1] != '\'' )
1156 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler
, (int _dummy
, siginfo_t
*info
, void *context
))
1159 * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
1160 * mono_w32process_signal_finished ().
1162 int old_errno
= errno
;
1164 mono_gc_finalize_notify ();
1166 mono_set_errno (old_errno
);
1170 process_add_sigchld_handler (void)
1172 struct sigaction sa
;
1174 sa
.sa_sigaction
= mono_sigchld_signal_handler
;
1175 sigemptyset (&sa
.sa_mask
);
1176 sa
.sa_flags
= SA_NOCLDSTOP
| SA_SIGINFO
| SA_RESTART
;
1177 g_assert (sigaction (SIGCHLD
, &sa
, NULL
) != -1);
1178 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "Added SIGCHLD handler");
1184 * mono_w32process_signal_finished:
1186 * Signal the exit semaphore for processes which have finished.
1189 mono_w32process_signal_finished (void)
1191 mono_coop_mutex_lock (&processes_mutex
);
1193 for (Process
* process
= processes
; process
; process
= process
->next
) {
1198 pid
= waitpid (process
->pid
, &status
, WNOHANG
);
1199 } while (pid
== -1 && errno
== EINTR
);
1201 // possible values of 'pid':
1202 // process->pid : the status changed for this child
1203 // 0 : status unchanged for this PID
1204 // ECHILD : process has been reaped elsewhere (or never existed)
1205 // EINVAL : invalid PID or other argument
1207 // Therefore, we ignore status unchanged (nothing to do) and error
1208 // events (process is cleaned up later).
1211 if (process
->signalled
)
1214 process
->signalled
= TRUE
;
1215 process
->status
= status
;
1216 mono_coop_sem_post (&process
->exit_sem
);
1219 mono_coop_mutex_unlock (&processes_mutex
);
1223 is_readable_or_executable (const char *prog
)
1226 int a
= access (prog
, R_OK
);
1227 int b
= access (prog
, X_OK
);
1228 if (a
!= 0 && b
!= 0)
1230 if (stat (prog
, &buf
))
1232 if (S_ISREG (buf
.st_mode
))
1238 is_executable (const char *prog
)
1241 if (access (prog
, X_OK
) != 0)
1243 if (stat (prog
, &buf
))
1245 if (S_ISREG (buf
.st_mode
))
1251 is_managed_binary (const char *filename
)
1253 int original_errno
= errno
;
1254 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1255 int file
= open (filename
, O_RDONLY
| O_LARGEFILE
);
1257 int file
= open (filename
, O_RDONLY
);
1260 unsigned char buffer
[8];
1261 off_t file_size
, optional_header_offset
;
1262 off_t pe_header_offset
, clr_header_offset
;
1263 gboolean managed
= FALSE
;
1265 guint32 first_word
, second_word
, magic_number
;
1267 /* If we are unable to open the file, then we definitely
1268 * can't say that it is managed. The child mono process
1269 * probably wouldn't be able to open it anyway.
1272 mono_set_errno (original_errno
);
1276 /* Retrieve the length of the file for future sanity checks. */
1277 file_size
= lseek (file
, 0, SEEK_END
);
1278 lseek (file
, 0, SEEK_SET
);
1280 /* We know we need to read a header field at offset 60. */
1284 num_read
= read (file
, buffer
, 2);
1286 if ((num_read
!= 2) || (buffer
[0] != 'M') || (buffer
[1] != 'Z'))
1289 new_offset
= lseek (file
, 60, SEEK_SET
);
1291 if (new_offset
!= 60)
1294 num_read
= read (file
, buffer
, 4);
1298 pe_header_offset
= buffer
[0]
1301 | (buffer
[3] << 24);
1303 if (pe_header_offset
+ 24 > file_size
)
1306 new_offset
= lseek (file
, pe_header_offset
, SEEK_SET
);
1308 if (new_offset
!= pe_header_offset
)
1311 num_read
= read (file
, buffer
, 4);
1313 if ((num_read
!= 4) || (buffer
[0] != 'P') || (buffer
[1] != 'E') || (buffer
[2] != 0) || (buffer
[3] != 0))
1317 * Verify that the header we want in the optional header data
1318 * is present in this binary.
1320 new_offset
= lseek (file
, pe_header_offset
+ 20, SEEK_SET
);
1322 if (new_offset
!= pe_header_offset
+ 20)
1325 num_read
= read (file
, buffer
, 2);
1327 if ((num_read
!= 2) || ((buffer
[0] | (buffer
[1] << 8)) < 216))
1330 optional_header_offset
= pe_header_offset
+ 24;
1332 /* Read the PE magic number */
1333 new_offset
= lseek (file
, optional_header_offset
, SEEK_SET
);
1335 if (new_offset
!= optional_header_offset
)
1338 num_read
= read (file
, buffer
, 2);
1343 magic_number
= (buffer
[0] | (buffer
[1] << 8));
1345 if (magic_number
== 0x10B) // PE32
1346 clr_header_offset
= 208;
1347 else if (magic_number
== 0x20B) // PE32+
1348 clr_header_offset
= 224;
1352 /* Read the CLR header address and size fields. These will be
1353 * zero if the binary is not managed.
1355 new_offset
= lseek (file
, optional_header_offset
+ clr_header_offset
, SEEK_SET
);
1357 if (new_offset
!= optional_header_offset
+ clr_header_offset
)
1360 num_read
= read (file
, buffer
, 8);
1362 /* We are not concerned with endianness, only with
1363 * whether it is zero or not.
1365 first_word
= *(guint32
*)&buffer
[0];
1366 second_word
= *(guint32
*)&buffer
[4];
1368 if ((num_read
!= 8) || (first_word
== 0) || (second_word
== 0))
1375 mono_set_errno (original_errno
);
1380 * Gets the biggest numbered file descriptor for the current process; failing
1381 * that, the system's file descriptor limit. This is called by the fork child
1384 static inline guint32
1388 struct procentry64 pe
;
1391 if (getprocs64 (&pe
, sizeof (pe
), NULL
, 0, &p
, 1) != -1) {
1392 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1393 "%s: maximum returned fd in child is %u",
1394 __func__
, pe
.pi_maxofile
);
1395 return pe
.pi_maxofile
; // biggest + 1
1398 // fallback to user/system limit if unsupported/error
1399 return eg_getdtablesize ();
1403 * Closes all of the process' opened file descriptors, applying a strategy
1404 * appropriate for the target system. This is called by the fork child in
1410 // TODO: Other platforms.
1411 // * On macOS, use proc_pidinfo + PROC_PIDLISTFDS? See:
1412 // http://blog.palominolabs.com/2012/06/19/getting-the-files-being-used-by-a-process-on-mac-os-x/
1413 // (I have no idea how this plays out on i/watch/tvOS.)
1414 // * On the other BSDs, there's likely a sysctl for this.
1415 // * On Solaris, there exists posix_spawn_file_actions_addclosefrom_np,
1416 // but that assumes we're using posix_spawn; we aren't, as we do some
1417 // complex stuff between fork and exec. There's likely a way to get
1418 // the FD list/count though (maybe look at addclosefrom source in
1419 // illumos?) or just walk /proc/pid/fd like Linux?
1420 #if defined (__linux__)
1421 /* Walk the file descriptors in /proc/self/fd/. Linux has no other API,
1422 * as far as I'm aware. Opening a directory won't create an FD. */
1426 d
= opendir ("/proc/self/fd/");
1428 while ((dp
= readdir (d
)) != NULL
) {
1429 if (dp
->d_name
[0] == '.')
1431 fd
= atoi (dp
->d_name
);
1438 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1439 "%s: opening fd dir failed, using fallback",
1442 #elif defined (__FreeBSD__)
1443 /* FreeBSD lets us get a list of FDs. There's a MIB to access them
1444 * directly, but it uses a lot of nasty variable length structures. The
1445 * system library libutil provides a nicer way to get a fixed length
1446 * version instead. */
1447 struct kinfo_file
*kif
;
1449 /* this is malloced but we won't need to free once we exec/exit */
1450 kif
= kinfo_getfile (getpid (), &count
);
1452 for (i
= 0; i
< count
; i
++) {
1453 /* negative FDs look to be used by the OS */
1454 if (kif
[i
].kf_fd
> 2) /* no neg + no stdio */
1455 close (kif
[i
].kf_fd
);
1459 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1460 "%s: kinfo_getfile failed, using fallback",
1463 #elif defined (_AIX)
1464 struct procentry64 pe
;
1465 /* this array struct is 1 MB, we're NOT putting it on the stack.
1466 * likewise no need to free; getprocs will fail if we use the smalller
1467 * versions if we have a lot of FDs (is it worth it?)
1469 struct fdsinfo_100K
*fds
;
1472 fds
= (struct fdsinfo_100K
*) g_malloc0 (sizeof (struct fdsinfo_100K
));
1474 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1475 "%s: fdsinfo alloc failed, using fallback",
1480 if (getprocs64 (&pe
, sizeof (pe
), fds
, sizeof (struct fdsinfo_100K
), &p
, 1) != -1) {
1481 for (int i
= 3; i
< pe
.pi_maxofile
; i
++) {
1482 if (fds
->pi_ufd
[i
].fp
!= 0)
1483 close (fds
->pi_ufd
[i
].fp
);
1487 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1488 "%s: getprocs64 failed, using fallback",
1493 /* Fallback: Close FDs blindly, according to an FD limit */
1494 for (guint32 i
= max_fd_count () - 1; i
> 2; i
--)
1499 process_create (const gunichar2
*appname
, const gunichar2
*cmdline
,
1500 const gunichar2
*cwd
, StartupHandles
*startup_handles
, MonoW32ProcessInfo
*process_info
)
1502 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1503 char *cmd
= NULL
, *prog
= NULL
, *full_prog
= NULL
, *args
= NULL
, *args_after_prog
= NULL
;
1504 char *dir
= NULL
, **env_strings
= NULL
, **argv
= NULL
;
1506 gboolean ret
= FALSE
;
1507 gpointer handle
= NULL
;
1508 GError
*gerr
= NULL
;
1509 int in_fd
, out_fd
, err_fd
;
1511 int startup_pipe
[2] = {-1, -1};
1517 mono_lazy_initialize (&process_sig_chld_once
, process_add_sigchld_handler
);
1520 /* appname and cmdline specify the executable and its args:
1522 * If appname is not NULL, it is the name of the executable.
1523 * Otherwise the executable is the first token in cmdline.
1525 * Executable searching:
1527 * If appname is not NULL, it can specify the full path and
1528 * file name, or else a partial name and the current directory
1529 * will be used. There is no additional searching.
1531 * If appname is NULL, the first whitespace-delimited token in
1532 * cmdline is used. If the name does not contain a full
1533 * directory path, the search sequence is:
1535 * 1) The directory containing the current process
1536 * 2) The current working directory
1537 * 3) The windows system directory (Ignored)
1538 * 4) The windows directory (Ignored)
1541 * Just to make things more interesting, tokens can contain
1542 * white space if they are surrounded by quotation marks. I'm
1543 * beginning to understand just why windows apps are generally
1544 * so crap, with an API like this :-(
1546 if (appname
!= NULL
) {
1547 cmd
= mono_unicode_to_external_checked (appname
, error
);
1549 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unicode conversion returned NULL; %s",
1550 __func__
, mono_error_get_message (error
));
1552 mono_error_cleanup (error
);
1553 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1557 switch_dir_separators(cmd
);
1560 if (cmdline
!= NULL
) {
1561 args
= mono_unicode_to_external_checked (cmdline
, error
);
1563 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
1565 mono_error_cleanup (error
);
1566 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1572 dir
= mono_unicode_to_external_checked (cwd
, error
);
1574 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
1576 mono_error_cleanup (error
);
1577 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1581 /* Turn all the slashes round the right way */
1582 switch_dir_separators(dir
);
1586 /* We can't put off locating the executable any longer :-( */
1589 if (g_ascii_isalpha (cmd
[0]) && (cmd
[1] == ':')) {
1590 /* Strip off the drive letter. I can't
1591 * believe that CP/M holdover is still
1594 g_memmove (cmd
, cmd
+2, strlen (cmd
)-2);
1595 cmd
[strlen (cmd
)-2] = '\0';
1598 unquoted
= g_shell_unquote (cmd
, NULL
);
1599 if (unquoted
[0] == '/') {
1600 /* Assume full path given */
1601 prog
= g_strdup (unquoted
);
1603 /* Executable existing ? */
1604 if (!is_readable_or_executable (prog
)) {
1605 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s",
1608 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1612 /* Search for file named by cmd in the current
1615 char *curdir
= g_get_current_dir ();
1617 prog
= g_strdup_printf ("%s/%s", curdir
, unquoted
);
1620 /* And make sure it's readable */
1621 if (!is_readable_or_executable (prog
)) {
1622 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s",
1625 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1631 args_after_prog
= args
;
1636 /* Dig out the first token from args, taking quotation
1637 * marks into account
1640 /* First, strip off all leading whitespace */
1641 args
= g_strchug (args
);
1643 /* args_after_prog points to the contents of args
1644 * after token has been set (otherwise argv[0] is
1647 args_after_prog
= args
;
1649 /* Assume the opening quote will always be the first
1652 if (args
[0] == '\"' || args
[0] == '\'') {
1654 for (i
= 1; args
[i
] != '\0' && args
[i
] != quote
; i
++);
1655 if (args
[i
+ 1] == '\0' || g_ascii_isspace (args
[i
+1])) {
1656 /* We found the first token */
1657 token
= g_strndup (args
+1, i
-1);
1658 args_after_prog
= g_strchug (args
+ i
+ 1);
1660 /* Quotation mark appeared in the
1661 * middle of the token. Just give the
1662 * whole first token, quotes and all,
1668 if (token
== NULL
) {
1669 /* No quote mark, or malformed */
1670 for (i
= 0; args
[i
] != '\0'; i
++) {
1671 if (g_ascii_isspace (args
[i
])) {
1672 token
= g_strndup (args
, i
);
1673 args_after_prog
= args
+ i
+ 1;
1679 if (token
== NULL
&& args
[0] != '\0') {
1680 /* Must be just one token in the string */
1681 token
= g_strdup (args
);
1682 args_after_prog
= NULL
;
1685 if (token
== NULL
) {
1687 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find what to exec", __func__
);
1689 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1693 /* Turn all the slashes round the right way. Only for
1696 switch_dir_separators(token
);
1698 if (g_ascii_isalpha (token
[0]) && (token
[1] == ':')) {
1699 /* Strip off the drive letter. I can't
1700 * believe that CP/M holdover is still
1703 g_memmove (token
, token
+2, strlen (token
)-2);
1704 token
[strlen (token
)-2] = '\0';
1707 if (token
[0] == '/') {
1708 /* Assume full path given */
1709 prog
= g_strdup (token
);
1711 /* Executable existing ? */
1712 if (!is_readable_or_executable (prog
)) {
1713 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s",
1716 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1720 char *curdir
= g_get_current_dir ();
1722 /* FIXME: Need to record the directory
1723 * containing the current process, and check
1724 * that for the new executable as the first
1728 prog
= g_strdup_printf ("%s/%s", curdir
, token
);
1731 /* I assume X_OK is the criterion to use,
1734 * X_OK is too strict *if* the target is a CLR binary
1736 if (!is_readable_or_executable (prog
)) {
1738 prog
= g_find_program_in_path (token
);
1740 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s", __func__
, token
);
1743 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1752 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Exec prog [%s] args [%s]",
1753 __func__
, prog
, args_after_prog
);
1755 /* Check for CLR binaries; if found, we will try to invoke
1756 * them using the same mono binary that started us.
1758 if (is_managed_binary (prog
)) {
1759 gunichar2
*newapp
, *newcmd
;
1760 gsize bytes_ignored
;
1762 newapp
= mono_unicode_from_external (cli_launcher
? cli_launcher
: "mono", &bytes_ignored
);
1765 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, appname
, utf16_space
, cmdline
, (const gunichar2
*)NULL
);
1767 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, cmdline
, (const gunichar2
*)NULL
);
1772 ret
= process_create (NULL
, newcmd
, cwd
, startup_handles
, process_info
);
1780 if (!is_executable (prog
)) {
1781 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Executable permisson not set on %s", __func__
, prog
);
1782 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1787 if (args_after_prog
!= NULL
&& *args_after_prog
) {
1790 qprog
= g_shell_quote (prog
);
1791 full_prog
= g_strconcat (qprog
, " ", args_after_prog
, NULL
);
1794 full_prog
= g_shell_quote (prog
);
1797 ret
= g_shell_parse_argv (full_prog
, NULL
, &argv
, &gerr
);
1799 g_message ("process_create: %s\n", gerr
->message
);
1800 g_error_free (gerr
);
1805 if (startup_handles
) {
1806 in_fd
= GPOINTER_TO_UINT (startup_handles
->input
);
1807 out_fd
= GPOINTER_TO_UINT (startup_handles
->output
);
1808 err_fd
= GPOINTER_TO_UINT (startup_handles
->error
);
1810 in_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_input ());
1811 out_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_output ());
1812 err_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_error ());
1816 * process->env_variables is a an array of MonoString*
1818 * If new_environ is not NULL it specifies the entire set of
1819 * environment variables in the new process. Otherwise the
1820 * new process inherits the same environment.
1822 if (process_info
->env_variables
) {
1823 MonoArrayHandle array
= MONO_HANDLE_NEW (MonoArray
, process_info
->env_variables
);
1824 MonoStringHandle var
= MONO_HANDLE_NEW (MonoString
, NULL
);
1825 gsize
const array_length
= mono_array_handle_length (array
);
1827 /* +2: one for the process handle value, and the last one is NULL */
1828 // What "process handle value"?
1829 env_strings
= g_new0 (gchar
*, array_length
+ 2);
1831 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1832 for (gsize i
= 0; i
< array_length
; ++i
) {
1833 MONO_HANDLE_ARRAY_GETREF (var
, array
, i
);
1834 gchandle_t gchandle
= 0;
1835 env_strings
[i
] = mono_unicode_to_external (mono_string_handle_pin_chars (var
, &gchandle
));
1836 mono_gchandle_free_internal (gchandle
);
1839 gsize env_count
= 0;
1840 for (i
= 0; environ
[i
] != NULL
; i
++)
1843 /* +2: one for the process handle value, and the last one is NULL */
1844 // What "process handle value"?
1845 env_strings
= g_new0 (gchar
*, env_count
+ 2);
1847 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1848 for (i
= 0; i
< env_count
; i
++)
1849 env_strings
[i
] = g_strdup (environ
[i
]);
1852 /* Create a pipe to make sure the child doesn't exit before
1853 * we can add the process to the linked list of processes */
1854 if (pipe (startup_pipe
) == -1) {
1855 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1856 * This is just for a very hard to hit race condition in the first place */
1857 startup_pipe
[0] = startup_pipe
[1] = -1;
1858 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: new process startup not synchronized. We may not notice if the newly created process exits immediately.", __func__
);
1861 switch (pid
= fork ()) {
1862 case -1: /* Error */ {
1863 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1867 case 0: /* Child */ {
1868 if (startup_pipe
[0] != -1) {
1869 /* Wait until the parent has updated it's internal data */
1870 ssize_t _i G_GNUC_UNUSED
= read (startup_pipe
[0], &dummy
, 1);
1871 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: child: parent has completed its setup", __func__
);
1872 close (startup_pipe
[0]);
1873 close (startup_pipe
[1]);
1876 /* should we detach from the process group? */
1878 /* Connect stdin, stdout and stderr */
1883 /* Close this child's file handles. */
1886 #ifdef DEBUG_ENABLED
1887 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: exec()ing [%s] in dir [%s]", __func__
, cmd
,
1888 dir
== NULL
?".":dir
);
1889 for (i
= 0; argv
[i
] != NULL
; i
++)
1890 g_message ("arg %" G_GUINT32_FORMAT
": [%s]", i
, argv
[i
]);
1892 for (i
= 0; env_strings
[i
] != NULL
; i
++)
1893 g_message ("env %" G_GUINT32_FORMAT
": [%s]", i
, env_strings
[i
]);
1897 if (dir
!= NULL
&& chdir (dir
) == -1) {
1903 execve (argv
[0], argv
, env_strings
);
1910 default: /* Parent */ {
1911 MonoW32Handle
*handle_data
;
1912 MonoW32HandleProcess process_handle
;
1914 memset (&process_handle
, 0, sizeof (process_handle
));
1915 process_handle
.pid
= pid
;
1916 process_handle
.child
= TRUE
;
1917 process_handle
.pname
= g_strdup (prog
);
1918 process_set_defaults (&process_handle
);
1920 /* Add our process into the linked list of processes */
1921 process
= (Process
*) g_malloc0 (sizeof (Process
));
1923 process
->handle_count
= 1;
1924 mono_coop_sem_init (&process
->exit_sem
, 0);
1926 process_handle
.process
= process
;
1928 handle
= mono_w32handle_new (MONO_W32TYPE_PROCESS
, &process_handle
);
1929 if (handle
== INVALID_HANDLE_VALUE
) {
1930 g_warning ("%s: error creating process handle", __func__
);
1932 mono_coop_sem_destroy (&process
->exit_sem
);
1935 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1940 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
1941 g_error ("%s: unknown handle %p", __func__
, handle
);
1943 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
)
1944 g_error ("%s: unknown process handle %p", __func__
, handle
);
1946 /* Keep the process handle artificially alive until the process
1947 * exits so that the information in the handle isn't lost. */
1948 process
->handle
= mono_w32handle_duplicate (handle_data
);
1950 mono_coop_mutex_lock (&processes_mutex
);
1951 process
->next
= processes
;
1952 mono_memory_barrier ();
1953 processes
= process
;
1954 mono_coop_mutex_unlock (&processes_mutex
);
1956 if (process_info
!= NULL
) {
1957 process_info
->process_handle
= handle
;
1958 process_info
->pid
= pid
;
1961 mono_w32handle_unref (handle_data
);
1967 if (startup_pipe
[1] != -1) {
1968 /* Write 1 byte, doesn't matter what */
1969 ssize_t _i G_GNUC_UNUSED
= write (startup_pipe
[1], startup_pipe
, 1);
1970 close (startup_pipe
[0]);
1971 close (startup_pipe
[1]);
1980 g_strfreev (env_strings
);
1983 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: returning handle %p for pid %d", __func__
, handle
, pid
);
1985 /* Check if something needs to be cleaned up. */
1986 processes_cleanup ();
1990 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
1992 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1996 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfoHandle proc_start_info
, MonoW32ProcessInfo
*process_info
, MonoError
*error
)
1998 MonoCreateProcessCoop coop
;
1999 mono_createprocess_coop_init (&coop
, proc_start_info
, process_info
);
2002 gboolean handler_needswait
= FALSE
;
2004 if (!coop
.filename
) {
2005 /* w2k returns TRUE for this, for some reason. */
2010 const gunichar2
*lpFile
;
2011 lpFile
= coop
.filename
;
2012 const gunichar2
*lpParameters
;
2013 lpParameters
= coop
.arguments
;
2014 const gunichar2
*lpDirectory
;
2015 lpDirectory
= coop
.length
.working_directory
? coop
.working_directory
: NULL
;
2017 /* Put both executable and parameters into the second argument
2018 * to process_create (), so it searches $PATH. The conversion
2019 * into and back out of utf8 is because there is no
2020 * g_strdup_printf () equivalent for gunichar2 :-(
2023 args
= utf16_concat (utf16_quote
, lpFile
, utf16_quote
, lpParameters
? utf16_space
: NULL
, lpParameters
, (const gunichar2
*)NULL
);
2025 mono_w32error_set_last (ERROR_INVALID_DATA
);
2029 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2032 if (!ret
&& mono_w32error_get_last () == ERROR_OUTOFMEMORY
)
2037 #if defined(TARGET_IOS) || defined(TARGET_ANDROID)
2038 // don't try the "open" handlers on iOS/Android, they don't exist there anyway
2042 static char *handler
;
2043 static gunichar2
*handler_utf16
;
2045 if (handler_utf16
== (gunichar2
*)-1) {
2051 handler
= g_strdup ("/usr/bin/open");
2052 handler_needswait
= TRUE
;
2055 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
2056 * if that fails, try to use gnome-open, then kfmclient
2059 handler
= g_find_program_in_path ("xdg-open");
2060 if (handler
!= NULL
)
2061 handler_needswait
= TRUE
;
2063 handler
= g_find_program_in_path ("gnome-open");
2064 if (handler
== NULL
){
2065 handler
= g_find_program_in_path ("kfmclient");
2066 if (handler
== NULL
){
2067 handler_utf16
= (gunichar2
*) -1;
2070 /* kfmclient needs exec argument */
2071 char *old
= handler
;
2072 handler
= g_strconcat (old
, " exec",
2083 handler_utf16
= g_utf8_to_utf16 (handler
, -1, NULL
, NULL
, NULL
);
2086 /* Put quotes around the filename, in case it's a url
2087 * that contains #'s (process_create() calls
2088 * g_shell_parse_argv(), which deliberately throws
2089 * away anything after an unquoted #). Fixes bug
2092 args
= utf16_concat (handler_utf16
, utf16_space
, utf16_quote
, lpFile
, utf16_quote
,
2093 lpParameters
? utf16_space
: NULL
, lpParameters
, (const gunichar2
*)NULL
);
2095 mono_w32error_set_last (ERROR_INVALID_DATA
);
2099 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2102 if (mono_w32error_get_last () != ERROR_OUTOFMEMORY
)
2103 mono_w32error_set_last (ERROR_INVALID_DATA
);
2108 if (handler_needswait
) {
2110 MonoW32HandleWaitRet waitret
;
2111 waitret
= process_wait ((MonoW32Handle
*)process_info
->process_handle
, MONO_INFINITE_WAIT
, NULL
);
2113 mono_get_exit_code_process (process_info
->process_handle
, &exitcode
);
2117 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
2118 mono_w32handle_close (process_info
->process_handle
);
2119 process_info
->process_handle
= INVALID_HANDLE_VALUE
;
2124 process_info
->pid
= -mono_w32error_get_last ();
2126 #if !defined(MONO_CROSS_COMPILE)
2127 process_info
->pid
= mono_w32process_get_pid (process_info
->process_handle
);
2129 process_info
->pid
= 0;
2133 mono_createprocess_coop_cleanup (&coop
);
2138 /* Only used when UseShellExecute is false */
2140 process_get_complete_path (const gunichar2
*appname
, gchar
**completed
)
2143 gboolean result
= FALSE
;
2145 char *utf8app
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
2147 if (g_path_is_absolute (utf8app
)) {
2148 *completed
= g_shell_quote (utf8app
);
2153 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
2154 *completed
= g_shell_quote (utf8app
);
2159 found
= g_find_program_in_path (utf8app
);
2160 if (found
== NULL
) {
2166 *completed
= g_shell_quote (found
);
2175 process_get_shell_arguments (MonoCreateProcessCoop
*coop
, gunichar2
**shell_path
)
2177 gchar
*complete_path
= NULL
;
2181 if (process_get_complete_path (coop
->filename
, &complete_path
)) {
2182 *shell_path
= g_utf8_to_utf16 (complete_path
, -1, NULL
, NULL
, NULL
);
2183 g_free (complete_path
);
2186 return *shell_path
!= NULL
;
2190 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfoHandle proc_start_info
,
2191 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
, MonoError
*error
)
2193 MonoCreateProcessCoop coop
;
2194 mono_createprocess_coop_init (&coop
, proc_start_info
, process_info
);
2197 StartupHandles startup_handles
;
2198 gunichar2
*shell_path
= NULL
;
2200 memset (&startup_handles
, 0, sizeof (startup_handles
));
2201 startup_handles
.input
= stdin_handle
;
2202 startup_handles
.output
= stdout_handle
;
2203 startup_handles
.error
= stderr_handle
;
2205 if (!process_get_shell_arguments (&coop
, &shell_path
)) {
2206 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
2212 args
= coop
.length
.arguments
? coop
.arguments
: NULL
;
2214 /* The default dir name is "". Turn that into NULL to mean "current directory" */
2216 dir
= coop
.length
.working_directory
? coop
.working_directory
: NULL
;
2218 ret
= process_create (shell_path
, args
, dir
, &startup_handles
, process_info
);
2221 process_info
->pid
= -mono_w32error_get_last ();
2224 g_free (shell_path
);
2225 mono_createprocess_coop_cleanup (&coop
);
2229 /* Returns an array of pids */
2231 ves_icall_System_Diagnostics_Process_GetProcesses_internal (MonoError
*error
)
2235 gpointer
*pidarray
= 0;
2236 MonoArrayHandle procs
= NULL_HANDLE_ARRAY
;
2238 // FIXME mono_process_list should probably return array of int
2239 // as all of the users of the elements truncate to that.
2242 pidarray
= mono_process_list (&count
);
2245 mono_error_set_not_supported (error
, "This system does not support EnumProcesses");
2248 procs
= mono_array_new_handle (mono_domain_get (), mono_get_int32_class (), count
, error
);
2249 if (!is_ok (error
)) {
2250 procs
= NULL_HANDLE_ARRAY
;
2254 MONO_ENTER_NO_SAFEPOINTS
;
2256 raw
= mono_array_addr_internal (MONO_HANDLE_RAW (procs
), guint32
, 0);
2257 if (sizeof (guint32
) == sizeof (gpointer
)) {
2258 memcpy (raw
, pidarray
, count
* sizeof (gint32
));
2260 for (int i
= 0; i
< count
; ++i
)
2261 raw
[i
] = GPOINTER_TO_UINT (pidarray
[i
]);
2264 MONO_EXIT_NO_SAFEPOINTS
;
2272 mono_w32process_set_cli_launcher (gchar
*path
)
2274 g_free (cli_launcher
);
2275 cli_launcher
= g_strdup (path
);
2279 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (MonoError
*error
)
2281 return current_process
;
2285 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
, MonoError
*error
)
2287 return mono_get_exit_code_process (handle
, exitcode
);
2291 mono_get_exit_code_process (gpointer handle
, gint32
*exitcode
)
2293 MonoW32Handle
*handle_data
;
2294 MonoW32HandleProcess
*process_handle
;
2299 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2300 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2301 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2305 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2306 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2307 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2308 mono_w32handle_unref (handle_data
);
2312 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2314 if (process_handle
->pid
== current_pid
) {
2315 *exitcode
= STILL_ACTIVE
;
2316 mono_w32handle_unref (handle_data
);
2320 /* A process handle is only signalled if the process has exited
2321 * and has been waited for. Make sure any process exit has been
2322 * noticed before checking if the process is signalled.
2323 * Fixes bug 325463. */
2324 mono_w32handle_wait_one (handle
, 0, TRUE
);
2326 *exitcode
= mono_w32handle_issignalled (handle_data
) ? process_handle
->exitstatus
: STILL_ACTIVE
;
2328 mono_w32handle_unref (handle_data
);
2334 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
, MonoError
*error
)
2336 return mono_w32handle_close (handle
);
2340 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
, MonoError
*error
)
2343 MonoW32Handle
*handle_data
;
2347 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2348 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2349 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2353 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2354 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2355 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2356 mono_w32handle_unref (handle_data
);
2360 pid
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
2362 ret
= kill (pid
, exitcode
== -1 ? SIGKILL
: SIGTERM
);
2364 mono_w32handle_unref (handle_data
);
2369 case EINVAL
: mono_w32error_set_last (ERROR_INVALID_PARAMETER
); break;
2370 case EPERM
: mono_w32error_set_last (ERROR_ACCESS_DENIED
); break;
2371 case ESRCH
: mono_w32error_set_last (ERROR_PROC_NOT_FOUND
); break;
2372 default: mono_w32error_set_last (ERROR_GEN_FAILURE
); break;
2375 mono_w32handle_unref (handle_data
);
2378 g_error ("kill() is not supported by this platform");
2383 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
, MonoError
*error
)
2385 MonoW32Handle
*handle_data
;
2386 MonoW32HandleProcess
*process_handle
;
2391 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2392 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2393 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2397 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2398 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2399 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2400 mono_w32handle_unref (handle_data
);
2404 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2406 if (!process_handle
->child
) {
2407 mono_w32handle_unref (handle_data
);
2411 *min
= process_handle
->min_working_set
;
2412 *max
= process_handle
->max_working_set
;
2414 mono_w32handle_unref (handle_data
);
2419 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
, MonoError
*error
)
2421 MonoW32Handle
*handle_data
;
2422 MonoW32HandleProcess
*process_handle
;
2424 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2425 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2426 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2430 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2431 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2432 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2433 mono_w32handle_unref (handle_data
);
2437 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2439 if (!process_handle
->child
) {
2440 mono_w32handle_unref (handle_data
);
2444 process_handle
->min_working_set
= min
;
2445 process_handle
->max_working_set
= max
;
2447 mono_w32handle_unref (handle_data
);
2452 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
, MonoError
*error
)
2454 #ifdef HAVE_GETPRIORITY
2455 MonoW32Handle
*handle_data
;
2460 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2461 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2462 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2466 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2467 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2468 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2469 mono_w32handle_unref (handle_data
);
2473 pid
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
2476 res
= getpriority (PRIO_PROCESS
, pid
);
2477 if (res
== -1 && errno
!= 0) {
2481 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2484 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2487 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2490 mono_w32handle_unref (handle_data
);
2495 ret
= MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2497 ret
= MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
;
2499 ret
= MONO_W32PROCESS_PRIORITY_CLASS_HIGH
;
2501 ret
= MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
2503 ret
= MONO_W32PROCESS_PRIORITY_CLASS_IDLE
;
2505 ret
= MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
2507 ret
= MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2509 mono_w32handle_unref (handle_data
);
2512 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2518 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
, MonoError
*error
)
2520 #ifdef HAVE_SETPRIORITY
2521 MonoW32Handle
*handle_data
;
2526 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2527 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2528 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2532 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2533 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2534 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2535 mono_w32handle_unref (handle_data
);
2539 pid
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
2541 switch (priorityClass
) {
2542 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE
:
2545 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
:
2548 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
:
2551 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
:
2554 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH
:
2557 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
:
2561 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2562 mono_w32handle_unref (handle_data
);
2566 ret
= setpriority (PRIO_PROCESS
, pid
, prio
);
2571 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2574 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2577 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2581 mono_w32handle_unref (handle_data
);
2584 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2590 ticks_to_processtime (guint64 ticks
, ProcessTime
*processtime
)
2592 processtime
->lowDateTime
= ticks
& 0xFFFFFFFF;
2593 processtime
->highDateTime
= ticks
>> 32;
2597 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creation_time
, gint64
*exit_time
, gint64
*kernel_time
, gint64
*user_time
, MonoError
*error
)
2599 MonoW32Handle
*handle_data
;
2600 MonoW32HandleProcess
*process_handle
;
2601 ProcessTime
*creation_processtime
, *exit_processtime
, *kernel_processtime
, *user_processtime
;
2603 if (!creation_time
|| !exit_time
|| !kernel_time
|| !user_time
) {
2604 /* Not sure if w32 allows NULLs here or not */
2608 creation_processtime
= (ProcessTime
*) creation_time
;
2609 exit_processtime
= (ProcessTime
*) exit_time
;
2610 kernel_processtime
= (ProcessTime
*) kernel_time
;
2611 user_processtime
= (ProcessTime
*) user_time
;
2613 memset (creation_processtime
, 0, sizeof (ProcessTime
));
2614 memset (exit_processtime
, 0, sizeof (ProcessTime
));
2615 memset (kernel_processtime
, 0, sizeof (ProcessTime
));
2616 memset (user_processtime
, 0, sizeof (ProcessTime
));
2618 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2619 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2620 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2624 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2625 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2626 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2627 mono_w32handle_unref (handle_data
);
2631 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2633 if (!process_handle
->child
) {
2634 gint64 start_ticks
, user_ticks
, kernel_ticks
;
2636 mono_process_get_times (GINT_TO_POINTER (process_handle
->pid
),
2637 &start_ticks
, &user_ticks
, &kernel_ticks
);
2639 ticks_to_processtime (start_ticks
, creation_processtime
);
2640 ticks_to_processtime (kernel_ticks
, kernel_processtime
);
2641 ticks_to_processtime (user_ticks
, user_processtime
);
2643 mono_w32handle_unref (handle_data
);
2647 ticks_to_processtime (process_handle
->create_time
, creation_processtime
);
2649 /* A process handle is only signalled if the process has
2650 * exited, otherwise exit_processtime isn't set */
2651 if (mono_w32handle_issignalled (handle_data
))
2652 ticks_to_processtime (process_handle
->exit_time
, exit_processtime
);
2654 #ifdef HAVE_GETRUSAGE
2655 if (process_handle
->pid
== getpid ()) {
2656 struct rusage time_data
;
2657 if (getrusage (RUSAGE_SELF
, &time_data
) == 0) {
2658 ticks_to_processtime ((guint64
)time_data
.ru_utime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_utime
.tv_usec
* 10, user_processtime
);
2659 ticks_to_processtime ((guint64
)time_data
.ru_stime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_stime
.tv_usec
* 10, kernel_processtime
);
2664 mono_w32handle_unref (handle_data
);
2668 static IMAGE_SECTION_HEADER
*
2669 get_enclosing_section_header (guint32 rva
, IMAGE_NT_HEADERS32
*nt_headers
)
2671 IMAGE_SECTION_HEADER
*section
= IMAGE_FIRST_SECTION32 (nt_headers
);
2674 for (i
= 0; i
< GUINT16_FROM_LE (nt_headers
->FileHeader
.NumberOfSections
); i
++, section
++) {
2675 guint32 size
= GUINT32_FROM_LE (section
->Misc
.VirtualSize
);
2677 size
= GUINT32_FROM_LE (section
->SizeOfRawData
);
2680 if ((rva
>= GUINT32_FROM_LE (section
->VirtualAddress
)) &&
2681 (rva
< (GUINT32_FROM_LE (section
->VirtualAddress
) + size
))) {
2689 /* This works for both 32bit and 64bit files, as the differences are
2690 * all after the section header block
2693 get_ptr_from_rva (guint32 rva
, IMAGE_NT_HEADERS32
*ntheaders
, gpointer file_map
)
2695 IMAGE_SECTION_HEADER
*section_header
;
2698 section_header
= get_enclosing_section_header (rva
, ntheaders
);
2699 if (section_header
== NULL
) {
2703 delta
= (guint32
)(GUINT32_FROM_LE (section_header
->VirtualAddress
) -
2704 GUINT32_FROM_LE (section_header
->PointerToRawData
));
2706 return((guint8
*)file_map
+ rva
- delta
);
2710 scan_resource_dir (IMAGE_RESOURCE_DIRECTORY
*root
, IMAGE_NT_HEADERS32
*nt_headers
, gpointer file_map
,
2711 IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
, int level
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2713 IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry
;
2714 gboolean is_string
, is_dir
;
2715 guint32 name_offset
, dir_offset
, data_offset
;
2717 swapped_entry
.Name
= GUINT32_FROM_LE (entry
->Name
);
2718 swapped_entry
.OffsetToData
= GUINT32_FROM_LE (entry
->OffsetToData
);
2720 is_string
= swapped_entry
.NameIsString
;
2721 is_dir
= swapped_entry
.DataIsDirectory
;
2722 name_offset
= swapped_entry
.NameOffset
;
2723 dir_offset
= swapped_entry
.OffsetToDirectory
;
2724 data_offset
= swapped_entry
.OffsetToData
;
2727 /* Normally holds a directory entry for each type of
2730 if ((is_string
== FALSE
&&
2731 name_offset
!= res_id
) ||
2732 (is_string
== TRUE
)) {
2735 } else if (level
== 1) {
2736 /* Normally holds a directory entry for each resource
2739 } else if (level
== 2) {
2740 /* Normally holds a directory entry for each language
2742 if ((is_string
== FALSE
&&
2743 name_offset
!= lang_id
&&
2745 (is_string
== TRUE
)) {
2749 g_assert_not_reached ();
2752 if (is_dir
== TRUE
) {
2753 IMAGE_RESOURCE_DIRECTORY
*res_dir
= (IMAGE_RESOURCE_DIRECTORY
*)((guint8
*)root
+ dir_offset
);
2754 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entries
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(res_dir
+ 1);
2757 entries
= GUINT16_FROM_LE (res_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (res_dir
->NumberOfIdEntries
);
2759 for (i
= 0; i
< entries
; i
++) {
2760 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entry
= &sub_entries
[i
];
2763 ret
= scan_resource_dir (root
, nt_headers
, file_map
,
2764 sub_entry
, level
+ 1, res_id
,
2773 IMAGE_RESOURCE_DATA_ENTRY
*data_entry
= (IMAGE_RESOURCE_DATA_ENTRY
*)((guint8
*)root
+ data_offset
);
2774 *size
= GUINT32_FROM_LE (data_entry
->Size
);
2776 return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry
->OffsetToData
), nt_headers
, file_map
));
2781 find_pe_file_resources32 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2783 IMAGE_DOS_HEADER
*dos_header
;
2784 IMAGE_NT_HEADERS32
*nt_headers
;
2785 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2786 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2787 guint32 resource_rva
, entries
, i
;
2788 gpointer ret
= NULL
;
2790 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2791 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2792 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2794 mono_w32error_set_last (ERROR_INVALID_DATA
);
2798 if (map_size
< sizeof(IMAGE_NT_HEADERS32
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2799 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: File is too small: %" G_GUINT32_FORMAT
, __func__
, map_size
);
2801 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2805 nt_headers
= (IMAGE_NT_HEADERS32
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2806 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2807 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad NT signature 0x%x", __func__
, nt_headers
->Signature
);
2809 mono_w32error_set_last (ERROR_INVALID_DATA
);
2813 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2814 /* Do 64-bit stuff */
2815 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2817 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2820 if (resource_rva
== 0) {
2821 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: No resources in file!", __func__
);
2823 mono_w32error_set_last (ERROR_INVALID_DATA
);
2827 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2828 if (resource_dir
== NULL
) {
2829 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find resource directory", __func__
);
2831 mono_w32error_set_last (ERROR_INVALID_DATA
);
2835 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2836 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2838 for (i
= 0; i
< entries
; i
++) {
2839 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2840 ret
= scan_resource_dir (resource_dir
,
2841 (IMAGE_NT_HEADERS32
*)nt_headers
,
2842 file_map
, direntry
, 0, res_id
,
2853 find_pe_file_resources64 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2855 IMAGE_DOS_HEADER
*dos_header
;
2856 IMAGE_NT_HEADERS64
*nt_headers
;
2857 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2858 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2859 guint32 resource_rva
, entries
, i
;
2860 gpointer ret
= NULL
;
2862 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2863 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2864 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2866 mono_w32error_set_last (ERROR_INVALID_DATA
);
2870 if (map_size
< sizeof(IMAGE_NT_HEADERS64
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2871 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: File is too small: %" G_GUINT32_FORMAT
, __func__
, map_size
);
2873 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2877 nt_headers
= (IMAGE_NT_HEADERS64
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2878 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2879 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad NT signature 0x%x", __func__
,
2880 nt_headers
->Signature
);
2882 mono_w32error_set_last (ERROR_INVALID_DATA
);
2886 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2887 /* Do 64-bit stuff */
2888 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2890 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2893 if (resource_rva
== 0) {
2894 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: No resources in file!", __func__
);
2896 mono_w32error_set_last (ERROR_INVALID_DATA
);
2900 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2901 if (resource_dir
== NULL
) {
2902 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find resource directory", __func__
);
2904 mono_w32error_set_last (ERROR_INVALID_DATA
);
2908 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2909 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2911 for (i
= 0; i
< entries
; i
++) {
2912 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2913 ret
= scan_resource_dir (resource_dir
,
2914 (IMAGE_NT_HEADERS32
*)nt_headers
,
2915 file_map
, direntry
, 0, res_id
,
2926 find_pe_file_resources (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2928 /* Figure this out when we support 64bit PE files */
2930 return find_pe_file_resources32 (file_map
, map_size
, res_id
,
2933 return find_pe_file_resources64 (file_map
, map_size
, res_id
,
2939 unicode_chars (const gunichar2
*str
)
2944 if (str
[len
] == '\0') {
2952 unicode_compare (const gunichar2
*str1
, const gunichar2
*str2
)
2954 while (*str1
&& *str2
) {
2955 if (*str1
!= *str2
) {
2962 return(*str1
== *str2
);
2965 /* compare a little-endian null-terminated utf16 string and a normal string.
2966 * Can be used only for ascii or latin1 chars.
2969 unicode_string_equals (const gunichar2
*str1
, const gchar
*str2
)
2971 while (*str1
&& *str2
) {
2972 if (GUINT16_TO_LE (*str1
) != *str2
) {
2979 return(*str1
== *str2
);
2989 /* Returns a pointer to the value data, because there's no way to know
2990 * how big that data is (value_len is set to zero for most blocks :-( )
2992 static gconstpointer
2993 get_versioninfo_block (gconstpointer data
, version_data
*block
)
2995 block
->data_len
= GUINT16_FROM_LE (*((guint16
*)data
));
2996 data
= (char *)data
+ sizeof(guint16
);
2997 block
->value_len
= GUINT16_FROM_LE (*((guint16
*)data
));
2998 data
= (char *)data
+ sizeof(guint16
);
3000 /* No idea what the type is supposed to indicate */
3001 block
->type
= GUINT16_FROM_LE (*((guint16
*)data
));
3002 data
= (char *)data
+ sizeof(guint16
);
3003 block
->key
= ((gunichar2
*)data
);
3005 /* Skip over the key (including the terminator) */
3006 data
= ((gunichar2
*)data
) + (unicode_chars (block
->key
) + 1);
3008 /* align on a 32-bit boundary */
3014 static gconstpointer
3015 get_fixedfileinfo_block (gconstpointer data
, version_data
*block
)
3017 gconstpointer data_ptr
;
3018 VS_FIXEDFILEINFO
*ffi
;
3020 data_ptr
= get_versioninfo_block (data
, block
);
3022 if (block
->value_len
!= sizeof(VS_FIXEDFILEINFO
)) {
3023 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: FIXEDFILEINFO size mismatch", __func__
);
3027 if (!unicode_string_equals (block
->key
, "VS_VERSION_INFO")) {
3028 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: VS_VERSION_INFO mismatch", __func__
);
3033 ffi
= ((VS_FIXEDFILEINFO
*)data_ptr
);
3034 if ((ffi
->dwSignature
!= VS_FFI_SIGNATURE
) ||
3035 (ffi
->dwStrucVersion
!= VS_FFI_STRUCVERSION
)) {
3036 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: FIXEDFILEINFO bad signature", __func__
);
3044 static gconstpointer
3045 get_varfileinfo_block (gconstpointer data_ptr
, version_data
*block
)
3047 /* data is pointing at a Var block
3049 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3054 static gconstpointer
3055 get_string_block (gconstpointer data_ptr
, const gunichar2
*string_key
, gpointer
*string_value
,
3056 guint32
*string_value_len
, version_data
*block
)
3058 guint16 data_len
= block
->data_len
;
3059 guint16 string_len
= 28; /* Length of the StringTable block */
3060 char *orig_data_ptr
= (char *)data_ptr
- 28;
3062 /* data_ptr is pointing at an array of one or more String blocks
3063 * with total length (not including alignment padding) of
3066 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3067 /* align on a 32-bit boundary */
3070 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3071 if (block
->data_len
== 0) {
3072 /* We must have hit padding, so give up
3075 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3080 string_len
= string_len
+ block
->data_len
;
3082 if (string_key
!= NULL
&&
3083 string_value
!= NULL
&&
3084 string_value_len
!= NULL
&&
3085 unicode_compare (string_key
, block
->key
) == TRUE
) {
3086 *string_value
= (gpointer
)data_ptr
;
3087 *string_value_len
= block
->value_len
;
3090 /* Skip over the value */
3091 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3097 /* Returns a pointer to the byte following the Stringtable block, or
3098 * NULL if the data read hits padding. We can't recover from this
3099 * because the data length does not include padding bytes, so it's not
3100 * possible to just return the start position + length
3102 * If lang == NULL it means we're just stepping through this block
3104 static gconstpointer
3105 get_stringtable_block (gconstpointer data_ptr
, gchar
*lang
, const gunichar2
*string_key
, gpointer
*string_value
,
3106 guint32
*string_value_len
, version_data
*block
)
3108 guint16 data_len
= block
->data_len
;
3109 guint16 string_len
= 36; /* length of the StringFileInfo block */
3111 gchar
*lowercase_lang
;
3113 /* data_ptr is pointing at an array of StringTable blocks,
3114 * with total length (not including alignment padding) of
3118 while(string_len
< data_len
) {
3119 /* align on a 32-bit boundary */
3122 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3123 if (block
->data_len
== 0) {
3124 /* We must have hit padding, so give up
3127 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3131 string_len
= string_len
+ block
->data_len
;
3133 found_lang
= g_utf16_to_utf8 (block
->key
, 8, NULL
, NULL
, NULL
);
3134 if (found_lang
== NULL
) {
3135 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid language key, giving up", __func__
);
3139 lowercase_lang
= g_utf8_strdown (found_lang
, -1);
3140 g_free (found_lang
);
3141 found_lang
= lowercase_lang
;
3142 lowercase_lang
= NULL
;
3144 if (lang
!= NULL
&& !strcmp (found_lang
, lang
)) {
3145 /* Got the one we're interested in */
3146 data_ptr
= get_string_block (data_ptr
, string_key
,
3148 string_value_len
, block
);
3150 data_ptr
= get_string_block (data_ptr
, NULL
, NULL
,
3154 g_free (found_lang
);
3156 if (data_ptr
== NULL
) {
3157 /* Child block hit padding */
3158 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3166 #if G_BYTE_ORDER == G_BIG_ENDIAN
3167 static gconstpointer
3168 big_up_string_block (gconstpointer data_ptr
, version_data
*block
)
3170 guint16 data_len
= block
->data_len
;
3171 guint16 string_len
= 28; /* Length of the StringTable block */
3173 char *orig_data_ptr
= (char *)data_ptr
- 28;
3175 /* data_ptr is pointing at an array of one or more String
3176 * blocks with total length (not including alignment padding)
3179 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3180 /* align on a 32-bit boundary */
3183 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3184 if (block
->data_len
== 0) {
3185 /* We must have hit padding, so give up
3188 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3192 string_len
= string_len
+ block
->data_len
;
3194 big_value
= g_convert ((gchar
*)block
->key
,
3195 unicode_chars (block
->key
) * 2,
3196 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3198 if (big_value
== NULL
) {
3199 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid string, giving up", __func__
);
3203 /* The swapped string should be exactly the same
3204 * length as the original little-endian one, but only
3205 * copy the number of original chars just to be on the
3208 memcpy (block
->key
, big_value
, unicode_chars (block
->key
) * 2);
3211 big_value
= g_convert ((gchar
*)data_ptr
,
3212 unicode_chars (data_ptr
) * 2,
3213 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3215 if (big_value
== NULL
) {
3216 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid data string, giving up", __func__
);
3219 memcpy ((gpointer
)data_ptr
, big_value
,
3220 unicode_chars (data_ptr
) * 2);
3223 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3229 /* Returns a pointer to the byte following the Stringtable block, or
3230 * NULL if the data read hits padding. We can't recover from this
3231 * because the data length does not include padding bytes, so it's not
3232 * possible to just return the start position + length
3234 static gconstpointer
3235 big_up_stringtable_block (gconstpointer data_ptr
, version_data
*block
)
3237 guint16 data_len
= block
->data_len
;
3238 guint16 string_len
= 36; /* length of the StringFileInfo block */
3241 /* data_ptr is pointing at an array of StringTable blocks,
3242 * with total length (not including alignment padding) of
3246 while(string_len
< data_len
) {
3247 /* align on a 32-bit boundary */
3250 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3251 if (block
->data_len
== 0) {
3252 /* We must have hit padding, so give up
3255 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3259 string_len
= string_len
+ block
->data_len
;
3261 big_value
= g_convert ((gchar
*)block
->key
, 16, "UTF-16BE",
3262 "UTF-16LE", NULL
, NULL
, NULL
);
3263 if (big_value
== NULL
) {
3264 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid string, giving up", __func__
);
3268 memcpy (block
->key
, big_value
, 16);
3271 data_ptr
= big_up_string_block (data_ptr
, block
);
3273 if (data_ptr
== NULL
) {
3274 /* Child block hit padding */
3275 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3283 /* Follows the data structures and turns all UTF-16 strings from the
3284 * LE found in the resource section into UTF-16BE
3287 big_up (gconstpointer datablock
, guint32 size
)
3289 gconstpointer data_ptr
;
3290 gint32 data_len
; /* signed to guard against underflow */
3293 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3294 if (data_ptr
!= NULL
) {
3295 VS_FIXEDFILEINFO
*ffi
= (VS_FIXEDFILEINFO
*)data_ptr
;
3297 /* Byteswap all the fields */
3298 ffi
->dwFileVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionMS
);
3299 ffi
->dwFileVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionLS
);
3300 ffi
->dwProductVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionMS
);
3301 ffi
->dwProductVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionLS
);
3302 ffi
->dwFileFlagsMask
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlagsMask
);
3303 ffi
->dwFileFlags
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlags
);
3304 ffi
->dwFileOS
= GUINT32_SWAP_LE_BE (ffi
->dwFileOS
);
3305 ffi
->dwFileType
= GUINT32_SWAP_LE_BE (ffi
->dwFileType
);
3306 ffi
->dwFileSubtype
= GUINT32_SWAP_LE_BE (ffi
->dwFileSubtype
);
3307 ffi
->dwFileDateMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateMS
);
3308 ffi
->dwFileDateLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateLS
);
3310 /* The FFI and header occupies the first 92 bytes
3312 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3313 data_len
= block
.data_len
- 92;
3315 /* There now follow zero or one StringFileInfo blocks
3316 * and zero or one VarFileInfo blocks
3318 while (data_len
> 0) {
3319 /* align on a 32-bit boundary */
3322 data_ptr
= get_versioninfo_block (data_ptr
, &block
);
3323 if (block
.data_len
== 0) {
3324 /* We must have hit padding, so give
3327 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3331 data_len
= data_len
- block
.data_len
;
3333 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3334 data_ptr
= get_varfileinfo_block (data_ptr
,
3336 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3337 } else if (unicode_string_equals (block
.key
,
3338 "StringFileInfo")) {
3339 data_ptr
= big_up_stringtable_block (data_ptr
,
3343 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Not a valid VERSIONINFO child block", __func__
);
3347 if (data_ptr
== NULL
) {
3348 /* Child block hit padding */
3349 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3358 mono_w32process_get_fileversion_info (const gunichar2
*filename
, gpointer
*data
)
3361 gpointer versioninfo
;
3369 file_map
= mono_pe_file_map (filename
, &map_size
, &map_handle
);
3373 versioninfo
= find_pe_file_resources (file_map
, map_size
, RT_VERSION
, 0, &datasize
);
3375 mono_pe_file_unmap (file_map
, map_handle
);
3379 *data
= g_malloc0 (datasize
);
3381 /* This could probably process the data so that mono_w32process_ver_query_value() doesn't have to follow the
3382 * data blocks every time. But hey, these functions aren't likely to appear in many profiles. */
3383 memcpy (*data
, versioninfo
, datasize
);
3385 #if G_BYTE_ORDER == G_BIG_ENDIAN
3386 big_up (*data
, datasize
);
3389 mono_pe_file_unmap (file_map
, map_handle
);
3395 mono_w32process_ver_query_value (gconstpointer datablock
, const gunichar2
*subblock
, gpointer
*buffer
, guint32
*len
)
3397 gchar
*subblock_utf8
, *lang_utf8
= NULL
;
3398 gboolean ret
= FALSE
;
3400 gconstpointer data_ptr
;
3401 gint32 data_len
; /* signed to guard against underflow */
3402 gboolean want_var
= FALSE
;
3403 gboolean want_string
= FALSE
;
3405 const gunichar2
*string_key
= NULL
;
3406 gpointer string_value
= NULL
;
3407 guint32 string_value_len
= 0;
3408 gchar
*lowercase_lang
;
3410 subblock_utf8
= g_utf16_to_utf8 (subblock
, -1, NULL
, NULL
, NULL
);
3411 if (subblock_utf8
== NULL
) {
3415 if (!strcmp (subblock_utf8
, "\\VarFileInfo\\Translation")) {
3417 } else if (!strncmp (subblock_utf8
, "\\StringFileInfo\\", 16)) {
3419 memcpy (lang
, subblock
+ 16, 8 * sizeof(gunichar2
));
3420 lang_utf8
= g_utf16_to_utf8 (lang
, 8, NULL
, NULL
, NULL
);
3421 lowercase_lang
= g_utf8_strdown (lang_utf8
, -1);
3423 lang_utf8
= lowercase_lang
;
3424 lowercase_lang
= NULL
;
3425 string_key
= subblock
+ 25;
3428 if (!strcmp (subblock_utf8
, "\\")) {
3429 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3430 if (data_ptr
!= NULL
) {
3431 *buffer
= (gpointer
)data_ptr
;
3432 *len
= block
.value_len
;
3436 } else if (want_var
|| want_string
) {
3437 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3438 if (data_ptr
!= NULL
) {
3439 /* The FFI and header occupies the first 92
3442 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3443 data_len
= block
.data_len
- 92;
3445 /* There now follow zero or one StringFileInfo
3446 * blocks and zero or one VarFileInfo blocks
3448 while (data_len
> 0) {
3449 /* align on a 32-bit boundary */
3452 data_ptr
= get_versioninfo_block (data_ptr
,
3454 if (block
.data_len
== 0) {
3455 /* We must have hit padding,
3456 * so give up processing now
3458 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3462 data_len
= data_len
- block
.data_len
;
3464 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3465 data_ptr
= get_varfileinfo_block (data_ptr
, &block
);
3467 *buffer
= (gpointer
)data_ptr
;
3468 *len
= block
.value_len
;
3472 /* Skip over the Var block */
3473 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3475 } else if (unicode_string_equals (block
.key
, "StringFileInfo")) {
3476 data_ptr
= get_stringtable_block (data_ptr
, lang_utf8
, string_key
, &string_value
, &string_value_len
, &block
);
3478 string_value
!= NULL
&&
3479 string_value_len
!= 0) {
3480 *buffer
= string_value
;
3481 *len
= unicode_chars ((const gunichar2
*)string_value
) + 1; /* Include trailing null */
3487 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Not a valid VERSIONINFO child block", __func__
);
3491 if (data_ptr
== NULL
) {
3492 /* Child block hit padding */
3493 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3505 g_free (subblock_utf8
);
3510 copy_lang (gunichar2
*lang_out
, guint32 lang_len
, const gchar
*text
)
3513 int chars
= strlen (text
);
3516 unitext
= g_utf8_to_utf16 (text
, -1, NULL
, NULL
, NULL
);
3517 g_assert (unitext
!= NULL
);
3519 if (chars
< (lang_len
- 1)) {
3520 memcpy (lang_out
, (gpointer
)unitext
, chars
* 2);
3521 lang_out
[chars
] = '\0';
3524 memcpy (lang_out
, (gpointer
)unitext
, (lang_len
- 1) * 2);
3525 lang_out
[lang_len
] = '\0';
3535 mono_w32process_ver_language_name (guint32 lang
, gunichar2
*lang_out
, guint32 lang_len
)
3537 int primary
, secondary
;
3538 const char *name
= NULL
;
3540 primary
= lang
& 0x3FF;
3541 secondary
= (lang
>> 10) & 0x3F;
3545 switch (secondary
) {
3546 case 0x01: name
= "Process Default Language"; break;
3550 switch (secondary
) {
3552 case 0x01: name
= "Arabic (Saudi Arabia)"; break;
3553 case 0x02: name
= "Arabic (Iraq)"; break;
3554 case 0x03: name
= "Arabic (Egypt)"; break;
3555 case 0x04: name
= "Arabic (Libya)"; break;
3556 case 0x05: name
= "Arabic (Algeria)"; break;
3557 case 0x06: name
= "Arabic (Morocco)"; break;
3558 case 0x07: name
= "Arabic (Tunisia)"; break;
3559 case 0x08: name
= "Arabic (Oman)"; break;
3560 case 0x09: name
= "Arabic (Yemen)"; break;
3561 case 0x0a: name
= "Arabic (Syria)"; break;
3562 case 0x0b: name
= "Arabic (Jordan)"; break;
3563 case 0x0c: name
= "Arabic (Lebanon)"; break;
3564 case 0x0d: name
= "Arabic (Kuwait)"; break;
3565 case 0x0e: name
= "Arabic (U.A.E.)"; break;
3566 case 0x0f: name
= "Arabic (Bahrain)"; break;
3567 case 0x10: name
= "Arabic (Qatar)"; break;
3571 switch (secondary
) {
3572 case 0x00: name
= "Bulgarian (Bulgaria)"; break;
3573 case 0x01: name
= "Bulgarian"; break;
3577 switch (secondary
) {
3578 case 0x00: name
= "Catalan (Spain)"; break;
3579 case 0x01: name
= "Catalan"; break;
3583 switch (secondary
) {
3585 case 0x01: name
= "Chinese (Taiwan)"; break;
3586 case 0x02: name
= "Chinese (PRC)"; break;
3587 case 0x03: name
= "Chinese (Hong Kong S.A.R.)"; break;
3588 case 0x04: name
= "Chinese (Singapore)"; break;
3589 case 0x05: name
= "Chinese (Macau S.A.R.)"; break;
3593 switch (secondary
) {
3594 case 0x00: name
= "Czech (Czech Republic)"; break;
3595 case 0x01: name
= "Czech"; break;
3599 switch (secondary
) {
3600 case 0x00: name
= "Danish (Denmark)"; break;
3601 case 0x01: name
= "Danish"; break;
3605 switch (secondary
) {
3607 case 0x01: name
= "German (Germany)"; break;
3608 case 0x02: name
= "German (Switzerland)"; break;
3609 case 0x03: name
= "German (Austria)"; break;
3610 case 0x04: name
= "German (Luxembourg)"; break;
3611 case 0x05: name
= "German (Liechtenstein)"; break;
3615 switch (secondary
) {
3616 case 0x00: name
= "Greek (Greece)"; break;
3617 case 0x01: name
= "Greek"; break;
3621 switch (secondary
) {
3623 case 0x01: name
= "English (United States)"; break;
3624 case 0x02: name
= "English (United Kingdom)"; break;
3625 case 0x03: name
= "English (Australia)"; break;
3626 case 0x04: name
= "English (Canada)"; break;
3627 case 0x05: name
= "English (New Zealand)"; break;
3628 case 0x06: name
= "English (Ireland)"; break;
3629 case 0x07: name
= "English (South Africa)"; break;
3630 case 0x08: name
= "English (Jamaica)"; break;
3631 case 0x09: name
= "English (Caribbean)"; break;
3632 case 0x0a: name
= "English (Belize)"; break;
3633 case 0x0b: name
= "English (Trinidad and Tobago)"; break;
3634 case 0x0c: name
= "English (Zimbabwe)"; break;
3635 case 0x0d: name
= "English (Philippines)"; break;
3636 case 0x10: name
= "English (India)"; break;
3637 case 0x11: name
= "English (Malaysia)"; break;
3638 case 0x12: name
= "English (Singapore)"; break;
3642 switch (secondary
) {
3643 case 0x00: name
= "Spanish (Spain)"; break;
3644 case 0x01: name
= "Spanish (Traditional Sort)"; break;
3645 case 0x02: name
= "Spanish (Mexico)"; break;
3646 case 0x03: name
= "Spanish (International Sort)"; break;
3647 case 0x04: name
= "Spanish (Guatemala)"; break;
3648 case 0x05: name
= "Spanish (Costa Rica)"; break;
3649 case 0x06: name
= "Spanish (Panama)"; break;
3650 case 0x07: name
= "Spanish (Dominican Republic)"; break;
3651 case 0x08: name
= "Spanish (Venezuela)"; break;
3652 case 0x09: name
= "Spanish (Colombia)"; break;
3653 case 0x0a: name
= "Spanish (Peru)"; break;
3654 case 0x0b: name
= "Spanish (Argentina)"; break;
3655 case 0x0c: name
= "Spanish (Ecuador)"; break;
3656 case 0x0d: name
= "Spanish (Chile)"; break;
3657 case 0x0e: name
= "Spanish (Uruguay)"; break;
3658 case 0x0f: name
= "Spanish (Paraguay)"; break;
3659 case 0x10: name
= "Spanish (Bolivia)"; break;
3660 case 0x11: name
= "Spanish (El Salvador)"; break;
3661 case 0x12: name
= "Spanish (Honduras)"; break;
3662 case 0x13: name
= "Spanish (Nicaragua)"; break;
3663 case 0x14: name
= "Spanish (Puerto Rico)"; break;
3664 case 0x15: name
= "Spanish (United States)"; break;
3668 switch (secondary
) {
3669 case 0x00: name
= "Finnish (Finland)"; break;
3670 case 0x01: name
= "Finnish"; break;
3674 switch (secondary
) {
3676 case 0x01: name
= "French (France)"; break;
3677 case 0x02: name
= "French (Belgium)"; break;
3678 case 0x03: name
= "French (Canada)"; break;
3679 case 0x04: name
= "French (Switzerland)"; break;
3680 case 0x05: name
= "French (Luxembourg)"; break;
3681 case 0x06: name
= "French (Monaco)"; break;
3685 switch (secondary
) {
3686 case 0x00: name
= "Hebrew (Israel)"; break;
3687 case 0x01: name
= "Hebrew"; break;
3691 switch (secondary
) {
3692 case 0x00: name
= "Hungarian (Hungary)"; break;
3693 case 0x01: name
= "Hungarian"; break;
3697 switch (secondary
) {
3698 case 0x00: name
= "Icelandic (Iceland)"; break;
3699 case 0x01: name
= "Icelandic"; break;
3703 switch (secondary
) {
3705 case 0x01: name
= "Italian (Italy)"; break;
3706 case 0x02: name
= "Italian (Switzerland)"; break;
3710 switch (secondary
) {
3711 case 0x00: name
= "Japanese (Japan)"; break;
3712 case 0x01: name
= "Japanese"; break;
3716 switch (secondary
) {
3717 case 0x00: name
= "Korean (Korea)"; break;
3718 case 0x01: name
= "Korean"; break;
3722 switch (secondary
) {
3724 case 0x01: name
= "Dutch (Netherlands)"; break;
3725 case 0x02: name
= "Dutch (Belgium)"; break;
3729 switch (secondary
) {
3731 case 0x01: name
= "Norwegian (Bokmal)"; break;
3732 case 0x02: name
= "Norwegian (Nynorsk)"; break;
3736 switch (secondary
) {
3737 case 0x00: name
= "Polish (Poland)"; break;
3738 case 0x01: name
= "Polish"; break;
3742 switch (secondary
) {
3744 case 0x01: name
= "Portuguese (Brazil)"; break;
3745 case 0x02: name
= "Portuguese (Portugal)"; break;
3749 switch (secondary
) {
3750 case 0x01: name
= "Romansh (Switzerland)"; break;
3754 switch (secondary
) {
3755 case 0x00: name
= "Romanian (Romania)"; break;
3756 case 0x01: name
= "Romanian"; break;
3760 switch (secondary
) {
3761 case 0x00: name
= "Russian (Russia)"; break;
3762 case 0x01: name
= "Russian"; break;
3766 switch (secondary
) {
3767 case 0x00: name
= "Croatian (Croatia)"; break;
3768 case 0x01: name
= "Croatian"; break;
3769 case 0x02: name
= "Serbian (Latin)"; break;
3770 case 0x03: name
= "Serbian (Cyrillic)"; break;
3771 case 0x04: name
= "Croatian (Bosnia and Herzegovina)"; break;
3772 case 0x05: name
= "Bosnian (Latin, Bosnia and Herzegovina)"; break;
3773 case 0x06: name
= "Serbian (Latin, Bosnia and Herzegovina)"; break;
3774 case 0x07: name
= "Serbian (Cyrillic, Bosnia and Herzegovina)"; break;
3775 case 0x08: name
= "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break;
3779 switch (secondary
) {
3780 case 0x00: name
= "Slovak (Slovakia)"; break;
3781 case 0x01: name
= "Slovak"; break;
3785 switch (secondary
) {
3786 case 0x00: name
= "Albanian (Albania)"; break;
3787 case 0x01: name
= "Albanian"; break;
3791 switch (secondary
) {
3792 case 0x00: name
= "Swedish (Sweden)"; break;
3793 case 0x01: name
= "Swedish"; break;
3794 case 0x02: name
= "Swedish (Finland)"; break;
3798 switch (secondary
) {
3799 case 0x00: name
= "Thai (Thailand)"; break;
3800 case 0x01: name
= "Thai"; break;
3804 switch (secondary
) {
3805 case 0x00: name
= "Turkish (Turkey)"; break;
3806 case 0x01: name
= "Turkish"; break;
3810 switch (secondary
) {
3811 case 0x00: name
= "Urdu (Islamic Republic of Pakistan)"; break;
3812 case 0x01: name
= "Urdu"; break;
3816 switch (secondary
) {
3817 case 0x00: name
= "Indonesian (Indonesia)"; break;
3818 case 0x01: name
= "Indonesian"; break;
3822 switch (secondary
) {
3823 case 0x00: name
= "Ukrainian (Ukraine)"; break;
3824 case 0x01: name
= "Ukrainian"; break;
3828 switch (secondary
) {
3829 case 0x00: name
= "Belarusian (Belarus)"; break;
3830 case 0x01: name
= "Belarusian"; break;
3834 switch (secondary
) {
3835 case 0x00: name
= "Slovenian (Slovenia)"; break;
3836 case 0x01: name
= "Slovenian"; break;
3840 switch (secondary
) {
3841 case 0x00: name
= "Estonian (Estonia)"; break;
3842 case 0x01: name
= "Estonian"; break;
3846 switch (secondary
) {
3847 case 0x00: name
= "Latvian (Latvia)"; break;
3848 case 0x01: name
= "Latvian"; break;
3852 switch (secondary
) {
3853 case 0x00: name
= "Lithuanian (Lithuania)"; break;
3854 case 0x01: name
= "Lithuanian"; break;
3858 switch (secondary
) {
3859 case 0x01: name
= "Tajik (Tajikistan)"; break;
3863 switch (secondary
) {
3864 case 0x00: name
= "Farsi (Iran)"; break;
3865 case 0x01: name
= "Farsi"; break;
3869 switch (secondary
) {
3870 case 0x00: name
= "Vietnamese (Viet Nam)"; break;
3871 case 0x01: name
= "Vietnamese"; break;
3875 switch (secondary
) {
3876 case 0x00: name
= "Armenian (Armenia)"; break;
3877 case 0x01: name
= "Armenian"; break;
3881 switch (secondary
) {
3882 case 0x00: name
= "Azeri (Latin) (Azerbaijan)"; break;
3883 case 0x01: name
= "Azeri (Latin)"; break;
3884 case 0x02: name
= "Azeri (Cyrillic)"; break;
3888 switch (secondary
) {
3889 case 0x00: name
= "Basque (Spain)"; break;
3890 case 0x01: name
= "Basque"; break;
3894 switch (secondary
) {
3895 case 0x01: name
= "Upper Sorbian (Germany)"; break;
3896 case 0x02: name
= "Lower Sorbian (Germany)"; break;
3900 switch (secondary
) {
3901 case 0x00: name
= "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
3902 case 0x01: name
= "FYRO Macedonian"; break;
3906 switch (secondary
) {
3907 case 0x00: name
= "Tswana (South Africa)"; break;
3908 case 0x01: name
= "Tswana"; break;
3912 switch (secondary
) {
3913 case 0x00: name
= "Xhosa (South Africa)"; break;
3914 case 0x01: name
= "Xhosa"; break;
3918 switch (secondary
) {
3919 case 0x00: name
= "Zulu (South Africa)"; break;
3920 case 0x01: name
= "Zulu"; break;
3924 switch (secondary
) {
3925 case 0x00: name
= "Afrikaans (South Africa)"; break;
3926 case 0x01: name
= "Afrikaans"; break;
3930 switch (secondary
) {
3931 case 0x00: name
= "Georgian (Georgia)"; break;
3932 case 0x01: name
= "Georgian"; break;
3936 switch (secondary
) {
3937 case 0x00: name
= "Faroese (Faroe Islands)"; break;
3938 case 0x01: name
= "Faroese"; break;
3942 switch (secondary
) {
3943 case 0x00: name
= "Hindi (India)"; break;
3944 case 0x01: name
= "Hindi"; break;
3948 switch (secondary
) {
3949 case 0x00: name
= "Maltese (Malta)"; break;
3950 case 0x01: name
= "Maltese"; break;
3954 switch (secondary
) {
3955 case 0x00: name
= "Sami (Northern) (Norway)"; break;
3956 case 0x01: name
= "Sami, Northern (Norway)"; break;
3957 case 0x02: name
= "Sami, Northern (Sweden)"; break;
3958 case 0x03: name
= "Sami, Northern (Finland)"; break;
3959 case 0x04: name
= "Sami, Lule (Norway)"; break;
3960 case 0x05: name
= "Sami, Lule (Sweden)"; break;
3961 case 0x06: name
= "Sami, Southern (Norway)"; break;
3962 case 0x07: name
= "Sami, Southern (Sweden)"; break;
3963 case 0x08: name
= "Sami, Skolt (Finland)"; break;
3964 case 0x09: name
= "Sami, Inari (Finland)"; break;
3968 switch (secondary
) {
3969 case 0x02: name
= "Irish (Ireland)"; break;
3973 switch (secondary
) {
3975 case 0x01: name
= "Malay (Malaysia)"; break;
3976 case 0x02: name
= "Malay (Brunei Darussalam)"; break;
3980 switch (secondary
) {
3981 case 0x00: name
= "Kazakh (Kazakhstan)"; break;
3982 case 0x01: name
= "Kazakh"; break;
3986 switch (secondary
) {
3987 case 0x00: name
= "Kyrgyz (Kyrgyzstan)"; break;
3988 case 0x01: name
= "Kyrgyz (Cyrillic)"; break;
3992 switch (secondary
) {
3993 case 0x00: name
= "Swahili (Kenya)"; break;
3994 case 0x01: name
= "Swahili"; break;
3998 switch (secondary
) {
3999 case 0x01: name
= "Turkmen (Turkmenistan)"; break;
4003 switch (secondary
) {
4004 case 0x00: name
= "Uzbek (Latin) (Uzbekistan)"; break;
4005 case 0x01: name
= "Uzbek (Latin)"; break;
4006 case 0x02: name
= "Uzbek (Cyrillic)"; break;
4010 switch (secondary
) {
4011 case 0x00: name
= "Tatar (Russia)"; break;
4012 case 0x01: name
= "Tatar"; break;
4016 switch (secondary
) {
4018 case 0x01: name
= "Bengali (India)"; break;
4022 switch (secondary
) {
4023 case 0x00: name
= "Punjabi (India)"; break;
4024 case 0x01: name
= "Punjabi"; break;
4028 switch (secondary
) {
4029 case 0x00: name
= "Gujarati (India)"; break;
4030 case 0x01: name
= "Gujarati"; break;
4034 switch (secondary
) {
4035 case 0x00: name
= "Tamil (India)"; break;
4036 case 0x01: name
= "Tamil"; break;
4040 switch (secondary
) {
4041 case 0x00: name
= "Telugu (India)"; break;
4042 case 0x01: name
= "Telugu"; break;
4046 switch (secondary
) {
4047 case 0x00: name
= "Kannada (India)"; break;
4048 case 0x01: name
= "Kannada"; break;
4052 switch (secondary
) {
4054 case 0x01: name
= "Malayalam (India)"; break;
4058 switch (secondary
) {
4059 case 0x01: name
= "Assamese (India)"; break;
4063 switch (secondary
) {
4064 case 0x00: name
= "Marathi (India)"; break;
4065 case 0x01: name
= "Marathi"; break;
4069 switch (secondary
) {
4070 case 0x00: name
= "Sanskrit (India)"; break;
4071 case 0x01: name
= "Sanskrit"; break;
4075 switch (secondary
) {
4076 case 0x00: name
= "Mongolian (Mongolia)"; break;
4077 case 0x01: name
= "Mongolian (Cyrillic)"; break;
4078 case 0x02: name
= "Mongolian (PRC)"; break;
4082 switch (secondary
) {
4083 case 0x01: name
= "Tibetan (PRC)"; break;
4084 case 0x02: name
= "Tibetan (Bhutan)"; break;
4088 switch (secondary
) {
4089 case 0x00: name
= "Welsh (United Kingdom)"; break;
4090 case 0x01: name
= "Welsh"; break;
4094 switch (secondary
) {
4095 case 0x01: name
= "Khmer (Cambodia)"; break;
4099 switch (secondary
) {
4100 case 0x01: name
= "Lao (Lao PDR)"; break;
4104 switch (secondary
) {
4105 case 0x00: name
= "Galician (Spain)"; break;
4106 case 0x01: name
= "Galician"; break;
4110 switch (secondary
) {
4111 case 0x00: name
= "Konkani (India)"; break;
4112 case 0x01: name
= "Konkani"; break;
4116 switch (secondary
) {
4117 case 0x00: name
= "Syriac (Syria)"; break;
4118 case 0x01: name
= "Syriac"; break;
4122 switch (secondary
) {
4123 case 0x01: name
= "Sinhala (Sri Lanka)"; break;
4127 switch (secondary
) {
4128 case 0x01: name
= "Inuktitut (Syllabics, Canada)"; break;
4129 case 0x02: name
= "Inuktitut (Latin, Canada)"; break;
4133 switch (secondary
) {
4134 case 0x01: name
= "Amharic (Ethiopia)"; break;
4138 switch (secondary
) {
4139 case 0x02: name
= "Tamazight (Algeria, Latin)"; break;
4143 switch (secondary
) {
4144 case 0x01: name
= "Nepali (Nepal)"; break;
4148 switch (secondary
) {
4149 case 0x01: name
= "Frisian (Netherlands)"; break;
4153 switch (secondary
) {
4154 case 0x01: name
= "Pashto (Afghanistan)"; break;
4158 switch (secondary
) {
4159 case 0x01: name
= "Filipino (Philippines)"; break;
4163 switch (secondary
) {
4164 case 0x00: name
= "Divehi (Maldives)"; break;
4165 case 0x01: name
= "Divehi"; break;
4169 switch (secondary
) {
4170 case 0x01: name
= "Hausa (Nigeria, Latin)"; break;
4174 switch (secondary
) {
4175 case 0x01: name
= "Yoruba (Nigeria)"; break;
4179 switch (secondary
) {
4181 case 0x01: name
= "Quechua (Bolivia)"; break;
4182 case 0x02: name
= "Quechua (Ecuador)"; break;
4183 case 0x03: name
= "Quechua (Peru)"; break;
4187 switch (secondary
) {
4188 case 0x00: name
= "Northern Sotho (South Africa)"; break;
4189 case 0x01: name
= "Northern Sotho"; break;
4193 switch (secondary
) {
4194 case 0x01: name
= "Bashkir (Russia)"; break;
4198 switch (secondary
) {
4199 case 0x01: name
= "Luxembourgish (Luxembourg)"; break;
4203 switch (secondary
) {
4204 case 0x01: name
= "Greenlandic (Greenland)"; break;
4208 switch (secondary
) {
4209 case 0x01: name
= "Yi (PRC)"; break;
4213 switch (secondary
) {
4214 case 0x01: name
= "Mapudungun (Chile)"; break;
4218 switch (secondary
) {
4219 case 0x01: name
= "Mohawk (Mohawk)"; break;
4223 switch (secondary
) {
4224 case 0x01: name
= "Breton (France)"; break;
4228 switch (secondary
) {
4229 case 0x00: name
= "Invariant Language (Invariant Country)"; break;
4233 switch (secondary
) {
4234 case 0x01: name
= "Uighur (PRC)"; break;
4238 switch (secondary
) {
4239 case 0x00: name
= "Maori (New Zealand)"; break;
4240 case 0x01: name
= "Maori"; break;
4244 switch (secondary
) {
4245 case 0x01: name
= "Corsican (France)"; break;
4249 switch (secondary
) {
4250 case 0x01: name
= "Alsatian (France)"; break;
4254 switch (secondary
) {
4255 case 0x01: name
= "Yakut (Russia)"; break;
4259 switch (secondary
) {
4260 case 0x01: name
= "K'iche (Guatemala)"; break;
4264 switch (secondary
) {
4265 case 0x01: name
= "Kinyarwanda (Rwanda)"; break;
4269 switch (secondary
) {
4270 case 0x01: name
= "Wolof (Senegal)"; break;
4274 switch (secondary
) {
4275 case 0x01: name
= "Dari (Afghanistan)"; break;
4280 name
= "Language Neutral";
4285 name
= "Language Neutral";
4287 return copy_lang (lang_out
, lang_len
, name
);
4290 #else /* ENABLE_NETCORE */
4293 mono_w32process_init (void)
4298 mono_w32process_cleanup (void)
4303 mono_w32process_set_cli_launcher (gchar
*path
)
4308 mono_w32process_signal_finished (void)
4312 #endif /* ENABLE_NETCORE */