[metadata] Fix leaks when handling a few attributes (#16675)
[mono-project.git] / mono / metadata / w32process-unix.c
blobe9da730b97a56477a26c17db7e8c6f24aed6bfa0
1 /**
2 * \file
3 * System.Diagnostics.Process support
5 * Author:
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.
13 #include <config.h>
14 #include <glib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <pthread.h>
19 #include <sched.h>
20 #include <sys/time.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #ifdef HAVE_SIGNAL_H
26 #include <signal.h>
27 #endif
28 #include <sys/time.h>
29 #include <fcntl.h>
30 #ifdef HAVE_SYS_PARAM_H
31 #include <sys/param.h>
32 #endif
33 #include <ctype.h>
35 #ifdef HAVE_SYS_WAIT_H
36 #include <sys/wait.h>
37 #endif
38 #ifdef HAVE_SYS_RESOURCE_H
39 #include <sys/resource.h>
40 #endif
42 #ifdef HAVE_SYS_MKDEV_H
43 #include <sys/mkdev.h>
44 #endif
46 #ifdef HAVE_UTIME_H
47 #include <utime.h>
48 #endif
50 // For close_my_fds
51 #if defined (_AIX)
52 #include <procinfo.h>
53 #elif defined (__FreeBSD__)
54 #include <sys/sysctl.h>
55 #include <sys/user.h>
56 #include <libutil.h>
57 #elif defined(__linux__)
58 #include <dirent.h>
59 #endif
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
95 #ifndef MAXPATHLEN
96 #define MAXPATHLEN 242
97 #endif
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)
112 G_BEGIN_DECLS
113 gchar ***_NSGetEnviron(void);
114 G_END_DECLS
115 #define environ (*_NSGetEnviron())
116 #else
117 static char *mono_environ[1] = { NULL };
118 #define environ mono_environ
119 #endif /* defined (TARGET_OSX) */
120 #else
121 G_BEGIN_DECLS
122 extern char **environ;
123 G_END_DECLS
124 #endif
125 #endif
127 typedef enum {
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
137 } StartupFlags;
139 typedef struct {
140 gpointer input;
141 gpointer output;
142 gpointer error;
143 } StartupHandles;
145 typedef struct {
146 #if G_BYTE_ORDER == G_BIG_ENDIAN
147 guint32 highDateTime;
148 guint32 lowDateTime;
149 #else
150 guint32 lowDateTime;
151 guint32 highDateTime;
152 #endif
153 } ProcessTime;
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.
168 gpointer handle;
169 gboolean signalled;
170 struct _Process *next;
171 } Process;
173 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
174 typedef struct {
175 pid_t pid;
176 gboolean child;
177 guint32 exitstatus;
178 gpointer main_thread;
179 guint64 create_time;
180 guint64 exit_time;
181 char *pname;
182 size_t min_working_set;
183 size_t max_working_set;
184 gboolean exited;
185 Process *process;
186 } MonoW32HandleProcess;
189 * VS_VERSIONINFO:
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)
202 * StringFileInfo:
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.)
213 * StringTable:
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)
224 * String:
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.
235 * VarFileInfo:
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)
246 * Var:
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
259 #else
260 #define VS_FFI_SIGNATURE 0xfeef04bd
261 #define VS_FFI_STRUCVERSION 0x00010000
262 #endif
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));
310 #if HAVE_SIGACTION
311 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
312 #endif
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 };
325 static MonoBoolean
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. */
329 static gboolean
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)
335 if (pid == 0)
336 return FALSE;
337 if (kill (pid, 0) == 0)
338 return TRUE;
339 if (errno == EPERM)
340 return TRUE;
341 return FALSE;
342 #elif defined(__HAIKU__)
343 team_info teamInfo;
344 if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
345 return TRUE;
346 return FALSE;
347 #else
348 gchar *dir = g_strdup_printf ("/proc/%d", pid);
349 gboolean result = access (dir, F_OK) == 0;
350 g_free (dir);
351 return result;
352 #endif
355 static void
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);
363 static const gchar*
364 process_typename (void)
366 return "Process";
369 static gsize
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;
380 int status;
381 gint64 start, now;
382 Process *process;
384 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT ")", __func__, handle_data, timeout);
386 if (alerted)
387 *alerted = FALSE;
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;
426 g_assert (process);
428 start = mono_msec_ticks ();
429 now = start;
431 while (1) {
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);
436 } else {
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);
445 break;
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);
461 *alerted = TRUE;
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);
472 else
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;
487 static void
488 processes_cleanup (void)
490 static gint32 cleaning_up;
491 Process *process;
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)
498 return;
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
506 * on the handle */
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) {
518 * Unlink the entry.
520 if (process == processes)
521 processes = process->next;
522 else
523 prev->next = process->next;
525 mono_coop_sem_destroy (&process->exit_sem);
526 g_free (process);
527 } else {
528 prev = process;
530 process = next;
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);
540 static void
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 */
557 NULL, /* signal */
558 NULL, /* own */
559 NULL, /* is_owned */
560 process_wait, /* special_wait */
561 NULL, /* prewait */
562 process_details, /* details */
563 process_typename, /* typename */
564 process_typesize, /* typesize */
567 static void
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 ();
577 static void
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);
587 if (utf8_progname) {
588 slash = strrchr (utf8_progname, '/');
589 if (slash)
590 process_handle->pname = g_strdup (slash+1);
591 else
592 process_handle->pname = g_strdup (utf8_progname);
593 g_free (utf8_progname);
597 void
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);
620 void
621 mono_w32process_cleanup (void)
623 g_free (cli_launcher);
626 static int
627 len16 (const gunichar2 *str)
629 int len = 0;
631 while (*str++ != 0)
632 len++;
634 return len;
637 static gunichar2 *
638 utf16_concat (const gunichar2 *first, ...)
640 va_list args;
641 int total = 0, i;
642 const gunichar2 *s;
643 const gunichar2 *p;
644 gunichar2 *ret;
646 va_start (args, first);
647 total += len16 (first);
648 for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *))
649 total += len16 (s);
650 va_end (args);
652 ret = g_new (gunichar2, total + 1);
653 if (ret == NULL)
654 return NULL;
656 ret [total] = 0;
657 i = 0;
658 for (s = first; *s != 0; s++)
659 ret [i++] = *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++)
663 ret [i++] = *p;
665 va_end (args);
667 return ret;
670 guint32
671 mono_w32process_get_pid (gpointer handle)
673 MonoW32Handle *handle_data;
674 guint32 ret;
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);
679 return 0;
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);
686 return 0;
689 ret = ((MonoW32HandleProcess*) handle_data->specific)->pid;
691 mono_w32handle_unref (handle_data);
693 return ret;
696 typedef struct {
697 guint32 pid;
698 gpointer handle;
699 } GetProcessForeachData;
701 static gboolean
702 get_process_foreach_callback (MonoW32Handle *handle_data, gpointer user_data)
704 GetProcessForeachData *foreach_data;
705 MonoW32HandleProcess *process_handle;
706 pid_t pid;
708 if (handle_data->type != MONO_W32TYPE_PROCESS)
709 return FALSE;
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;
716 if (pid == 0)
717 return FALSE;
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
723 * unsignalled. */
724 if (foreach_data->pid != pid)
725 return FALSE;
726 if (mono_w32handle_issignalled (handle_data))
727 return FALSE;
729 foreach_data->handle = mono_w32handle_duplicate (handle_data);
730 return TRUE;
733 HANDLE
734 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid, MonoError *error)
736 GetProcessForeachData foreach_data;
737 gpointer handle;
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;
745 if (handle) {
746 /* get_process_foreach_callback already added a ref */
747 return handle;
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);
763 return NULL;
766 return handle;
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);
772 return NULL;
775 static gboolean
776 match_procname_to_modulename (char *procname, char *modulename)
778 char* lastsep = NULL;
779 char* lastsep2 = NULL;
780 char* pname = NULL;
781 char* mname = NULL;
782 gboolean result = FALSE;
784 if (procname == NULL || modulename == NULL)
785 return (FALSE);
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))
792 result = TRUE;
794 if (!result) {
795 lastsep = strrchr (mname, '/');
796 if (lastsep)
797 if (!strcmp (lastsep+1, pname))
798 result = TRUE;
799 if (!result) {
800 lastsep2 = strrchr (pname, '/');
801 if (lastsep2){
802 if (lastsep) {
803 if (!strcmp (lastsep+1, lastsep2+1))
804 result = TRUE;
805 } else {
806 if (!strcmp (mname, lastsep2+1))
807 result = TRUE;
813 g_free (pname);
814 g_free (mname);
816 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: result is %" G_GINT32_FORMAT, __func__, result);
817 return result;
820 gboolean
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);
828 int i;
829 pid_t pid;
830 char *pname = NULL;
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))
842 return FALSE;
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);
847 return FALSE;
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);
854 return FALSE;
857 process_handle = (MonoW32HandleProcess*) handle_data->specific;
859 pid = process_handle->pid;
860 pname = g_strdup (process_handle->pname);
862 if (!pname) {
863 modules[0] = NULL;
864 *needed = sizeof(gpointer);
865 mono_w32handle_unref (handle_data);
866 return TRUE;
869 mods = mono_w32process_get_modules (pid);
870 if (!mods) {
871 modules[0] = NULL;
872 *needed = sizeof(gpointer);
873 g_free (pname);
874 mono_w32handle_unref (handle_data);
875 return TRUE;
878 count = 0;
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
886 * be a problem.
888 modules[0] = NULL;
889 mods_iter = mods;
890 for (i = 0; mods_iter; i++) {
891 if (i < avail - 1) {
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;
897 else
898 modules[i + 1] = module->address_start;
900 mono_w32process_module_free ((MonoW32ProcessModule *)mods_iter->data);
901 mods_iter = g_slist_next (mods_iter);
902 count++;
905 /* count + 1 to leave slot 0 for the main module */
906 *needed = sizeof(gpointer) * (count + 1);
908 g_slist_free (mods);
909 g_free (pname);
910 mono_w32handle_unref (handle_data);
911 return TRUE;
914 gunichar2 *
915 mono_w32process_module_get_filename (gpointer handle, gpointer module, guint32 *len)
917 gint pid;
918 gsize bytes = 0;
919 gchar *path;
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);
925 *len = 0;
927 pid = mono_w32process_get_pid (handle);
928 if (pid == 0)
929 return NULL;
931 path = mono_w32process_get_path (pid);
932 if (path == NULL)
933 return NULL;
935 proc_path = mono_unicode_from_external (path, &bytes);
937 *len = bytes / sizeof (gunichar2);
939 g_free (path);
940 return proc_path;
943 gunichar2 *
944 mono_w32process_module_get_name (gpointer handle, gpointer module, guint32 *len)
946 MonoW32Handle *handle_data;
947 MonoW32HandleProcess *process_handle;
948 pid_t pid;
949 gunichar2 *procname;
950 char *procname_ext = NULL;
951 gsize bytes = 0;
952 GSList *mods = NULL, *mods_iter;
953 MonoW32ProcessModule *found_module;
954 char *pname = NULL;
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);
959 *len = 0;
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);
964 return NULL;
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);
971 return NULL;
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);
982 g_free (pname);
983 mono_w32handle_unref (handle_data);
984 return 0;
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);
1009 if (!procname_ext)
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);
1014 g_free (pname);
1016 if (procname_ext) {
1017 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Process name is [%s]", __func__,
1018 procname_ext);
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);
1023 /* bugger */
1024 g_free (procname_ext);
1025 mono_w32handle_unref (handle_data);
1026 return NULL;
1029 *len = bytes / sizeof (gunichar2);
1031 g_free (procname_ext);
1032 mono_w32handle_unref (handle_data);
1033 return procname;
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);
1038 return NULL;
1041 gboolean
1042 mono_w32process_module_get_information (gpointer handle, gpointer module, MODULEINFO *modinfo, guint32 size)
1044 MonoW32Handle *handle_data;
1045 MonoW32HandleProcess *process_handle;
1046 pid_t pid;
1047 GSList *mods = NULL, *mods_iter;
1048 MonoW32ProcessModule *found_module;
1049 gboolean ret = FALSE;
1050 char *pname = NULL;
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))
1056 return FALSE;
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);
1061 return FALSE;
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);
1068 return FALSE;
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);
1077 if (!mods) {
1078 g_free (pname);
1079 mono_w32handle_unref (handle_data);
1080 return FALSE;
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;
1089 if (ret == FALSE &&
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;
1095 ret = TRUE;
1098 mono_w32process_module_free (found_module);
1101 g_slist_free (mods);
1102 g_free (pname);
1103 mono_w32handle_unref (handle_data);
1104 return ret;
1107 static void
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] != '\'' )
1116 path[i] = '/';
1120 #if HAVE_SIGACTION
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);
1135 static void
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");
1147 #endif
1150 * mono_w32process_signal_finished:
1152 * Signal the exit semaphore for processes which have finished.
1154 void
1155 mono_w32process_signal_finished (void)
1157 mono_coop_mutex_lock (&processes_mutex);
1159 for (Process* process = processes; process; process = process->next) {
1160 int status = -1;
1161 int pid;
1163 do {
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).
1175 if (pid <= 0)
1176 continue;
1177 if (process->signalled)
1178 continue;
1180 process->signalled = TRUE;
1181 process->status = status;
1182 mono_coop_sem_post (&process->exit_sem);
1185 mono_coop_mutex_unlock (&processes_mutex);
1188 static gboolean
1189 is_readable_or_executable (const char *prog)
1191 struct stat buf;
1192 int a = access (prog, R_OK);
1193 int b = access (prog, X_OK);
1194 if (a != 0 && b != 0)
1195 return FALSE;
1196 if (stat (prog, &buf))
1197 return FALSE;
1198 if (S_ISREG (buf.st_mode))
1199 return TRUE;
1200 return FALSE;
1203 static gboolean
1204 is_executable (const char *prog)
1206 struct stat buf;
1207 if (access (prog, X_OK) != 0)
1208 return FALSE;
1209 if (stat (prog, &buf))
1210 return FALSE;
1211 if (S_ISREG (buf.st_mode))
1212 return TRUE;
1213 return FALSE;
1216 static gboolean
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);
1222 #else
1223 int file = open (filename, O_RDONLY);
1224 #endif
1225 off_t new_offset;
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;
1230 int num_read;
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.
1237 if (file < 0) {
1238 mono_set_errno (original_errno);
1239 return FALSE;
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. */
1247 if (file_size < 64)
1248 goto leave;
1250 num_read = read (file, buffer, 2);
1252 if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
1253 goto leave;
1255 new_offset = lseek (file, 60, SEEK_SET);
1257 if (new_offset != 60)
1258 goto leave;
1260 num_read = read (file, buffer, 4);
1262 if (num_read != 4)
1263 goto leave;
1264 pe_header_offset = buffer[0]
1265 | (buffer[1] << 8)
1266 | (buffer[2] << 16)
1267 | (buffer[3] << 24);
1269 if (pe_header_offset + 24 > file_size)
1270 goto leave;
1272 new_offset = lseek (file, pe_header_offset, SEEK_SET);
1274 if (new_offset != pe_header_offset)
1275 goto leave;
1277 num_read = read (file, buffer, 4);
1279 if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
1280 goto leave;
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)
1289 goto leave;
1291 num_read = read (file, buffer, 2);
1293 if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
1294 goto leave;
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)
1302 goto leave;
1304 num_read = read (file, buffer, 2);
1306 if (num_read != 2)
1307 goto leave;
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;
1315 else
1316 goto leave;
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)
1324 goto leave;
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))
1335 goto leave;
1337 managed = TRUE;
1339 leave:
1340 close (file);
1341 mono_set_errno (original_errno);
1342 return managed;
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
1348 * in close_my_fds.
1350 static guint32
1351 max_fd_count (void)
1353 #if defined (_AIX)
1354 struct procentry64 pe;
1355 pid_t p;
1356 p = getpid ();
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
1363 #endif
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
1371 * process_create.
1373 static void
1374 close_my_fds (void)
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. */
1389 struct dirent *dp;
1390 DIR *d;
1391 int fd;
1392 d = opendir ("/proc/self/fd/");
1393 if (d) {
1394 while ((dp = readdir (d)) != NULL) {
1395 if (dp->d_name [0] == '.')
1396 continue;
1397 fd = atoi (dp->d_name);
1398 if (fd > 2)
1399 close (fd);
1401 closedir (d);
1402 return;
1403 } else {
1404 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
1405 "%s: opening fd dir failed, using fallback",
1406 __func__);
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;
1414 int count, i;
1415 /* this is malloced but we won't need to free once we exec/exit */
1416 kif = kinfo_getfile (getpid (), &count);
1417 if (kif) {
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);
1423 return;
1424 } else {
1425 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
1426 "%s: kinfo_getfile failed, using fallback",
1427 __func__);
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;
1436 pid_t p;
1437 p = getpid ();
1438 fds = (struct fdsinfo_100K *) g_malloc0 (sizeof (struct fdsinfo_100K));
1439 if (!fds) {
1440 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
1441 "%s: fdsinfo alloc failed, using fallback",
1442 __func__);
1443 goto 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);
1451 return;
1452 } else {
1453 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
1454 "%s: getprocs64 failed, using fallback",
1455 __func__);
1457 fallback:
1458 #endif
1459 /* Fallback: Close FDs blindly, according to an FD limit */
1460 for (guint32 i = max_fd_count () - 1; i > 2; i--)
1461 close (i);
1464 static gboolean
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;
1471 guint32 i;
1472 gboolean ret = FALSE;
1473 gpointer handle = NULL;
1474 GError *gerr = NULL;
1475 int in_fd, out_fd, err_fd;
1476 pid_t pid = 0;
1477 int startup_pipe [2] = {-1, -1};
1478 int dummy;
1479 Process *process;
1480 ERROR_DECL (error);
1482 #if HAVE_SIGACTION
1483 mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
1484 #endif
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)
1505 * 5) $PATH
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);
1514 if (cmd == NULL) {
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);
1520 goto free_strings;
1523 switch_dir_separators(cmd);
1526 if (cmdline != NULL) {
1527 args = mono_unicode_to_external_checked (cmdline, error);
1528 if (args == NULL) {
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);
1533 goto free_strings;
1537 if (cwd != NULL) {
1538 dir = mono_unicode_to_external_checked (cwd, error);
1539 if (dir == NULL) {
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);
1544 goto free_strings;
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 :-( */
1553 if (cmd != NULL) {
1554 char *unquoted;
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
1558 * visible...
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",
1572 __func__, prog);
1573 g_free (unquoted);
1574 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1575 goto free_strings;
1577 } else {
1578 /* Search for file named by cmd in the current
1579 * directory
1581 char *curdir = g_get_current_dir ();
1583 prog = g_strdup_printf ("%s/%s", curdir, unquoted);
1584 g_free (curdir);
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",
1589 __func__, prog);
1590 g_free (unquoted);
1591 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1592 goto free_strings;
1595 g_free (unquoted);
1597 args_after_prog = args;
1598 } else {
1599 char *token = NULL;
1600 char quote;
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
1611 * duplicated)
1613 args_after_prog = args;
1615 /* Assume the opening quote will always be the first
1616 * character
1618 if (args[0] == '\"' || args [0] == '\'') {
1619 quote = 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);
1625 } else {
1626 /* Quotation mark appeared in the
1627 * middle of the token. Just give the
1628 * whole first token, quotes and all,
1629 * to exec.
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;
1640 break;
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) {
1652 /* Give up */
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);
1656 goto free_strings;
1659 /* Turn all the slashes round the right way. Only for
1660 * the prg. name
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
1667 * visible...
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",
1680 __func__, token);
1681 g_free (token);
1682 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1683 goto free_strings;
1685 } else {
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
1691 * place to look
1694 prog = g_strdup_printf ("%s/%s", curdir, token);
1695 g_free (curdir);
1697 /* I assume X_OK is the criterion to use,
1698 * rather than F_OK
1700 * X_OK is too strict *if* the target is a CLR binary
1702 if (!is_readable_or_executable (prog)) {
1703 g_free (prog);
1704 prog = g_find_program_in_path (token);
1705 if (prog == NULL) {
1706 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s", __func__, token);
1708 g_free (token);
1709 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
1710 goto free_strings;
1715 g_free (token);
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);
1729 if (newapp) {
1730 if (appname)
1731 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, (const gunichar2 *)NULL);
1732 else
1733 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, (const gunichar2 *)NULL);
1735 g_free (newapp);
1737 if (newcmd) {
1738 ret = process_create (NULL, newcmd, cwd, startup_handles, process_info);
1740 g_free (newcmd);
1742 goto free_strings;
1745 } else {
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);
1749 goto free_strings;
1753 if (args_after_prog != NULL && *args_after_prog) {
1754 char *qprog;
1756 qprog = g_shell_quote (prog);
1757 full_prog = g_strconcat (qprog, " ", args_after_prog, (const char*)NULL);
1758 g_free (qprog);
1759 } else {
1760 full_prog = g_shell_quote (prog);
1763 ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
1764 if (ret == FALSE) {
1765 g_message ("process_create: %s\n", gerr->message);
1766 g_error_free (gerr);
1767 gerr = NULL;
1768 goto free_strings;
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);
1775 } else {
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);
1804 } else {
1805 gsize env_count = 0;
1806 for (i = 0; environ[i] != NULL; i++)
1807 env_count++;
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);
1830 ret = FALSE;
1831 break;
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 */
1845 dup2 (in_fd, 0);
1846 dup2 (out_fd, 1);
1847 dup2 (err_fd, 2);
1849 /* Close this child's file handles. */
1850 close_my_fds ();
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]);
1860 #endif
1862 /* set cwd */
1863 if (dir != NULL && chdir (dir) == -1) {
1864 /* set error */
1865 _exit (-1);
1868 /* exec */
1869 execve (argv[0], argv, env_strings);
1871 /* set error */
1872 _exit (-1);
1874 break;
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));
1888 process->pid = pid;
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);
1899 g_free (process);
1901 mono_w32error_set_last (ERROR_OUTOFMEMORY);
1902 ret = FALSE;
1903 break;
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);
1929 break;
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]);
1940 free_strings:
1941 g_free (cmd);
1942 g_free (full_prog);
1943 g_free (prog);
1944 g_free (args);
1945 g_free (dir);
1946 g_strfreev (env_strings);
1947 g_strfreev (argv);
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 ();
1954 return ret;
1955 #else
1956 mono_w32error_set_last (ERROR_NOT_SUPPORTED);
1957 return FALSE;
1958 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
1961 MonoBoolean
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);
1967 gboolean ret;
1968 gboolean handler_needswait = FALSE;
1970 if (!coop.filename) {
1971 /* w2k returns TRUE for this, for some reason. */
1972 ret = TRUE;
1973 goto done;
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 :-(
1988 gunichar2 *args;
1989 args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters ? utf16_space : NULL, lpParameters, (const gunichar2 *)NULL);
1990 if (args == NULL) {
1991 mono_w32error_set_last (ERROR_INVALID_DATA);
1992 ret = FALSE;
1993 goto done;
1995 ret = process_create (NULL, args, lpDirectory, NULL, process_info);
1996 g_free (args);
1998 if (!ret && mono_w32error_get_last () == ERROR_OUTOFMEMORY)
1999 goto done;
2001 if (!ret) {
2003 #if defined(TARGET_IOS) || defined(TARGET_ANDROID)
2004 // don't try the "open" handlers on iOS/Android, they don't exist there anyway
2005 goto done;
2006 #endif
2008 static char *handler;
2009 static gunichar2 *handler_utf16;
2011 if (handler_utf16 == (gunichar2 *)-1) {
2012 ret = FALSE;
2013 goto done;
2016 #ifdef HOST_DARWIN
2017 handler = g_strdup ("/usr/bin/open");
2018 handler_needswait = TRUE;
2019 #else
2021 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
2022 * if that fails, try to use gnome-open, then kfmclient
2024 MONO_ENTER_GC_SAFE;
2025 handler = g_find_program_in_path ("xdg-open");
2026 if (handler != NULL)
2027 handler_needswait = TRUE;
2028 else {
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;
2034 ret = FALSE;
2035 } else {
2036 /* kfmclient needs exec argument */
2037 char *old = handler;
2038 handler = g_strconcat (old, " exec",
2039 (const char*)NULL);
2040 g_free (old);
2044 MONO_EXIT_GC_SAFE;
2045 if (ret == FALSE){
2046 goto done;
2048 #endif
2049 handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
2050 g_free (handler);
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
2056 * 371567.
2058 args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
2059 lpParameters ? utf16_space : NULL, lpParameters, (const gunichar2 *)NULL);
2060 if (args == NULL) {
2061 mono_w32error_set_last (ERROR_INVALID_DATA);
2062 ret = FALSE;
2063 goto done;
2065 ret = process_create (NULL, args, lpDirectory, NULL, process_info);
2066 g_free (args);
2067 if (!ret) {
2068 if (mono_w32error_get_last () != ERROR_OUTOFMEMORY)
2069 mono_w32error_set_last (ERROR_INVALID_DATA);
2070 ret = FALSE;
2071 goto done;
2074 if (handler_needswait) {
2075 gint32 exitcode;
2076 MonoW32HandleWaitRet waitret;
2077 waitret = process_wait ((MonoW32Handle*)process_info->process_handle, MONO_INFINITE_WAIT, NULL);
2078 (void)waitret;
2079 mono_get_exit_code_process (process_info->process_handle, &exitcode);
2080 if (exitcode != 0)
2081 ret = FALSE;
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;
2088 done:
2089 if (ret == FALSE) {
2090 process_info->pid = -mono_w32error_get_last ();
2091 } else {
2092 #if !defined(MONO_CROSS_COMPILE)
2093 process_info->pid = mono_w32process_get_pid (process_info->process_handle);
2094 #else
2095 process_info->pid = 0;
2096 #endif
2099 mono_createprocess_coop_cleanup (&coop);
2101 return ret;
2104 /* Only used when UseShellExecute is false */
2105 static gboolean
2106 process_get_complete_path (const gunichar2 *appname, gchar **completed)
2108 char *found = NULL;
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);
2115 result = TRUE;
2116 goto exit;
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);
2121 result = TRUE;
2122 goto exit;
2125 found = g_find_program_in_path (utf8app);
2126 if (found == NULL) {
2127 *completed = NULL;
2128 result = FALSE;
2129 goto exit;
2132 *completed = g_shell_quote (found);
2133 result = TRUE;
2134 exit:
2135 g_free (found);
2136 g_free (utf8app);
2137 return result;
2140 static gboolean
2141 process_get_shell_arguments (MonoCreateProcessCoop *coop, gunichar2 **shell_path)
2143 gchar *complete_path = NULL;
2145 *shell_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;
2155 MonoBoolean
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);
2162 gboolean ret;
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;
2173 ret = FALSE;
2174 goto exit;
2177 gunichar2 *args;
2178 args = coop.length.arguments ? coop.arguments : NULL;
2180 /* The default dir name is "". Turn that into NULL to mean "current directory" */
2181 gunichar2 *dir;
2182 dir = coop.length.working_directory ? coop.working_directory : NULL;
2184 ret = process_create (shell_path, args, dir, &startup_handles, process_info);
2186 if (!ret)
2187 process_info->pid = -mono_w32error_get_last ();
2189 exit:
2190 g_free (shell_path);
2191 mono_createprocess_coop_cleanup (&coop);
2192 return ret;
2195 /* Returns an array of pids */
2196 MonoArrayHandle
2197 ves_icall_System_Diagnostics_Process_GetProcesses_internal (MonoError *error)
2199 int count = 0;
2200 guint32 *raw = 0;
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.
2207 MONO_ENTER_GC_SAFE;
2208 pidarray = mono_process_list (&count);
2209 MONO_EXIT_GC_SAFE;
2210 if (!pidarray) {
2211 mono_error_set_not_supported (error, "This system does not support EnumProcesses");
2212 goto exit;
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;
2217 goto exit;
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));
2225 } else {
2226 for (int i = 0; i < count; ++i)
2227 raw [i] = GPOINTER_TO_UINT (pidarray [i]);
2230 MONO_EXIT_NO_SAFEPOINTS;
2232 exit:
2233 g_free (pidarray);
2234 return procs;
2237 void
2238 mono_w32process_set_cli_launcher (gchar *path)
2240 g_free (cli_launcher);
2241 cli_launcher = g_strdup (path);
2244 gpointer
2245 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (MonoError *error)
2247 return current_process;
2250 MonoBoolean
2251 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode, MonoError *error)
2253 return mono_get_exit_code_process (handle, exitcode);
2256 static MonoBoolean
2257 mono_get_exit_code_process (gpointer handle, gint32 *exitcode)
2259 MonoW32Handle *handle_data;
2260 MonoW32HandleProcess *process_handle;
2262 if (!exitcode)
2263 return FALSE;
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);
2268 return FALSE;
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);
2275 return FALSE;
2278 process_handle = (MonoW32HandleProcess*) handle_data->specific;
2280 if (process_handle->pid == current_pid) {
2281 *exitcode = STILL_ACTIVE;
2282 mono_w32handle_unref (handle_data);
2283 return TRUE;
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);
2296 return TRUE;
2299 MonoBoolean
2300 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle, MonoError *error)
2302 return mono_w32handle_close (handle);
2305 MonoBoolean
2306 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode, MonoError *error)
2308 #ifdef HAVE_KILL
2309 MonoW32Handle *handle_data;
2310 int ret;
2311 pid_t pid;
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);
2316 return FALSE;
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);
2323 return FALSE;
2326 pid = ((MonoW32HandleProcess*) handle_data->specific)->pid;
2328 ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
2329 if (ret == 0) {
2330 mono_w32handle_unref (handle_data);
2331 return TRUE;
2334 switch (errno) {
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);
2342 return FALSE;
2343 #else
2344 g_error ("kill() is not supported by this platform");
2345 #endif
2348 MonoBoolean
2349 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max, MonoError *error)
2351 MonoW32Handle *handle_data;
2352 MonoW32HandleProcess *process_handle;
2354 if (!min || !max)
2355 return FALSE;
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);
2360 return FALSE;
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);
2367 return FALSE;
2370 process_handle = (MonoW32HandleProcess*) handle_data->specific;
2372 if (!process_handle->child) {
2373 mono_w32handle_unref (handle_data);
2374 return FALSE;
2377 *min = process_handle->min_working_set;
2378 *max = process_handle->max_working_set;
2380 mono_w32handle_unref (handle_data);
2381 return TRUE;
2384 MonoBoolean
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);
2393 return FALSE;
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);
2400 return FALSE;
2403 process_handle = (MonoW32HandleProcess*) handle_data->specific;
2405 if (!process_handle->child) {
2406 mono_w32handle_unref (handle_data);
2407 return FALSE;
2410 process_handle->min_working_set = min;
2411 process_handle->max_working_set = max;
2413 mono_w32handle_unref (handle_data);
2414 return TRUE;
2417 gint32
2418 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle, MonoError *error)
2420 #ifdef HAVE_GETPRIORITY
2421 MonoW32Handle *handle_data;
2422 gint res;
2423 gint32 ret;
2424 pid_t pid;
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);
2429 return 0;
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);
2436 return 0;
2439 pid = ((MonoW32HandleProcess*) handle_data->specific)->pid;
2441 mono_set_errno (0);
2442 res = getpriority (PRIO_PROCESS, pid);
2443 if (res == -1 && errno != 0) {
2444 switch (errno) {
2445 case EPERM:
2446 case EACCES:
2447 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2448 break;
2449 case ESRCH:
2450 mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
2451 break;
2452 default:
2453 mono_w32error_set_last (ERROR_GEN_FAILURE);
2456 mono_w32handle_unref (handle_data);
2457 return 0;
2460 if (res == 0)
2461 ret = MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2462 else if (res < -15)
2463 ret = MONO_W32PROCESS_PRIORITY_CLASS_REALTIME;
2464 else if (res < -10)
2465 ret = MONO_W32PROCESS_PRIORITY_CLASS_HIGH;
2466 else if (res < 0)
2467 ret = MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2468 else if (res > 10)
2469 ret = MONO_W32PROCESS_PRIORITY_CLASS_IDLE;
2470 else if (res > 0)
2471 ret = MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2472 else
2473 ret = MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
2475 mono_w32handle_unref (handle_data);
2476 return ret;
2477 #else
2478 mono_w32error_set_last (ERROR_NOT_SUPPORTED);
2479 return 0;
2480 #endif
2483 MonoBoolean
2484 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass, MonoError *error)
2486 #ifdef HAVE_SETPRIORITY
2487 MonoW32Handle *handle_data;
2488 int ret;
2489 int prio;
2490 pid_t pid;
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);
2495 return FALSE;
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);
2502 return FALSE;
2505 pid = ((MonoW32HandleProcess*) handle_data->specific)->pid;
2507 switch (priorityClass) {
2508 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
2509 prio = 19;
2510 break;
2511 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
2512 prio = 10;
2513 break;
2514 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL:
2515 prio = 0;
2516 break;
2517 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
2518 prio = -5;
2519 break;
2520 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH:
2521 prio = -11;
2522 break;
2523 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME:
2524 prio = -20;
2525 break;
2526 default:
2527 mono_w32error_set_last (ERROR_INVALID_PARAMETER);
2528 mono_w32handle_unref (handle_data);
2529 return FALSE;
2532 ret = setpriority (PRIO_PROCESS, pid, prio);
2533 if (ret == -1) {
2534 switch (errno) {
2535 case EPERM:
2536 case EACCES:
2537 mono_w32error_set_last (ERROR_ACCESS_DENIED);
2538 break;
2539 case ESRCH:
2540 mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
2541 break;
2542 default:
2543 mono_w32error_set_last (ERROR_GEN_FAILURE);
2547 mono_w32handle_unref (handle_data);
2548 return ret == 0;
2549 #else
2550 mono_w32error_set_last (ERROR_NOT_SUPPORTED);
2551 return FALSE;
2552 #endif
2555 static void
2556 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
2558 processtime->lowDateTime = ticks & 0xFFFFFFFF;
2559 processtime->highDateTime = ticks >> 32;
2562 MonoBoolean
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 */
2571 return FALSE;
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);
2587 return FALSE;
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);
2594 return FALSE;
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);
2610 return TRUE;
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);
2628 #endif
2630 mono_w32handle_unref (handle_data);
2631 return TRUE;
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);
2638 guint32 i;
2640 for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) {
2641 guint32 size = GUINT32_FROM_LE (section->Misc.VirtualSize);
2642 if (size == 0) {
2643 size = GUINT32_FROM_LE (section->SizeOfRawData);
2646 if ((rva >= GUINT32_FROM_LE (section->VirtualAddress)) &&
2647 (rva < (GUINT32_FROM_LE (section->VirtualAddress) + size))) {
2648 return(section);
2652 return(NULL);
2655 /* This works for both 32bit and 64bit files, as the differences are
2656 * all after the section header block
2658 static gpointer
2659 get_ptr_from_rva (guint32 rva, IMAGE_NT_HEADERS32 *ntheaders, gpointer file_map)
2661 IMAGE_SECTION_HEADER *section_header;
2662 guint32 delta;
2664 section_header = get_enclosing_section_header (rva, ntheaders);
2665 if (section_header == NULL) {
2666 return(NULL);
2669 delta = (guint32)(GUINT32_FROM_LE (section_header->VirtualAddress) -
2670 GUINT32_FROM_LE (section_header->PointerToRawData));
2672 return((guint8 *)file_map + rva - delta);
2675 static gpointer
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;
2692 if (level == 0) {
2693 /* Normally holds a directory entry for each type of
2694 * resource
2696 if ((is_string == FALSE &&
2697 name_offset != res_id) ||
2698 (is_string == TRUE)) {
2699 return(NULL);
2701 } else if (level == 1) {
2702 /* Normally holds a directory entry for each resource
2703 * item
2705 } else if (level == 2) {
2706 /* Normally holds a directory entry for each language
2708 if ((is_string == FALSE &&
2709 name_offset != lang_id &&
2710 lang_id != 0) ||
2711 (is_string == TRUE)) {
2712 return(NULL);
2714 } else {
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);
2721 guint32 entries, i;
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];
2727 gpointer ret;
2729 ret = scan_resource_dir (root, nt_headers, file_map,
2730 sub_entry, level + 1, res_id,
2731 lang_id, size);
2732 if (ret != NULL) {
2733 return(ret);
2737 return(NULL);
2738 } else {
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));
2746 static gpointer
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);
2761 return(NULL);
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);
2768 return(NULL);
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);
2776 return(NULL);
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);
2782 } else {
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);
2790 return(NULL);
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);
2798 return(NULL);
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,
2809 lang_id, size);
2810 if (ret != NULL) {
2811 return(ret);
2815 return(NULL);
2818 static gpointer
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);
2833 return(NULL);
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);
2840 return(NULL);
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);
2849 return(NULL);
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);
2855 } else {
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);
2863 return(NULL);
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);
2871 return(NULL);
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,
2882 lang_id, size);
2883 if (ret != NULL) {
2884 return(ret);
2888 return(NULL);
2891 static gpointer
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 */
2895 if (1) {
2896 return find_pe_file_resources32 (file_map, map_size, res_id,
2897 lang_id, size);
2898 } else {
2899 return find_pe_file_resources64 (file_map, map_size, res_id,
2900 lang_id, size);
2904 static guint32
2905 unicode_chars (const gunichar2 *str)
2907 guint32 len = 0;
2909 do {
2910 if (str[len] == '\0') {
2911 return(len);
2913 len++;
2914 } while(1);
2917 static gboolean
2918 unicode_compare (const gunichar2 *str1, const gunichar2 *str2)
2920 while (*str1 && *str2) {
2921 if (*str1 != *str2) {
2922 return(FALSE);
2924 ++str1;
2925 ++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.
2934 static gboolean
2935 unicode_string_equals (const gunichar2 *str1, const gchar *str2)
2937 while (*str1 && *str2) {
2938 if (GUINT16_TO_LE (*str1) != *str2) {
2939 return(FALSE);
2941 ++str1;
2942 ++str2;
2945 return(*str1 == *str2);
2948 typedef struct {
2949 guint16 data_len;
2950 guint16 value_len;
2951 guint16 type;
2952 gunichar2 *key;
2953 } version_data;
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 */
2975 ALIGN32 (data);
2977 return(data);
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__);
2990 return(NULL);
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__);
2996 return(NULL);
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__);
3004 return(NULL);
3007 return(data_ptr);
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);
3017 return(data_ptr);
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
3030 * data_len
3032 while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
3033 /* align on a 32-bit boundary */
3034 ALIGN32 (data_ptr);
3036 data_ptr = get_versioninfo_block (data_ptr, block);
3037 if (block->data_len == 0) {
3038 /* We must have hit padding, so give up
3039 * processing now
3041 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
3043 return(NULL);
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;
3060 return(data_ptr);
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 */
3076 gchar *found_lang;
3077 gchar *lowercase_lang;
3079 /* data_ptr is pointing at an array of StringTable blocks,
3080 * with total length (not including alignment padding) of
3081 * data_len
3084 while(string_len < data_len) {
3085 /* align on a 32-bit boundary */
3086 ALIGN32 (data_ptr);
3088 data_ptr = get_versioninfo_block (data_ptr, block);
3089 if (block->data_len == 0) {
3090 /* We must have hit padding, so give up
3091 * processing now
3093 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
3094 return(NULL);
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__);
3102 return(NULL);
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,
3113 string_value,
3114 string_value_len, block);
3115 } else {
3116 data_ptr = get_string_block (data_ptr, NULL, NULL,
3117 NULL, block);
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__);
3125 return(NULL);
3129 return(data_ptr);
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 */
3138 gchar *big_value;
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)
3143 * of data_len
3145 while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
3146 /* align on a 32-bit boundary */
3147 ALIGN32 (data_ptr);
3149 data_ptr = get_versioninfo_block (data_ptr, block);
3150 if (block->data_len == 0) {
3151 /* We must have hit padding, so give up
3152 * processing now
3154 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
3155 return(NULL);
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,
3163 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__);
3166 return(NULL);
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
3172 * safe side
3174 memcpy (block->key, big_value, unicode_chars (block->key) * 2);
3175 g_free (big_value);
3177 big_value = g_convert ((gchar *)data_ptr,
3178 unicode_chars (data_ptr) * 2,
3179 "UTF-16BE", "UTF-16LE", NULL, NULL,
3180 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__);
3183 return(NULL);
3185 memcpy ((gpointer)data_ptr, big_value,
3186 unicode_chars (data_ptr) * 2);
3187 g_free (big_value);
3189 data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
3192 return(data_ptr);
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 */
3205 gchar *big_value;
3207 /* data_ptr is pointing at an array of StringTable blocks,
3208 * with total length (not including alignment padding) of
3209 * data_len
3212 while(string_len < data_len) {
3213 /* align on a 32-bit boundary */
3214 ALIGN32 (data_ptr);
3216 data_ptr = get_versioninfo_block (data_ptr, block);
3217 if (block->data_len == 0) {
3218 /* We must have hit padding, so give up
3219 * processing now
3221 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
3222 return(NULL);
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__);
3231 return(NULL);
3234 memcpy (block->key, big_value, 16);
3235 g_free (big_value);
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__);
3242 return(NULL);
3246 return(data_ptr);
3249 /* Follows the data structures and turns all UTF-16 strings from the
3250 * LE found in the resource section into UTF-16BE
3252 static void
3253 big_up (gconstpointer datablock, guint32 size)
3255 gconstpointer data_ptr;
3256 gint32 data_len; /* signed to guard against underflow */
3257 version_data block;
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 */
3286 ALIGN32 (data_ptr);
3288 data_ptr = get_versioninfo_block (data_ptr, &block);
3289 if (block.data_len == 0) {
3290 /* We must have hit padding, so give
3291 * up processing now
3293 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
3294 return;
3297 data_len = data_len - block.data_len;
3299 if (unicode_string_equals (block.key, "VarFileInfo")) {
3300 data_ptr = get_varfileinfo_block (data_ptr,
3301 &block);
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,
3306 &block);
3307 } else {
3308 /* Bogus data */
3309 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Not a valid VERSIONINFO child block", __func__);
3310 return;
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__);
3316 return;
3321 #endif
3323 gboolean
3324 mono_w32process_get_fileversion_info (const gunichar2 *filename, gpointer *data)
3326 gpointer file_map;
3327 gpointer versioninfo;
3328 void *map_handle;
3329 gint32 map_size;
3330 gsize datasize;
3332 g_assert (data);
3333 *data = NULL;
3335 file_map = mono_pe_file_map (filename, &map_size, &map_handle);
3336 if (!file_map)
3337 return FALSE;
3339 versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION, 0, &datasize);
3340 if (!versioninfo) {
3341 mono_pe_file_unmap (file_map, map_handle);
3342 return FALSE;
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);
3353 #endif
3355 mono_pe_file_unmap (file_map, map_handle);
3357 return TRUE;
3360 gboolean
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;
3365 version_data block;
3366 gconstpointer data_ptr;
3367 gint32 data_len; /* signed to guard against underflow */
3368 gboolean want_var = FALSE;
3369 gboolean want_string = FALSE;
3370 gunichar2 lang[8];
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) {
3378 return(FALSE);
3381 if (!strcmp (subblock_utf8, "\\VarFileInfo\\Translation")) {
3382 want_var = TRUE;
3383 } else if (!strncmp (subblock_utf8, "\\StringFileInfo\\", 16)) {
3384 want_string = TRUE;
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);
3388 g_free (lang_utf8);
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;
3400 ret = TRUE;
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
3406 * bytes
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 */
3416 ALIGN32 (data_ptr);
3418 data_ptr = get_versioninfo_block (data_ptr,
3419 &block);
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__);
3425 goto done;
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);
3432 if (want_var) {
3433 *buffer = (gpointer)data_ptr;
3434 *len = block.value_len;
3435 ret = TRUE;
3436 goto done;
3437 } else {
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);
3443 if (want_string &&
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 */
3448 ret = TRUE;
3449 goto done;
3451 } else {
3452 /* Bogus data */
3453 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Not a valid VERSIONINFO child block", __func__);
3454 goto done;
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__);
3460 goto done;
3466 done:
3467 if (lang_utf8) {
3468 g_free (lang_utf8);
3471 g_free (subblock_utf8);
3472 return(ret);
3475 static guint32
3476 copy_lang (gunichar2 *lang_out, guint32 lang_len, const gchar *text)
3478 gunichar2 *unitext;
3479 int chars = strlen (text);
3480 int ret;
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';
3488 ret = chars;
3489 } else {
3490 memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2);
3491 lang_out[lang_len] = '\0';
3492 ret = lang_len;
3495 g_free (unitext);
3497 return(ret);
3500 guint32
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;
3509 switch(primary) {
3510 case 0x00:
3511 switch (secondary) {
3512 case 0x01: name = "Process Default Language"; break;
3514 break;
3515 case 0x01:
3516 switch (secondary) {
3517 case 0x00:
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;
3535 break;
3536 case 0x02:
3537 switch (secondary) {
3538 case 0x00: name = "Bulgarian (Bulgaria)"; break;
3539 case 0x01: name = "Bulgarian"; break;
3541 break;
3542 case 0x03:
3543 switch (secondary) {
3544 case 0x00: name = "Catalan (Spain)"; break;
3545 case 0x01: name = "Catalan"; break;
3547 break;
3548 case 0x04:
3549 switch (secondary) {
3550 case 0x00:
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;
3557 break;
3558 case 0x05:
3559 switch (secondary) {
3560 case 0x00: name = "Czech (Czech Republic)"; break;
3561 case 0x01: name = "Czech"; break;
3563 break;
3564 case 0x06:
3565 switch (secondary) {
3566 case 0x00: name = "Danish (Denmark)"; break;
3567 case 0x01: name = "Danish"; break;
3569 break;
3570 case 0x07:
3571 switch (secondary) {
3572 case 0x00:
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;
3579 break;
3580 case 0x08:
3581 switch (secondary) {
3582 case 0x00: name = "Greek (Greece)"; break;
3583 case 0x01: name = "Greek"; break;
3585 break;
3586 case 0x09:
3587 switch (secondary) {
3588 case 0x00:
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;
3606 break;
3607 case 0x0a:
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;
3632 break;
3633 case 0x0b:
3634 switch (secondary) {
3635 case 0x00: name = "Finnish (Finland)"; break;
3636 case 0x01: name = "Finnish"; break;
3638 break;
3639 case 0x0c:
3640 switch (secondary) {
3641 case 0x00:
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;
3649 break;
3650 case 0x0d:
3651 switch (secondary) {
3652 case 0x00: name = "Hebrew (Israel)"; break;
3653 case 0x01: name = "Hebrew"; break;
3655 break;
3656 case 0x0e:
3657 switch (secondary) {
3658 case 0x00: name = "Hungarian (Hungary)"; break;
3659 case 0x01: name = "Hungarian"; break;
3661 break;
3662 case 0x0f:
3663 switch (secondary) {
3664 case 0x00: name = "Icelandic (Iceland)"; break;
3665 case 0x01: name = "Icelandic"; break;
3667 break;
3668 case 0x10:
3669 switch (secondary) {
3670 case 0x00:
3671 case 0x01: name = "Italian (Italy)"; break;
3672 case 0x02: name = "Italian (Switzerland)"; break;
3674 break;
3675 case 0x11:
3676 switch (secondary) {
3677 case 0x00: name = "Japanese (Japan)"; break;
3678 case 0x01: name = "Japanese"; break;
3680 break;
3681 case 0x12:
3682 switch (secondary) {
3683 case 0x00: name = "Korean (Korea)"; break;
3684 case 0x01: name = "Korean"; break;
3686 break;
3687 case 0x13:
3688 switch (secondary) {
3689 case 0x00:
3690 case 0x01: name = "Dutch (Netherlands)"; break;
3691 case 0x02: name = "Dutch (Belgium)"; break;
3693 break;
3694 case 0x14:
3695 switch (secondary) {
3696 case 0x00:
3697 case 0x01: name = "Norwegian (Bokmal)"; break;
3698 case 0x02: name = "Norwegian (Nynorsk)"; break;
3700 break;
3701 case 0x15:
3702 switch (secondary) {
3703 case 0x00: name = "Polish (Poland)"; break;
3704 case 0x01: name = "Polish"; break;
3706 break;
3707 case 0x16:
3708 switch (secondary) {
3709 case 0x00:
3710 case 0x01: name = "Portuguese (Brazil)"; break;
3711 case 0x02: name = "Portuguese (Portugal)"; break;
3713 break;
3714 case 0x17:
3715 switch (secondary) {
3716 case 0x01: name = "Romansh (Switzerland)"; break;
3718 break;
3719 case 0x18:
3720 switch (secondary) {
3721 case 0x00: name = "Romanian (Romania)"; break;
3722 case 0x01: name = "Romanian"; break;
3724 break;
3725 case 0x19:
3726 switch (secondary) {
3727 case 0x00: name = "Russian (Russia)"; break;
3728 case 0x01: name = "Russian"; break;
3730 break;
3731 case 0x1a:
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;
3743 break;
3744 case 0x1b:
3745 switch (secondary) {
3746 case 0x00: name = "Slovak (Slovakia)"; break;
3747 case 0x01: name = "Slovak"; break;
3749 break;
3750 case 0x1c:
3751 switch (secondary) {
3752 case 0x00: name = "Albanian (Albania)"; break;
3753 case 0x01: name = "Albanian"; break;
3755 break;
3756 case 0x1d:
3757 switch (secondary) {
3758 case 0x00: name = "Swedish (Sweden)"; break;
3759 case 0x01: name = "Swedish"; break;
3760 case 0x02: name = "Swedish (Finland)"; break;
3762 break;
3763 case 0x1e:
3764 switch (secondary) {
3765 case 0x00: name = "Thai (Thailand)"; break;
3766 case 0x01: name = "Thai"; break;
3768 break;
3769 case 0x1f:
3770 switch (secondary) {
3771 case 0x00: name = "Turkish (Turkey)"; break;
3772 case 0x01: name = "Turkish"; break;
3774 break;
3775 case 0x20:
3776 switch (secondary) {
3777 case 0x00: name = "Urdu (Islamic Republic of Pakistan)"; break;
3778 case 0x01: name = "Urdu"; break;
3780 break;
3781 case 0x21:
3782 switch (secondary) {
3783 case 0x00: name = "Indonesian (Indonesia)"; break;
3784 case 0x01: name = "Indonesian"; break;
3786 break;
3787 case 0x22:
3788 switch (secondary) {
3789 case 0x00: name = "Ukrainian (Ukraine)"; break;
3790 case 0x01: name = "Ukrainian"; break;
3792 break;
3793 case 0x23:
3794 switch (secondary) {
3795 case 0x00: name = "Belarusian (Belarus)"; break;
3796 case 0x01: name = "Belarusian"; break;
3798 break;
3799 case 0x24:
3800 switch (secondary) {
3801 case 0x00: name = "Slovenian (Slovenia)"; break;
3802 case 0x01: name = "Slovenian"; break;
3804 break;
3805 case 0x25:
3806 switch (secondary) {
3807 case 0x00: name = "Estonian (Estonia)"; break;
3808 case 0x01: name = "Estonian"; break;
3810 break;
3811 case 0x26:
3812 switch (secondary) {
3813 case 0x00: name = "Latvian (Latvia)"; break;
3814 case 0x01: name = "Latvian"; break;
3816 break;
3817 case 0x27:
3818 switch (secondary) {
3819 case 0x00: name = "Lithuanian (Lithuania)"; break;
3820 case 0x01: name = "Lithuanian"; break;
3822 break;
3823 case 0x28:
3824 switch (secondary) {
3825 case 0x01: name = "Tajik (Tajikistan)"; break;
3827 break;
3828 case 0x29:
3829 switch (secondary) {
3830 case 0x00: name = "Farsi (Iran)"; break;
3831 case 0x01: name = "Farsi"; break;
3833 break;
3834 case 0x2a:
3835 switch (secondary) {
3836 case 0x00: name = "Vietnamese (Viet Nam)"; break;
3837 case 0x01: name = "Vietnamese"; break;
3839 break;
3840 case 0x2b:
3841 switch (secondary) {
3842 case 0x00: name = "Armenian (Armenia)"; break;
3843 case 0x01: name = "Armenian"; break;
3845 break;
3846 case 0x2c:
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;
3852 break;
3853 case 0x2d:
3854 switch (secondary) {
3855 case 0x00: name = "Basque (Spain)"; break;
3856 case 0x01: name = "Basque"; break;
3858 break;
3859 case 0x2e:
3860 switch (secondary) {
3861 case 0x01: name = "Upper Sorbian (Germany)"; break;
3862 case 0x02: name = "Lower Sorbian (Germany)"; break;
3864 break;
3865 case 0x2f:
3866 switch (secondary) {
3867 case 0x00: name = "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
3868 case 0x01: name = "FYRO Macedonian"; break;
3870 break;
3871 case 0x32:
3872 switch (secondary) {
3873 case 0x00: name = "Tswana (South Africa)"; break;
3874 case 0x01: name = "Tswana"; break;
3876 break;
3877 case 0x34:
3878 switch (secondary) {
3879 case 0x00: name = "Xhosa (South Africa)"; break;
3880 case 0x01: name = "Xhosa"; break;
3882 break;
3883 case 0x35:
3884 switch (secondary) {
3885 case 0x00: name = "Zulu (South Africa)"; break;
3886 case 0x01: name = "Zulu"; break;
3888 break;
3889 case 0x36:
3890 switch (secondary) {
3891 case 0x00: name = "Afrikaans (South Africa)"; break;
3892 case 0x01: name = "Afrikaans"; break;
3894 break;
3895 case 0x37:
3896 switch (secondary) {
3897 case 0x00: name = "Georgian (Georgia)"; break;
3898 case 0x01: name = "Georgian"; break;
3900 break;
3901 case 0x38:
3902 switch (secondary) {
3903 case 0x00: name = "Faroese (Faroe Islands)"; break;
3904 case 0x01: name = "Faroese"; break;
3906 break;
3907 case 0x39:
3908 switch (secondary) {
3909 case 0x00: name = "Hindi (India)"; break;
3910 case 0x01: name = "Hindi"; break;
3912 break;
3913 case 0x3a:
3914 switch (secondary) {
3915 case 0x00: name = "Maltese (Malta)"; break;
3916 case 0x01: name = "Maltese"; break;
3918 break;
3919 case 0x3b:
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;
3932 break;
3933 case 0x3c:
3934 switch (secondary) {
3935 case 0x02: name = "Irish (Ireland)"; break;
3937 break;
3938 case 0x3e:
3939 switch (secondary) {
3940 case 0x00:
3941 case 0x01: name = "Malay (Malaysia)"; break;
3942 case 0x02: name = "Malay (Brunei Darussalam)"; break;
3944 break;
3945 case 0x3f:
3946 switch (secondary) {
3947 case 0x00: name = "Kazakh (Kazakhstan)"; break;
3948 case 0x01: name = "Kazakh"; break;
3950 break;
3951 case 0x40:
3952 switch (secondary) {
3953 case 0x00: name = "Kyrgyz (Kyrgyzstan)"; break;
3954 case 0x01: name = "Kyrgyz (Cyrillic)"; break;
3956 break;
3957 case 0x41:
3958 switch (secondary) {
3959 case 0x00: name = "Swahili (Kenya)"; break;
3960 case 0x01: name = "Swahili"; break;
3962 break;
3963 case 0x42:
3964 switch (secondary) {
3965 case 0x01: name = "Turkmen (Turkmenistan)"; break;
3967 break;
3968 case 0x43:
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;
3974 break;
3975 case 0x44:
3976 switch (secondary) {
3977 case 0x00: name = "Tatar (Russia)"; break;
3978 case 0x01: name = "Tatar"; break;
3980 break;
3981 case 0x45:
3982 switch (secondary) {
3983 case 0x00:
3984 case 0x01: name = "Bengali (India)"; break;
3986 break;
3987 case 0x46:
3988 switch (secondary) {
3989 case 0x00: name = "Punjabi (India)"; break;
3990 case 0x01: name = "Punjabi"; break;
3992 break;
3993 case 0x47:
3994 switch (secondary) {
3995 case 0x00: name = "Gujarati (India)"; break;
3996 case 0x01: name = "Gujarati"; break;
3998 break;
3999 case 0x49:
4000 switch (secondary) {
4001 case 0x00: name = "Tamil (India)"; break;
4002 case 0x01: name = "Tamil"; break;
4004 break;
4005 case 0x4a:
4006 switch (secondary) {
4007 case 0x00: name = "Telugu (India)"; break;
4008 case 0x01: name = "Telugu"; break;
4010 break;
4011 case 0x4b:
4012 switch (secondary) {
4013 case 0x00: name = "Kannada (India)"; break;
4014 case 0x01: name = "Kannada"; break;
4016 break;
4017 case 0x4c:
4018 switch (secondary) {
4019 case 0x00:
4020 case 0x01: name = "Malayalam (India)"; break;
4022 break;
4023 case 0x4d:
4024 switch (secondary) {
4025 case 0x01: name = "Assamese (India)"; break;
4027 break;
4028 case 0x4e:
4029 switch (secondary) {
4030 case 0x00: name = "Marathi (India)"; break;
4031 case 0x01: name = "Marathi"; break;
4033 break;
4034 case 0x4f:
4035 switch (secondary) {
4036 case 0x00: name = "Sanskrit (India)"; break;
4037 case 0x01: name = "Sanskrit"; break;
4039 break;
4040 case 0x50:
4041 switch (secondary) {
4042 case 0x00: name = "Mongolian (Mongolia)"; break;
4043 case 0x01: name = "Mongolian (Cyrillic)"; break;
4044 case 0x02: name = "Mongolian (PRC)"; break;
4046 break;
4047 case 0x51:
4048 switch (secondary) {
4049 case 0x01: name = "Tibetan (PRC)"; break;
4050 case 0x02: name = "Tibetan (Bhutan)"; break;
4052 break;
4053 case 0x52:
4054 switch (secondary) {
4055 case 0x00: name = "Welsh (United Kingdom)"; break;
4056 case 0x01: name = "Welsh"; break;
4058 break;
4059 case 0x53:
4060 switch (secondary) {
4061 case 0x01: name = "Khmer (Cambodia)"; break;
4063 break;
4064 case 0x54:
4065 switch (secondary) {
4066 case 0x01: name = "Lao (Lao PDR)"; break;
4068 break;
4069 case 0x56:
4070 switch (secondary) {
4071 case 0x00: name = "Galician (Spain)"; break;
4072 case 0x01: name = "Galician"; break;
4074 break;
4075 case 0x57:
4076 switch (secondary) {
4077 case 0x00: name = "Konkani (India)"; break;
4078 case 0x01: name = "Konkani"; break;
4080 break;
4081 case 0x5a:
4082 switch (secondary) {
4083 case 0x00: name = "Syriac (Syria)"; break;
4084 case 0x01: name = "Syriac"; break;
4086 break;
4087 case 0x5b:
4088 switch (secondary) {
4089 case 0x01: name = "Sinhala (Sri Lanka)"; break;
4091 break;
4092 case 0x5d:
4093 switch (secondary) {
4094 case 0x01: name = "Inuktitut (Syllabics, Canada)"; break;
4095 case 0x02: name = "Inuktitut (Latin, Canada)"; break;
4097 break;
4098 case 0x5e:
4099 switch (secondary) {
4100 case 0x01: name = "Amharic (Ethiopia)"; break;
4102 break;
4103 case 0x5f:
4104 switch (secondary) {
4105 case 0x02: name = "Tamazight (Algeria, Latin)"; break;
4107 break;
4108 case 0x61:
4109 switch (secondary) {
4110 case 0x01: name = "Nepali (Nepal)"; break;
4112 break;
4113 case 0x62:
4114 switch (secondary) {
4115 case 0x01: name = "Frisian (Netherlands)"; break;
4117 break;
4118 case 0x63:
4119 switch (secondary) {
4120 case 0x01: name = "Pashto (Afghanistan)"; break;
4122 break;
4123 case 0x64:
4124 switch (secondary) {
4125 case 0x01: name = "Filipino (Philippines)"; break;
4127 break;
4128 case 0x65:
4129 switch (secondary) {
4130 case 0x00: name = "Divehi (Maldives)"; break;
4131 case 0x01: name = "Divehi"; break;
4133 break;
4134 case 0x68:
4135 switch (secondary) {
4136 case 0x01: name = "Hausa (Nigeria, Latin)"; break;
4138 break;
4139 case 0x6a:
4140 switch (secondary) {
4141 case 0x01: name = "Yoruba (Nigeria)"; break;
4143 break;
4144 case 0x6b:
4145 switch (secondary) {
4146 case 0x00:
4147 case 0x01: name = "Quechua (Bolivia)"; break;
4148 case 0x02: name = "Quechua (Ecuador)"; break;
4149 case 0x03: name = "Quechua (Peru)"; break;
4151 break;
4152 case 0x6c:
4153 switch (secondary) {
4154 case 0x00: name = "Northern Sotho (South Africa)"; break;
4155 case 0x01: name = "Northern Sotho"; break;
4157 break;
4158 case 0x6d:
4159 switch (secondary) {
4160 case 0x01: name = "Bashkir (Russia)"; break;
4162 break;
4163 case 0x6e:
4164 switch (secondary) {
4165 case 0x01: name = "Luxembourgish (Luxembourg)"; break;
4167 break;
4168 case 0x6f:
4169 switch (secondary) {
4170 case 0x01: name = "Greenlandic (Greenland)"; break;
4172 break;
4173 case 0x78:
4174 switch (secondary) {
4175 case 0x01: name = "Yi (PRC)"; break;
4177 break;
4178 case 0x7a:
4179 switch (secondary) {
4180 case 0x01: name = "Mapudungun (Chile)"; break;
4182 break;
4183 case 0x7c:
4184 switch (secondary) {
4185 case 0x01: name = "Mohawk (Mohawk)"; break;
4187 break;
4188 case 0x7e:
4189 switch (secondary) {
4190 case 0x01: name = "Breton (France)"; break;
4192 break;
4193 case 0x7f:
4194 switch (secondary) {
4195 case 0x00: name = "Invariant Language (Invariant Country)"; break;
4197 break;
4198 case 0x80:
4199 switch (secondary) {
4200 case 0x01: name = "Uighur (PRC)"; break;
4202 break;
4203 case 0x81:
4204 switch (secondary) {
4205 case 0x00: name = "Maori (New Zealand)"; break;
4206 case 0x01: name = "Maori"; break;
4208 break;
4209 case 0x83:
4210 switch (secondary) {
4211 case 0x01: name = "Corsican (France)"; break;
4213 break;
4214 case 0x84:
4215 switch (secondary) {
4216 case 0x01: name = "Alsatian (France)"; break;
4218 break;
4219 case 0x85:
4220 switch (secondary) {
4221 case 0x01: name = "Yakut (Russia)"; break;
4223 break;
4224 case 0x86:
4225 switch (secondary) {
4226 case 0x01: name = "K'iche (Guatemala)"; break;
4228 break;
4229 case 0x87:
4230 switch (secondary) {
4231 case 0x01: name = "Kinyarwanda (Rwanda)"; break;
4233 break;
4234 case 0x88:
4235 switch (secondary) {
4236 case 0x01: name = "Wolof (Senegal)"; break;
4238 break;
4239 case 0x8c:
4240 switch (secondary) {
4241 case 0x01: name = "Dari (Afghanistan)"; break;
4243 break;
4245 default:
4246 name = "Language Neutral";
4250 if (!name)
4251 name = "Language Neutral";
4253 return copy_lang (lang_out, lang_len, name);
4256 #else /* ENABLE_NETCORE */
4258 void
4259 mono_w32process_init (void)
4263 void
4264 mono_w32process_cleanup (void)
4268 void
4269 mono_w32process_set_cli_launcher (gchar *path)
4273 void
4274 mono_w32process_signal_finished (void)
4278 #endif /* ENABLE_NETCORE */