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
, guint32
*len
)
920 gunichar2
*proc_path
;
922 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Getting module file name, process handle %p module %p " G_GUINT32_FORMAT
,
923 __func__
, handle
, module
);
927 pid
= mono_w32process_get_pid (handle
);
931 path
= mono_w32process_get_path (pid
);
935 proc_path
= mono_unicode_from_external (path
, &bytes
);
937 *len
= bytes
/ sizeof (gunichar2
);
944 mono_w32process_module_get_name (gpointer handle
, gpointer module
, guint32
*len
)
946 MonoW32Handle
*handle_data
;
947 MonoW32HandleProcess
*process_handle
;
950 char *procname_ext
= NULL
;
952 GSList
*mods
= NULL
, *mods_iter
;
953 MonoW32ProcessModule
*found_module
;
956 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Getting module base name, process handle %p module %p " G_GUINT32_FORMAT
,
957 __func__
, handle
, module
);
961 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
962 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
963 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
967 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
968 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
969 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
970 mono_w32handle_unref (handle_data
);
974 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
976 pid
= process_handle
->pid
;
977 pname
= g_strdup (process_handle
->pname
);
979 mods
= mono_w32process_get_modules (pid
);
980 if (!mods
&& module
!= NULL
) {
981 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't get modules %p", __func__
, handle
);
983 mono_w32handle_unref (handle_data
);
987 /* If module != NULL compare the address.
988 * If module == NULL we are looking for the main module.
989 * The best we can do for now check it the module name end with the process name.
991 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
992 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
993 if (procname_ext
== NULL
&&
994 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
995 (module
!= NULL
&& found_module
->address_start
== module
))) {
996 procname_ext
= g_path_get_basename (found_module
->filename
);
999 mono_w32process_module_free (found_module
);
1002 if (procname_ext
== NULL
) {
1003 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find procname_ext from procmods %p", __func__
, handle
);
1004 /* If it's *still* null, we might have hit the
1005 * case where reading /proc/$pid/maps gives an
1006 * empty file for this user.
1008 procname_ext
= mono_w32process_get_name (pid
);
1010 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
);
1013 g_slist_free (mods
);
1017 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Process name is [%s]", __func__
,
1020 procname
= mono_unicode_from_external (procname_ext
, &bytes
);
1021 if (procname
== NULL
) {
1022 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't get procname %p", __func__
, handle
);
1024 g_free (procname_ext
);
1025 mono_w32handle_unref (handle_data
);
1029 *len
= bytes
/ sizeof (gunichar2
);
1031 g_free (procname_ext
);
1032 mono_w32handle_unref (handle_data
);
1036 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find procname_ext %p", __func__
, handle
);
1037 mono_w32handle_unref (handle_data
);
1042 mono_w32process_module_get_information (gpointer handle
, gpointer module
, MODULEINFO
*modinfo
, guint32 size
)
1044 MonoW32Handle
*handle_data
;
1045 MonoW32HandleProcess
*process_handle
;
1047 GSList
*mods
= NULL
, *mods_iter
;
1048 MonoW32ProcessModule
*found_module
;
1049 gboolean ret
= FALSE
;
1052 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Getting module info, process handle %p module %p",
1053 __func__
, handle
, module
);
1055 if (modinfo
== NULL
|| size
< sizeof (MODULEINFO
))
1058 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
1059 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
1060 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
1064 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
1065 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
1066 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
1067 mono_w32handle_unref (handle_data
);
1071 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
1073 pid
= process_handle
->pid
;
1074 pname
= g_strdup (process_handle
->pname
);
1076 mods
= mono_w32process_get_modules (pid
);
1079 mono_w32handle_unref (handle_data
);
1083 /* If module != NULL compare the address.
1084 * If module == NULL we are looking for the main module.
1085 * The best we can do for now check it the module name end with the process name.
1087 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1088 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1090 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1091 (module
!= NULL
&& found_module
->address_start
== module
))) {
1092 modinfo
->lpBaseOfDll
= found_module
->address_start
;
1093 modinfo
->SizeOfImage
= (gsize
)(found_module
->address_end
) - (gsize
)(found_module
->address_start
);
1094 modinfo
->EntryPoint
= found_module
->address_offset
;
1098 mono_w32process_module_free (found_module
);
1101 g_slist_free (mods
);
1103 mono_w32handle_unref (handle_data
);
1108 switch_dir_separators (char *path
)
1110 size_t i
, pathLength
= strlen(path
);
1112 /* Turn all the slashes round the right way, except for \' */
1113 /* There are probably other characters that need to be excluded as well. */
1114 for (i
= 0; i
< pathLength
; i
++) {
1115 if (path
[i
] == '\\' && i
< pathLength
- 1 && path
[i
+1] != '\'' )
1122 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler
, (int _dummy
, siginfo_t
*info
, void *context
))
1125 * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
1126 * mono_w32process_signal_finished ().
1128 int old_errno
= errno
;
1130 mono_gc_finalize_notify ();
1132 mono_set_errno (old_errno
);
1136 process_add_sigchld_handler (void)
1138 struct sigaction sa
;
1140 sa
.sa_sigaction
= mono_sigchld_signal_handler
;
1141 sigemptyset (&sa
.sa_mask
);
1142 sa
.sa_flags
= SA_NOCLDSTOP
| SA_SIGINFO
| SA_RESTART
;
1143 g_assert (sigaction (SIGCHLD
, &sa
, NULL
) != -1);
1144 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "Added SIGCHLD handler");
1150 * mono_w32process_signal_finished:
1152 * Signal the exit semaphore for processes which have finished.
1155 mono_w32process_signal_finished (void)
1157 mono_coop_mutex_lock (&processes_mutex
);
1159 for (Process
* process
= processes
; process
; process
= process
->next
) {
1164 pid
= waitpid (process
->pid
, &status
, WNOHANG
);
1165 } while (pid
== -1 && errno
== EINTR
);
1167 // possible values of 'pid':
1168 // process->pid : the status changed for this child
1169 // 0 : status unchanged for this PID
1170 // ECHILD : process has been reaped elsewhere (or never existed)
1171 // EINVAL : invalid PID or other argument
1173 // Therefore, we ignore status unchanged (nothing to do) and error
1174 // events (process is cleaned up later).
1177 if (process
->signalled
)
1180 process
->signalled
= TRUE
;
1181 process
->status
= status
;
1182 mono_coop_sem_post (&process
->exit_sem
);
1185 mono_coop_mutex_unlock (&processes_mutex
);
1189 is_readable_or_executable (const char *prog
)
1192 int a
= access (prog
, R_OK
);
1193 int b
= access (prog
, X_OK
);
1194 if (a
!= 0 && b
!= 0)
1196 if (stat (prog
, &buf
))
1198 if (S_ISREG (buf
.st_mode
))
1204 is_executable (const char *prog
)
1207 if (access (prog
, X_OK
) != 0)
1209 if (stat (prog
, &buf
))
1211 if (S_ISREG (buf
.st_mode
))
1217 is_managed_binary (const char *filename
)
1219 int original_errno
= errno
;
1220 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1221 int file
= open (filename
, O_RDONLY
| O_LARGEFILE
);
1223 int file
= open (filename
, O_RDONLY
);
1226 unsigned char buffer
[8];
1227 off_t file_size
, optional_header_offset
;
1228 off_t pe_header_offset
, clr_header_offset
;
1229 gboolean managed
= FALSE
;
1231 guint32 first_word
, second_word
, magic_number
;
1233 /* If we are unable to open the file, then we definitely
1234 * can't say that it is managed. The child mono process
1235 * probably wouldn't be able to open it anyway.
1238 mono_set_errno (original_errno
);
1242 /* Retrieve the length of the file for future sanity checks. */
1243 file_size
= lseek (file
, 0, SEEK_END
);
1244 lseek (file
, 0, SEEK_SET
);
1246 /* We know we need to read a header field at offset 60. */
1250 num_read
= read (file
, buffer
, 2);
1252 if ((num_read
!= 2) || (buffer
[0] != 'M') || (buffer
[1] != 'Z'))
1255 new_offset
= lseek (file
, 60, SEEK_SET
);
1257 if (new_offset
!= 60)
1260 num_read
= read (file
, buffer
, 4);
1264 pe_header_offset
= buffer
[0]
1267 | (buffer
[3] << 24);
1269 if (pe_header_offset
+ 24 > file_size
)
1272 new_offset
= lseek (file
, pe_header_offset
, SEEK_SET
);
1274 if (new_offset
!= pe_header_offset
)
1277 num_read
= read (file
, buffer
, 4);
1279 if ((num_read
!= 4) || (buffer
[0] != 'P') || (buffer
[1] != 'E') || (buffer
[2] != 0) || (buffer
[3] != 0))
1283 * Verify that the header we want in the optional header data
1284 * is present in this binary.
1286 new_offset
= lseek (file
, pe_header_offset
+ 20, SEEK_SET
);
1288 if (new_offset
!= pe_header_offset
+ 20)
1291 num_read
= read (file
, buffer
, 2);
1293 if ((num_read
!= 2) || ((buffer
[0] | (buffer
[1] << 8)) < 216))
1296 optional_header_offset
= pe_header_offset
+ 24;
1298 /* Read the PE magic number */
1299 new_offset
= lseek (file
, optional_header_offset
, SEEK_SET
);
1301 if (new_offset
!= optional_header_offset
)
1304 num_read
= read (file
, buffer
, 2);
1309 magic_number
= (buffer
[0] | (buffer
[1] << 8));
1311 if (magic_number
== 0x10B) // PE32
1312 clr_header_offset
= 208;
1313 else if (magic_number
== 0x20B) // PE32+
1314 clr_header_offset
= 224;
1318 /* Read the CLR header address and size fields. These will be
1319 * zero if the binary is not managed.
1321 new_offset
= lseek (file
, optional_header_offset
+ clr_header_offset
, SEEK_SET
);
1323 if (new_offset
!= optional_header_offset
+ clr_header_offset
)
1326 num_read
= read (file
, buffer
, 8);
1328 /* We are not concerned with endianness, only with
1329 * whether it is zero or not.
1331 first_word
= *(guint32
*)&buffer
[0];
1332 second_word
= *(guint32
*)&buffer
[4];
1334 if ((num_read
!= 8) || (first_word
== 0) || (second_word
== 0))
1341 mono_set_errno (original_errno
);
1346 * Gets the biggest numbered file descriptor for the current process; failing
1347 * that, the system's file descriptor limit. This is called by the fork child
1354 struct procentry64 pe
;
1357 if (getprocs64 (&pe
, sizeof (pe
), NULL
, 0, &p
, 1) != -1) {
1358 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1359 "%s: maximum returned fd in child is %u",
1360 __func__
, pe
.pi_maxofile
);
1361 return pe
.pi_maxofile
; // biggest + 1
1364 // fallback to user/system limit if unsupported/error
1365 return eg_getdtablesize ();
1369 * Closes all of the process' opened file descriptors, applying a strategy
1370 * appropriate for the target system. This is called by the fork child in
1376 // TODO: Other platforms.
1377 // * On macOS, use proc_pidinfo + PROC_PIDLISTFDS? See:
1378 // http://blog.palominolabs.com/2012/06/19/getting-the-files-being-used-by-a-process-on-mac-os-x/
1379 // (I have no idea how this plays out on i/watch/tvOS.)
1380 // * On the other BSDs, there's likely a sysctl for this.
1381 // * On Solaris, there exists posix_spawn_file_actions_addclosefrom_np,
1382 // but that assumes we're using posix_spawn; we aren't, as we do some
1383 // complex stuff between fork and exec. There's likely a way to get
1384 // the FD list/count though (maybe look at addclosefrom source in
1385 // illumos?) or just walk /proc/pid/fd like Linux?
1386 #if defined (__linux__)
1387 /* Walk the file descriptors in /proc/self/fd/. Linux has no other API,
1388 * as far as I'm aware. Opening a directory won't create an FD. */
1392 d
= opendir ("/proc/self/fd/");
1394 while ((dp
= readdir (d
)) != NULL
) {
1395 if (dp
->d_name
[0] == '.')
1397 fd
= atoi (dp
->d_name
);
1404 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1405 "%s: opening fd dir failed, using fallback",
1408 #elif defined (__FreeBSD__)
1409 /* FreeBSD lets us get a list of FDs. There's a MIB to access them
1410 * directly, but it uses a lot of nasty variable length structures. The
1411 * system library libutil provides a nicer way to get a fixed length
1412 * version instead. */
1413 struct kinfo_file
*kif
;
1415 /* this is malloced but we won't need to free once we exec/exit */
1416 kif
= kinfo_getfile (getpid (), &count
);
1418 for (i
= 0; i
< count
; i
++) {
1419 /* negative FDs look to be used by the OS */
1420 if (kif
[i
].kf_fd
> 2) /* no neg + no stdio */
1421 close (kif
[i
].kf_fd
);
1425 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1426 "%s: kinfo_getfile failed, using fallback",
1429 #elif defined (_AIX)
1430 struct procentry64 pe
;
1431 /* this array struct is 1 MB, we're NOT putting it on the stack.
1432 * likewise no need to free; getprocs will fail if we use the smalller
1433 * versions if we have a lot of FDs (is it worth it?)
1435 struct fdsinfo_100K
*fds
;
1438 fds
= (struct fdsinfo_100K
*) g_malloc0 (sizeof (struct fdsinfo_100K
));
1440 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1441 "%s: fdsinfo alloc failed, using fallback",
1446 if (getprocs64 (&pe
, sizeof (pe
), fds
, sizeof (struct fdsinfo_100K
), &p
, 1) != -1) {
1447 for (int i
= 3; i
< pe
.pi_maxofile
; i
++) {
1448 if (fds
->pi_ufd
[i
].fp
!= 0)
1449 close (fds
->pi_ufd
[i
].fp
);
1453 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
,
1454 "%s: getprocs64 failed, using fallback",
1459 /* Fallback: Close FDs blindly, according to an FD limit */
1460 for (guint32 i
= max_fd_count () - 1; i
> 2; i
--)
1465 process_create (const gunichar2
*appname
, const gunichar2
*cmdline
,
1466 const gunichar2
*cwd
, StartupHandles
*startup_handles
, MonoW32ProcessInfo
*process_info
)
1468 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1469 char *cmd
= NULL
, *prog
= NULL
, *full_prog
= NULL
, *args
= NULL
, *args_after_prog
= NULL
;
1470 char *dir
= NULL
, **env_strings
= NULL
, **argv
= NULL
;
1472 gboolean ret
= FALSE
;
1473 gpointer handle
= NULL
;
1474 GError
*gerr
= NULL
;
1475 int in_fd
, out_fd
, err_fd
;
1477 int startup_pipe
[2] = {-1, -1};
1483 mono_lazy_initialize (&process_sig_chld_once
, process_add_sigchld_handler
);
1486 /* appname and cmdline specify the executable and its args:
1488 * If appname is not NULL, it is the name of the executable.
1489 * Otherwise the executable is the first token in cmdline.
1491 * Executable searching:
1493 * If appname is not NULL, it can specify the full path and
1494 * file name, or else a partial name and the current directory
1495 * will be used. There is no additional searching.
1497 * If appname is NULL, the first whitespace-delimited token in
1498 * cmdline is used. If the name does not contain a full
1499 * directory path, the search sequence is:
1501 * 1) The directory containing the current process
1502 * 2) The current working directory
1503 * 3) The windows system directory (Ignored)
1504 * 4) The windows directory (Ignored)
1507 * Just to make things more interesting, tokens can contain
1508 * white space if they are surrounded by quotation marks. I'm
1509 * beginning to understand just why windows apps are generally
1510 * so crap, with an API like this :-(
1512 if (appname
!= NULL
) {
1513 cmd
= mono_unicode_to_external_checked (appname
, error
);
1515 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unicode conversion returned NULL; %s",
1516 __func__
, mono_error_get_message (error
));
1518 mono_error_cleanup (error
);
1519 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1523 switch_dir_separators(cmd
);
1526 if (cmdline
!= NULL
) {
1527 args
= mono_unicode_to_external_checked (cmdline
, error
);
1529 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
1531 mono_error_cleanup (error
);
1532 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1538 dir
= mono_unicode_to_external_checked (cwd
, error
);
1540 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unicode conversion returned NULL; %s", __func__
, mono_error_get_message (error
));
1542 mono_error_cleanup (error
);
1543 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1547 /* Turn all the slashes round the right way */
1548 switch_dir_separators(dir
);
1552 /* We can't put off locating the executable any longer :-( */
1555 if (g_ascii_isalpha (cmd
[0]) && (cmd
[1] == ':')) {
1556 /* Strip off the drive letter. I can't
1557 * believe that CP/M holdover is still
1560 g_memmove (cmd
, cmd
+2, strlen (cmd
)-2);
1561 cmd
[strlen (cmd
)-2] = '\0';
1564 unquoted
= g_shell_unquote (cmd
, NULL
);
1565 if (unquoted
[0] == '/') {
1566 /* Assume full path given */
1567 prog
= g_strdup (unquoted
);
1569 /* Executable existing ? */
1570 if (!is_readable_or_executable (prog
)) {
1571 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s",
1574 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1578 /* Search for file named by cmd in the current
1581 char *curdir
= g_get_current_dir ();
1583 prog
= g_strdup_printf ("%s/%s", curdir
, unquoted
);
1586 /* And make sure it's readable */
1587 if (!is_readable_or_executable (prog
)) {
1588 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s",
1591 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1597 args_after_prog
= args
;
1602 /* Dig out the first token from args, taking quotation
1603 * marks into account
1606 /* First, strip off all leading whitespace */
1607 args
= g_strchug (args
);
1609 /* args_after_prog points to the contents of args
1610 * after token has been set (otherwise argv[0] is
1613 args_after_prog
= args
;
1615 /* Assume the opening quote will always be the first
1618 if (args
[0] == '\"' || args
[0] == '\'') {
1620 for (i
= 1; args
[i
] != '\0' && args
[i
] != quote
; i
++);
1621 if (args
[i
+ 1] == '\0' || g_ascii_isspace (args
[i
+1])) {
1622 /* We found the first token */
1623 token
= g_strndup (args
+1, i
-1);
1624 args_after_prog
= g_strchug (args
+ i
+ 1);
1626 /* Quotation mark appeared in the
1627 * middle of the token. Just give the
1628 * whole first token, quotes and all,
1634 if (token
== NULL
) {
1635 /* No quote mark, or malformed */
1636 for (i
= 0; args
[i
] != '\0'; i
++) {
1637 if (g_ascii_isspace (args
[i
])) {
1638 token
= g_strndup (args
, i
);
1639 args_after_prog
= args
+ i
+ 1;
1645 if (token
== NULL
&& args
[0] != '\0') {
1646 /* Must be just one token in the string */
1647 token
= g_strdup (args
);
1648 args_after_prog
= NULL
;
1651 if (token
== NULL
) {
1653 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find what to exec", __func__
);
1655 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1659 /* Turn all the slashes round the right way. Only for
1662 switch_dir_separators(token
);
1664 if (g_ascii_isalpha (token
[0]) && (token
[1] == ':')) {
1665 /* Strip off the drive letter. I can't
1666 * believe that CP/M holdover is still
1669 g_memmove (token
, token
+2, strlen (token
)-2);
1670 token
[strlen (token
)-2] = '\0';
1673 if (token
[0] == '/') {
1674 /* Assume full path given */
1675 prog
= g_strdup (token
);
1677 /* Executable existing ? */
1678 if (!is_readable_or_executable (prog
)) {
1679 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s",
1682 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1686 char *curdir
= g_get_current_dir ();
1688 /* FIXME: Need to record the directory
1689 * containing the current process, and check
1690 * that for the new executable as the first
1694 prog
= g_strdup_printf ("%s/%s", curdir
, token
);
1697 /* I assume X_OK is the criterion to use,
1700 * X_OK is too strict *if* the target is a CLR binary
1702 if (!is_readable_or_executable (prog
)) {
1704 prog
= g_find_program_in_path (token
);
1706 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Couldn't find executable %s", __func__
, token
);
1709 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1718 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Exec prog [%s] args [%s]",
1719 __func__
, prog
, args_after_prog
);
1721 /* Check for CLR binaries; if found, we will try to invoke
1722 * them using the same mono binary that started us.
1724 if (is_managed_binary (prog
)) {
1725 gunichar2
*newapp
, *newcmd
;
1726 gsize bytes_ignored
;
1728 newapp
= mono_unicode_from_external (cli_launcher
? cli_launcher
: "mono", &bytes_ignored
);
1731 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, appname
, utf16_space
, cmdline
, (const gunichar2
*)NULL
);
1733 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, cmdline
, (const gunichar2
*)NULL
);
1738 ret
= process_create (NULL
, newcmd
, cwd
, startup_handles
, process_info
);
1746 if (!is_executable (prog
)) {
1747 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Executable permisson not set on %s", __func__
, prog
);
1748 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1753 if (args_after_prog
!= NULL
&& *args_after_prog
) {
1756 qprog
= g_shell_quote (prog
);
1757 full_prog
= g_strconcat (qprog
, " ", args_after_prog
, (const char*)NULL
);
1760 full_prog
= g_shell_quote (prog
);
1763 ret
= g_shell_parse_argv (full_prog
, NULL
, &argv
, &gerr
);
1765 g_message ("process_create: %s\n", gerr
->message
);
1766 g_error_free (gerr
);
1771 if (startup_handles
) {
1772 in_fd
= GPOINTER_TO_UINT (startup_handles
->input
);
1773 out_fd
= GPOINTER_TO_UINT (startup_handles
->output
);
1774 err_fd
= GPOINTER_TO_UINT (startup_handles
->error
);
1776 in_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_input ());
1777 out_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_output ());
1778 err_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_error ());
1782 * process->env_variables is a an array of MonoString*
1784 * If new_environ is not NULL it specifies the entire set of
1785 * environment variables in the new process. Otherwise the
1786 * new process inherits the same environment.
1788 if (process_info
->env_variables
) {
1789 MonoArrayHandle array
= MONO_HANDLE_NEW (MonoArray
, process_info
->env_variables
);
1790 MonoStringHandle var
= MONO_HANDLE_NEW (MonoString
, NULL
);
1791 gsize
const array_length
= mono_array_handle_length (array
);
1793 /* +2: one for the process handle value, and the last one is NULL */
1794 // What "process handle value"?
1795 env_strings
= g_new0 (gchar
*, array_length
+ 2);
1797 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1798 for (gsize i
= 0; i
< array_length
; ++i
) {
1799 MONO_HANDLE_ARRAY_GETREF (var
, array
, i
);
1800 gchandle_t gchandle
= 0;
1801 env_strings
[i
] = mono_unicode_to_external (mono_string_handle_pin_chars (var
, &gchandle
));
1802 mono_gchandle_free_internal (gchandle
);
1805 gsize env_count
= 0;
1806 for (i
= 0; environ
[i
] != NULL
; i
++)
1809 /* +2: one for the process handle value, and the last one is NULL */
1810 // What "process handle value"?
1811 env_strings
= g_new0 (gchar
*, env_count
+ 2);
1813 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1814 for (i
= 0; i
< env_count
; i
++)
1815 env_strings
[i
] = g_strdup (environ
[i
]);
1818 /* Create a pipe to make sure the child doesn't exit before
1819 * we can add the process to the linked list of processes */
1820 if (pipe (startup_pipe
) == -1) {
1821 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1822 * This is just for a very hard to hit race condition in the first place */
1823 startup_pipe
[0] = startup_pipe
[1] = -1;
1824 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__
);
1827 switch (pid
= fork ()) {
1828 case -1: /* Error */ {
1829 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1833 case 0: /* Child */ {
1834 if (startup_pipe
[0] != -1) {
1835 /* Wait until the parent has updated it's internal data */
1836 ssize_t _i G_GNUC_UNUSED
= read (startup_pipe
[0], &dummy
, 1);
1837 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: child: parent has completed its setup", __func__
);
1838 close (startup_pipe
[0]);
1839 close (startup_pipe
[1]);
1842 /* should we detach from the process group? */
1844 /* Connect stdin, stdout and stderr */
1849 /* Close this child's file handles. */
1852 #ifdef DEBUG_ENABLED
1853 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: exec()ing [%s] in dir [%s]", __func__
, cmd
,
1854 dir
== NULL
?".":dir
);
1855 for (i
= 0; argv
[i
] != NULL
; i
++)
1856 g_message ("arg %" G_GUINT32_FORMAT
": [%s]", i
, argv
[i
]);
1858 for (i
= 0; env_strings
[i
] != NULL
; i
++)
1859 g_message ("env %" G_GUINT32_FORMAT
": [%s]", i
, env_strings
[i
]);
1863 if (dir
!= NULL
&& chdir (dir
) == -1) {
1869 execve (argv
[0], argv
, env_strings
);
1876 default: /* Parent */ {
1877 MonoW32Handle
*handle_data
;
1878 MonoW32HandleProcess process_handle
;
1880 memset (&process_handle
, 0, sizeof (process_handle
));
1881 process_handle
.pid
= pid
;
1882 process_handle
.child
= TRUE
;
1883 process_handle
.pname
= g_strdup (prog
);
1884 process_set_defaults (&process_handle
);
1886 /* Add our process into the linked list of processes */
1887 process
= (Process
*) g_malloc0 (sizeof (Process
));
1889 process
->handle_count
= 1;
1890 mono_coop_sem_init (&process
->exit_sem
, 0);
1892 process_handle
.process
= process
;
1894 handle
= mono_w32handle_new (MONO_W32TYPE_PROCESS
, &process_handle
);
1895 if (handle
== INVALID_HANDLE_VALUE
) {
1896 g_warning ("%s: error creating process handle", __func__
);
1898 mono_coop_sem_destroy (&process
->exit_sem
);
1901 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1906 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
1907 g_error ("%s: unknown handle %p", __func__
, handle
);
1909 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
)
1910 g_error ("%s: unknown process handle %p", __func__
, handle
);
1912 /* Keep the process handle artificially alive until the process
1913 * exits so that the information in the handle isn't lost. */
1914 process
->handle
= mono_w32handle_duplicate (handle_data
);
1916 mono_coop_mutex_lock (&processes_mutex
);
1917 process
->next
= processes
;
1918 mono_memory_barrier ();
1919 processes
= process
;
1920 mono_coop_mutex_unlock (&processes_mutex
);
1922 if (process_info
!= NULL
) {
1923 process_info
->process_handle
= handle
;
1924 process_info
->pid
= pid
;
1927 mono_w32handle_unref (handle_data
);
1933 if (startup_pipe
[1] != -1) {
1934 /* Write 1 byte, doesn't matter what */
1935 ssize_t _i G_GNUC_UNUSED
= write (startup_pipe
[1], startup_pipe
, 1);
1936 close (startup_pipe
[0]);
1937 close (startup_pipe
[1]);
1946 g_strfreev (env_strings
);
1949 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: returning handle %p for pid %d", __func__
, handle
, pid
);
1951 /* Check if something needs to be cleaned up. */
1952 processes_cleanup ();
1956 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
1958 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1962 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfoHandle proc_start_info
, MonoW32ProcessInfo
*process_info
, MonoError
*error
)
1964 MonoCreateProcessCoop coop
;
1965 mono_createprocess_coop_init (&coop
, proc_start_info
, process_info
);
1968 gboolean handler_needswait
= FALSE
;
1970 if (!coop
.filename
) {
1971 /* w2k returns TRUE for this, for some reason. */
1976 const gunichar2
*lpFile
;
1977 lpFile
= coop
.filename
;
1978 const gunichar2
*lpParameters
;
1979 lpParameters
= coop
.arguments
;
1980 const gunichar2
*lpDirectory
;
1981 lpDirectory
= coop
.length
.working_directory
? coop
.working_directory
: NULL
;
1983 /* Put both executable and parameters into the second argument
1984 * to process_create (), so it searches $PATH. The conversion
1985 * into and back out of utf8 is because there is no
1986 * g_strdup_printf () equivalent for gunichar2 :-(
1989 args
= utf16_concat (utf16_quote
, lpFile
, utf16_quote
, lpParameters
? utf16_space
: NULL
, lpParameters
, (const gunichar2
*)NULL
);
1991 mono_w32error_set_last (ERROR_INVALID_DATA
);
1995 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
1998 if (!ret
&& mono_w32error_get_last () == ERROR_OUTOFMEMORY
)
2003 #if defined(TARGET_IOS) || defined(TARGET_ANDROID)
2004 // don't try the "open" handlers on iOS/Android, they don't exist there anyway
2008 static char *handler
;
2009 static gunichar2
*handler_utf16
;
2011 if (handler_utf16
== (gunichar2
*)-1) {
2017 handler
= g_strdup ("/usr/bin/open");
2018 handler_needswait
= TRUE
;
2021 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
2022 * if that fails, try to use gnome-open, then kfmclient
2025 handler
= g_find_program_in_path ("xdg-open");
2026 if (handler
!= NULL
)
2027 handler_needswait
= TRUE
;
2029 handler
= g_find_program_in_path ("gnome-open");
2030 if (handler
== NULL
){
2031 handler
= g_find_program_in_path ("kfmclient");
2032 if (handler
== NULL
){
2033 handler_utf16
= (gunichar2
*) -1;
2036 /* kfmclient needs exec argument */
2037 char *old
= handler
;
2038 handler
= g_strconcat (old
, " exec",
2049 handler_utf16
= g_utf8_to_utf16 (handler
, -1, NULL
, NULL
, NULL
);
2052 /* Put quotes around the filename, in case it's a url
2053 * that contains #'s (process_create() calls
2054 * g_shell_parse_argv(), which deliberately throws
2055 * away anything after an unquoted #). Fixes bug
2058 args
= utf16_concat (handler_utf16
, utf16_space
, utf16_quote
, lpFile
, utf16_quote
,
2059 lpParameters
? utf16_space
: NULL
, lpParameters
, (const gunichar2
*)NULL
);
2061 mono_w32error_set_last (ERROR_INVALID_DATA
);
2065 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2068 if (mono_w32error_get_last () != ERROR_OUTOFMEMORY
)
2069 mono_w32error_set_last (ERROR_INVALID_DATA
);
2074 if (handler_needswait
) {
2076 MonoW32HandleWaitRet waitret
;
2077 waitret
= process_wait ((MonoW32Handle
*)process_info
->process_handle
, MONO_INFINITE_WAIT
, NULL
);
2079 mono_get_exit_code_process (process_info
->process_handle
, &exitcode
);
2083 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
2084 mono_w32handle_close (process_info
->process_handle
);
2085 process_info
->process_handle
= INVALID_HANDLE_VALUE
;
2090 process_info
->pid
= -mono_w32error_get_last ();
2092 #if !defined(MONO_CROSS_COMPILE)
2093 process_info
->pid
= mono_w32process_get_pid (process_info
->process_handle
);
2095 process_info
->pid
= 0;
2099 mono_createprocess_coop_cleanup (&coop
);
2104 /* Only used when UseShellExecute is false */
2106 process_get_complete_path (const gunichar2
*appname
, gchar
**completed
)
2109 gboolean result
= FALSE
;
2111 char *utf8app
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
2113 if (g_path_is_absolute (utf8app
)) {
2114 *completed
= g_shell_quote (utf8app
);
2119 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
2120 *completed
= g_shell_quote (utf8app
);
2125 found
= g_find_program_in_path (utf8app
);
2126 if (found
== NULL
) {
2132 *completed
= g_shell_quote (found
);
2141 process_get_shell_arguments (MonoCreateProcessCoop
*coop
, gunichar2
**shell_path
)
2143 gchar
*complete_path
= NULL
;
2147 if (process_get_complete_path (coop
->filename
, &complete_path
)) {
2148 *shell_path
= g_utf8_to_utf16 (complete_path
, -1, NULL
, NULL
, NULL
);
2149 g_free (complete_path
);
2152 return *shell_path
!= NULL
;
2156 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfoHandle proc_start_info
,
2157 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
, MonoError
*error
)
2159 MonoCreateProcessCoop coop
;
2160 mono_createprocess_coop_init (&coop
, proc_start_info
, process_info
);
2163 StartupHandles startup_handles
;
2164 gunichar2
*shell_path
= NULL
;
2166 memset (&startup_handles
, 0, sizeof (startup_handles
));
2167 startup_handles
.input
= stdin_handle
;
2168 startup_handles
.output
= stdout_handle
;
2169 startup_handles
.error
= stderr_handle
;
2171 if (!process_get_shell_arguments (&coop
, &shell_path
)) {
2172 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
2178 args
= coop
.length
.arguments
? coop
.arguments
: NULL
;
2180 /* The default dir name is "". Turn that into NULL to mean "current directory" */
2182 dir
= coop
.length
.working_directory
? coop
.working_directory
: NULL
;
2184 ret
= process_create (shell_path
, args
, dir
, &startup_handles
, process_info
);
2187 process_info
->pid
= -mono_w32error_get_last ();
2190 g_free (shell_path
);
2191 mono_createprocess_coop_cleanup (&coop
);
2195 /* Returns an array of pids */
2197 ves_icall_System_Diagnostics_Process_GetProcesses_internal (MonoError
*error
)
2201 gpointer
*pidarray
= 0;
2202 MonoArrayHandle procs
= NULL_HANDLE_ARRAY
;
2204 // FIXME mono_process_list should probably return array of int
2205 // as all of the users of the elements truncate to that.
2208 pidarray
= mono_process_list (&count
);
2211 mono_error_set_not_supported (error
, "This system does not support EnumProcesses");
2214 procs
= mono_array_new_handle (mono_domain_get (), mono_get_int32_class (), count
, error
);
2215 if (!is_ok (error
)) {
2216 procs
= NULL_HANDLE_ARRAY
;
2220 MONO_ENTER_NO_SAFEPOINTS
;
2222 raw
= mono_array_addr_internal (MONO_HANDLE_RAW (procs
), guint32
, 0);
2223 if (sizeof (guint32
) == sizeof (gpointer
)) {
2224 memcpy (raw
, pidarray
, count
* sizeof (gint32
));
2226 for (int i
= 0; i
< count
; ++i
)
2227 raw
[i
] = GPOINTER_TO_UINT (pidarray
[i
]);
2230 MONO_EXIT_NO_SAFEPOINTS
;
2238 mono_w32process_set_cli_launcher (gchar
*path
)
2240 g_free (cli_launcher
);
2241 cli_launcher
= g_strdup (path
);
2245 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (MonoError
*error
)
2247 return current_process
;
2251 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
, MonoError
*error
)
2253 return mono_get_exit_code_process (handle
, exitcode
);
2257 mono_get_exit_code_process (gpointer handle
, gint32
*exitcode
)
2259 MonoW32Handle
*handle_data
;
2260 MonoW32HandleProcess
*process_handle
;
2265 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2266 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2267 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2271 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2272 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2273 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2274 mono_w32handle_unref (handle_data
);
2278 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2280 if (process_handle
->pid
== current_pid
) {
2281 *exitcode
= STILL_ACTIVE
;
2282 mono_w32handle_unref (handle_data
);
2286 /* A process handle is only signalled if the process has exited
2287 * and has been waited for. Make sure any process exit has been
2288 * noticed before checking if the process is signalled.
2289 * Fixes bug 325463. */
2290 mono_w32handle_wait_one (handle
, 0, TRUE
);
2292 *exitcode
= mono_w32handle_issignalled (handle_data
) ? process_handle
->exitstatus
: STILL_ACTIVE
;
2294 mono_w32handle_unref (handle_data
);
2300 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
, MonoError
*error
)
2302 return mono_w32handle_close (handle
);
2306 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
, MonoError
*error
)
2309 MonoW32Handle
*handle_data
;
2313 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2314 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2315 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2319 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2320 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2321 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2322 mono_w32handle_unref (handle_data
);
2326 pid
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
2328 ret
= kill (pid
, exitcode
== -1 ? SIGKILL
: SIGTERM
);
2330 mono_w32handle_unref (handle_data
);
2335 case EINVAL
: mono_w32error_set_last (ERROR_INVALID_PARAMETER
); break;
2336 case EPERM
: mono_w32error_set_last (ERROR_ACCESS_DENIED
); break;
2337 case ESRCH
: mono_w32error_set_last (ERROR_PROC_NOT_FOUND
); break;
2338 default: mono_w32error_set_last (ERROR_GEN_FAILURE
); break;
2341 mono_w32handle_unref (handle_data
);
2344 g_error ("kill() is not supported by this platform");
2349 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
, MonoError
*error
)
2351 MonoW32Handle
*handle_data
;
2352 MonoW32HandleProcess
*process_handle
;
2357 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2358 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2359 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2363 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2364 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2365 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2366 mono_w32handle_unref (handle_data
);
2370 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2372 if (!process_handle
->child
) {
2373 mono_w32handle_unref (handle_data
);
2377 *min
= process_handle
->min_working_set
;
2378 *max
= process_handle
->max_working_set
;
2380 mono_w32handle_unref (handle_data
);
2385 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
, MonoError
*error
)
2387 MonoW32Handle
*handle_data
;
2388 MonoW32HandleProcess
*process_handle
;
2390 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2391 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2392 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2396 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2397 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2398 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2399 mono_w32handle_unref (handle_data
);
2403 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2405 if (!process_handle
->child
) {
2406 mono_w32handle_unref (handle_data
);
2410 process_handle
->min_working_set
= min
;
2411 process_handle
->max_working_set
= max
;
2413 mono_w32handle_unref (handle_data
);
2418 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
, MonoError
*error
)
2420 #ifdef HAVE_GETPRIORITY
2421 MonoW32Handle
*handle_data
;
2426 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2427 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2428 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2432 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2433 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2434 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2435 mono_w32handle_unref (handle_data
);
2439 pid
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
2442 res
= getpriority (PRIO_PROCESS
, pid
);
2443 if (res
== -1 && errno
!= 0) {
2447 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2450 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2453 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2456 mono_w32handle_unref (handle_data
);
2461 ret
= MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2463 ret
= MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
;
2465 ret
= MONO_W32PROCESS_PRIORITY_CLASS_HIGH
;
2467 ret
= MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
2469 ret
= MONO_W32PROCESS_PRIORITY_CLASS_IDLE
;
2471 ret
= MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
2473 ret
= MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2475 mono_w32handle_unref (handle_data
);
2478 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2484 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
, MonoError
*error
)
2486 #ifdef HAVE_SETPRIORITY
2487 MonoW32Handle
*handle_data
;
2492 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2493 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2494 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2498 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2499 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2500 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2501 mono_w32handle_unref (handle_data
);
2505 pid
= ((MonoW32HandleProcess
*) handle_data
->specific
)->pid
;
2507 switch (priorityClass
) {
2508 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE
:
2511 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
:
2514 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
:
2517 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
:
2520 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH
:
2523 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
:
2527 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2528 mono_w32handle_unref (handle_data
);
2532 ret
= setpriority (PRIO_PROCESS
, pid
, prio
);
2537 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2540 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2543 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2547 mono_w32handle_unref (handle_data
);
2550 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2556 ticks_to_processtime (guint64 ticks
, ProcessTime
*processtime
)
2558 processtime
->lowDateTime
= ticks
& 0xFFFFFFFF;
2559 processtime
->highDateTime
= ticks
>> 32;
2563 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creation_time
, gint64
*exit_time
, gint64
*kernel_time
, gint64
*user_time
, MonoError
*error
)
2565 MonoW32Handle
*handle_data
;
2566 MonoW32HandleProcess
*process_handle
;
2567 ProcessTime
*creation_processtime
, *exit_processtime
, *kernel_processtime
, *user_processtime
;
2569 if (!creation_time
|| !exit_time
|| !kernel_time
|| !user_time
) {
2570 /* Not sure if w32 allows NULLs here or not */
2574 creation_processtime
= (ProcessTime
*) creation_time
;
2575 exit_processtime
= (ProcessTime
*) exit_time
;
2576 kernel_processtime
= (ProcessTime
*) kernel_time
;
2577 user_processtime
= (ProcessTime
*) user_time
;
2579 memset (creation_processtime
, 0, sizeof (ProcessTime
));
2580 memset (exit_processtime
, 0, sizeof (ProcessTime
));
2581 memset (kernel_processtime
, 0, sizeof (ProcessTime
));
2582 memset (user_processtime
, 0, sizeof (ProcessTime
));
2584 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
2585 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown handle %p", __func__
, handle
);
2586 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2590 if (handle_data
->type
!= MONO_W32TYPE_PROCESS
) {
2591 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: unknown process handle %p", __func__
, handle
);
2592 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2593 mono_w32handle_unref (handle_data
);
2597 process_handle
= (MonoW32HandleProcess
*) handle_data
->specific
;
2599 if (!process_handle
->child
) {
2600 gint64 start_ticks
, user_ticks
, kernel_ticks
;
2602 mono_process_get_times (GINT_TO_POINTER (process_handle
->pid
),
2603 &start_ticks
, &user_ticks
, &kernel_ticks
);
2605 ticks_to_processtime (start_ticks
, creation_processtime
);
2606 ticks_to_processtime (kernel_ticks
, kernel_processtime
);
2607 ticks_to_processtime (user_ticks
, user_processtime
);
2609 mono_w32handle_unref (handle_data
);
2613 ticks_to_processtime (process_handle
->create_time
, creation_processtime
);
2615 /* A process handle is only signalled if the process has
2616 * exited, otherwise exit_processtime isn't set */
2617 if (mono_w32handle_issignalled (handle_data
))
2618 ticks_to_processtime (process_handle
->exit_time
, exit_processtime
);
2620 #ifdef HAVE_GETRUSAGE
2621 if (process_handle
->pid
== getpid ()) {
2622 struct rusage time_data
;
2623 if (getrusage (RUSAGE_SELF
, &time_data
) == 0) {
2624 ticks_to_processtime ((guint64
)time_data
.ru_utime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_utime
.tv_usec
* 10, user_processtime
);
2625 ticks_to_processtime ((guint64
)time_data
.ru_stime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_stime
.tv_usec
* 10, kernel_processtime
);
2630 mono_w32handle_unref (handle_data
);
2634 static IMAGE_SECTION_HEADER
*
2635 get_enclosing_section_header (guint32 rva
, IMAGE_NT_HEADERS32
*nt_headers
)
2637 IMAGE_SECTION_HEADER
*section
= IMAGE_FIRST_SECTION32 (nt_headers
);
2640 for (i
= 0; i
< GUINT16_FROM_LE (nt_headers
->FileHeader
.NumberOfSections
); i
++, section
++) {
2641 guint32 size
= GUINT32_FROM_LE (section
->Misc
.VirtualSize
);
2643 size
= GUINT32_FROM_LE (section
->SizeOfRawData
);
2646 if ((rva
>= GUINT32_FROM_LE (section
->VirtualAddress
)) &&
2647 (rva
< (GUINT32_FROM_LE (section
->VirtualAddress
) + size
))) {
2655 /* This works for both 32bit and 64bit files, as the differences are
2656 * all after the section header block
2659 get_ptr_from_rva (guint32 rva
, IMAGE_NT_HEADERS32
*ntheaders
, gpointer file_map
)
2661 IMAGE_SECTION_HEADER
*section_header
;
2664 section_header
= get_enclosing_section_header (rva
, ntheaders
);
2665 if (section_header
== NULL
) {
2669 delta
= (guint32
)(GUINT32_FROM_LE (section_header
->VirtualAddress
) -
2670 GUINT32_FROM_LE (section_header
->PointerToRawData
));
2672 return((guint8
*)file_map
+ rva
- delta
);
2676 scan_resource_dir (IMAGE_RESOURCE_DIRECTORY
*root
, IMAGE_NT_HEADERS32
*nt_headers
, gpointer file_map
,
2677 IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
, int level
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2679 IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry
;
2680 gboolean is_string
, is_dir
;
2681 guint32 name_offset
, dir_offset
, data_offset
;
2683 swapped_entry
.Name
= GUINT32_FROM_LE (entry
->Name
);
2684 swapped_entry
.OffsetToData
= GUINT32_FROM_LE (entry
->OffsetToData
);
2686 is_string
= swapped_entry
.NameIsString
;
2687 is_dir
= swapped_entry
.DataIsDirectory
;
2688 name_offset
= swapped_entry
.NameOffset
;
2689 dir_offset
= swapped_entry
.OffsetToDirectory
;
2690 data_offset
= swapped_entry
.OffsetToData
;
2693 /* Normally holds a directory entry for each type of
2696 if ((is_string
== FALSE
&&
2697 name_offset
!= res_id
) ||
2698 (is_string
== TRUE
)) {
2701 } else if (level
== 1) {
2702 /* Normally holds a directory entry for each resource
2705 } else if (level
== 2) {
2706 /* Normally holds a directory entry for each language
2708 if ((is_string
== FALSE
&&
2709 name_offset
!= lang_id
&&
2711 (is_string
== TRUE
)) {
2715 g_assert_not_reached ();
2718 if (is_dir
== TRUE
) {
2719 IMAGE_RESOURCE_DIRECTORY
*res_dir
= (IMAGE_RESOURCE_DIRECTORY
*)((guint8
*)root
+ dir_offset
);
2720 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entries
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(res_dir
+ 1);
2723 entries
= GUINT16_FROM_LE (res_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (res_dir
->NumberOfIdEntries
);
2725 for (i
= 0; i
< entries
; i
++) {
2726 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entry
= &sub_entries
[i
];
2729 ret
= scan_resource_dir (root
, nt_headers
, file_map
,
2730 sub_entry
, level
+ 1, res_id
,
2739 IMAGE_RESOURCE_DATA_ENTRY
*data_entry
= (IMAGE_RESOURCE_DATA_ENTRY
*)((guint8
*)root
+ data_offset
);
2740 *size
= GUINT32_FROM_LE (data_entry
->Size
);
2742 return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry
->OffsetToData
), nt_headers
, file_map
));
2747 find_pe_file_resources32 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2749 IMAGE_DOS_HEADER
*dos_header
;
2750 IMAGE_NT_HEADERS32
*nt_headers
;
2751 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2752 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2753 guint32 resource_rva
, entries
, i
;
2754 gpointer ret
= NULL
;
2756 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2757 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2758 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2760 mono_w32error_set_last (ERROR_INVALID_DATA
);
2764 if (map_size
< sizeof(IMAGE_NT_HEADERS32
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2765 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: File is too small: %" G_GUINT32_FORMAT
, __func__
, map_size
);
2767 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2771 nt_headers
= (IMAGE_NT_HEADERS32
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2772 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2773 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad NT signature 0x%x", __func__
, nt_headers
->Signature
);
2775 mono_w32error_set_last (ERROR_INVALID_DATA
);
2779 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2780 /* Do 64-bit stuff */
2781 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2783 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2786 if (resource_rva
== 0) {
2787 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: No resources in file!", __func__
);
2789 mono_w32error_set_last (ERROR_INVALID_DATA
);
2793 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2794 if (resource_dir
== NULL
) {
2795 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find resource directory", __func__
);
2797 mono_w32error_set_last (ERROR_INVALID_DATA
);
2801 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2802 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2804 for (i
= 0; i
< entries
; i
++) {
2805 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2806 ret
= scan_resource_dir (resource_dir
,
2807 (IMAGE_NT_HEADERS32
*)nt_headers
,
2808 file_map
, direntry
, 0, res_id
,
2819 find_pe_file_resources64 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2821 IMAGE_DOS_HEADER
*dos_header
;
2822 IMAGE_NT_HEADERS64
*nt_headers
;
2823 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2824 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2825 guint32 resource_rva
, entries
, i
;
2826 gpointer ret
= NULL
;
2828 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2829 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2830 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2832 mono_w32error_set_last (ERROR_INVALID_DATA
);
2836 if (map_size
< sizeof(IMAGE_NT_HEADERS64
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2837 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: File is too small: %" G_GUINT32_FORMAT
, __func__
, map_size
);
2839 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2843 nt_headers
= (IMAGE_NT_HEADERS64
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2844 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2845 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Bad NT signature 0x%x", __func__
,
2846 nt_headers
->Signature
);
2848 mono_w32error_set_last (ERROR_INVALID_DATA
);
2852 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2853 /* Do 64-bit stuff */
2854 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2856 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2859 if (resource_rva
== 0) {
2860 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: No resources in file!", __func__
);
2862 mono_w32error_set_last (ERROR_INVALID_DATA
);
2866 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2867 if (resource_dir
== NULL
) {
2868 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Can't find resource directory", __func__
);
2870 mono_w32error_set_last (ERROR_INVALID_DATA
);
2874 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2875 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2877 for (i
= 0; i
< entries
; i
++) {
2878 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2879 ret
= scan_resource_dir (resource_dir
,
2880 (IMAGE_NT_HEADERS32
*)nt_headers
,
2881 file_map
, direntry
, 0, res_id
,
2892 find_pe_file_resources (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, gsize
*size
)
2894 /* Figure this out when we support 64bit PE files */
2896 return find_pe_file_resources32 (file_map
, map_size
, res_id
,
2899 return find_pe_file_resources64 (file_map
, map_size
, res_id
,
2905 unicode_chars (const gunichar2
*str
)
2910 if (str
[len
] == '\0') {
2918 unicode_compare (const gunichar2
*str1
, const gunichar2
*str2
)
2920 while (*str1
&& *str2
) {
2921 if (*str1
!= *str2
) {
2928 return(*str1
== *str2
);
2931 /* compare a little-endian null-terminated utf16 string and a normal string.
2932 * Can be used only for ascii or latin1 chars.
2935 unicode_string_equals (const gunichar2
*str1
, const gchar
*str2
)
2937 while (*str1
&& *str2
) {
2938 if (GUINT16_TO_LE (*str1
) != *str2
) {
2945 return(*str1
== *str2
);
2955 /* Returns a pointer to the value data, because there's no way to know
2956 * how big that data is (value_len is set to zero for most blocks :-( )
2958 static gconstpointer
2959 get_versioninfo_block (gconstpointer data
, version_data
*block
)
2961 block
->data_len
= GUINT16_FROM_LE (*((guint16
*)data
));
2962 data
= (char *)data
+ sizeof(guint16
);
2963 block
->value_len
= GUINT16_FROM_LE (*((guint16
*)data
));
2964 data
= (char *)data
+ sizeof(guint16
);
2966 /* No idea what the type is supposed to indicate */
2967 block
->type
= GUINT16_FROM_LE (*((guint16
*)data
));
2968 data
= (char *)data
+ sizeof(guint16
);
2969 block
->key
= ((gunichar2
*)data
);
2971 /* Skip over the key (including the terminator) */
2972 data
= ((gunichar2
*)data
) + (unicode_chars (block
->key
) + 1);
2974 /* align on a 32-bit boundary */
2980 static gconstpointer
2981 get_fixedfileinfo_block (gconstpointer data
, version_data
*block
)
2983 gconstpointer data_ptr
;
2984 VS_FIXEDFILEINFO
*ffi
;
2986 data_ptr
= get_versioninfo_block (data
, block
);
2988 if (block
->value_len
!= sizeof(VS_FIXEDFILEINFO
)) {
2989 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: FIXEDFILEINFO size mismatch", __func__
);
2993 if (!unicode_string_equals (block
->key
, "VS_VERSION_INFO")) {
2994 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: VS_VERSION_INFO mismatch", __func__
);
2999 ffi
= ((VS_FIXEDFILEINFO
*)data_ptr
);
3000 if ((ffi
->dwSignature
!= VS_FFI_SIGNATURE
) ||
3001 (ffi
->dwStrucVersion
!= VS_FFI_STRUCVERSION
)) {
3002 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: FIXEDFILEINFO bad signature", __func__
);
3010 static gconstpointer
3011 get_varfileinfo_block (gconstpointer data_ptr
, version_data
*block
)
3013 /* data is pointing at a Var block
3015 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3020 static gconstpointer
3021 get_string_block (gconstpointer data_ptr
, const gunichar2
*string_key
, gpointer
*string_value
,
3022 guint32
*string_value_len
, version_data
*block
)
3024 guint16 data_len
= block
->data_len
;
3025 guint16 string_len
= 28; /* Length of the StringTable block */
3026 char *orig_data_ptr
= (char *)data_ptr
- 28;
3028 /* data_ptr is pointing at an array of one or more String blocks
3029 * with total length (not including alignment padding) of
3032 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3033 /* align on a 32-bit boundary */
3036 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3037 if (block
->data_len
== 0) {
3038 /* We must have hit padding, so give up
3041 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3046 string_len
= string_len
+ block
->data_len
;
3048 if (string_key
!= NULL
&&
3049 string_value
!= NULL
&&
3050 string_value_len
!= NULL
&&
3051 unicode_compare (string_key
, block
->key
) == TRUE
) {
3052 *string_value
= (gpointer
)data_ptr
;
3053 *string_value_len
= block
->value_len
;
3056 /* Skip over the value */
3057 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3063 /* Returns a pointer to the byte following the Stringtable block, or
3064 * NULL if the data read hits padding. We can't recover from this
3065 * because the data length does not include padding bytes, so it's not
3066 * possible to just return the start position + length
3068 * If lang == NULL it means we're just stepping through this block
3070 static gconstpointer
3071 get_stringtable_block (gconstpointer data_ptr
, gchar
*lang
, const gunichar2
*string_key
, gpointer
*string_value
,
3072 guint32
*string_value_len
, version_data
*block
)
3074 guint16 data_len
= block
->data_len
;
3075 guint16 string_len
= 36; /* length of the StringFileInfo block */
3077 gchar
*lowercase_lang
;
3079 /* data_ptr is pointing at an array of StringTable blocks,
3080 * with total length (not including alignment padding) of
3084 while(string_len
< data_len
) {
3085 /* align on a 32-bit boundary */
3088 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3089 if (block
->data_len
== 0) {
3090 /* We must have hit padding, so give up
3093 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3097 string_len
= string_len
+ block
->data_len
;
3099 found_lang
= g_utf16_to_utf8 (block
->key
, 8, NULL
, NULL
, NULL
);
3100 if (found_lang
== NULL
) {
3101 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid language key, giving up", __func__
);
3105 lowercase_lang
= g_utf8_strdown (found_lang
, -1);
3106 g_free (found_lang
);
3107 found_lang
= lowercase_lang
;
3108 lowercase_lang
= NULL
;
3110 if (lang
!= NULL
&& !strcmp (found_lang
, lang
)) {
3111 /* Got the one we're interested in */
3112 data_ptr
= get_string_block (data_ptr
, string_key
,
3114 string_value_len
, block
);
3116 data_ptr
= get_string_block (data_ptr
, NULL
, NULL
,
3120 g_free (found_lang
);
3122 if (data_ptr
== NULL
) {
3123 /* Child block hit padding */
3124 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3132 #if G_BYTE_ORDER == G_BIG_ENDIAN
3133 static gconstpointer
3134 big_up_string_block (gconstpointer data_ptr
, version_data
*block
)
3136 guint16 data_len
= block
->data_len
;
3137 guint16 string_len
= 28; /* Length of the StringTable block */
3139 char *orig_data_ptr
= (char *)data_ptr
- 28;
3141 /* data_ptr is pointing at an array of one or more String
3142 * blocks with total length (not including alignment padding)
3145 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3146 /* align on a 32-bit boundary */
3149 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3150 if (block
->data_len
== 0) {
3151 /* We must have hit padding, so give up
3154 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3158 string_len
= string_len
+ block
->data_len
;
3160 big_value
= g_convert ((gchar
*)block
->key
,
3161 unicode_chars (block
->key
) * 2,
3162 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3164 if (big_value
== NULL
) {
3165 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid string, giving up", __func__
);
3169 /* The swapped string should be exactly the same
3170 * length as the original little-endian one, but only
3171 * copy the number of original chars just to be on the
3174 memcpy (block
->key
, big_value
, unicode_chars (block
->key
) * 2);
3177 big_value
= g_convert ((gchar
*)data_ptr
,
3178 unicode_chars (data_ptr
) * 2,
3179 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3181 if (big_value
== NULL
) {
3182 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid data string, giving up", __func__
);
3185 memcpy ((gpointer
)data_ptr
, big_value
,
3186 unicode_chars (data_ptr
) * 2);
3189 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3195 /* Returns a pointer to the byte following the Stringtable block, or
3196 * NULL if the data read hits padding. We can't recover from this
3197 * because the data length does not include padding bytes, so it's not
3198 * possible to just return the start position + length
3200 static gconstpointer
3201 big_up_stringtable_block (gconstpointer data_ptr
, version_data
*block
)
3203 guint16 data_len
= block
->data_len
;
3204 guint16 string_len
= 36; /* length of the StringFileInfo block */
3207 /* data_ptr is pointing at an array of StringTable blocks,
3208 * with total length (not including alignment padding) of
3212 while(string_len
< data_len
) {
3213 /* align on a 32-bit boundary */
3216 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3217 if (block
->data_len
== 0) {
3218 /* We must have hit padding, so give up
3221 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3225 string_len
= string_len
+ block
->data_len
;
3227 big_value
= g_convert ((gchar
*)block
->key
, 16, "UTF-16BE",
3228 "UTF-16LE", NULL
, NULL
, NULL
);
3229 if (big_value
== NULL
) {
3230 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Didn't find a valid string, giving up", __func__
);
3234 memcpy (block
->key
, big_value
, 16);
3237 data_ptr
= big_up_string_block (data_ptr
, block
);
3239 if (data_ptr
== NULL
) {
3240 /* Child block hit padding */
3241 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3249 /* Follows the data structures and turns all UTF-16 strings from the
3250 * LE found in the resource section into UTF-16BE
3253 big_up (gconstpointer datablock
, guint32 size
)
3255 gconstpointer data_ptr
;
3256 gint32 data_len
; /* signed to guard against underflow */
3259 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3260 if (data_ptr
!= NULL
) {
3261 VS_FIXEDFILEINFO
*ffi
= (VS_FIXEDFILEINFO
*)data_ptr
;
3263 /* Byteswap all the fields */
3264 ffi
->dwFileVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionMS
);
3265 ffi
->dwFileVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionLS
);
3266 ffi
->dwProductVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionMS
);
3267 ffi
->dwProductVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionLS
);
3268 ffi
->dwFileFlagsMask
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlagsMask
);
3269 ffi
->dwFileFlags
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlags
);
3270 ffi
->dwFileOS
= GUINT32_SWAP_LE_BE (ffi
->dwFileOS
);
3271 ffi
->dwFileType
= GUINT32_SWAP_LE_BE (ffi
->dwFileType
);
3272 ffi
->dwFileSubtype
= GUINT32_SWAP_LE_BE (ffi
->dwFileSubtype
);
3273 ffi
->dwFileDateMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateMS
);
3274 ffi
->dwFileDateLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateLS
);
3276 /* The FFI and header occupies the first 92 bytes
3278 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3279 data_len
= block
.data_len
- 92;
3281 /* There now follow zero or one StringFileInfo blocks
3282 * and zero or one VarFileInfo blocks
3284 while (data_len
> 0) {
3285 /* align on a 32-bit boundary */
3288 data_ptr
= get_versioninfo_block (data_ptr
, &block
);
3289 if (block
.data_len
== 0) {
3290 /* We must have hit padding, so give
3293 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3297 data_len
= data_len
- block
.data_len
;
3299 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3300 data_ptr
= get_varfileinfo_block (data_ptr
,
3302 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3303 } else if (unicode_string_equals (block
.key
,
3304 "StringFileInfo")) {
3305 data_ptr
= big_up_stringtable_block (data_ptr
,
3309 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Not a valid VERSIONINFO child block", __func__
);
3313 if (data_ptr
== NULL
) {
3314 /* Child block hit padding */
3315 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3324 mono_w32process_get_fileversion_info (const gunichar2
*filename
, gpointer
*data
)
3327 gpointer versioninfo
;
3335 file_map
= mono_pe_file_map (filename
, &map_size
, &map_handle
);
3339 versioninfo
= find_pe_file_resources (file_map
, map_size
, RT_VERSION
, 0, &datasize
);
3341 mono_pe_file_unmap (file_map
, map_handle
);
3345 *data
= g_malloc0 (datasize
);
3347 /* This could probably process the data so that mono_w32process_ver_query_value() doesn't have to follow the
3348 * data blocks every time. But hey, these functions aren't likely to appear in many profiles. */
3349 memcpy (*data
, versioninfo
, datasize
);
3351 #if G_BYTE_ORDER == G_BIG_ENDIAN
3352 big_up (*data
, datasize
);
3355 mono_pe_file_unmap (file_map
, map_handle
);
3361 mono_w32process_ver_query_value (gconstpointer datablock
, const gunichar2
*subblock
, gpointer
*buffer
, guint32
*len
)
3363 gchar
*subblock_utf8
, *lang_utf8
= NULL
;
3364 gboolean ret
= FALSE
;
3366 gconstpointer data_ptr
;
3367 gint32 data_len
; /* signed to guard against underflow */
3368 gboolean want_var
= FALSE
;
3369 gboolean want_string
= FALSE
;
3371 const gunichar2
*string_key
= NULL
;
3372 gpointer string_value
= NULL
;
3373 guint32 string_value_len
= 0;
3374 gchar
*lowercase_lang
;
3376 subblock_utf8
= g_utf16_to_utf8 (subblock
, -1, NULL
, NULL
, NULL
);
3377 if (subblock_utf8
== NULL
) {
3381 if (!strcmp (subblock_utf8
, "\\VarFileInfo\\Translation")) {
3383 } else if (!strncmp (subblock_utf8
, "\\StringFileInfo\\", 16)) {
3385 memcpy (lang
, subblock
+ 16, 8 * sizeof(gunichar2
));
3386 lang_utf8
= g_utf16_to_utf8 (lang
, 8, NULL
, NULL
, NULL
);
3387 lowercase_lang
= g_utf8_strdown (lang_utf8
, -1);
3389 lang_utf8
= lowercase_lang
;
3390 lowercase_lang
= NULL
;
3391 string_key
= subblock
+ 25;
3394 if (!strcmp (subblock_utf8
, "\\")) {
3395 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3396 if (data_ptr
!= NULL
) {
3397 *buffer
= (gpointer
)data_ptr
;
3398 *len
= block
.value_len
;
3402 } else if (want_var
|| want_string
) {
3403 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3404 if (data_ptr
!= NULL
) {
3405 /* The FFI and header occupies the first 92
3408 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3409 data_len
= block
.data_len
- 92;
3411 /* There now follow zero or one StringFileInfo
3412 * blocks and zero or one VarFileInfo blocks
3414 while (data_len
> 0) {
3415 /* align on a 32-bit boundary */
3418 data_ptr
= get_versioninfo_block (data_ptr
,
3420 if (block
.data_len
== 0) {
3421 /* We must have hit padding,
3422 * so give up processing now
3424 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Hit 0-length block, giving up", __func__
);
3428 data_len
= data_len
- block
.data_len
;
3430 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3431 data_ptr
= get_varfileinfo_block (data_ptr
, &block
);
3433 *buffer
= (gpointer
)data_ptr
;
3434 *len
= block
.value_len
;
3438 /* Skip over the Var block */
3439 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3441 } else if (unicode_string_equals (block
.key
, "StringFileInfo")) {
3442 data_ptr
= get_stringtable_block (data_ptr
, lang_utf8
, string_key
, &string_value
, &string_value_len
, &block
);
3444 string_value
!= NULL
&&
3445 string_value_len
!= 0) {
3446 *buffer
= string_value
;
3447 *len
= unicode_chars ((const gunichar2
*)string_value
) + 1; /* Include trailing null */
3453 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Not a valid VERSIONINFO child block", __func__
);
3457 if (data_ptr
== NULL
) {
3458 /* Child block hit padding */
3459 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_PROCESS
, "%s: Child block hit 0-length block, giving up", __func__
);
3471 g_free (subblock_utf8
);
3476 copy_lang (gunichar2
*lang_out
, guint32 lang_len
, const gchar
*text
)
3479 int chars
= strlen (text
);
3482 unitext
= g_utf8_to_utf16 (text
, -1, NULL
, NULL
, NULL
);
3483 g_assert (unitext
!= NULL
);
3485 if (chars
< (lang_len
- 1)) {
3486 memcpy (lang_out
, (gpointer
)unitext
, chars
* 2);
3487 lang_out
[chars
] = '\0';
3490 memcpy (lang_out
, (gpointer
)unitext
, (lang_len
- 1) * 2);
3491 lang_out
[lang_len
] = '\0';
3501 mono_w32process_ver_language_name (guint32 lang
, gunichar2
*lang_out
, guint32 lang_len
)
3503 int primary
, secondary
;
3504 const char *name
= NULL
;
3506 primary
= lang
& 0x3FF;
3507 secondary
= (lang
>> 10) & 0x3F;
3511 switch (secondary
) {
3512 case 0x01: name
= "Process Default Language"; break;
3516 switch (secondary
) {
3518 case 0x01: name
= "Arabic (Saudi Arabia)"; break;
3519 case 0x02: name
= "Arabic (Iraq)"; break;
3520 case 0x03: name
= "Arabic (Egypt)"; break;
3521 case 0x04: name
= "Arabic (Libya)"; break;
3522 case 0x05: name
= "Arabic (Algeria)"; break;
3523 case 0x06: name
= "Arabic (Morocco)"; break;
3524 case 0x07: name
= "Arabic (Tunisia)"; break;
3525 case 0x08: name
= "Arabic (Oman)"; break;
3526 case 0x09: name
= "Arabic (Yemen)"; break;
3527 case 0x0a: name
= "Arabic (Syria)"; break;
3528 case 0x0b: name
= "Arabic (Jordan)"; break;
3529 case 0x0c: name
= "Arabic (Lebanon)"; break;
3530 case 0x0d: name
= "Arabic (Kuwait)"; break;
3531 case 0x0e: name
= "Arabic (U.A.E.)"; break;
3532 case 0x0f: name
= "Arabic (Bahrain)"; break;
3533 case 0x10: name
= "Arabic (Qatar)"; break;
3537 switch (secondary
) {
3538 case 0x00: name
= "Bulgarian (Bulgaria)"; break;
3539 case 0x01: name
= "Bulgarian"; break;
3543 switch (secondary
) {
3544 case 0x00: name
= "Catalan (Spain)"; break;
3545 case 0x01: name
= "Catalan"; break;
3549 switch (secondary
) {
3551 case 0x01: name
= "Chinese (Taiwan)"; break;
3552 case 0x02: name
= "Chinese (PRC)"; break;
3553 case 0x03: name
= "Chinese (Hong Kong S.A.R.)"; break;
3554 case 0x04: name
= "Chinese (Singapore)"; break;
3555 case 0x05: name
= "Chinese (Macau S.A.R.)"; break;
3559 switch (secondary
) {
3560 case 0x00: name
= "Czech (Czech Republic)"; break;
3561 case 0x01: name
= "Czech"; break;
3565 switch (secondary
) {
3566 case 0x00: name
= "Danish (Denmark)"; break;
3567 case 0x01: name
= "Danish"; break;
3571 switch (secondary
) {
3573 case 0x01: name
= "German (Germany)"; break;
3574 case 0x02: name
= "German (Switzerland)"; break;
3575 case 0x03: name
= "German (Austria)"; break;
3576 case 0x04: name
= "German (Luxembourg)"; break;
3577 case 0x05: name
= "German (Liechtenstein)"; break;
3581 switch (secondary
) {
3582 case 0x00: name
= "Greek (Greece)"; break;
3583 case 0x01: name
= "Greek"; break;
3587 switch (secondary
) {
3589 case 0x01: name
= "English (United States)"; break;
3590 case 0x02: name
= "English (United Kingdom)"; break;
3591 case 0x03: name
= "English (Australia)"; break;
3592 case 0x04: name
= "English (Canada)"; break;
3593 case 0x05: name
= "English (New Zealand)"; break;
3594 case 0x06: name
= "English (Ireland)"; break;
3595 case 0x07: name
= "English (South Africa)"; break;
3596 case 0x08: name
= "English (Jamaica)"; break;
3597 case 0x09: name
= "English (Caribbean)"; break;
3598 case 0x0a: name
= "English (Belize)"; break;
3599 case 0x0b: name
= "English (Trinidad and Tobago)"; break;
3600 case 0x0c: name
= "English (Zimbabwe)"; break;
3601 case 0x0d: name
= "English (Philippines)"; break;
3602 case 0x10: name
= "English (India)"; break;
3603 case 0x11: name
= "English (Malaysia)"; break;
3604 case 0x12: name
= "English (Singapore)"; break;
3608 switch (secondary
) {
3609 case 0x00: name
= "Spanish (Spain)"; break;
3610 case 0x01: name
= "Spanish (Traditional Sort)"; break;
3611 case 0x02: name
= "Spanish (Mexico)"; break;
3612 case 0x03: name
= "Spanish (International Sort)"; break;
3613 case 0x04: name
= "Spanish (Guatemala)"; break;
3614 case 0x05: name
= "Spanish (Costa Rica)"; break;
3615 case 0x06: name
= "Spanish (Panama)"; break;
3616 case 0x07: name
= "Spanish (Dominican Republic)"; break;
3617 case 0x08: name
= "Spanish (Venezuela)"; break;
3618 case 0x09: name
= "Spanish (Colombia)"; break;
3619 case 0x0a: name
= "Spanish (Peru)"; break;
3620 case 0x0b: name
= "Spanish (Argentina)"; break;
3621 case 0x0c: name
= "Spanish (Ecuador)"; break;
3622 case 0x0d: name
= "Spanish (Chile)"; break;
3623 case 0x0e: name
= "Spanish (Uruguay)"; break;
3624 case 0x0f: name
= "Spanish (Paraguay)"; break;
3625 case 0x10: name
= "Spanish (Bolivia)"; break;
3626 case 0x11: name
= "Spanish (El Salvador)"; break;
3627 case 0x12: name
= "Spanish (Honduras)"; break;
3628 case 0x13: name
= "Spanish (Nicaragua)"; break;
3629 case 0x14: name
= "Spanish (Puerto Rico)"; break;
3630 case 0x15: name
= "Spanish (United States)"; break;
3634 switch (secondary
) {
3635 case 0x00: name
= "Finnish (Finland)"; break;
3636 case 0x01: name
= "Finnish"; break;
3640 switch (secondary
) {
3642 case 0x01: name
= "French (France)"; break;
3643 case 0x02: name
= "French (Belgium)"; break;
3644 case 0x03: name
= "French (Canada)"; break;
3645 case 0x04: name
= "French (Switzerland)"; break;
3646 case 0x05: name
= "French (Luxembourg)"; break;
3647 case 0x06: name
= "French (Monaco)"; break;
3651 switch (secondary
) {
3652 case 0x00: name
= "Hebrew (Israel)"; break;
3653 case 0x01: name
= "Hebrew"; break;
3657 switch (secondary
) {
3658 case 0x00: name
= "Hungarian (Hungary)"; break;
3659 case 0x01: name
= "Hungarian"; break;
3663 switch (secondary
) {
3664 case 0x00: name
= "Icelandic (Iceland)"; break;
3665 case 0x01: name
= "Icelandic"; break;
3669 switch (secondary
) {
3671 case 0x01: name
= "Italian (Italy)"; break;
3672 case 0x02: name
= "Italian (Switzerland)"; break;
3676 switch (secondary
) {
3677 case 0x00: name
= "Japanese (Japan)"; break;
3678 case 0x01: name
= "Japanese"; break;
3682 switch (secondary
) {
3683 case 0x00: name
= "Korean (Korea)"; break;
3684 case 0x01: name
= "Korean"; break;
3688 switch (secondary
) {
3690 case 0x01: name
= "Dutch (Netherlands)"; break;
3691 case 0x02: name
= "Dutch (Belgium)"; break;
3695 switch (secondary
) {
3697 case 0x01: name
= "Norwegian (Bokmal)"; break;
3698 case 0x02: name
= "Norwegian (Nynorsk)"; break;
3702 switch (secondary
) {
3703 case 0x00: name
= "Polish (Poland)"; break;
3704 case 0x01: name
= "Polish"; break;
3708 switch (secondary
) {
3710 case 0x01: name
= "Portuguese (Brazil)"; break;
3711 case 0x02: name
= "Portuguese (Portugal)"; break;
3715 switch (secondary
) {
3716 case 0x01: name
= "Romansh (Switzerland)"; break;
3720 switch (secondary
) {
3721 case 0x00: name
= "Romanian (Romania)"; break;
3722 case 0x01: name
= "Romanian"; break;
3726 switch (secondary
) {
3727 case 0x00: name
= "Russian (Russia)"; break;
3728 case 0x01: name
= "Russian"; break;
3732 switch (secondary
) {
3733 case 0x00: name
= "Croatian (Croatia)"; break;
3734 case 0x01: name
= "Croatian"; break;
3735 case 0x02: name
= "Serbian (Latin)"; break;
3736 case 0x03: name
= "Serbian (Cyrillic)"; break;
3737 case 0x04: name
= "Croatian (Bosnia and Herzegovina)"; break;
3738 case 0x05: name
= "Bosnian (Latin, Bosnia and Herzegovina)"; break;
3739 case 0x06: name
= "Serbian (Latin, Bosnia and Herzegovina)"; break;
3740 case 0x07: name
= "Serbian (Cyrillic, Bosnia and Herzegovina)"; break;
3741 case 0x08: name
= "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break;
3745 switch (secondary
) {
3746 case 0x00: name
= "Slovak (Slovakia)"; break;
3747 case 0x01: name
= "Slovak"; break;
3751 switch (secondary
) {
3752 case 0x00: name
= "Albanian (Albania)"; break;
3753 case 0x01: name
= "Albanian"; break;
3757 switch (secondary
) {
3758 case 0x00: name
= "Swedish (Sweden)"; break;
3759 case 0x01: name
= "Swedish"; break;
3760 case 0x02: name
= "Swedish (Finland)"; break;
3764 switch (secondary
) {
3765 case 0x00: name
= "Thai (Thailand)"; break;
3766 case 0x01: name
= "Thai"; break;
3770 switch (secondary
) {
3771 case 0x00: name
= "Turkish (Turkey)"; break;
3772 case 0x01: name
= "Turkish"; break;
3776 switch (secondary
) {
3777 case 0x00: name
= "Urdu (Islamic Republic of Pakistan)"; break;
3778 case 0x01: name
= "Urdu"; break;
3782 switch (secondary
) {
3783 case 0x00: name
= "Indonesian (Indonesia)"; break;
3784 case 0x01: name
= "Indonesian"; break;
3788 switch (secondary
) {
3789 case 0x00: name
= "Ukrainian (Ukraine)"; break;
3790 case 0x01: name
= "Ukrainian"; break;
3794 switch (secondary
) {
3795 case 0x00: name
= "Belarusian (Belarus)"; break;
3796 case 0x01: name
= "Belarusian"; break;
3800 switch (secondary
) {
3801 case 0x00: name
= "Slovenian (Slovenia)"; break;
3802 case 0x01: name
= "Slovenian"; break;
3806 switch (secondary
) {
3807 case 0x00: name
= "Estonian (Estonia)"; break;
3808 case 0x01: name
= "Estonian"; break;
3812 switch (secondary
) {
3813 case 0x00: name
= "Latvian (Latvia)"; break;
3814 case 0x01: name
= "Latvian"; break;
3818 switch (secondary
) {
3819 case 0x00: name
= "Lithuanian (Lithuania)"; break;
3820 case 0x01: name
= "Lithuanian"; break;
3824 switch (secondary
) {
3825 case 0x01: name
= "Tajik (Tajikistan)"; break;
3829 switch (secondary
) {
3830 case 0x00: name
= "Farsi (Iran)"; break;
3831 case 0x01: name
= "Farsi"; break;
3835 switch (secondary
) {
3836 case 0x00: name
= "Vietnamese (Viet Nam)"; break;
3837 case 0x01: name
= "Vietnamese"; break;
3841 switch (secondary
) {
3842 case 0x00: name
= "Armenian (Armenia)"; break;
3843 case 0x01: name
= "Armenian"; break;
3847 switch (secondary
) {
3848 case 0x00: name
= "Azeri (Latin) (Azerbaijan)"; break;
3849 case 0x01: name
= "Azeri (Latin)"; break;
3850 case 0x02: name
= "Azeri (Cyrillic)"; break;
3854 switch (secondary
) {
3855 case 0x00: name
= "Basque (Spain)"; break;
3856 case 0x01: name
= "Basque"; break;
3860 switch (secondary
) {
3861 case 0x01: name
= "Upper Sorbian (Germany)"; break;
3862 case 0x02: name
= "Lower Sorbian (Germany)"; break;
3866 switch (secondary
) {
3867 case 0x00: name
= "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
3868 case 0x01: name
= "FYRO Macedonian"; break;
3872 switch (secondary
) {
3873 case 0x00: name
= "Tswana (South Africa)"; break;
3874 case 0x01: name
= "Tswana"; break;
3878 switch (secondary
) {
3879 case 0x00: name
= "Xhosa (South Africa)"; break;
3880 case 0x01: name
= "Xhosa"; break;
3884 switch (secondary
) {
3885 case 0x00: name
= "Zulu (South Africa)"; break;
3886 case 0x01: name
= "Zulu"; break;
3890 switch (secondary
) {
3891 case 0x00: name
= "Afrikaans (South Africa)"; break;
3892 case 0x01: name
= "Afrikaans"; break;
3896 switch (secondary
) {
3897 case 0x00: name
= "Georgian (Georgia)"; break;
3898 case 0x01: name
= "Georgian"; break;
3902 switch (secondary
) {
3903 case 0x00: name
= "Faroese (Faroe Islands)"; break;
3904 case 0x01: name
= "Faroese"; break;
3908 switch (secondary
) {
3909 case 0x00: name
= "Hindi (India)"; break;
3910 case 0x01: name
= "Hindi"; break;
3914 switch (secondary
) {
3915 case 0x00: name
= "Maltese (Malta)"; break;
3916 case 0x01: name
= "Maltese"; break;
3920 switch (secondary
) {
3921 case 0x00: name
= "Sami (Northern) (Norway)"; break;
3922 case 0x01: name
= "Sami, Northern (Norway)"; break;
3923 case 0x02: name
= "Sami, Northern (Sweden)"; break;
3924 case 0x03: name
= "Sami, Northern (Finland)"; break;
3925 case 0x04: name
= "Sami, Lule (Norway)"; break;
3926 case 0x05: name
= "Sami, Lule (Sweden)"; break;
3927 case 0x06: name
= "Sami, Southern (Norway)"; break;
3928 case 0x07: name
= "Sami, Southern (Sweden)"; break;
3929 case 0x08: name
= "Sami, Skolt (Finland)"; break;
3930 case 0x09: name
= "Sami, Inari (Finland)"; break;
3934 switch (secondary
) {
3935 case 0x02: name
= "Irish (Ireland)"; break;
3939 switch (secondary
) {
3941 case 0x01: name
= "Malay (Malaysia)"; break;
3942 case 0x02: name
= "Malay (Brunei Darussalam)"; break;
3946 switch (secondary
) {
3947 case 0x00: name
= "Kazakh (Kazakhstan)"; break;
3948 case 0x01: name
= "Kazakh"; break;
3952 switch (secondary
) {
3953 case 0x00: name
= "Kyrgyz (Kyrgyzstan)"; break;
3954 case 0x01: name
= "Kyrgyz (Cyrillic)"; break;
3958 switch (secondary
) {
3959 case 0x00: name
= "Swahili (Kenya)"; break;
3960 case 0x01: name
= "Swahili"; break;
3964 switch (secondary
) {
3965 case 0x01: name
= "Turkmen (Turkmenistan)"; break;
3969 switch (secondary
) {
3970 case 0x00: name
= "Uzbek (Latin) (Uzbekistan)"; break;
3971 case 0x01: name
= "Uzbek (Latin)"; break;
3972 case 0x02: name
= "Uzbek (Cyrillic)"; break;
3976 switch (secondary
) {
3977 case 0x00: name
= "Tatar (Russia)"; break;
3978 case 0x01: name
= "Tatar"; break;
3982 switch (secondary
) {
3984 case 0x01: name
= "Bengali (India)"; break;
3988 switch (secondary
) {
3989 case 0x00: name
= "Punjabi (India)"; break;
3990 case 0x01: name
= "Punjabi"; break;
3994 switch (secondary
) {
3995 case 0x00: name
= "Gujarati (India)"; break;
3996 case 0x01: name
= "Gujarati"; break;
4000 switch (secondary
) {
4001 case 0x00: name
= "Tamil (India)"; break;
4002 case 0x01: name
= "Tamil"; break;
4006 switch (secondary
) {
4007 case 0x00: name
= "Telugu (India)"; break;
4008 case 0x01: name
= "Telugu"; break;
4012 switch (secondary
) {
4013 case 0x00: name
= "Kannada (India)"; break;
4014 case 0x01: name
= "Kannada"; break;
4018 switch (secondary
) {
4020 case 0x01: name
= "Malayalam (India)"; break;
4024 switch (secondary
) {
4025 case 0x01: name
= "Assamese (India)"; break;
4029 switch (secondary
) {
4030 case 0x00: name
= "Marathi (India)"; break;
4031 case 0x01: name
= "Marathi"; break;
4035 switch (secondary
) {
4036 case 0x00: name
= "Sanskrit (India)"; break;
4037 case 0x01: name
= "Sanskrit"; break;
4041 switch (secondary
) {
4042 case 0x00: name
= "Mongolian (Mongolia)"; break;
4043 case 0x01: name
= "Mongolian (Cyrillic)"; break;
4044 case 0x02: name
= "Mongolian (PRC)"; break;
4048 switch (secondary
) {
4049 case 0x01: name
= "Tibetan (PRC)"; break;
4050 case 0x02: name
= "Tibetan (Bhutan)"; break;
4054 switch (secondary
) {
4055 case 0x00: name
= "Welsh (United Kingdom)"; break;
4056 case 0x01: name
= "Welsh"; break;
4060 switch (secondary
) {
4061 case 0x01: name
= "Khmer (Cambodia)"; break;
4065 switch (secondary
) {
4066 case 0x01: name
= "Lao (Lao PDR)"; break;
4070 switch (secondary
) {
4071 case 0x00: name
= "Galician (Spain)"; break;
4072 case 0x01: name
= "Galician"; break;
4076 switch (secondary
) {
4077 case 0x00: name
= "Konkani (India)"; break;
4078 case 0x01: name
= "Konkani"; break;
4082 switch (secondary
) {
4083 case 0x00: name
= "Syriac (Syria)"; break;
4084 case 0x01: name
= "Syriac"; break;
4088 switch (secondary
) {
4089 case 0x01: name
= "Sinhala (Sri Lanka)"; break;
4093 switch (secondary
) {
4094 case 0x01: name
= "Inuktitut (Syllabics, Canada)"; break;
4095 case 0x02: name
= "Inuktitut (Latin, Canada)"; break;
4099 switch (secondary
) {
4100 case 0x01: name
= "Amharic (Ethiopia)"; break;
4104 switch (secondary
) {
4105 case 0x02: name
= "Tamazight (Algeria, Latin)"; break;
4109 switch (secondary
) {
4110 case 0x01: name
= "Nepali (Nepal)"; break;
4114 switch (secondary
) {
4115 case 0x01: name
= "Frisian (Netherlands)"; break;
4119 switch (secondary
) {
4120 case 0x01: name
= "Pashto (Afghanistan)"; break;
4124 switch (secondary
) {
4125 case 0x01: name
= "Filipino (Philippines)"; break;
4129 switch (secondary
) {
4130 case 0x00: name
= "Divehi (Maldives)"; break;
4131 case 0x01: name
= "Divehi"; break;
4135 switch (secondary
) {
4136 case 0x01: name
= "Hausa (Nigeria, Latin)"; break;
4140 switch (secondary
) {
4141 case 0x01: name
= "Yoruba (Nigeria)"; break;
4145 switch (secondary
) {
4147 case 0x01: name
= "Quechua (Bolivia)"; break;
4148 case 0x02: name
= "Quechua (Ecuador)"; break;
4149 case 0x03: name
= "Quechua (Peru)"; break;
4153 switch (secondary
) {
4154 case 0x00: name
= "Northern Sotho (South Africa)"; break;
4155 case 0x01: name
= "Northern Sotho"; break;
4159 switch (secondary
) {
4160 case 0x01: name
= "Bashkir (Russia)"; break;
4164 switch (secondary
) {
4165 case 0x01: name
= "Luxembourgish (Luxembourg)"; break;
4169 switch (secondary
) {
4170 case 0x01: name
= "Greenlandic (Greenland)"; break;
4174 switch (secondary
) {
4175 case 0x01: name
= "Yi (PRC)"; break;
4179 switch (secondary
) {
4180 case 0x01: name
= "Mapudungun (Chile)"; break;
4184 switch (secondary
) {
4185 case 0x01: name
= "Mohawk (Mohawk)"; break;
4189 switch (secondary
) {
4190 case 0x01: name
= "Breton (France)"; break;
4194 switch (secondary
) {
4195 case 0x00: name
= "Invariant Language (Invariant Country)"; break;
4199 switch (secondary
) {
4200 case 0x01: name
= "Uighur (PRC)"; break;
4204 switch (secondary
) {
4205 case 0x00: name
= "Maori (New Zealand)"; break;
4206 case 0x01: name
= "Maori"; break;
4210 switch (secondary
) {
4211 case 0x01: name
= "Corsican (France)"; break;
4215 switch (secondary
) {
4216 case 0x01: name
= "Alsatian (France)"; break;
4220 switch (secondary
) {
4221 case 0x01: name
= "Yakut (Russia)"; break;
4225 switch (secondary
) {
4226 case 0x01: name
= "K'iche (Guatemala)"; break;
4230 switch (secondary
) {
4231 case 0x01: name
= "Kinyarwanda (Rwanda)"; break;
4235 switch (secondary
) {
4236 case 0x01: name
= "Wolof (Senegal)"; break;
4240 switch (secondary
) {
4241 case 0x01: name
= "Dari (Afghanistan)"; break;
4246 name
= "Language Neutral";
4251 name
= "Language Neutral";
4253 return copy_lang (lang_out
, lang_len
, name
);
4256 #else /* ENABLE_NETCORE */
4259 mono_w32process_init (void)
4264 mono_w32process_cleanup (void)
4269 mono_w32process_set_cli_launcher (gchar
*path
)
4274 mono_w32process_signal_finished (void)
4278 #endif /* ENABLE_NETCORE */