"Fossies" - the Fresh Open Source Software Archive

Member "mono-6.12.0.122/mono/metadata/w32process-unix.c" (22 Feb 2021, 122763 Bytes) of package /linux/misc/mono-sources/mono/mono-6.12.0.122.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "w32process-unix.c": 6.10.0.104_vs_6.12.0.90.

    1 /**
    2  * \file
    3  * System.Diagnostics.Process support
    4  *
    5  * Author:
    6  *  Dick Porter (dick@ximian.com)
    7  *
    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.
   11  */
   12 
   13 #include <config.h>
   14 #include <glib.h>
   15 
   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 #include <signal.h>
   26 #include <sys/time.h>
   27 #include <fcntl.h>
   28 #ifdef HAVE_SYS_PARAM_H
   29 #include <sys/param.h>
   30 #endif
   31 #include <ctype.h>
   32 
   33 #ifdef HAVE_SYS_WAIT_H
   34 #include <sys/wait.h>
   35 #endif
   36 #ifdef HAVE_SYS_RESOURCE_H
   37 #include <sys/resource.h>
   38 #endif
   39 
   40 #ifdef HAVE_SYS_MKDEV_H
   41 #include <sys/mkdev.h>
   42 #endif
   43 
   44 #ifdef HAVE_UTIME_H
   45 #include <utime.h>
   46 #endif
   47 
   48 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
   49 // For close_my_fds
   50 #if defined (_AIX)
   51 #include <procinfo.h>
   52 #elif defined (__FreeBSD__)
   53 #include <sys/sysctl.h>
   54 #include <sys/user.h>
   55 #include <libutil.h>
   56 #elif defined(__linux__)
   57 #include <dirent.h>
   58 #endif
   59 #endif
   60 
   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-once.h>
   89 #include <mono/utils/mono-error-internals.h>
   90 #include <mono/utils/mono-threads-coop.h>
   91 #include "object-internals.h"
   92 #include "icall-decl.h"
   93 
   94 #ifndef ENABLE_NETCORE
   95 
   96 #ifndef MAXPATHLEN
   97 #define MAXPATHLEN 242
   98 #endif
   99 
  100 #define STILL_ACTIVE ((int) 0x00000103)
  101 
  102 #define LOGDEBUG(...)
  103 /* define LOGDEBUG(...) g_message(__VA_ARGS__)  */
  104 
  105 /* The process' environment strings */
  106 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
  107 #if defined(__APPLE__)
  108 #if defined (TARGET_OSX)
  109 /* Apple defines this in crt_externs.h but doesn't provide that header for 
  110  * arm-apple-darwin9.  We'll manually define the symbol on Apple as it does
  111  * in fact exist on all implementations (so far) 
  112  */
  113 G_BEGIN_DECLS
  114 gchar ***_NSGetEnviron(void);
  115 G_END_DECLS
  116 #define environ (*_NSGetEnviron())
  117 #else
  118 static char *mono_environ[1] = { NULL };
  119 #define environ mono_environ
  120 #endif /* defined (TARGET_OSX) */
  121 #else
  122 G_BEGIN_DECLS
  123 extern char **environ;
  124 G_END_DECLS
  125 #endif
  126 #endif
  127 
  128 typedef enum {
  129     STARTF_USESHOWWINDOW=0x001,
  130     STARTF_USESIZE=0x002,
  131     STARTF_USEPOSITION=0x004,
  132     STARTF_USECOUNTCHARS=0x008,
  133     STARTF_USEFILLATTRIBUTE=0x010,
  134     STARTF_RUNFULLSCREEN=0x020,
  135     STARTF_FORCEONFEEDBACK=0x040,
  136     STARTF_FORCEOFFFEEDBACK=0x080,
  137     STARTF_USESTDHANDLES=0x100
  138 } StartupFlags;
  139 
  140 typedef struct {
  141     gpointer input;
  142     gpointer output;
  143     gpointer error;
  144 } StartupHandles;
  145 
  146 typedef struct {
  147 #if G_BYTE_ORDER == G_BIG_ENDIAN
  148     guint32 highDateTime;
  149     guint32 lowDateTime;
  150 #else
  151     guint32 lowDateTime;
  152     guint32 highDateTime;
  153 #endif
  154 } ProcessTime;
  155 
  156 /*
  157  * Process describes processes we create.
  158  * It contains a semaphore that can be waited on in order to wait
  159  * for process termination.
  160  */
  161 typedef struct _Process {
  162     pid_t pid; /* the pid of the process. This value is only valid until the process has exited. */
  163     MonoCoopSem exit_sem; /* this semaphore will be released when the process exits */
  164     int status; /* the exit status */
  165     gint32 handle_count; /* the number of handles to this process instance */
  166     /* we keep a ref to the creating _WapiHandle_process handle until
  167      * the process has exited, so that the information there isn't lost.
  168      */
  169     gpointer handle;
  170     gboolean signalled;
  171     struct _Process *next;
  172 } Process;
  173 
  174 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
  175 typedef struct {
  176     pid_t pid;
  177     gboolean child;
  178     guint32 exitstatus;
  179     gpointer main_thread;
  180     guint64 create_time;
  181     guint64 exit_time;
  182     char *pname;
  183     size_t min_working_set;
  184     size_t max_working_set;
  185     gboolean exited;
  186     Process *process;
  187 } MonoW32HandleProcess;
  188 
  189 /*
  190  * VS_VERSIONINFO:
  191  *
  192  * 2 bytes: Length in bytes (this block, and all child blocks. does _not_ include alignment padding between blocks)
  193  * 2 bytes: Length in bytes of VS_FIXEDFILEINFO struct
  194  * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
  195  * Variable length unicode string (null terminated): Key (currently "VS_VERSION_INFO")
  196  * Variable length padding to align VS_FIXEDFILEINFO on a 32-bit boundary
  197  * VS_FIXEDFILEINFO struct
  198  * Variable length padding to align Child struct on a 32-bit boundary
  199  * Child struct (zero or one StringFileInfo structs, zero or one VarFileInfo structs)
  200  */
  201 
  202 /*
  203  * StringFileInfo:
  204  *
  205  * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
  206  * 2 bytes: Value length (always zero)
  207  * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
  208  * Variable length unicode string: Key (currently "StringFileInfo")
  209  * Variable length padding to align Child struct on a 32-bit boundary
  210  * 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.)
  211  */
  212 
  213 /*
  214  * StringTable:
  215  *
  216  * 2 bytes: Length in bytes (includes this block as well as all Child blocks, but excludes any padding between String blocks)
  217  * 2 bytes: Value length (always zero)
  218  * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
  219  * 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.
  220  * Variable length padding to align Child struct on a 32-bit boundary
  221  * Child structs (an array of one or more String structs (each aligned on a 32-bit boundary)
  222  */
  223 
  224 /*
  225  * String:
  226  *
  227  * 2 bytes: Length in bytes (of this block)
  228  * 2 bytes: Value length (the length in words of the Value member)
  229  * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
  230  * Variable length unicode string: Key. arbitrary string, identifies data.
  231  * Variable length padding to align Value on a 32-bit boundary
  232  * Value: Variable length unicode string, holding data.
  233  */
  234 
  235 /*
  236  * VarFileInfo:
  237  *
  238  * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
  239  * 2 bytes: Value length (always zero)
  240  * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
  241  * Variable length unicode string: Key (currently "VarFileInfo")
  242  * Variable length padding to align Child struct on a 32-bit boundary
  243  * Child structs (a Var struct)
  244  */
  245 
  246 /*
  247  * Var:
  248  *
  249  * 2 bytes: Length in bytes of this block
  250  * 2 bytes: Value length in bytes of the Value
  251  * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
  252  * Variable length unicode string: Key ("Translation")
  253  * Variable length padding to align Value on a 32-bit boundary
  254  * 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.
  255  */
  256 
  257 #if G_BYTE_ORDER == G_BIG_ENDIAN
  258 #define VS_FFI_SIGNATURE    0xbd04effe
  259 #define VS_FFI_STRUCVERSION 0x00000100
  260 #else
  261 #define VS_FFI_SIGNATURE    0xfeef04bd
  262 #define VS_FFI_STRUCVERSION 0x00010000
  263 #endif
  264 
  265 #define VOS_UNKNOWN     0x00000000
  266 #define VOS_DOS         0x00010000
  267 #define VOS_OS216       0x00020000
  268 #define VOS_OS232       0x00030000
  269 #define VOS_NT          0x00040000
  270 #define VOS__BASE       0x00000000
  271 #define VOS__WINDOWS16      0x00000001
  272 #define VOS__PM16       0x00000002
  273 #define VOS__PM32       0x00000003
  274 #define VOS__WINDOWS32      0x00000004
  275 /* Should "embrace and extend" here with some entries for linux etc */
  276 
  277 #define VOS_DOS_WINDOWS16   0x00010001
  278 #define VOS_DOS_WINDOWS32   0x00010004
  279 #define VOS_OS216_PM16      0x00020002
  280 #define VOS_OS232_PM32      0x00030003
  281 #define VOS_NT_WINDOWS32    0x00040004
  282 
  283 #define VFT_UNKNOWN     0x0000
  284 #define VFT_APP         0x0001
  285 #define VFT_DLL         0x0002
  286 #define VFT_DRV         0x0003
  287 #define VFT_FONT        0x0004
  288 #define VFT_VXD         0x0005
  289 #define VFT_STATIC_LIB      0x0007
  290 
  291 #define VFT2_UNKNOWN        0x0000
  292 #define VFT2_DRV_PRINTER    0x0001
  293 #define VFT2_DRV_KEYBOARD   0x0002
  294 #define VFT2_DRV_LANGUAGE   0x0003
  295 #define VFT2_DRV_DISPLAY    0x0004
  296 #define VFT2_DRV_MOUSE      0x0005
  297 #define VFT2_DRV_NETWORK    0x0006
  298 #define VFT2_DRV_SYSTEM     0x0007
  299 #define VFT2_DRV_INSTALLABLE    0x0008
  300 #define VFT2_DRV_SOUND      0x0009
  301 #define VFT2_DRV_COMM       0x000a
  302 #define VFT2_DRV_INPUTMETHOD    0x000b
  303 #define VFT2_FONT_RASTER    0x0001
  304 #define VFT2_FONT_VECTOR    0x0002
  305 #define VFT2_FONT_TRUETYPE  0x0003
  306 
  307 #define MAKELANGID(primary,secondary) ((guint16)((secondary << 10) | (primary)))
  308 
  309 #define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
  310 
  311 #if HAVE_SIGACTION
  312 static mono_lazy_init_t process_sig_chld_once = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
  313 #endif
  314 
  315 static gchar *cli_launcher;
  316 
  317 static Process *processes;
  318 static MonoCoopMutex processes_mutex;
  319 
  320 static pid_t current_pid;
  321 static gpointer current_process;
  322 
  323 static const gunichar2 utf16_space [2] = { 0x20, 0 };
  324 static const gunichar2 utf16_quote [2] = { 0x22, 0 };
  325 
  326 static MonoBoolean
  327 mono_get_exit_code_process (gpointer handle, gint32 *exitcode);
  328 
  329 /* Check if a pid is valid - i.e. if a process exists with this pid. */
  330 static gboolean
  331 process_is_alive (pid_t pid)
  332 {
  333 #if defined(HOST_WATCHOS)
  334     return TRUE; // TODO: Rewrite using sysctl
  335 #elif defined(HOST_DARWIN) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(_AIX)
  336     if (pid == 0)
  337         return FALSE;
  338     if (kill (pid, 0) == 0)
  339         return TRUE;
  340     if (errno == EPERM)
  341         return TRUE;
  342     return FALSE;
  343 #elif defined(__HAIKU__)
  344     team_info teamInfo;
  345     if (get_team_info ((team_id)pid, &teamInfo) == B_OK)
  346         return TRUE;
  347     return FALSE;
  348 #else
  349     gchar *dir = g_strdup_printf ("/proc/%d", pid);
  350     gboolean result = access (dir, F_OK) == 0;
  351     g_free (dir);
  352     return result;
  353 #endif
  354 }
  355 
  356 static void
  357 process_details (MonoW32Handle *handle_data)
  358 {
  359     MonoW32HandleProcess *process_handle = (MonoW32HandleProcess *) handle_data->specific;
  360     g_print ("pid: %d, exited: %s, exitstatus: %d",
  361         process_handle->pid, process_handle->exited ? "true" : "false", process_handle->exitstatus);
  362 }
  363 
  364 static const gchar*
  365 process_typename (void)
  366 {
  367     return "Process";
  368 }
  369 
  370 static gsize
  371 process_typesize (void)
  372 {
  373     return sizeof (MonoW32HandleProcess);
  374 }
  375 
  376 static MonoW32HandleWaitRet
  377 process_wait (MonoW32Handle *handle_data, guint32 timeout, gboolean *alerted)
  378 {
  379     MonoW32HandleProcess *process_handle;
  380     pid_t pid G_GNUC_UNUSED, ret;
  381     int status;
  382     gint64 start, now;
  383     Process *process;
  384 
  385     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT ")", __func__, handle_data, timeout);
  386 
  387     if (alerted)
  388         *alerted = FALSE;
  389 
  390     process_handle = (MonoW32HandleProcess*) handle_data->specific;
  391 
  392     if (process_handle->exited) {
  393         /* We've already done this one */
  394         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): Process already exited", __func__, handle_data, timeout);
  395         return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
  396     }
  397 
  398     pid = process_handle->pid;
  399 
  400     if (pid == mono_process_current_pid ()) {
  401         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on current process", __func__, handle_data, timeout);
  402         return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
  403     }
  404 
  405     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): PID: %d", __func__, handle_data, timeout, pid);
  406 
  407     if (!process_handle->child) {
  408         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 
  410         if (!process_is_alive (pid)) {
  411             /* assume the process has exited */
  412             process_handle->exited = TRUE;
  413             process_handle->exitstatus = -1;
  414             mono_w32handle_set_signal_state (handle_data, TRUE, TRUE);
  415 
  416             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);
  417             return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
  418         }
  419 
  420         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);
  421         return MONO_W32HANDLE_WAIT_RET_FAILED;
  422     }
  423 
  424     /* We don't need to lock processes here, the entry
  425      * has a handle_count > 0 which means it will not be freed. */
  426     process = process_handle->process;
  427     g_assert (process);
  428 
  429     start = mono_msec_ticks ();
  430     now = start;
  431 
  432     while (1) {
  433         if (timeout != MONO_INFINITE_WAIT) {
  434             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore for %" G_GINT64_FORMAT " ms...",
  435                 __func__, handle_data, timeout, timeout - (now - start));
  436             ret = mono_coop_sem_timedwait (&process->exit_sem, (timeout - (now - start)), alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
  437         } else {
  438             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): waiting on semaphore forever...",
  439                 __func__, handle_data, timeout);
  440             ret = mono_coop_sem_wait (&process->exit_sem, alerted ? MONO_SEM_FLAGS_ALERTABLE : MONO_SEM_FLAGS_NONE);
  441         }
  442 
  443         if (ret == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
  444             /* Success, process has exited */
  445             mono_coop_sem_post (&process->exit_sem);
  446             break;
  447         }
  448 
  449         if (ret == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
  450             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): wait timeout (timeout = 0)", __func__, handle_data, timeout);
  451             return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
  452         }
  453 
  454         now = mono_msec_ticks ();
  455         if (now - start >= timeout) {
  456             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): wait timeout", __func__, handle_data, timeout);
  457             return MONO_W32HANDLE_WAIT_RET_TIMEOUT;
  458         }
  459 
  460         if (alerted && ret == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
  461             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): wait alerted", __func__, handle_data, timeout);
  462             *alerted = TRUE;
  463             return MONO_W32HANDLE_WAIT_RET_ALERTED;
  464         }
  465     }
  466 
  467     /* Process must have exited */
  468     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): Waited successfully", __func__, handle_data, timeout);
  469 
  470     status = process->status;
  471     if (WIFSIGNALED (status))
  472         process_handle->exitstatus = 128 + WTERMSIG (status);
  473     else
  474         process_handle->exitstatus = WEXITSTATUS (status);
  475 
  476     process_handle->exit_time = mono_100ns_datetime ();
  477 
  478     process_handle->exited = TRUE;
  479 
  480     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s (%p, %" G_GUINT32_FORMAT "): Setting pid %d signalled, exit status %d",
  481            __func__, handle_data, timeout, process_handle->pid, process_handle->exitstatus);
  482 
  483     mono_w32handle_set_signal_state (handle_data, TRUE, TRUE);
  484 
  485     return MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
  486 }
  487 
  488 static void
  489 processes_cleanup (void)
  490 {
  491     static gint32 cleaning_up;
  492     Process *process;
  493     Process *prev = NULL;
  494 
  495     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s", __func__);
  496 
  497     /* Ensure we're not in here in multiple threads at once, nor recursive. */
  498     if (mono_atomic_cas_i32 (&cleaning_up, 1, 0) != 0)
  499         return;
  500 
  501     /*
  502      * This needs to be done outside the lock but atomically, hence the CAS above.
  503      */
  504     for (process = processes; process; process = process->next) {
  505         if (process->signalled && process->handle) {
  506             /* This process has exited and we need to remove the artifical ref
  507              * on the handle */
  508             mono_w32handle_close (process->handle);
  509             process->handle = NULL;
  510         }
  511     }
  512 
  513     mono_coop_mutex_lock (&processes_mutex);
  514 
  515     for (process = processes; process;) {
  516         Process *next = process->next;
  517         if (process->handle_count == 0 && process->signalled) {
  518             /*
  519              * Unlink the entry.
  520              */
  521             if (process == processes)
  522                 processes = process->next;
  523             else
  524                 prev->next = process->next;
  525 
  526             mono_coop_sem_destroy (&process->exit_sem);
  527             g_free (process);
  528         } else {
  529             prev = process;
  530         }
  531         process = next;
  532     }
  533 
  534     mono_coop_mutex_unlock (&processes_mutex);
  535 
  536     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s done", __func__);
  537 
  538     mono_atomic_xchg_i32 (&cleaning_up, 0);
  539 }
  540 
  541 static void
  542 process_close (gpointer data)
  543 {
  544     MonoW32HandleProcess *process_handle;
  545 
  546     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s", __func__);
  547 
  548     process_handle = (MonoW32HandleProcess *) data;
  549     g_free (process_handle->pname);
  550     process_handle->pname = NULL;
  551     if (process_handle->process)
  552         mono_atomic_dec_i32 (&process_handle->process->handle_count);
  553     processes_cleanup ();
  554 }
  555 
  556 static const MonoW32HandleOps process_ops = {
  557     process_close,      /* close_shared */
  558     NULL,               /* signal */
  559     NULL,               /* own */
  560     NULL,               /* is_owned */
  561     process_wait,           /* special_wait */
  562     NULL,               /* prewait */
  563     process_details,    /* details */
  564     process_typename,   /* typename */
  565     process_typesize,   /* typesize */
  566 };
  567 
  568 static void
  569 process_set_defaults (MonoW32HandleProcess *process_handle)
  570 {
  571     /* These seem to be the defaults on w2k */
  572     process_handle->min_working_set = 204800;
  573     process_handle->max_working_set = 1413120;
  574 
  575     process_handle->create_time = mono_100ns_datetime ();
  576 }
  577 
  578 static void
  579 process_set_name (MonoW32HandleProcess *process_handle)
  580 {
  581     char *progname, *utf8_progname, *slash;
  582 
  583     progname = g_get_prgname ();
  584     utf8_progname = mono_utf8_from_external (progname);
  585 
  586     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: using [%s] as prog name", __func__, progname);
  587 
  588     if (utf8_progname) {
  589         slash = strrchr (utf8_progname, '/');
  590         if (slash)
  591             process_handle->pname = g_strdup (slash+1);
  592         else
  593             process_handle->pname = g_strdup (utf8_progname);
  594         g_free (utf8_progname);
  595     }
  596 }
  597 
  598 static mono_once_t init_state = MONO_ONCE_INIT;
  599 
  600 void
  601 mono_w32process_init (void)
  602 {
  603     MonoW32HandleProcess process_handle;
  604 
  605     mono_w32handle_register_ops (MONO_W32TYPE_PROCESS, &process_ops);
  606 
  607     mono_w32handle_register_capabilities (MONO_W32TYPE_PROCESS,
  608         (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SPECIAL_WAIT));
  609 
  610     current_pid = getpid ();
  611 
  612     memset (&process_handle, 0, sizeof (process_handle));
  613     process_handle.pid = current_pid;
  614     process_set_defaults (&process_handle);
  615     process_set_name (&process_handle);
  616 
  617     current_process = mono_w32handle_new (MONO_W32TYPE_PROCESS, &process_handle);
  618     g_assert (current_process != INVALID_HANDLE_VALUE);
  619 
  620     mono_coop_mutex_init (&processes_mutex);
  621     mono_once (&init_state, &mono_w32process_platform_init_once);
  622 }
  623 
  624 void
  625 mono_w32process_cleanup (void)
  626 {
  627     g_free (cli_launcher);
  628 }
  629 
  630 static int
  631 len16 (const gunichar2 *str)
  632 {
  633     int len = 0;
  634 
  635     while (*str++ != 0)
  636         len++;
  637 
  638     return len;
  639 }
  640 
  641 static gunichar2 *
  642 utf16_concat (const gunichar2 *first, ...)
  643 {
  644     va_list args;
  645     int total = 0, i;
  646     const gunichar2 *s;
  647     const gunichar2 *p;
  648     gunichar2 *ret;
  649 
  650     va_start (args, first);
  651     total += len16 (first);
  652     for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg(args, gunichar2 *))
  653         total += len16 (s);
  654     va_end (args);
  655 
  656     ret = g_new (gunichar2, total + 1);
  657     if (ret == NULL)
  658         return NULL;
  659 
  660     ret [total] = 0;
  661     i = 0;
  662     for (s = first; *s != 0; s++)
  663         ret [i++] = *s;
  664     va_start (args, first);
  665     for (s = va_arg (args, gunichar2 *); s != NULL; s = va_arg (args, gunichar2 *)){
  666         for (p = s; *p != 0; p++)
  667             ret [i++] = *p;
  668     }
  669     va_end (args);
  670 
  671     return ret;
  672 }
  673 
  674 guint32
  675 mono_w32process_get_pid (gpointer handle)
  676 {
  677     MonoW32Handle *handle_data;
  678     guint32 ret;
  679 
  680     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
  681         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
  682         mono_w32error_set_last (ERROR_INVALID_HANDLE);
  683         return 0;
  684     }
  685 
  686     if (handle_data->type != MONO_W32TYPE_PROCESS) {
  687         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
  688         mono_w32error_set_last (ERROR_INVALID_HANDLE);
  689         mono_w32handle_unref (handle_data);
  690         return 0;
  691     }
  692 
  693     ret = ((MonoW32HandleProcess*) handle_data->specific)->pid;
  694 
  695     mono_w32handle_unref (handle_data);
  696 
  697     return ret;
  698 }
  699 
  700 typedef struct {
  701     guint32 pid;
  702     gpointer handle;
  703 } GetProcessForeachData;
  704 
  705 static gboolean
  706 get_process_foreach_callback (MonoW32Handle *handle_data, gpointer user_data)
  707 {
  708     GetProcessForeachData *foreach_data;
  709     MonoW32HandleProcess *process_handle;
  710     pid_t pid;
  711 
  712     if (handle_data->type != MONO_W32TYPE_PROCESS)
  713         return FALSE;
  714 
  715     process_handle = (MonoW32HandleProcess*) handle_data->specific;
  716 
  717     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: looking at process %d", __func__, process_handle->pid);
  718 
  719     pid = process_handle->pid;
  720     if (pid == 0)
  721         return FALSE;
  722 
  723     foreach_data = (GetProcessForeachData*) user_data;
  724 
  725     /* It's possible to have more than one process handle with the
  726      * same pid, but only the one running process can be
  727      * unsignalled. */
  728     if (foreach_data->pid != pid)
  729         return FALSE;
  730     if (mono_w32handle_issignalled (handle_data))
  731         return FALSE;
  732 
  733     foreach_data->handle = mono_w32handle_duplicate (handle_data);
  734     return TRUE;
  735 }
  736 
  737 HANDLE
  738 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid, MonoError *error)
  739 {
  740     GetProcessForeachData foreach_data;
  741     gpointer handle;
  742 
  743     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: looking for process %d", __func__, pid);
  744 
  745     memset (&foreach_data, 0, sizeof (foreach_data));
  746     foreach_data.pid = pid;
  747     mono_w32handle_foreach (get_process_foreach_callback, &foreach_data);
  748     handle = foreach_data.handle;
  749     if (handle) {
  750         /* get_process_foreach_callback already added a ref */
  751         return handle;
  752     }
  753 
  754     if (process_is_alive (pid)) {
  755         /* non-child process */
  756         MonoW32HandleProcess process_handle;
  757 
  758         memset (&process_handle, 0, sizeof (process_handle));
  759         process_handle.pid = pid;
  760         process_handle.pname = mono_w32process_get_name (pid);
  761 
  762         handle = mono_w32handle_new (MONO_W32TYPE_PROCESS, &process_handle);
  763         if (handle == INVALID_HANDLE_VALUE) {
  764             g_warning ("%s: error creating process handle", __func__);
  765 
  766             mono_w32error_set_last (ERROR_OUTOFMEMORY);
  767             return NULL;
  768         }
  769 
  770         return handle;
  771     }
  772 
  773     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find pid %d", __func__, pid);
  774 
  775     mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
  776     return NULL;
  777 }
  778 
  779 static gboolean
  780 match_procname_to_modulename (char *procname, char *modulename)
  781 {
  782     char* lastsep = NULL;
  783     char* lastsep2 = NULL;
  784     char* pname = NULL;
  785     char* mname = NULL;
  786     gboolean result = FALSE;
  787 
  788     if (procname == NULL || modulename == NULL)
  789         return (FALSE);
  790 
  791     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: procname=\"%s\", modulename=\"%s\"", __func__, procname, modulename);
  792     pname = mono_path_resolve_symlinks (procname);
  793     mname = mono_path_resolve_symlinks (modulename);
  794 
  795     if (!strcmp (pname, mname))
  796         result = TRUE;
  797 
  798     if (!result) {
  799         lastsep = strrchr (mname, '/');
  800         if (lastsep)
  801             if (!strcmp (lastsep+1, pname))
  802                 result = TRUE;
  803         if (!result) {
  804             lastsep2 = strrchr (pname, '/');
  805             if (lastsep2){
  806                 if (lastsep) {
  807                     if (!strcmp (lastsep+1, lastsep2+1))
  808                         result = TRUE;
  809                 } else {
  810                     if (!strcmp (mname, lastsep2+1))
  811                         result = TRUE;
  812                 }
  813             }
  814         }
  815     }
  816 
  817     g_free (pname);
  818     g_free (mname);
  819 
  820     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: result is %" G_GINT32_FORMAT, __func__, result);
  821     return result;
  822 }
  823 
  824 gboolean
  825 mono_w32process_try_get_modules (gpointer handle, gpointer *modules, guint32 size, guint32 *needed)
  826 {
  827     MonoW32Handle *handle_data;
  828     MonoW32HandleProcess *process_handle;
  829     GSList *mods = NULL, *mods_iter;
  830     MonoW32ProcessModule *module;
  831     guint32 count, avail = size / sizeof(gpointer);
  832     int i;
  833     pid_t pid;
  834     char *pname = NULL;
  835 
  836     /* Store modules in an array of pointers (main module as
  837      * modules[0]), using the load address for each module as a
  838      * token.  (Use 'NULL' as an alternative for the main module
  839      * so that the simple implementation can just return one item
  840      * for now.)  Get the info from /proc/<pid>/maps on linux,
  841      * /proc/<pid>/map on FreeBSD, other systems will have to
  842      * implement /dev/kmem reading or whatever other horrid
  843      * technique is needed.
  844      */
  845     if (size < sizeof(gpointer))
  846         return FALSE;
  847 
  848     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
  849         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
  850         mono_w32error_set_last (ERROR_INVALID_HANDLE);
  851         return FALSE;
  852     }
  853 
  854     if (handle_data->type != MONO_W32TYPE_PROCESS) {
  855         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
  856         mono_w32error_set_last (ERROR_INVALID_HANDLE);
  857         mono_w32handle_unref (handle_data);
  858         return FALSE;
  859     }
  860 
  861     process_handle = (MonoW32HandleProcess*) handle_data->specific;
  862 
  863     pid = process_handle->pid;
  864     pname = g_strdup (process_handle->pname);
  865 
  866     if (!pname) {
  867         modules[0] = NULL;
  868         *needed = sizeof(gpointer);
  869         mono_w32handle_unref (handle_data);
  870         return TRUE;
  871     }
  872 
  873     mods = mono_w32process_get_modules (pid);
  874     if (!mods) {
  875         modules[0] = NULL;
  876         *needed = sizeof(gpointer);
  877         g_free (pname);
  878         mono_w32handle_unref (handle_data);
  879         return TRUE;
  880     }
  881 
  882     count = 0;
  883 
  884     /*
  885      * Use the NULL shortcut, as the first line in
  886      * /proc/<pid>/maps isn't the executable, and we need
  887      * that first in the returned list. Check the module name
  888      * to see if it ends with the proc name and substitute
  889      * the first entry with it.  FIXME if this turns out to
  890      * be a problem.
  891      */
  892     modules[0] = NULL;
  893     mods_iter = mods;
  894     for (i = 0; mods_iter; i++) {
  895         if (i < avail - 1) {
  896             module = (MonoW32ProcessModule *)mods_iter->data;
  897             if (modules[0] != NULL)
  898                 modules[i] = module->address_start;
  899             else if (match_procname_to_modulename (pname, module->filename))
  900                 modules[0] = module->address_start;
  901             else
  902                 modules[i + 1] = module->address_start;
  903         }
  904         mono_w32process_module_free ((MonoW32ProcessModule *)mods_iter->data);
  905         mods_iter = g_slist_next (mods_iter);
  906         count++;
  907     }
  908 
  909     /* count + 1 to leave slot 0 for the main module */
  910     *needed = sizeof(gpointer) * (count + 1);
  911 
  912     g_slist_free (mods);
  913     g_free (pname);
  914     mono_w32handle_unref (handle_data);
  915     return TRUE;
  916 }
  917 
  918 gboolean
  919 mono_w32process_module_get_filename (gpointer handle, gpointer module, gunichar2 **str, guint32 *len)
  920 {
  921     gint pid;
  922     gsize bytes = 0;
  923     gchar *path;
  924     gunichar2 *proc_path;
  925 
  926     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Getting module file name, process handle %p module %p " G_GUINT32_FORMAT,
  927                 __func__, handle, module);
  928 
  929     if (str == NULL || len == NULL)
  930         return FALSE;
  931 
  932     *str = NULL;
  933     *len = 0;
  934 
  935     pid = mono_w32process_get_pid (handle);
  936     if (pid == 0)
  937         return FALSE;
  938 
  939     path = mono_w32process_get_path (pid);
  940     if (path == NULL)
  941         return FALSE;
  942 
  943     proc_path = mono_unicode_from_external (path, &bytes);
  944 
  945     if (proc_path == NULL) {
  946         g_free (path);
  947         return FALSE;
  948     }
  949 
  950     *str = mono_unicode_from_external (path, &bytes);
  951     *len = bytes / sizeof (gunichar2);
  952 
  953     g_free (path);
  954     return TRUE;
  955 }
  956 
  957 gboolean
  958 mono_w32process_module_get_name (gpointer handle, gpointer module, gunichar2 **str, guint32 *len)
  959 {
  960     MonoW32Handle *handle_data;
  961     MonoW32HandleProcess *process_handle;
  962     pid_t pid;
  963     gunichar2 *procname;
  964     char *procname_ext = NULL;
  965     gsize bytes = 0;
  966     GSList *mods = NULL, *mods_iter;
  967     MonoW32ProcessModule *found_module;
  968     char *pname = NULL;
  969 
  970     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Getting module base name, process handle %p module %p " G_GUINT32_FORMAT,
  971            __func__, handle, module);
  972 
  973     if (str == NULL || len == NULL)
  974         return FALSE;
  975 
  976     *str = NULL;
  977     *len = 0;
  978 
  979     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
  980         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
  981         mono_w32error_set_last (ERROR_INVALID_HANDLE);
  982         return FALSE;
  983     }
  984 
  985     if (handle_data->type != MONO_W32TYPE_PROCESS) {
  986         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
  987         mono_w32error_set_last (ERROR_INVALID_HANDLE);
  988         mono_w32handle_unref (handle_data);
  989         return FALSE;
  990     }
  991 
  992     process_handle = (MonoW32HandleProcess*) handle_data->specific;
  993 
  994     pid = process_handle->pid;
  995     pname = g_strdup (process_handle->pname);
  996 
  997     mods = mono_w32process_get_modules (pid);
  998     if (!mods && module != NULL) {
  999         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't get modules %p", __func__, handle);
 1000         g_free (pname);
 1001         mono_w32handle_unref (handle_data);
 1002         return FALSE;
 1003     }
 1004 
 1005     /* If module != NULL compare the address.
 1006      * If module == NULL we are looking for the main module.
 1007      * The best we can do for now check it the module name end with the process name.
 1008      */
 1009     for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
 1010         found_module = (MonoW32ProcessModule *)mods_iter->data;
 1011         if (procname_ext == NULL &&
 1012             ((module == NULL && match_procname_to_modulename (pname, found_module->filename)) ||
 1013              (module != NULL && found_module->address_start == module))) {
 1014             procname_ext = g_path_get_basename (found_module->filename);
 1015         }
 1016 
 1017         mono_w32process_module_free (found_module);
 1018     }
 1019 
 1020     if (procname_ext == NULL) {
 1021         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find procname_ext from procmods %p", __func__, handle);
 1022         /* If it's *still* null, we might have hit the
 1023          * case where reading /proc/$pid/maps gives an
 1024          * empty file for this user.
 1025          */
 1026         procname_ext = mono_w32process_get_name (pid);
 1027         if (!procname_ext)
 1028             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find procname_ext from proc_get_name %p pid %d", __func__, handle, pid);
 1029     }
 1030 
 1031     g_slist_free (mods);
 1032     g_free (pname);
 1033 
 1034     if (procname_ext) {
 1035         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Process name is [%s]", __func__,
 1036                procname_ext);
 1037 
 1038         procname = mono_unicode_from_external (procname_ext, &bytes);
 1039         if (procname == NULL) {
 1040             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't get procname %p", __func__, handle);
 1041             /* bugger */
 1042             g_free (procname_ext);
 1043             mono_w32handle_unref (handle_data);
 1044             return FALSE;
 1045         }
 1046 
 1047         *str = procname;
 1048         *len = bytes / sizeof (gunichar2);
 1049 
 1050         g_free (procname_ext);
 1051         mono_w32handle_unref (handle_data);
 1052         return TRUE;
 1053     }
 1054 
 1055     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find procname_ext %p", __func__, handle);
 1056     mono_w32handle_unref (handle_data);
 1057     return FALSE;
 1058 }
 1059 
 1060 gboolean
 1061 mono_w32process_module_get_information (gpointer handle, gpointer module, MODULEINFO *modinfo, guint32 size)
 1062 {
 1063     MonoW32Handle *handle_data;
 1064     MonoW32HandleProcess *process_handle;
 1065     pid_t pid;
 1066     GSList *mods = NULL, *mods_iter;
 1067     MonoW32ProcessModule *found_module;
 1068     gboolean ret = FALSE;
 1069     char *pname = NULL;
 1070 
 1071     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Getting module info, process handle %p module %p",
 1072            __func__, handle, module);
 1073 
 1074     if (modinfo == NULL || size < sizeof (MODULEINFO))
 1075         return FALSE;
 1076 
 1077     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 1078         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 1079         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 1080         return FALSE;
 1081     }
 1082 
 1083     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 1084         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 1085         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 1086         mono_w32handle_unref (handle_data);
 1087         return FALSE;
 1088     }
 1089 
 1090     process_handle = (MonoW32HandleProcess*) handle_data->specific;
 1091 
 1092     pid = process_handle->pid;
 1093     pname = g_strdup (process_handle->pname);
 1094 
 1095     mods = mono_w32process_get_modules (pid);
 1096     if (!mods) {
 1097         g_free (pname);
 1098         mono_w32handle_unref (handle_data);
 1099         return FALSE;
 1100     }
 1101 
 1102     /* If module != NULL compare the address.
 1103      * If module == NULL we are looking for the main module.
 1104      * The best we can do for now check it the module name end with the process name.
 1105      */
 1106     for (mods_iter = mods; mods_iter; mods_iter = g_slist_next (mods_iter)) {
 1107             found_module = (MonoW32ProcessModule *)mods_iter->data;
 1108             if (ret == FALSE &&
 1109                 ((module == NULL && match_procname_to_modulename (pname, found_module->filename)) ||
 1110                  (module != NULL && found_module->address_start == module))) {
 1111                 modinfo->lpBaseOfDll = found_module->address_start;
 1112                 modinfo->SizeOfImage = (gsize)(found_module->address_end) - (gsize)(found_module->address_start);
 1113                 modinfo->EntryPoint = found_module->address_offset;
 1114                 ret = TRUE;
 1115             }
 1116 
 1117             mono_w32process_module_free (found_module);
 1118     }
 1119 
 1120     g_slist_free (mods);
 1121     g_free (pname);
 1122     mono_w32handle_unref (handle_data);
 1123     return ret;
 1124 }
 1125 
 1126 #if HAVE_SIGACTION
 1127 
 1128 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler, (int _dummy, siginfo_t *info, void *context))
 1129 {
 1130     /*
 1131      * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
 1132      * mono_w32process_signal_finished ().
 1133      */
 1134     int old_errno = errno;
 1135 
 1136     mono_gc_finalize_notify ();
 1137 
 1138     mono_set_errno (old_errno);
 1139 }
 1140 
 1141 static void
 1142 process_add_sigchld_handler (void)
 1143 {
 1144     struct sigaction sa;
 1145 
 1146     sa.sa_sigaction = mono_sigchld_signal_handler;
 1147     sigemptyset (&sa.sa_mask);
 1148     sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;
 1149     g_assert (sigaction (SIGCHLD, &sa, NULL) != -1);
 1150     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "Added SIGCHLD handler");
 1151 }
 1152 
 1153 #endif
 1154 
 1155 /*
 1156  * mono_w32process_signal_finished:
 1157  *
 1158  *   Signal the exit semaphore for processes which have finished.
 1159  */
 1160 void
 1161 mono_w32process_signal_finished (void)
 1162 {
 1163     mono_coop_mutex_lock (&processes_mutex);
 1164 
 1165     for (Process* process = processes; process; process = process->next) {
 1166         int status = -1;
 1167         int pid;
 1168 
 1169         do {
 1170             pid = waitpid (process->pid, &status, WNOHANG);
 1171         } while (pid == -1 && errno == EINTR);
 1172 
 1173         // possible values of 'pid':
 1174         //  process->pid : the status changed for this child
 1175         //  0            : status unchanged for this PID
 1176         //  ECHILD       : process has been reaped elsewhere (or never existed)
 1177         //  EINVAL       : invalid PID or other argument
 1178 
 1179         // Therefore, we ignore status unchanged (nothing to do) and error
 1180         // events (process is cleaned up later).
 1181         if (pid <= 0)
 1182             continue;
 1183         if (process->signalled)
 1184             continue;
 1185 
 1186         process->signalled = TRUE;
 1187         process->status = status;
 1188         mono_coop_sem_post (&process->exit_sem);
 1189     }
 1190 
 1191     mono_coop_mutex_unlock (&processes_mutex);
 1192 }
 1193 
 1194 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
 1195 static void
 1196 switch_dir_separators (char *path)
 1197 {
 1198     size_t i, pathLength = strlen (path);
 1199 
 1200     /* Turn all the slashes round the right way, except for \' */
 1201     /* There are probably other characters that need to be excluded as well. */
 1202     for (i = 0; i < pathLength; i++) {
 1203         if (path[i] == '\\' && i < pathLength - 1 && path[i + 1] != '\'')
 1204             path[i] = '/';
 1205     }
 1206 }
 1207 
 1208 static gboolean
 1209 is_readable_or_executable (const char *prog)
 1210 {
 1211     struct stat buf;
 1212     int a = access (prog, R_OK);
 1213     int b = access (prog, X_OK);
 1214     if (a != 0 && b != 0)
 1215         return FALSE;
 1216     if (stat (prog, &buf))
 1217         return FALSE;
 1218     if (S_ISREG (buf.st_mode))
 1219         return TRUE;
 1220     return FALSE;
 1221 }
 1222 
 1223 static gboolean
 1224 is_executable (const char *prog)
 1225 {
 1226     struct stat buf;
 1227     if (access (prog, X_OK) != 0)
 1228         return FALSE;
 1229     if (stat (prog, &buf))
 1230         return FALSE;
 1231     if (S_ISREG (buf.st_mode))
 1232         return TRUE;
 1233     return FALSE;
 1234 }
 1235 
 1236 static gboolean
 1237 is_managed_binary (const char *filename)
 1238 {
 1239     int original_errno = errno;
 1240 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
 1241     int file = open (filename, O_RDONLY | O_LARGEFILE);
 1242 #else
 1243     int file = open (filename, O_RDONLY);
 1244 #endif
 1245     off_t new_offset;
 1246     unsigned char buffer[8];
 1247     off_t file_size, optional_header_offset;
 1248     off_t pe_header_offset, clr_header_offset;
 1249     gboolean managed = FALSE;
 1250     int num_read;
 1251     guint32 first_word, second_word, magic_number;
 1252     
 1253     /* If we are unable to open the file, then we definitely
 1254      * can't say that it is managed. The child mono process
 1255      * probably wouldn't be able to open it anyway.
 1256      */
 1257     if (file < 0) {
 1258         mono_set_errno (original_errno);
 1259         return FALSE;
 1260     }
 1261 
 1262     /* Retrieve the length of the file for future sanity checks. */
 1263     file_size = lseek (file, 0, SEEK_END);
 1264     lseek (file, 0, SEEK_SET);
 1265 
 1266     /* We know we need to read a header field at offset 60. */
 1267     if (file_size < 64)
 1268         goto leave;
 1269 
 1270     num_read = read (file, buffer, 2);
 1271 
 1272     if ((num_read != 2) || (buffer[0] != 'M') || (buffer[1] != 'Z'))
 1273         goto leave;
 1274 
 1275     new_offset = lseek (file, 60, SEEK_SET);
 1276 
 1277     if (new_offset != 60)
 1278         goto leave;
 1279     
 1280     num_read = read (file, buffer, 4);
 1281 
 1282     if (num_read != 4)
 1283         goto leave;
 1284     pe_header_offset =  buffer[0]
 1285         | (buffer[1] <<  8)
 1286         | (buffer[2] << 16)
 1287         | (buffer[3] << 24);
 1288     
 1289     if (pe_header_offset + 24 > file_size)
 1290         goto leave;
 1291 
 1292     new_offset = lseek (file, pe_header_offset, SEEK_SET);
 1293 
 1294     if (new_offset != pe_header_offset)
 1295         goto leave;
 1296 
 1297     num_read = read (file, buffer, 4);
 1298 
 1299     if ((num_read != 4) || (buffer[0] != 'P') || (buffer[1] != 'E') || (buffer[2] != 0) || (buffer[3] != 0))
 1300         goto leave;
 1301 
 1302     /*
 1303      * Verify that the header we want in the optional header data
 1304      * is present in this binary.
 1305      */
 1306     new_offset = lseek (file, pe_header_offset + 20, SEEK_SET);
 1307 
 1308     if (new_offset != pe_header_offset + 20)
 1309         goto leave;
 1310 
 1311     num_read = read (file, buffer, 2);
 1312 
 1313     if ((num_read != 2) || ((buffer[0] | (buffer[1] << 8)) < 216))
 1314         goto leave;
 1315 
 1316     optional_header_offset = pe_header_offset + 24;
 1317 
 1318     /* Read the PE magic number */
 1319     new_offset = lseek (file, optional_header_offset, SEEK_SET);
 1320     
 1321     if (new_offset != optional_header_offset)
 1322         goto leave;
 1323 
 1324     num_read = read (file, buffer, 2);
 1325 
 1326     if (num_read != 2)
 1327         goto leave;
 1328 
 1329     magic_number = (buffer[0] | (buffer[1] << 8));
 1330     
 1331     if (magic_number == 0x10B)  // PE32
 1332         clr_header_offset = 208;
 1333     else if (magic_number == 0x20B)  // PE32+
 1334         clr_header_offset = 224;
 1335     else
 1336         goto leave;
 1337 
 1338     /* Read the CLR header address and size fields. These will be
 1339      * zero if the binary is not managed.
 1340      */
 1341     new_offset = lseek (file, optional_header_offset + clr_header_offset, SEEK_SET);
 1342 
 1343     if (new_offset != optional_header_offset + clr_header_offset)
 1344         goto leave;
 1345 
 1346     num_read = read (file, buffer, 8);
 1347     
 1348     /* We are not concerned with endianness, only with
 1349      * whether it is zero or not.
 1350      */
 1351     first_word = *(guint32 *)&buffer[0];
 1352     second_word = *(guint32 *)&buffer[4];
 1353     
 1354     if ((num_read != 8) || (first_word == 0) || (second_word == 0))
 1355         goto leave;
 1356     
 1357     managed = TRUE;
 1358 
 1359 leave:
 1360     close (file);
 1361     mono_set_errno (original_errno);
 1362     return managed;
 1363 }
 1364 
 1365 /**
 1366  * Gets the biggest numbered file descriptor for the current process; failing
 1367  * that, the system's file descriptor limit. This is called by the fork child
 1368  * in close_my_fds.
 1369  */
 1370 static guint32
 1371 max_fd_count (void)
 1372 {
 1373 #if defined (_AIX)
 1374     struct procentry64 pe;
 1375     pid_t p;
 1376     p = getpid ();
 1377     if (getprocs64 (&pe, sizeof (pe), NULL, 0, &p, 1) != -1) {
 1378         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
 1379                "%s: maximum returned fd in child is %u",
 1380                __func__, pe.pi_maxofile);
 1381         return pe.pi_maxofile; // biggest + 1
 1382     }
 1383 #endif
 1384     // fallback to user/system limit if unsupported/error
 1385     return eg_getdtablesize ();
 1386 }
 1387 
 1388 /**
 1389  * Closes all of the process' opened file descriptors, applying a strategy
 1390  * appropriate for the target system. This is called by the fork child in
 1391  * process_create.
 1392  */
 1393 static void
 1394 close_my_fds (void)
 1395 {
 1396 // TODO: Other platforms.
 1397 //       * On macOS, use proc_pidinfo + PROC_PIDLISTFDS? See:
 1398 //         http://blog.palominolabs.com/2012/06/19/getting-the-files-being-used-by-a-process-on-mac-os-x/
 1399 //         (I have no idea how this plays out on i/watch/tvOS.)
 1400 //       * On the other BSDs, there's likely a sysctl for this.
 1401 //       * On Solaris, there exists posix_spawn_file_actions_addclosefrom_np,
 1402 //         but that assumes we're using posix_spawn; we aren't, as we do some
 1403 //         complex stuff between fork and exec. There's likely a way to get
 1404 //         the FD list/count though (maybe look at addclosefrom source in
 1405 //         illumos?) or just walk /proc/pid/fd like Linux?
 1406 #if defined (__linux__)
 1407     /* Walk the file descriptors in /proc/self/fd/. Linux has no other API,
 1408      * as far as I'm aware. Opening a directory won't create an FD. */
 1409     struct dirent *dp;
 1410     DIR *d;
 1411     int fd;
 1412     d = opendir ("/proc/self/fd/");
 1413     if (d) {
 1414         while ((dp = readdir (d)) != NULL) {
 1415             if (dp->d_name [0] == '.')
 1416                 continue;
 1417             fd = atoi (dp->d_name);
 1418             if (fd > 2)
 1419                 close (fd);
 1420         }
 1421         closedir (d);
 1422         return;
 1423     } else {
 1424         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
 1425                "%s: opening fd dir failed, using fallback",
 1426                __func__);
 1427     }
 1428 #elif defined (__FreeBSD__)
 1429     /* FreeBSD lets us get a list of FDs. There's a MIB to access them
 1430      * directly, but it uses a lot of nasty variable length structures. The
 1431      * system library libutil provides a nicer way to get a fixed length
 1432      * version instead. */
 1433     struct kinfo_file *kif;
 1434     int count, i;
 1435     /* this is malloced but we won't need to free once we exec/exit */
 1436     kif = kinfo_getfile (getpid (), &count);
 1437     if (kif) {
 1438         for (i = 0; i < count; i++) {
 1439             /* negative FDs look to be used by the OS */
 1440             if (kif [i].kf_fd > 2) /* no neg + no stdio */
 1441                 close (kif [i].kf_fd);
 1442         }
 1443         return;
 1444     } else {
 1445         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
 1446                "%s: kinfo_getfile failed, using fallback",
 1447                __func__);
 1448     }
 1449 #elif defined (_AIX)
 1450     struct procentry64 pe;
 1451     /* this array struct is 1 MB, we're NOT putting it on the stack.
 1452      * likewise no need to free; getprocs will fail if we use the smalller
 1453      * versions if we have a lot of FDs (is it worth it?)
 1454      */
 1455     struct fdsinfo_100K *fds;
 1456     pid_t p;
 1457     p = getpid ();
 1458     fds = (struct fdsinfo_100K *) g_malloc0 (sizeof (struct fdsinfo_100K));
 1459     if (!fds) {
 1460         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
 1461                "%s: fdsinfo alloc failed, using fallback",
 1462                __func__);
 1463         goto fallback;
 1464     }
 1465 
 1466     if (getprocs64 (&pe, sizeof (pe), fds, sizeof (struct fdsinfo_100K), &p, 1) != -1) {
 1467         for (int i = 3; i < pe.pi_maxofile; i++) {
 1468             if (fds->pi_ufd [i].fp != 0)
 1469                 close (fds->pi_ufd [i].fp);
 1470         }
 1471         return;
 1472     } else {
 1473         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS,
 1474                "%s: getprocs64 failed, using fallback",
 1475                __func__);
 1476     }
 1477 fallback:
 1478 #endif
 1479     /* Fallback: Close FDs blindly, according to an FD limit */
 1480     for (guint32 i = max_fd_count () - 1; i > 2; i--)
 1481         close (i);
 1482 }
 1483 #endif
 1484 
 1485 static gboolean
 1486 process_create (const gunichar2 *appname, const gunichar2 *cmdline,
 1487     const gunichar2 *cwd, StartupHandles *startup_handles, MonoW32ProcessInfo *process_info)
 1488 {
 1489 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
 1490     char *cmd = NULL, *prog = NULL, *full_prog = NULL, *args = NULL, *args_after_prog = NULL;
 1491     char *dir = NULL, **env_strings = NULL, **argv = NULL;
 1492     guint32 i;
 1493     gboolean ret = FALSE;
 1494     gpointer handle = NULL;
 1495     GError *gerr = NULL;
 1496     int in_fd, out_fd, err_fd;
 1497     pid_t pid = 0;
 1498     int startup_pipe [2] = {-1, -1};
 1499     int dummy;
 1500     Process *process;
 1501     ERROR_DECL (error);
 1502 
 1503 #if HAVE_SIGACTION
 1504     mono_lazy_initialize (&process_sig_chld_once, process_add_sigchld_handler);
 1505 #endif
 1506 
 1507     /* appname and cmdline specify the executable and its args:
 1508      *
 1509      * If appname is not NULL, it is the name of the executable.
 1510      * Otherwise the executable is the first token in cmdline.
 1511      *
 1512      * Executable searching:
 1513      *
 1514      * If appname is not NULL, it can specify the full path and
 1515      * file name, or else a partial name and the current directory
 1516      * will be used.  There is no additional searching.
 1517      *
 1518      * If appname is NULL, the first whitespace-delimited token in
 1519      * cmdline is used.  If the name does not contain a full
 1520      * directory path, the search sequence is:
 1521      *
 1522      * 1) The directory containing the current process
 1523      * 2) The current working directory
 1524      * 3) The windows system directory  (Ignored)
 1525      * 4) The windows directory (Ignored)
 1526      * 5) $PATH
 1527      *
 1528      * Just to make things more interesting, tokens can contain
 1529      * white space if they are surrounded by quotation marks.  I'm
 1530      * beginning to understand just why windows apps are generally
 1531      * so crap, with an API like this :-(
 1532      */
 1533     if (appname != NULL) {
 1534         cmd = mono_unicode_to_external_checked (appname, error);
 1535         if (cmd == NULL) {
 1536             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL; %s",
 1537                    __func__, mono_error_get_message (error));
 1538 
 1539             mono_error_cleanup (error);
 1540             mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
 1541             goto free_strings;
 1542         }
 1543 
 1544         switch_dir_separators(cmd);
 1545     }
 1546 
 1547     if (cmdline != NULL) {
 1548         args = mono_unicode_to_external_checked (cmdline, error);
 1549         if (args == NULL) {
 1550             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
 1551 
 1552             mono_error_cleanup (error);
 1553             mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
 1554             goto free_strings;
 1555         }
 1556     }
 1557 
 1558     if (cwd != NULL) {
 1559         dir = mono_unicode_to_external_checked (cwd, error);
 1560         if (dir == NULL) {
 1561             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unicode conversion returned NULL; %s", __func__, mono_error_get_message (error));
 1562 
 1563             mono_error_cleanup (error);
 1564             mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
 1565             goto free_strings;
 1566         }
 1567 
 1568         /* Turn all the slashes round the right way */
 1569         switch_dir_separators(dir);
 1570     }
 1571 
 1572 
 1573     /* We can't put off locating the executable any longer :-( */
 1574     if (cmd != NULL) {
 1575         char *unquoted;
 1576         if (g_ascii_isalpha (cmd[0]) && (cmd[1] == ':')) {
 1577             /* Strip off the drive letter.  I can't
 1578              * believe that CP/M holdover is still
 1579              * visible...
 1580              */
 1581             g_memmove (cmd, cmd+2, strlen (cmd)-2);
 1582             cmd[strlen (cmd)-2] = '\0';
 1583         }
 1584 
 1585         unquoted = g_shell_unquote (cmd, NULL);
 1586         if (unquoted[0] == '/') {
 1587             /* Assume full path given */
 1588             prog = g_strdup (unquoted);
 1589 
 1590             /* Executable existing ? */
 1591             if (!is_readable_or_executable (prog)) {
 1592                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s",
 1593                        __func__, prog);
 1594                 g_free (unquoted);
 1595                 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
 1596                 goto free_strings;
 1597             }
 1598         } else {
 1599             /* Search for file named by cmd in the current
 1600              * directory
 1601              */
 1602             char *curdir = g_get_current_dir ();
 1603 
 1604             prog = g_strdup_printf ("%s/%s", curdir, unquoted);
 1605             g_free (curdir);
 1606 
 1607             /* And make sure it's readable */
 1608             if (!is_readable_or_executable (prog)) {
 1609                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s",
 1610                        __func__, prog);
 1611                 g_free (unquoted);
 1612                 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
 1613                 goto free_strings;
 1614             }
 1615         }
 1616         g_free (unquoted);
 1617 
 1618         args_after_prog = args;
 1619     } else {
 1620         char *token = NULL;
 1621         char quote;
 1622 
 1623         /* Dig out the first token from args, taking quotation
 1624          * marks into account
 1625          */
 1626 
 1627         /* First, strip off all leading whitespace */
 1628         args = g_strchug (args);
 1629 
 1630         /* args_after_prog points to the contents of args
 1631          * after token has been set (otherwise argv[0] is
 1632          * duplicated)
 1633          */
 1634         args_after_prog = args;
 1635 
 1636         /* Assume the opening quote will always be the first
 1637          * character
 1638          */
 1639         if (args[0] == '\"' || args [0] == '\'') {
 1640             quote = args [0];
 1641             for (i = 1; args[i] != '\0' && args[i] != quote; i++);
 1642             if (args [i + 1] == '\0' || g_ascii_isspace (args[i+1])) {
 1643                 /* We found the first token */
 1644                 token = g_strndup (args+1, i-1);
 1645                 args_after_prog = g_strchug (args + i + 1);
 1646             } else {
 1647                 /* Quotation mark appeared in the
 1648                  * middle of the token.  Just give the
 1649                  * whole first token, quotes and all,
 1650                  * to exec.
 1651                  */
 1652             }
 1653         }
 1654 
 1655         if (token == NULL) {
 1656             /* No quote mark, or malformed */
 1657             for (i = 0; args[i] != '\0'; i++) {
 1658                 if (g_ascii_isspace (args[i])) {
 1659                     token = g_strndup (args, i);
 1660                     args_after_prog = args + i + 1;
 1661                     break;
 1662                 }
 1663             }
 1664         }
 1665 
 1666         if (token == NULL && args[0] != '\0') {
 1667             /* Must be just one token in the string */
 1668             token = g_strdup (args);
 1669             args_after_prog = NULL;
 1670         }
 1671 
 1672         if (token == NULL) {
 1673             /* Give up */
 1674             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find what to exec", __func__);
 1675 
 1676             mono_w32error_set_last (ERROR_PATH_NOT_FOUND);
 1677             goto free_strings;
 1678         }
 1679 
 1680         /* Turn all the slashes round the right way. Only for
 1681          * the prg. name
 1682          */
 1683         switch_dir_separators(token);
 1684 
 1685         if (g_ascii_isalpha (token[0]) && (token[1] == ':')) {
 1686             /* Strip off the drive letter.  I can't
 1687              * believe that CP/M holdover is still
 1688              * visible...
 1689              */
 1690             g_memmove (token, token+2, strlen (token)-2);
 1691             token[strlen (token)-2] = '\0';
 1692         }
 1693 
 1694         if (token[0] == '/') {
 1695             /* Assume full path given */
 1696             prog = g_strdup (token);
 1697 
 1698             /* Executable existing ? */
 1699             if (!is_readable_or_executable (prog)) {
 1700                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s",
 1701                        __func__, token);
 1702                 g_free (token);
 1703                 mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
 1704                 goto free_strings;
 1705             }
 1706         } else {
 1707             char *curdir = g_get_current_dir ();
 1708 
 1709             /* FIXME: Need to record the directory
 1710              * containing the current process, and check
 1711              * that for the new executable as the first
 1712              * place to look
 1713              */
 1714 
 1715             prog = g_strdup_printf ("%s/%s", curdir, token);
 1716             g_free (curdir);
 1717 
 1718             /* I assume X_OK is the criterion to use,
 1719              * rather than F_OK
 1720              *
 1721              * X_OK is too strict *if* the target is a CLR binary
 1722              */
 1723             if (!is_readable_or_executable (prog)) {
 1724                 g_free (prog);
 1725                 prog = g_find_program_in_path (token);
 1726                 if (prog == NULL) {
 1727                     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Couldn't find executable %s", __func__, token);
 1728 
 1729                     g_free (token);
 1730                     mono_w32error_set_last (ERROR_FILE_NOT_FOUND);
 1731                     goto free_strings;
 1732                 }
 1733             }
 1734         }
 1735 
 1736         g_free (token);
 1737     }
 1738 
 1739     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Exec prog [%s] args [%s]",
 1740         __func__, prog, args_after_prog);
 1741 
 1742     /* Check for CLR binaries; if found, we will try to invoke
 1743      * them using the same mono binary that started us.
 1744      */
 1745     if (is_managed_binary (prog)) {
 1746         gunichar2 *newapp, *newcmd;
 1747         gsize bytes_ignored;
 1748 
 1749         newapp = mono_unicode_from_external (cli_launcher ? cli_launcher : "mono", &bytes_ignored);
 1750         if (newapp) {
 1751             if (appname)
 1752                 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, appname, utf16_space, cmdline, (const gunichar2 *)NULL);
 1753             else
 1754                 newcmd = utf16_concat (utf16_quote, newapp, utf16_quote, utf16_space, cmdline, (const gunichar2 *)NULL);
 1755 
 1756             g_free (newapp);
 1757 
 1758             if (newcmd) {
 1759                 ret = process_create (NULL, newcmd, cwd, startup_handles, process_info);
 1760 
 1761                 g_free (newcmd);
 1762 
 1763                 goto free_strings;
 1764             }
 1765         }
 1766     } else {
 1767         if (!is_executable (prog)) {
 1768             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Executable permisson not set on %s", __func__, prog);
 1769             mono_w32error_set_last (ERROR_ACCESS_DENIED);
 1770             goto free_strings;
 1771         }
 1772     }
 1773 
 1774     if (args_after_prog != NULL && *args_after_prog) {
 1775         char *qprog;
 1776 
 1777         qprog = g_shell_quote (prog);
 1778         full_prog = g_strconcat (qprog, " ", args_after_prog, (const char*)NULL);
 1779         g_free (qprog);
 1780     } else {
 1781         full_prog = g_shell_quote (prog);
 1782     }
 1783 
 1784     ret = g_shell_parse_argv (full_prog, NULL, &argv, &gerr);
 1785     if (ret == FALSE) {
 1786         g_message ("process_create: %s\n", gerr->message);
 1787         g_error_free (gerr);
 1788         gerr = NULL;
 1789         goto free_strings;
 1790     }
 1791 
 1792     if (startup_handles) {
 1793         in_fd = GPOINTER_TO_UINT (startup_handles->input);
 1794         out_fd = GPOINTER_TO_UINT (startup_handles->output);
 1795         err_fd = GPOINTER_TO_UINT (startup_handles->error);
 1796     } else {
 1797         in_fd = GPOINTER_TO_UINT (mono_w32file_get_console_input ());
 1798         out_fd = GPOINTER_TO_UINT (mono_w32file_get_console_output ());
 1799         err_fd = GPOINTER_TO_UINT (mono_w32file_get_console_error ());
 1800     }
 1801 
 1802     /*
 1803      * process->env_variables is a an array of MonoString*
 1804      *
 1805      * If new_environ is not NULL it specifies the entire set of
 1806      * environment variables in the new process.  Otherwise the
 1807      * new process inherits the same environment.
 1808      */
 1809     if (process_info->env_variables) {
 1810         MonoArrayHandle array = MONO_HANDLE_NEW (MonoArray, process_info->env_variables);
 1811         MonoStringHandle var = MONO_HANDLE_NEW (MonoString, NULL);
 1812         gsize const array_length = mono_array_handle_length (array);
 1813 
 1814         /* +2: one for the process handle value, and the last one is NULL */
 1815         // What "process handle value"?
 1816         env_strings = g_new0 (gchar*, array_length + 2);
 1817 
 1818         /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
 1819         for (gsize i = 0; i < array_length; ++i) {
 1820             MONO_HANDLE_ARRAY_GETREF (var, array, i);
 1821             gchandle_t gchandle = 0;
 1822             env_strings [i] = mono_unicode_to_external (mono_string_handle_pin_chars (var, &gchandle));
 1823             mono_gchandle_free_internal (gchandle);
 1824         }
 1825     } else {
 1826         gsize env_count = 0;
 1827         for (i = 0; environ[i] != NULL; i++)
 1828             env_count++;
 1829 
 1830         /* +2: one for the process handle value, and the last one is NULL */
 1831         // What "process handle value"?
 1832         env_strings = g_new0 (gchar*, env_count + 2);
 1833 
 1834         /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
 1835         for (i = 0; i < env_count; i++)
 1836             env_strings [i] = g_strdup (environ[i]);
 1837     }
 1838 
 1839     /* Create a pipe to make sure the child doesn't exit before
 1840      * we can add the process to the linked list of processes */
 1841     if (pipe (startup_pipe) == -1) {
 1842         /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
 1843          * This is just for a very hard to hit race condition in the first place */
 1844         startup_pipe [0] = startup_pipe [1] = -1;
 1845         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__);
 1846     }
 1847 
 1848     switch (pid = fork ()) {
 1849     case -1: /* Error */ {
 1850         mono_w32error_set_last (ERROR_OUTOFMEMORY);
 1851         ret = FALSE;
 1852         break;
 1853     }
 1854     case 0: /* Child */ {
 1855         if (startup_pipe [0] != -1) {
 1856             /* Wait until the parent has updated it's internal data */
 1857             ssize_t _i G_GNUC_UNUSED = read (startup_pipe [0], &dummy, 1);
 1858             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: child: parent has completed its setup", __func__);
 1859             close (startup_pipe [0]);
 1860             close (startup_pipe [1]);
 1861         }
 1862 
 1863         /* should we detach from the process group? */
 1864 
 1865         /* Connect stdin, stdout and stderr */
 1866         dup2 (in_fd, 0);
 1867         dup2 (out_fd, 1);
 1868         dup2 (err_fd, 2);
 1869 
 1870         /* Close this child's file handles. */
 1871         close_my_fds ();
 1872 
 1873 #ifdef DEBUG_ENABLED
 1874         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: exec()ing [%s] in dir [%s]", __func__, cmd,
 1875                dir == NULL?".":dir);
 1876         for (i = 0; argv[i] != NULL; i++)
 1877             g_message ("arg %" G_GUINT32_FORMAT ": [%s]", i, argv[i]);
 1878 
 1879         for (i = 0; env_strings[i] != NULL; i++)
 1880             g_message ("env %" G_GUINT32_FORMAT ": [%s]", i, env_strings[i]);
 1881 #endif
 1882 
 1883         /* set cwd */
 1884         if (dir != NULL && chdir (dir) == -1) {
 1885             /* set error */
 1886             _exit (-1);
 1887         }
 1888 
 1889         /* exec */
 1890         execve (argv[0], argv, env_strings);
 1891 
 1892         /* set error */
 1893         _exit (-1);
 1894 
 1895         break;
 1896     }
 1897     default: /* Parent */ {
 1898         MonoW32Handle *handle_data;
 1899         MonoW32HandleProcess process_handle;
 1900 
 1901         memset (&process_handle, 0, sizeof (process_handle));
 1902         process_handle.pid = pid;
 1903         process_handle.child = TRUE;
 1904         process_handle.pname = g_strdup (prog);
 1905         process_set_defaults (&process_handle);
 1906 
 1907         /* Add our process into the linked list of processes */
 1908         process = (Process *) g_malloc0 (sizeof (Process));
 1909         process->pid = pid;
 1910         process->handle_count = 1;
 1911         mono_coop_sem_init (&process->exit_sem, 0);
 1912 
 1913         process_handle.process = process;
 1914 
 1915         handle = mono_w32handle_new (MONO_W32TYPE_PROCESS, &process_handle);
 1916         if (handle == INVALID_HANDLE_VALUE) {
 1917             g_warning ("%s: error creating process handle", __func__);
 1918 
 1919             mono_coop_sem_destroy (&process->exit_sem);
 1920             g_free (process);
 1921 
 1922             mono_w32error_set_last (ERROR_OUTOFMEMORY);
 1923             ret = FALSE;
 1924             break;
 1925         }
 1926 
 1927         if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
 1928             g_error ("%s: unknown handle %p", __func__, handle);
 1929 
 1930         if (handle_data->type != MONO_W32TYPE_PROCESS)
 1931             g_error ("%s: unknown process handle %p", __func__, handle);
 1932 
 1933         /* Keep the process handle artificially alive until the process
 1934          * exits so that the information in the handle isn't lost. */
 1935         process->handle = mono_w32handle_duplicate (handle_data);
 1936 
 1937         mono_coop_mutex_lock (&processes_mutex);
 1938         process->next = processes;
 1939         mono_memory_barrier ();
 1940         processes = process;
 1941         mono_coop_mutex_unlock (&processes_mutex);
 1942 
 1943         if (process_info != NULL) {
 1944             process_info->process_handle = handle;
 1945             process_info->pid = pid;
 1946         }
 1947 
 1948         mono_w32handle_unref (handle_data);
 1949 
 1950         break;
 1951     }
 1952     }
 1953 
 1954     if (startup_pipe [1] != -1) {
 1955         /* Write 1 byte, doesn't matter what */
 1956         ssize_t _i G_GNUC_UNUSED = write (startup_pipe [1], startup_pipe, 1);
 1957         close (startup_pipe [0]);
 1958         close (startup_pipe [1]);
 1959     }
 1960 
 1961 free_strings:
 1962     g_free (cmd);
 1963     g_free (full_prog);
 1964     g_free (prog);
 1965     g_free (args);
 1966     g_free (dir);
 1967     g_strfreev (env_strings);
 1968     g_strfreev (argv);
 1969 
 1970     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: returning handle %p for pid %d", __func__, handle, pid);
 1971 
 1972     /* Check if something needs to be cleaned up. */
 1973     processes_cleanup ();
 1974 
 1975     return ret;
 1976 #else
 1977     mono_w32error_set_last (ERROR_NOT_SUPPORTED);
 1978     return FALSE;
 1979 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
 1980 }
 1981 
 1982 MonoBoolean
 1983 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfoHandle proc_start_info, MonoW32ProcessInfo *process_info, MonoError *error)
 1984 {
 1985     MonoCreateProcessCoop coop;
 1986     mono_createprocess_coop_init (&coop, proc_start_info, process_info);
 1987 
 1988     gboolean ret;
 1989     gboolean handler_needswait = FALSE;
 1990 
 1991     if (!coop.filename) {
 1992         /* w2k returns TRUE for this, for some reason. */
 1993         ret = TRUE;
 1994         goto done;
 1995     }
 1996 
 1997     const gunichar2 *lpFile;
 1998     lpFile = coop.filename;
 1999     const gunichar2 *lpParameters;
 2000     lpParameters = coop.arguments;
 2001     const gunichar2 *lpDirectory;
 2002     lpDirectory = coop.length.working_directory ? coop.working_directory : NULL;
 2003 
 2004     /* Put both executable and parameters into the second argument
 2005      * to process_create (), so it searches $PATH.  The conversion
 2006      * into and back out of utf8 is because there is no
 2007      * g_strdup_printf () equivalent for gunichar2 :-(
 2008      */
 2009     gunichar2 *args;
 2010     args = utf16_concat (utf16_quote, lpFile, utf16_quote, lpParameters ? utf16_space : NULL, lpParameters, (const gunichar2 *)NULL);
 2011     if (args == NULL) {
 2012         mono_w32error_set_last (ERROR_INVALID_DATA);
 2013         ret = FALSE;
 2014         goto done;
 2015     }
 2016     ret = process_create (NULL, args, lpDirectory, NULL, process_info);
 2017     g_free (args);
 2018 
 2019     if (!ret && mono_w32error_get_last () == ERROR_OUTOFMEMORY)
 2020         goto done;
 2021 
 2022     if (!ret) {
 2023 
 2024 #if defined(TARGET_IOS) || defined(TARGET_ANDROID)
 2025         // don't try the "open" handlers on iOS/Android, they don't exist there anyway
 2026         goto done;
 2027 #endif
 2028 
 2029         static char *handler;
 2030         static gunichar2 *handler_utf16;
 2031 #ifndef HOST_DARWIN
 2032         gboolean finished = FALSE;
 2033 #endif
 2034 
 2035         if (handler_utf16 == (gunichar2 *)-1) {
 2036             ret = FALSE;
 2037             goto done;
 2038         }
 2039 
 2040 #ifdef HOST_DARWIN
 2041         handler = g_strdup ("/usr/bin/open");
 2042         handler_needswait = TRUE;
 2043 #else
 2044         /*
 2045          * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
 2046          * if that fails, try to use gnome-open, then kfmclient
 2047          */
 2048         MONO_ENTER_GC_SAFE;
 2049         handler = g_find_program_in_path ("xdg-open");
 2050         if (handler != NULL)
 2051             handler_needswait = TRUE;
 2052         else {
 2053             handler = g_find_program_in_path ("gnome-open");
 2054             if (handler == NULL){
 2055                 handler = g_find_program_in_path ("kfmclient");
 2056                 if (handler == NULL){
 2057                     handler_utf16 = (gunichar2 *) -1;
 2058                     ret = FALSE;
 2059                     finished = TRUE;
 2060                 } else {
 2061                     /* kfmclient needs exec argument */
 2062                     char *old = handler;
 2063                     handler = g_strconcat (old, " exec",
 2064                                    (const char*)NULL);
 2065                     g_free (old);
 2066                 }
 2067             }
 2068         }
 2069         MONO_EXIT_GC_SAFE;
 2070         if (finished)
 2071             goto done;
 2072 #endif
 2073         handler_utf16 = g_utf8_to_utf16 (handler, -1, NULL, NULL, NULL);
 2074         g_free (handler);
 2075 
 2076         /* Put quotes around the filename, in case it's a url
 2077          * that contains #'s (process_create() calls
 2078          * g_shell_parse_argv(), which deliberately throws
 2079          * away anything after an unquoted #).  Fixes bug
 2080          * 371567.
 2081          */
 2082         args = utf16_concat (handler_utf16, utf16_space, utf16_quote, lpFile, utf16_quote,
 2083             lpParameters ? utf16_space : NULL, lpParameters, (const gunichar2 *)NULL);
 2084         if (args == NULL) {
 2085             mono_w32error_set_last (ERROR_INVALID_DATA);
 2086             ret = FALSE;
 2087             goto done;
 2088         }
 2089         ret = process_create (NULL, args, lpDirectory, NULL, process_info);
 2090         g_free (args);
 2091         if (!ret) {
 2092             if (mono_w32error_get_last () != ERROR_OUTOFMEMORY)
 2093                 mono_w32error_set_last (ERROR_INVALID_DATA);
 2094             ret = FALSE;
 2095             goto done;
 2096         }
 2097 
 2098         if (handler_needswait) {
 2099             gint32 exitcode;
 2100             MonoW32HandleWaitRet waitret;
 2101             waitret = process_wait ((MonoW32Handle*)process_info->process_handle, MONO_INFINITE_WAIT, NULL);
 2102             (void)waitret;
 2103             mono_get_exit_code_process (process_info->process_handle, &exitcode);
 2104             if (exitcode != 0)
 2105                 ret = FALSE;
 2106         }
 2107         /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
 2108         mono_w32handle_close (process_info->process_handle);
 2109         process_info->process_handle = INVALID_HANDLE_VALUE;
 2110     }
 2111 
 2112 done:
 2113     if (ret == FALSE) {
 2114         process_info->pid = -mono_w32error_get_last ();
 2115     } else {
 2116 #if !defined(MONO_CROSS_COMPILE)
 2117         process_info->pid = mono_w32process_get_pid (process_info->process_handle);
 2118 #else
 2119         process_info->pid = 0;
 2120 #endif
 2121     }
 2122 
 2123     mono_createprocess_coop_cleanup (&coop);
 2124 
 2125     return ret;
 2126 }
 2127 
 2128 /* Only used when UseShellExecute is false */
 2129 static gboolean
 2130 process_get_complete_path (const gunichar2 *appname, gchar **completed)
 2131 {
 2132     char *found = NULL;
 2133     gboolean result = FALSE;
 2134 
 2135     char *utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
 2136 
 2137     if (g_path_is_absolute (utf8app)) {
 2138         *completed = g_shell_quote (utf8app);
 2139         result = TRUE;
 2140         goto exit;
 2141     }
 2142 
 2143     if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
 2144         *completed = g_shell_quote (utf8app);
 2145         result = TRUE;
 2146         goto exit;
 2147     }
 2148     
 2149     found = g_find_program_in_path (utf8app);
 2150     if (found == NULL) {
 2151         *completed = NULL;
 2152         result = FALSE;
 2153         goto exit;
 2154     }
 2155 
 2156     *completed = g_shell_quote (found);
 2157     result = TRUE;
 2158 exit:
 2159     g_free (found);
 2160     g_free (utf8app);
 2161     return result;
 2162 }
 2163 
 2164 static gboolean
 2165 process_get_shell_arguments (MonoCreateProcessCoop *coop, gunichar2 **shell_path)
 2166 {
 2167     gchar *complete_path = NULL;
 2168 
 2169     *shell_path = NULL;
 2170 
 2171     if (process_get_complete_path (coop->filename, &complete_path)) {
 2172         *shell_path = g_utf8_to_utf16 (complete_path, -1, NULL, NULL, NULL);
 2173         g_free (complete_path);
 2174     }
 2175 
 2176     return *shell_path != NULL;
 2177 }
 2178 
 2179 MonoBoolean
 2180 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfoHandle proc_start_info,
 2181     HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info, MonoError *error)
 2182 {
 2183     MonoCreateProcessCoop coop;
 2184     mono_createprocess_coop_init (&coop, proc_start_info, process_info);
 2185 
 2186     gboolean ret;
 2187     StartupHandles startup_handles;
 2188     gunichar2 *shell_path = NULL;
 2189 
 2190     memset (&startup_handles, 0, sizeof (startup_handles));
 2191     startup_handles.input = stdin_handle;
 2192     startup_handles.output = stdout_handle;
 2193     startup_handles.error = stderr_handle;
 2194 
 2195     if (!process_get_shell_arguments (&coop, &shell_path)) {
 2196         process_info->pid = -ERROR_FILE_NOT_FOUND;
 2197         ret = FALSE;
 2198         goto exit;
 2199     }
 2200 
 2201     gunichar2 *args;
 2202     args = coop.length.arguments ? coop.arguments : NULL;
 2203 
 2204     /* The default dir name is "".  Turn that into NULL to mean "current directory" */
 2205     gunichar2 *dir;
 2206     dir = coop.length.working_directory ? coop.working_directory : NULL;
 2207 
 2208     ret = process_create (shell_path, args, dir, &startup_handles, process_info);
 2209 
 2210     if (!ret)
 2211         process_info->pid = -mono_w32error_get_last ();
 2212 
 2213 exit:
 2214     g_free (shell_path);
 2215     mono_createprocess_coop_cleanup (&coop);
 2216     return ret;
 2217 }
 2218 
 2219 /* Returns an array of pids */
 2220 MonoArrayHandle
 2221 ves_icall_System_Diagnostics_Process_GetProcesses_internal (MonoError *error)
 2222 {
 2223     int count = 0;
 2224     guint32 *raw = 0;
 2225     gpointer *pidarray = 0;
 2226     MonoArrayHandle procs = NULL_HANDLE_ARRAY;
 2227 
 2228     // FIXME mono_process_list should probably return array of int
 2229     // as all of the users of the elements truncate to that.
 2230 
 2231     MONO_ENTER_GC_SAFE;
 2232     pidarray = mono_process_list (&count);
 2233     MONO_EXIT_GC_SAFE;
 2234     if (!pidarray) {
 2235         mono_error_set_not_supported (error, "This system does not support EnumProcesses");
 2236         goto exit;
 2237     }
 2238     procs = mono_array_new_handle (mono_domain_get (), mono_get_int32_class (), count, error);
 2239     if (!is_ok (error)) {
 2240         procs = NULL_HANDLE_ARRAY;
 2241         goto exit;
 2242     }
 2243 
 2244     MONO_ENTER_NO_SAFEPOINTS;
 2245 
 2246     raw = mono_array_addr_internal (MONO_HANDLE_RAW (procs), guint32, 0);
 2247     if (sizeof (guint32) == sizeof (gpointer)) {
 2248         memcpy (raw, pidarray, count * sizeof (gint32));
 2249     } else {
 2250         for (int i = 0; i < count; ++i)
 2251             raw [i] = GPOINTER_TO_UINT (pidarray [i]);
 2252     }
 2253 
 2254     MONO_EXIT_NO_SAFEPOINTS;
 2255 
 2256 exit:
 2257     g_free (pidarray);
 2258     return procs;
 2259 }
 2260 
 2261 void
 2262 mono_w32process_set_cli_launcher (gchar *path)
 2263 {
 2264     g_free (cli_launcher);
 2265     cli_launcher = g_strdup (path);
 2266 }
 2267 
 2268 gpointer
 2269 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
 2270 {
 2271     return current_process;
 2272 }
 2273 
 2274 MonoBoolean
 2275 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
 2276 {
 2277     return mono_get_exit_code_process (handle, exitcode);
 2278 }
 2279 
 2280 static MonoBoolean
 2281 mono_get_exit_code_process (gpointer handle, gint32 *exitcode)
 2282 {
 2283     MonoW32Handle *handle_data;
 2284     MonoW32HandleProcess *process_handle;
 2285 
 2286     if (!exitcode)
 2287         return FALSE;
 2288 
 2289     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2290         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2291         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2292         return FALSE;
 2293     }
 2294 
 2295     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2296         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2297         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2298         mono_w32handle_unref (handle_data);
 2299         return FALSE;
 2300     }
 2301 
 2302     process_handle = (MonoW32HandleProcess*) handle_data->specific;
 2303 
 2304     if (process_handle->pid == current_pid) {
 2305         *exitcode = STILL_ACTIVE;
 2306         mono_w32handle_unref (handle_data);
 2307         return TRUE;
 2308     }
 2309 
 2310     /* A process handle is only signalled if the process has exited
 2311      * and has been waited for. Make sure any process exit has been
 2312      * noticed before checking if the process is signalled.
 2313      * Fixes bug 325463. */
 2314     mono_w32handle_wait_one (handle, 0, TRUE);
 2315 
 2316     *exitcode = mono_w32handle_issignalled (handle_data) ? process_handle->exitstatus : STILL_ACTIVE;
 2317 
 2318     mono_w32handle_unref (handle_data);
 2319 
 2320     return TRUE;
 2321 }
 2322 
 2323 MonoBoolean
 2324 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
 2325 {
 2326     return mono_w32handle_close (handle);
 2327 }
 2328 
 2329 MonoBoolean
 2330 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
 2331 {
 2332 #ifdef HAVE_KILL
 2333     MonoW32Handle *handle_data;
 2334     int ret;
 2335     pid_t pid;
 2336 
 2337     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2338         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2339         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2340         return FALSE;
 2341     }
 2342 
 2343     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2344         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2345         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2346         mono_w32handle_unref (handle_data);
 2347         return FALSE;
 2348     }
 2349 
 2350     pid = ((MonoW32HandleProcess*) handle_data->specific)->pid;
 2351 
 2352     ret = kill (pid, exitcode == -1 ? SIGKILL : SIGTERM);
 2353     if (ret == 0) {
 2354         mono_w32handle_unref (handle_data);
 2355         return TRUE;
 2356     }
 2357 
 2358     switch (errno) {
 2359     case EINVAL: mono_w32error_set_last (ERROR_INVALID_PARAMETER); break;
 2360     case EPERM:  mono_w32error_set_last (ERROR_ACCESS_DENIED);     break;
 2361     case ESRCH:  mono_w32error_set_last (ERROR_PROC_NOT_FOUND);    break;
 2362     default:     mono_w32error_set_last (ERROR_GEN_FAILURE);       break;
 2363     }
 2364 
 2365     mono_w32handle_unref (handle_data);
 2366     return FALSE;
 2367 #else
 2368     g_error ("kill() is not supported by this platform");
 2369 #endif
 2370 }
 2371 
 2372 MonoBoolean
 2373 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
 2374 {
 2375     MonoW32Handle *handle_data;
 2376     MonoW32HandleProcess *process_handle;
 2377 
 2378     if (!min || !max)
 2379         return FALSE;
 2380 
 2381     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2382         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2383         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2384         return FALSE;
 2385     }
 2386 
 2387     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2388         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2389         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2390         mono_w32handle_unref (handle_data);
 2391         return FALSE;
 2392     }
 2393 
 2394     process_handle = (MonoW32HandleProcess*) handle_data->specific;
 2395 
 2396     if (!process_handle->child) {
 2397         mono_w32handle_unref (handle_data);
 2398         return FALSE;
 2399     }
 2400 
 2401     *min = process_handle->min_working_set;
 2402     *max = process_handle->max_working_set;
 2403 
 2404     mono_w32handle_unref (handle_data);
 2405     return TRUE;
 2406 }
 2407 
 2408 MonoBoolean
 2409 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
 2410 {
 2411     MonoW32Handle *handle_data;
 2412     MonoW32HandleProcess *process_handle;
 2413 
 2414     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2415         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2416         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2417         return FALSE;
 2418     }
 2419 
 2420     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2421         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2422         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2423         mono_w32handle_unref (handle_data);
 2424         return FALSE;
 2425     }
 2426 
 2427     process_handle = (MonoW32HandleProcess*) handle_data->specific;
 2428 
 2429     if (!process_handle->child) {
 2430         mono_w32handle_unref (handle_data);
 2431         return FALSE;
 2432     }
 2433 
 2434     process_handle->min_working_set = min;
 2435     process_handle->max_working_set = max;
 2436 
 2437     mono_w32handle_unref (handle_data);
 2438     return TRUE;
 2439 }
 2440 
 2441 gint32
 2442 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
 2443 {
 2444 #ifdef HAVE_GETPRIORITY
 2445     MonoW32Handle *handle_data;
 2446     gint res;
 2447     gint32 ret;
 2448     pid_t pid;
 2449 
 2450     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2451         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2452         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2453         return 0;
 2454     }
 2455 
 2456     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2457         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2458         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2459         mono_w32handle_unref (handle_data);
 2460         return 0;
 2461     }
 2462 
 2463     pid = ((MonoW32HandleProcess*) handle_data->specific)->pid;
 2464 
 2465     mono_set_errno (0);
 2466     res = getpriority (PRIO_PROCESS, pid);
 2467     if (res == -1 && errno != 0) {
 2468         switch (errno) {
 2469         case EPERM:
 2470         case EACCES:
 2471             mono_w32error_set_last (ERROR_ACCESS_DENIED);
 2472             break;
 2473         case ESRCH:
 2474             mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
 2475             break;
 2476         default:
 2477             mono_w32error_set_last (ERROR_GEN_FAILURE);
 2478         }
 2479 
 2480         mono_w32handle_unref (handle_data);
 2481         return 0;
 2482     }
 2483 
 2484     if (res == 0)
 2485         ret = MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
 2486     else if (res < -15)
 2487         ret = MONO_W32PROCESS_PRIORITY_CLASS_REALTIME;
 2488     else if (res < -10)
 2489         ret = MONO_W32PROCESS_PRIORITY_CLASS_HIGH;
 2490     else if (res < 0)
 2491         ret = MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
 2492     else if (res > 10)
 2493         ret = MONO_W32PROCESS_PRIORITY_CLASS_IDLE;
 2494     else if (res > 0)
 2495         ret = MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
 2496     else
 2497         ret = MONO_W32PROCESS_PRIORITY_CLASS_NORMAL;
 2498 
 2499     mono_w32handle_unref (handle_data);
 2500     return ret;
 2501 #else
 2502     mono_w32error_set_last (ERROR_NOT_SUPPORTED);
 2503     return 0;
 2504 #endif
 2505 }
 2506 
 2507 MonoBoolean
 2508 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
 2509 {
 2510 #ifdef HAVE_SETPRIORITY
 2511     MonoW32Handle *handle_data;
 2512     int ret;
 2513     int prio;
 2514     pid_t pid;
 2515 
 2516     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2517         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2518         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2519         return FALSE;
 2520     }
 2521 
 2522     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2523         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2524         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2525         mono_w32handle_unref (handle_data);
 2526         return FALSE;
 2527     }
 2528 
 2529     pid = ((MonoW32HandleProcess*) handle_data->specific)->pid;
 2530 
 2531     switch (priorityClass) {
 2532     case MONO_W32PROCESS_PRIORITY_CLASS_IDLE:
 2533         prio = 19;
 2534         break;
 2535     case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
 2536         prio = 10;
 2537         break;
 2538     case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL:
 2539         prio = 0;
 2540         break;
 2541     case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
 2542         prio = -5;
 2543         break;
 2544     case MONO_W32PROCESS_PRIORITY_CLASS_HIGH:
 2545         prio = -11;
 2546         break;
 2547     case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME:
 2548         prio = -20;
 2549         break;
 2550     default:
 2551         mono_w32error_set_last (ERROR_INVALID_PARAMETER);
 2552         mono_w32handle_unref (handle_data);
 2553         return FALSE;
 2554     }
 2555 
 2556     ret = setpriority (PRIO_PROCESS, pid, prio);
 2557     if (ret == -1) {
 2558         switch (errno) {
 2559         case EPERM:
 2560         case EACCES:
 2561             mono_w32error_set_last (ERROR_ACCESS_DENIED);
 2562             break;
 2563         case ESRCH:
 2564             mono_w32error_set_last (ERROR_PROC_NOT_FOUND);
 2565             break;
 2566         default:
 2567             mono_w32error_set_last (ERROR_GEN_FAILURE);
 2568         }
 2569     }
 2570 
 2571     mono_w32handle_unref (handle_data);
 2572     return ret == 0;
 2573 #else
 2574     mono_w32error_set_last (ERROR_NOT_SUPPORTED);
 2575     return FALSE;
 2576 #endif
 2577 }
 2578 
 2579 static void
 2580 ticks_to_processtime (guint64 ticks, ProcessTime *processtime)
 2581 {
 2582     processtime->lowDateTime = ticks & 0xFFFFFFFF;
 2583     processtime->highDateTime = ticks >> 32;
 2584 }
 2585 
 2586 MonoBoolean
 2587 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creation_time, gint64 *exit_time, gint64 *kernel_time, gint64 *user_time)
 2588 {
 2589     MonoW32Handle *handle_data;
 2590     MonoW32HandleProcess *process_handle;
 2591     ProcessTime *creation_processtime, *exit_processtime, *kernel_processtime, *user_processtime;
 2592 
 2593     if (!creation_time || !exit_time || !kernel_time || !user_time) {
 2594         /* Not sure if w32 allows NULLs here or not */
 2595         return FALSE;
 2596     }
 2597 
 2598     creation_processtime = (ProcessTime*) creation_time;
 2599     exit_processtime = (ProcessTime*) exit_time;
 2600     kernel_processtime = (ProcessTime*) kernel_time;
 2601     user_processtime = (ProcessTime*) user_time;
 2602 
 2603     memset (creation_processtime, 0, sizeof (ProcessTime));
 2604     memset (exit_processtime, 0, sizeof (ProcessTime));
 2605     memset (kernel_processtime, 0, sizeof (ProcessTime));
 2606     memset (user_processtime, 0, sizeof (ProcessTime));
 2607 
 2608     if (!mono_w32handle_lookup_and_ref (handle, &handle_data)) {
 2609         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown handle %p", __func__, handle);
 2610         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2611         return FALSE;
 2612     }
 2613 
 2614     if (handle_data->type != MONO_W32TYPE_PROCESS) {
 2615         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: unknown process handle %p", __func__, handle);
 2616         mono_w32error_set_last (ERROR_INVALID_HANDLE);
 2617         mono_w32handle_unref (handle_data);
 2618         return FALSE;
 2619     }
 2620 
 2621     process_handle = (MonoW32HandleProcess*) handle_data->specific;
 2622 
 2623     if (!process_handle->child) {
 2624         gint64 start_ticks, user_ticks, kernel_ticks;
 2625 
 2626         mono_process_get_times (GINT_TO_POINTER (process_handle->pid),
 2627             &start_ticks, &user_ticks, &kernel_ticks);
 2628 
 2629         ticks_to_processtime (start_ticks, creation_processtime);
 2630         ticks_to_processtime (kernel_ticks, kernel_processtime);
 2631         ticks_to_processtime (user_ticks, user_processtime);
 2632 
 2633         mono_w32handle_unref (handle_data);
 2634         return TRUE;
 2635     }
 2636 
 2637     ticks_to_processtime (process_handle->create_time, creation_processtime);
 2638 
 2639     /* A process handle is only signalled if the process has
 2640      * exited, otherwise exit_processtime isn't set */
 2641     if (mono_w32handle_issignalled (handle_data))
 2642         ticks_to_processtime (process_handle->exit_time, exit_processtime);
 2643 
 2644 #ifdef HAVE_GETRUSAGE
 2645     if (process_handle->pid == getpid ()) {
 2646         struct rusage time_data;
 2647         if (getrusage (RUSAGE_SELF, &time_data) == 0) {
 2648             ticks_to_processtime ((guint64)time_data.ru_utime.tv_sec * 10000000 + (guint64)time_data.ru_utime.tv_usec * 10, user_processtime);
 2649             ticks_to_processtime ((guint64)time_data.ru_stime.tv_sec * 10000000 + (guint64)time_data.ru_stime.tv_usec * 10, kernel_processtime);
 2650         }
 2651     }
 2652 #endif
 2653 
 2654     mono_w32handle_unref (handle_data);
 2655     return TRUE;
 2656 }
 2657 
 2658 static IMAGE_SECTION_HEADER *
 2659 get_enclosing_section_header (guint32 rva, IMAGE_NT_HEADERS32 *nt_headers)
 2660 {
 2661     IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION32 (nt_headers);
 2662     guint32 i;
 2663 
 2664     for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) {
 2665         guint32 size = GUINT32_FROM_LE (section->Misc.VirtualSize);
 2666         if (size == 0) {
 2667             size = GUINT32_FROM_LE (section->SizeOfRawData);
 2668         }
 2669 
 2670         if ((rva >= GUINT32_FROM_LE (section->VirtualAddress)) &&
 2671             (rva < (GUINT32_FROM_LE (section->VirtualAddress) + size))) {
 2672             return(section);
 2673         }
 2674     }
 2675 
 2676     return(NULL);
 2677 }
 2678 
 2679 /* This works for both 32bit and 64bit files, as the differences are
 2680  * all after the section header block
 2681  */
 2682 static gpointer
 2683 get_ptr_from_rva (guint32 rva, IMAGE_NT_HEADERS32 *ntheaders, gpointer file_map)
 2684 {
 2685     IMAGE_SECTION_HEADER *section_header;
 2686     guint32 delta;
 2687 
 2688     section_header = get_enclosing_section_header (rva, ntheaders);
 2689     if (section_header == NULL) {
 2690         return(NULL);
 2691     }
 2692 
 2693     delta = (guint32)(GUINT32_FROM_LE (section_header->VirtualAddress) -
 2694               GUINT32_FROM_LE (section_header->PointerToRawData));
 2695 
 2696     return((guint8 *)file_map + rva - delta);
 2697 }
 2698 
 2699 static gpointer
 2700 scan_resource_dir (IMAGE_RESOURCE_DIRECTORY *root, IMAGE_NT_HEADERS32 *nt_headers, gpointer file_map,
 2701     IMAGE_RESOURCE_DIRECTORY_ENTRY *entry, int level, guint32 res_id, guint32 lang_id, gsize *size)
 2702 {
 2703     IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry;
 2704     gboolean is_string, is_dir;
 2705     guint32 name_offset, dir_offset, data_offset;
 2706 
 2707     swapped_entry.Name = GUINT32_FROM_LE (entry->Name);
 2708     swapped_entry.OffsetToData = GUINT32_FROM_LE (entry->OffsetToData);
 2709 
 2710     is_string = swapped_entry.NameIsString;
 2711     is_dir = swapped_entry.DataIsDirectory;
 2712     name_offset = swapped_entry.NameOffset;
 2713     dir_offset = swapped_entry.OffsetToDirectory;
 2714     data_offset = swapped_entry.OffsetToData;
 2715 
 2716     if (level == 0) {
 2717         /* Normally holds a directory entry for each type of
 2718          * resource
 2719          */
 2720         if ((is_string == FALSE &&
 2721              name_offset != res_id) ||
 2722             (is_string == TRUE)) {
 2723             return(NULL);
 2724         }
 2725     } else if (level == 1) {
 2726         /* Normally holds a directory entry for each resource
 2727          * item
 2728          */
 2729     } else if (level == 2) {
 2730         /* Normally holds a directory entry for each language
 2731          */
 2732         if ((is_string == FALSE &&
 2733              name_offset != lang_id &&
 2734              lang_id != 0) ||
 2735             (is_string == TRUE)) {
 2736             return(NULL);
 2737         }
 2738     } else {
 2739         g_assert_not_reached ();
 2740     }
 2741 
 2742     if (is_dir == TRUE) {
 2743         IMAGE_RESOURCE_DIRECTORY *res_dir = (IMAGE_RESOURCE_DIRECTORY *)((guint8 *)root + dir_offset);
 2744         IMAGE_RESOURCE_DIRECTORY_ENTRY *sub_entries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(res_dir + 1);
 2745         guint32 entries, i;
 2746 
 2747         entries = GUINT16_FROM_LE (res_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (res_dir->NumberOfIdEntries);
 2748 
 2749         for (i = 0; i < entries; i++) {
 2750             IMAGE_RESOURCE_DIRECTORY_ENTRY *sub_entry = &sub_entries[i];
 2751             gpointer ret;
 2752 
 2753             ret = scan_resource_dir (root, nt_headers, file_map,
 2754                          sub_entry, level + 1, res_id,
 2755                          lang_id, size);
 2756             if (ret != NULL) {
 2757                 return(ret);
 2758             }
 2759         }
 2760 
 2761         return(NULL);
 2762     } else {
 2763         IMAGE_RESOURCE_DATA_ENTRY *data_entry = (IMAGE_RESOURCE_DATA_ENTRY *)((guint8 *)root + data_offset);
 2764         *size = GUINT32_FROM_LE (data_entry->Size);
 2765 
 2766         return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry->OffsetToData), nt_headers, file_map));
 2767     }
 2768 }
 2769 
 2770 static gpointer
 2771 find_pe_file_resources32 (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, gsize *size)
 2772 {
 2773     IMAGE_DOS_HEADER *dos_header;
 2774     IMAGE_NT_HEADERS32 *nt_headers;
 2775     IMAGE_RESOURCE_DIRECTORY *resource_dir;
 2776     IMAGE_RESOURCE_DIRECTORY_ENTRY *resource_dir_entry;
 2777     guint32 resource_rva, entries, i;
 2778     gpointer ret = NULL;
 2779 
 2780     dos_header = (IMAGE_DOS_HEADER *)file_map;
 2781     if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
 2782         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic);
 2783 
 2784         mono_w32error_set_last (ERROR_INVALID_DATA);
 2785         return(NULL);
 2786     }
 2787 
 2788     if (map_size < sizeof(IMAGE_NT_HEADERS32) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
 2789         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size);
 2790 
 2791         mono_w32error_set_last (ERROR_BAD_LENGTH);
 2792         return(NULL);
 2793     }
 2794 
 2795     nt_headers = (IMAGE_NT_HEADERS32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
 2796     if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
 2797         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad NT signature 0x%x", __func__, nt_headers->Signature);
 2798 
 2799         mono_w32error_set_last (ERROR_INVALID_DATA);
 2800         return(NULL);
 2801     }
 2802 
 2803     if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
 2804         /* Do 64-bit stuff */
 2805         resource_rva = GUINT32_FROM_LE (((IMAGE_NT_HEADERS64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
 2806     } else {
 2807         resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
 2808     }
 2809 
 2810     if (resource_rva == 0) {
 2811         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: No resources in file!", __func__);
 2812 
 2813         mono_w32error_set_last (ERROR_INVALID_DATA);
 2814         return(NULL);
 2815     }
 2816 
 2817     resource_dir = (IMAGE_RESOURCE_DIRECTORY *)get_ptr_from_rva (resource_rva, (IMAGE_NT_HEADERS32 *)nt_headers, file_map);
 2818     if (resource_dir == NULL) {
 2819         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find resource directory", __func__);
 2820 
 2821         mono_w32error_set_last (ERROR_INVALID_DATA);
 2822         return(NULL);
 2823     }
 2824 
 2825     entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
 2826     resource_dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resource_dir + 1);
 2827 
 2828     for (i = 0; i < entries; i++) {
 2829         IMAGE_RESOURCE_DIRECTORY_ENTRY *direntry = &resource_dir_entry[i];
 2830         ret = scan_resource_dir (resource_dir,
 2831                      (IMAGE_NT_HEADERS32 *)nt_headers,
 2832                      file_map, direntry, 0, res_id,
 2833                      lang_id, size);
 2834         if (ret != NULL) {
 2835             return(ret);
 2836         }
 2837     }
 2838 
 2839     return(NULL);
 2840 }
 2841 
 2842 static gpointer
 2843 find_pe_file_resources64 (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, gsize *size)
 2844 {
 2845     IMAGE_DOS_HEADER *dos_header;
 2846     IMAGE_NT_HEADERS64 *nt_headers;
 2847     IMAGE_RESOURCE_DIRECTORY *resource_dir;
 2848     IMAGE_RESOURCE_DIRECTORY_ENTRY *resource_dir_entry;
 2849     guint32 resource_rva, entries, i;
 2850     gpointer ret = NULL;
 2851 
 2852     dos_header = (IMAGE_DOS_HEADER *)file_map;
 2853     if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
 2854         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad dos signature 0x%x", __func__, dos_header->e_magic);
 2855 
 2856         mono_w32error_set_last (ERROR_INVALID_DATA);
 2857         return(NULL);
 2858     }
 2859 
 2860     if (map_size < sizeof(IMAGE_NT_HEADERS64) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
 2861         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: File is too small: %" G_GUINT32_FORMAT, __func__, map_size);
 2862 
 2863         mono_w32error_set_last (ERROR_BAD_LENGTH);
 2864         return(NULL);
 2865     }
 2866 
 2867     nt_headers = (IMAGE_NT_HEADERS64 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
 2868     if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
 2869         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Bad NT signature 0x%x", __func__,
 2870                nt_headers->Signature);
 2871 
 2872         mono_w32error_set_last (ERROR_INVALID_DATA);
 2873         return(NULL);
 2874     }
 2875 
 2876     if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
 2877         /* Do 64-bit stuff */
 2878         resource_rva = GUINT32_FROM_LE (((IMAGE_NT_HEADERS64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
 2879     } else {
 2880         resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
 2881     }
 2882 
 2883     if (resource_rva == 0) {
 2884         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: No resources in file!", __func__);
 2885 
 2886         mono_w32error_set_last (ERROR_INVALID_DATA);
 2887         return(NULL);
 2888     }
 2889 
 2890     resource_dir = (IMAGE_RESOURCE_DIRECTORY *)get_ptr_from_rva (resource_rva, (IMAGE_NT_HEADERS32 *)nt_headers, file_map);
 2891     if (resource_dir == NULL) {
 2892         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Can't find resource directory", __func__);
 2893 
 2894         mono_w32error_set_last (ERROR_INVALID_DATA);
 2895         return(NULL);
 2896     }
 2897 
 2898     entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
 2899     resource_dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resource_dir + 1);
 2900 
 2901     for (i = 0; i < entries; i++) {
 2902         IMAGE_RESOURCE_DIRECTORY_ENTRY *direntry = &resource_dir_entry[i];
 2903         ret = scan_resource_dir (resource_dir,
 2904                      (IMAGE_NT_HEADERS32 *)nt_headers,
 2905                      file_map, direntry, 0, res_id,
 2906                      lang_id, size);
 2907         if (ret != NULL) {
 2908             return(ret);
 2909         }
 2910     }
 2911 
 2912     return(NULL);
 2913 }
 2914 
 2915 static gpointer
 2916 find_pe_file_resources (gpointer file_map, guint32 map_size, guint32 res_id, guint32 lang_id, gsize *size)
 2917 {
 2918     /* Figure this out when we support 64bit PE files */
 2919     if (1) {
 2920         return find_pe_file_resources32 (file_map, map_size, res_id,
 2921                          lang_id, size);
 2922     } else {
 2923         return find_pe_file_resources64 (file_map, map_size, res_id,
 2924                          lang_id, size);
 2925     }
 2926 }
 2927 
 2928 static guint32
 2929 unicode_chars (const gunichar2 *str)
 2930 {
 2931     guint32 len = 0;
 2932 
 2933     do {
 2934         if (str[len] == '\0') {
 2935             return(len);
 2936         }
 2937         len++;
 2938     } while(1);
 2939 }
 2940 
 2941 static gboolean
 2942 unicode_compare (const gunichar2 *str1, const gunichar2 *str2)
 2943 {
 2944     while (*str1 && *str2) {
 2945         if (*str1 != *str2) {
 2946             return(FALSE);
 2947         }
 2948         ++str1;
 2949         ++str2;
 2950     }
 2951 
 2952     return(*str1 == *str2);
 2953 }
 2954 
 2955 /* compare a little-endian null-terminated utf16 string and a normal string.
 2956  * Can be used only for ascii or latin1 chars.
 2957  */
 2958 static gboolean
 2959 unicode_string_equals (const gunichar2 *str1, const gchar *str2)
 2960 {
 2961     while (*str1 && *str2) {
 2962         if (GUINT16_TO_LE (*str1) != *str2) {
 2963             return(FALSE);
 2964         }
 2965         ++str1;
 2966         ++str2;
 2967     }
 2968 
 2969     return(*str1 == *str2);
 2970 }
 2971 
 2972 typedef struct {
 2973     guint16 data_len;
 2974     guint16 value_len;
 2975     guint16 type;
 2976     gunichar2 *key;
 2977 } version_data;
 2978 
 2979 /* Returns a pointer to the value data, because there's no way to know
 2980  * how big that data is (value_len is set to zero for most blocks :-( )
 2981  */
 2982 static gconstpointer
 2983 get_versioninfo_block (gconstpointer data, version_data *block)
 2984 {
 2985     block->data_len = GUINT16_FROM_LE (*((guint16 *)data));
 2986     data = (char *)data + sizeof(guint16);
 2987     block->value_len = GUINT16_FROM_LE (*((guint16 *)data));
 2988     data = (char *)data + sizeof(guint16);
 2989 
 2990     /* No idea what the type is supposed to indicate */
 2991     block->type = GUINT16_FROM_LE (*((guint16 *)data));
 2992     data = (char *)data + sizeof(guint16);
 2993     block->key = ((gunichar2 *)data);
 2994 
 2995     /* Skip over the key (including the terminator) */
 2996     data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1);
 2997 
 2998     /* align on a 32-bit boundary */
 2999     ALIGN32 (data);
 3000 
 3001     return(data);
 3002 }
 3003 
 3004 static gconstpointer
 3005 get_fixedfileinfo_block (gconstpointer data, version_data *block)
 3006 {
 3007     gconstpointer data_ptr;
 3008     VS_FIXEDFILEINFO *ffi;
 3009 
 3010     data_ptr = get_versioninfo_block (data, block);
 3011 
 3012     if (block->value_len != sizeof(VS_FIXEDFILEINFO)) {
 3013         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: FIXEDFILEINFO size mismatch", __func__);
 3014         return(NULL);
 3015     }
 3016 
 3017     if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) {
 3018         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: VS_VERSION_INFO mismatch", __func__);
 3019 
 3020         return(NULL);
 3021     }
 3022 
 3023     ffi = ((VS_FIXEDFILEINFO *)data_ptr);
 3024     if ((ffi->dwSignature != VS_FFI_SIGNATURE) ||
 3025         (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) {
 3026         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: FIXEDFILEINFO bad signature", __func__);
 3027 
 3028         return(NULL);
 3029     }
 3030 
 3031     return(data_ptr);
 3032 }
 3033 
 3034 static gconstpointer
 3035 get_varfileinfo_block (gconstpointer data_ptr, version_data *block)
 3036 {
 3037     /* data is pointing at a Var block
 3038      */
 3039     data_ptr = get_versioninfo_block (data_ptr, block);
 3040 
 3041     return(data_ptr);
 3042 }
 3043 
 3044 static gconstpointer
 3045 get_string_block (gconstpointer data_ptr, const gunichar2 *string_key, gpointer *string_value,
 3046     guint32 *string_value_len, version_data *block)
 3047 {
 3048     guint16 data_len = block->data_len;
 3049     guint16 string_len = 28; /* Length of the StringTable block */
 3050     char *orig_data_ptr = (char *)data_ptr - 28;
 3051 
 3052     /* data_ptr is pointing at an array of one or more String blocks
 3053      * with total length (not including alignment padding) of
 3054      * data_len
 3055      */
 3056     while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
 3057         /* align on a 32-bit boundary */
 3058         ALIGN32 (data_ptr);
 3059 
 3060         data_ptr = get_versioninfo_block (data_ptr, block);
 3061         if (block->data_len == 0) {
 3062             /* We must have hit padding, so give up
 3063              * processing now
 3064              */
 3065             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
 3066 
 3067             return(NULL);
 3068         }
 3069 
 3070         string_len = string_len + block->data_len;
 3071 
 3072         if (string_key != NULL &&
 3073             string_value != NULL &&
 3074             string_value_len != NULL &&
 3075             unicode_compare (string_key, block->key) == TRUE) {
 3076             *string_value = (gpointer)data_ptr;
 3077             *string_value_len = block->value_len;
 3078         }
 3079 
 3080         /* Skip over the value */
 3081         data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
 3082     }
 3083 
 3084     return(data_ptr);
 3085 }
 3086 
 3087 /* Returns a pointer to the byte following the Stringtable block, or
 3088  * NULL if the data read hits padding.  We can't recover from this
 3089  * because the data length does not include padding bytes, so it's not
 3090  * possible to just return the start position + length
 3091  *
 3092  * If lang == NULL it means we're just stepping through this block
 3093  */
 3094 static gconstpointer
 3095 get_stringtable_block (gconstpointer data_ptr, gchar *lang, const gunichar2 *string_key, gpointer *string_value,
 3096     guint32 *string_value_len, version_data *block)
 3097 {
 3098     guint16 data_len = block->data_len;
 3099     guint16 string_len = 36; /* length of the StringFileInfo block */
 3100     gchar *found_lang;
 3101     gchar *lowercase_lang;
 3102 
 3103     /* data_ptr is pointing at an array of StringTable blocks,
 3104      * with total length (not including alignment padding) of
 3105      * data_len
 3106      */
 3107 
 3108     while(string_len < data_len) {
 3109         /* align on a 32-bit boundary */
 3110         ALIGN32 (data_ptr);
 3111 
 3112         data_ptr = get_versioninfo_block (data_ptr, block);
 3113         if (block->data_len == 0) {
 3114             /* We must have hit padding, so give up
 3115              * processing now
 3116              */
 3117             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
 3118             return(NULL);
 3119         }
 3120 
 3121         string_len = string_len + block->data_len;
 3122 
 3123         found_lang = g_utf16_to_utf8 (block->key, 8, NULL, NULL, NULL);
 3124         if (found_lang == NULL) {
 3125             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid language key, giving up", __func__);
 3126             return(NULL);
 3127         }
 3128 
 3129         lowercase_lang = g_utf8_strdown (found_lang, -1);
 3130         g_free (found_lang);
 3131         found_lang = lowercase_lang;
 3132         lowercase_lang = NULL;
 3133 
 3134         if (lang != NULL && !strcmp (found_lang, lang)) {
 3135             /* Got the one we're interested in */
 3136             data_ptr = get_string_block (data_ptr, string_key,
 3137                              string_value,
 3138                              string_value_len, block);
 3139         } else {
 3140             data_ptr = get_string_block (data_ptr, NULL, NULL,
 3141                              NULL, block);
 3142         }
 3143 
 3144         g_free (found_lang);
 3145 
 3146         if (data_ptr == NULL) {
 3147             /* Child block hit padding */
 3148             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__);
 3149             return(NULL);
 3150         }
 3151     }
 3152 
 3153     return(data_ptr);
 3154 }
 3155 
 3156 #if G_BYTE_ORDER == G_BIG_ENDIAN
 3157 static gconstpointer
 3158 big_up_string_block (gconstpointer data_ptr, version_data *block)
 3159 {
 3160     guint16 data_len = block->data_len;
 3161     guint16 string_len = 28; /* Length of the StringTable block */
 3162     gchar *big_value;
 3163     char *orig_data_ptr = (char *)data_ptr - 28;
 3164 
 3165     /* data_ptr is pointing at an array of one or more String
 3166      * blocks with total length (not including alignment padding)
 3167      * of data_len
 3168      */
 3169     while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
 3170         /* align on a 32-bit boundary */
 3171         ALIGN32 (data_ptr);
 3172 
 3173         data_ptr = get_versioninfo_block (data_ptr, block);
 3174         if (block->data_len == 0) {
 3175             /* We must have hit padding, so give up
 3176              * processing now
 3177              */
 3178             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
 3179             return(NULL);
 3180         }
 3181 
 3182         string_len = string_len + block->data_len;
 3183 
 3184         big_value = g_convert ((gchar *)block->key,
 3185                        unicode_chars (block->key) * 2,
 3186                        "UTF-16BE", "UTF-16LE", NULL, NULL,
 3187                        NULL);
 3188         if (big_value == NULL) {
 3189             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid string, giving up", __func__);
 3190             return(NULL);
 3191         }
 3192 
 3193         /* The swapped string should be exactly the same
 3194          * length as the original little-endian one, but only
 3195          * copy the number of original chars just to be on the
 3196          * safe side
 3197          */
 3198         memcpy (block->key, big_value, unicode_chars (block->key) * 2);
 3199         g_free (big_value);
 3200 
 3201         big_value = g_convert ((gchar *)data_ptr,
 3202                        unicode_chars ((const gunichar2*)data_ptr) * 2,
 3203                        "UTF-16BE", "UTF-16LE", NULL, NULL,
 3204                        NULL);
 3205         if (big_value == NULL) {
 3206             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid data string, giving up", __func__);
 3207             return(NULL);
 3208         }
 3209         memcpy ((gpointer)data_ptr, big_value,
 3210             unicode_chars ((const gunichar2*)data_ptr) * sizeof(gunichar2));
 3211         g_free (big_value);
 3212 
 3213         data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
 3214     }
 3215 
 3216     return(data_ptr);
 3217 }
 3218 
 3219 /* Returns a pointer to the byte following the Stringtable block, or
 3220  * NULL if the data read hits padding.  We can't recover from this
 3221  * because the data length does not include padding bytes, so it's not
 3222  * possible to just return the start position + length
 3223  */
 3224 static gconstpointer
 3225 big_up_stringtable_block (gconstpointer data_ptr, version_data *block)
 3226 {
 3227     guint16 data_len = block->data_len;
 3228     guint16 string_len = 36; /* length of the StringFileInfo block */
 3229     gchar *big_value;
 3230 
 3231     /* data_ptr is pointing at an array of StringTable blocks,
 3232      * with total length (not including alignment padding) of
 3233      * data_len
 3234      */
 3235 
 3236     while(string_len < data_len) {
 3237         /* align on a 32-bit boundary */
 3238         ALIGN32 (data_ptr);
 3239 
 3240         data_ptr = get_versioninfo_block (data_ptr, block);
 3241         if (block->data_len == 0) {
 3242             /* We must have hit padding, so give up
 3243              * processing now
 3244              */
 3245             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
 3246             return(NULL);
 3247         }
 3248 
 3249         string_len = string_len + block->data_len;
 3250 
 3251         big_value = g_convert ((gchar *)block->key, 16, "UTF-16BE",
 3252                        "UTF-16LE", NULL, NULL, NULL);
 3253         if (big_value == NULL) {
 3254             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Didn't find a valid string, giving up", __func__);
 3255             return(NULL);
 3256         }
 3257 
 3258         memcpy (block->key, big_value, 16);
 3259         g_free (big_value);
 3260 
 3261         data_ptr = big_up_string_block (data_ptr, block);
 3262 
 3263         if (data_ptr == NULL) {
 3264             /* Child block hit padding */
 3265             mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__);
 3266             return(NULL);
 3267         }
 3268     }
 3269 
 3270     return(data_ptr);
 3271 }
 3272 
 3273 /* Follows the data structures and turns all UTF-16 strings from the
 3274  * LE found in the resource section into UTF-16BE
 3275  */
 3276 static void
 3277 big_up (gconstpointer datablock, guint32 size)
 3278 {
 3279     gconstpointer data_ptr;
 3280     gint32 data_len; /* signed to guard against underflow */
 3281     version_data block;
 3282 
 3283     data_ptr = get_fixedfileinfo_block (datablock, &block);
 3284     if (data_ptr != NULL) {
 3285         VS_FIXEDFILEINFO *ffi = (VS_FIXEDFILEINFO *)data_ptr;
 3286 
 3287         /* Byteswap all the fields */
 3288         ffi->dwFileVersionMS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionMS);
 3289         ffi->dwFileVersionLS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionLS);
 3290         ffi->dwProductVersionMS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionMS);
 3291         ffi->dwProductVersionLS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionLS);
 3292         ffi->dwFileFlagsMask = GUINT32_SWAP_LE_BE (ffi->dwFileFlagsMask);
 3293         ffi->dwFileFlags = GUINT32_SWAP_LE_BE (ffi->dwFileFlags);
 3294         ffi->dwFileOS = GUINT32_SWAP_LE_BE (ffi->dwFileOS);
 3295         ffi->dwFileType = GUINT32_SWAP_LE_BE (ffi->dwFileType);
 3296         ffi->dwFileSubtype = GUINT32_SWAP_LE_BE (ffi->dwFileSubtype);
 3297         ffi->dwFileDateMS = GUINT32_SWAP_LE_BE (ffi->dwFileDateMS);
 3298         ffi->dwFileDateLS = GUINT32_SWAP_LE_BE (ffi->dwFileDateLS);
 3299 
 3300         /* The FFI and header occupies the first 92 bytes
 3301          */
 3302         data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
 3303         data_len = block.data_len - 92;
 3304 
 3305         /* There now follow zero or one StringFileInfo blocks
 3306          * and zero or one VarFileInfo blocks
 3307          */
 3308         while (data_len > 0) {
 3309             /* align on a 32-bit boundary */
 3310             ALIGN32 (data_ptr);
 3311 
 3312             data_ptr = get_versioninfo_block (data_ptr, &block);
 3313             if (block.data_len == 0) {
 3314                 /* We must have hit padding, so give
 3315                  * up processing now
 3316                  */
 3317                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
 3318                 return;
 3319             }
 3320 
 3321             data_len = data_len - block.data_len;
 3322 
 3323             if (unicode_string_equals (block.key, "VarFileInfo")) {
 3324                 data_ptr = get_varfileinfo_block (data_ptr,
 3325                                   &block);
 3326                 data_ptr = ((guchar *)data_ptr) + block.value_len;
 3327             } else if (unicode_string_equals (block.key,
 3328                               "StringFileInfo")) {
 3329                 data_ptr = big_up_stringtable_block (data_ptr,
 3330                                      &block);
 3331             } else {
 3332                 /* Bogus data */
 3333                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Not a valid VERSIONINFO child block", __func__);
 3334                 return;
 3335             }
 3336 
 3337             if (data_ptr == NULL) {
 3338                 /* Child block hit padding */
 3339                 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__);
 3340                 return;
 3341             }
 3342         }
 3343     }
 3344 }
 3345 #endif
 3346 
 3347 gboolean
 3348 mono_w32process_get_fileversion_info (const gunichar2 *filename, gpointer *data)
 3349 {
 3350     gpointer file_map;
 3351     gpointer versioninfo;
 3352     void *map_handle;
 3353     gint32 map_size;
 3354     gsize datasize;
 3355 
 3356     g_assert (data);
 3357     *data = NULL;
 3358 
 3359     file_map = mono_pe_file_map (filename, &map_size, &map_handle);
 3360     if (!file_map)
 3361         return FALSE;
 3362 
 3363     versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION, 0, &datasize);
 3364     if (!versioninfo) {
 3365         mono_pe_file_unmap (file_map, map_handle);
 3366         return FALSE;
 3367     }
 3368 
 3369     *data = g_malloc0 (datasize);
 3370 
 3371     /* This could probably process the data so that mono_w32process_ver_query_value() doesn't have to follow the
 3372      * data blocks every time. But hey, these functions aren't likely to appear in many profiles. */
 3373     memcpy (*data, versioninfo, datasize);
 3374 
 3375 #if G_BYTE_ORDER == G_BIG_ENDIAN
 3376     big_up (*data, datasize);
 3377 #endif
 3378 
 3379     mono_pe_file_unmap (file_map, map_handle);
 3380 
 3381     return TRUE;
 3382 }
 3383 
 3384 gboolean
 3385 mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len)
 3386 {
 3387     gchar *subblock_utf8, *lang_utf8 = NULL;
 3388     gboolean ret = FALSE;
 3389     version_data block;
 3390     gconstpointer data_ptr;
 3391     gint32 data_len; /* signed to guard against underflow */
 3392     gboolean want_var = FALSE;
 3393     gboolean want_string = FALSE;
 3394     gunichar2 lang[8];
 3395     const gunichar2 *string_key = NULL;
 3396     gpointer string_value = NULL;
 3397     guint32 string_value_len = 0;
 3398     gchar *lowercase_lang;
 3399 
 3400     subblock_utf8 = g_utf16_to_utf8 (subblock, -1, NULL, NULL, NULL);
 3401     if (subblock_utf8 == NULL) {
 3402         return(FALSE);
 3403     }
 3404 
 3405     if (!strcmp (subblock_utf8, "\\VarFileInfo\\Translation")) {
 3406         want_var = TRUE;
 3407     } else if (!strncmp (subblock_utf8, "\\StringFileInfo\\", 16)) {
 3408         want_string = TRUE;
 3409         memcpy (lang, subblock + 16, 8 * sizeof(gunichar2));
 3410         lang_utf8 = g_utf16_to_utf8 (lang, 8, NULL, NULL, NULL);
 3411         lowercase_lang = g_utf8_strdown (lang_utf8, -1);
 3412         g_free (lang_utf8);
 3413         lang_utf8 = lowercase_lang;
 3414         lowercase_lang = NULL;
 3415         string_key = subblock + 25;
 3416     }
 3417 
 3418     if (!strcmp (subblock_utf8, "\\")) {
 3419         data_ptr = get_fixedfileinfo_block (datablock, &block);
 3420         if (data_ptr != NULL) {
 3421             *buffer = (gpointer)data_ptr;
 3422             *len = block.value_len;
 3423 
 3424             ret = TRUE;
 3425         }
 3426     } else if (want_var || want_string) {
 3427         data_ptr = get_fixedfileinfo_block (datablock, &block);
 3428         if (data_ptr != NULL) {
 3429             /* The FFI and header occupies the first 92
 3430              * bytes
 3431              */
 3432             data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
 3433             data_len = block.data_len - 92;
 3434 
 3435             /* There now follow zero or one StringFileInfo
 3436              * blocks and zero or one VarFileInfo blocks
 3437              */
 3438             while (data_len > 0) {
 3439                 /* align on a 32-bit boundary */
 3440                 ALIGN32 (data_ptr);
 3441 
 3442                 data_ptr = get_versioninfo_block (data_ptr,
 3443                                   &block);
 3444                 if (block.data_len == 0) {
 3445                     /* We must have hit padding,
 3446                      * so give up processing now
 3447                      */
 3448                     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Hit 0-length block, giving up", __func__);
 3449                     goto done;
 3450                 }
 3451 
 3452                 data_len = data_len - block.data_len;
 3453 
 3454                 if (unicode_string_equals (block.key, "VarFileInfo")) {
 3455                     data_ptr = get_varfileinfo_block (data_ptr, &block);
 3456                     if (want_var) {
 3457                         *buffer = (gpointer)data_ptr;
 3458                         *len = block.value_len;
 3459                         ret = TRUE;
 3460                         goto done;
 3461                     } else {
 3462                         /* Skip over the Var block */
 3463                         data_ptr = ((guchar *)data_ptr) + block.value_len;
 3464                     }
 3465                 } else if (unicode_string_equals (block.key, "StringFileInfo")) {
 3466                     data_ptr = get_stringtable_block (data_ptr, lang_utf8, string_key, &string_value, &string_value_len, &block);
 3467                     if (want_string &&
 3468                         string_value != NULL &&
 3469                         string_value_len != 0) {
 3470                         *buffer = string_value;
 3471                         *len = unicode_chars ((const gunichar2 *)string_value) + 1; /* Include trailing null */
 3472                         ret = TRUE;
 3473                         goto done;
 3474                     }
 3475                 } else {
 3476                     /* Bogus data */
 3477                     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Not a valid VERSIONINFO child block", __func__);
 3478                     goto done;
 3479                 }
 3480 
 3481                 if (data_ptr == NULL) {
 3482                     /* Child block hit padding */
 3483                     mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_PROCESS, "%s: Child block hit 0-length block, giving up", __func__);
 3484                     goto done;
 3485                 }
 3486             }
 3487         }
 3488     }
 3489 
 3490   done:
 3491     if (lang_utf8) {
 3492         g_free (lang_utf8);
 3493     }
 3494 
 3495     g_free (subblock_utf8);
 3496     return(ret);
 3497 }
 3498 
 3499 static guint32
 3500 copy_lang (gunichar2 *lang_out, guint32 lang_len, const gchar *text)
 3501 {
 3502     gunichar2 *unitext;
 3503     int chars = strlen (text);
 3504     int ret;
 3505 
 3506     unitext = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL);
 3507     g_assert (unitext != NULL);
 3508 
 3509     if (chars < (lang_len - 1)) {
 3510         memcpy (lang_out, (gpointer)unitext, chars * 2);
 3511         lang_out[chars] = '\0';
 3512         ret = chars;
 3513     } else {
 3514         memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2);
 3515         lang_out[lang_len] = '\0';
 3516         ret = lang_len;
 3517     }
 3518 
 3519     g_free (unitext);
 3520 
 3521     return(ret);
 3522 }
 3523 
 3524 guint32
 3525 mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
 3526 {
 3527     int primary, secondary;
 3528     const char *name = NULL;
 3529 
 3530     primary = lang & 0x3FF;
 3531     secondary = (lang >> 10) & 0x3F;
 3532 
 3533     switch(primary) {
 3534     case 0x00:
 3535         switch (secondary) {
 3536         case 0x01: name = "Process Default Language"; break;
 3537         }
 3538         break;
 3539     case 0x01:
 3540         switch (secondary) {
 3541         case 0x00:
 3542         case 0x01: name = "Arabic (Saudi Arabia)"; break;
 3543         case 0x02: name = "Arabic (Iraq)"; break;
 3544         case 0x03: name = "Arabic (Egypt)"; break;
 3545         case 0x04: name = "Arabic (Libya)"; break;
 3546         case 0x05: name = "Arabic (Algeria)"; break;
 3547         case 0x06: name = "Arabic (Morocco)"; break;
 3548         case 0x07: name = "Arabic (Tunisia)"; break;
 3549         case 0x08: name = "Arabic (Oman)"; break;
 3550         case 0x09: name = "Arabic (Yemen)"; break;
 3551         case 0x0a: name = "Arabic (Syria)"; break;
 3552         case 0x0b: name = "Arabic (Jordan)"; break;
 3553         case 0x0c: name = "Arabic (Lebanon)"; break;
 3554         case 0x0d: name = "Arabic (Kuwait)"; break;
 3555         case 0x0e: name = "Arabic (U.A.E.)"; break;
 3556         case 0x0f: name = "Arabic (Bahrain)"; break;
 3557         case 0x10: name = "Arabic (Qatar)"; break;
 3558         }
 3559         break;
 3560     case 0x02:
 3561         switch (secondary) {
 3562         case 0x00: name = "Bulgarian (Bulgaria)"; break;
 3563         case 0x01: name = "Bulgarian"; break;
 3564         }
 3565         break;
 3566     case 0x03:
 3567         switch (secondary) {
 3568         case 0x00: name = "Catalan (Spain)"; break;
 3569         case 0x01: name = "Catalan"; break;
 3570         }
 3571         break;
 3572     case 0x04:
 3573         switch (secondary) {
 3574         case 0x00:
 3575         case 0x01: name = "Chinese (Taiwan)"; break;
 3576         case 0x02: name = "Chinese (PRC)"; break;
 3577         case 0x03: name = "Chinese (Hong Kong S.A.R.)"; break;
 3578         case 0x04: name = "Chinese (Singapore)"; break;
 3579         case 0x05: name = "Chinese (Macau S.A.R.)"; break;
 3580         }
 3581         break;
 3582     case 0x05:
 3583         switch (secondary) {
 3584         case 0x00: name = "Czech (Czech Republic)"; break;
 3585         case 0x01: name = "Czech"; break;
 3586         }
 3587         break;
 3588     case 0x06:
 3589         switch (secondary) {
 3590         case 0x00: name = "Danish (Denmark)"; break;
 3591         case 0x01: name = "Danish"; break;
 3592         }
 3593         break;
 3594     case 0x07:
 3595         switch (secondary) {
 3596         case 0x00:
 3597         case 0x01: name = "German (Germany)"; break;
 3598         case 0x02: name = "German (Switzerland)"; break;
 3599         case 0x03: name = "German (Austria)"; break;
 3600         case 0x04: name = "German (Luxembourg)"; break;
 3601         case 0x05: name = "German (Liechtenstein)"; break;
 3602         }
 3603         break;
 3604     case 0x08:
 3605         switch (secondary) {
 3606         case 0x00: name = "Greek (Greece)"; break;
 3607         case 0x01: name = "Greek"; break;
 3608         }
 3609         break;
 3610     case 0x09:
 3611         switch (secondary) {
 3612         case 0x00:
 3613         case 0x01: name = "English (United States)"; break;
 3614         case 0x02: name = "English (United Kingdom)"; break;
 3615         case 0x03: name = "English (Australia)"; break;
 3616         case 0x04: name = "English (Canada)"; break;
 3617         case 0x05: name = "English (New Zealand)"; break;
 3618         case 0x06: name = "English (Ireland)"; break;
 3619         case 0x07: name = "English (South Africa)"; break;
 3620         case 0x08: name = "English (Jamaica)"; break;
 3621         case 0x09: name = "English (Caribbean)"; break;
 3622         case 0x0a: name = "English (Belize)"; break;
 3623         case 0x0b: name = "English (Trinidad and Tobago)"; break;
 3624         case 0x0c: name = "English (Zimbabwe)"; break;
 3625         case 0x0d: name = "English (Philippines)"; break;
 3626         case 0x10: name = "English (India)"; break;
 3627         case 0x11: name = "English (Malaysia)"; break;
 3628         case 0x12: name = "English (Singapore)"; break;
 3629         }
 3630         break;
 3631     case 0x0a:
 3632         switch (secondary) {
 3633         case 0x00: name = "Spanish (Spain)"; break;
 3634         case 0x01: name = "Spanish (Traditional Sort)"; break;
 3635         case 0x02: name = "Spanish (Mexico)"; break;
 3636         case 0x03: name = "Spanish (International Sort)"; break;
 3637         case 0x04: name = "Spanish (Guatemala)"; break;
 3638         case 0x05: name = "Spanish (Costa Rica)"; break;
 3639         case 0x06: name = "Spanish (Panama)"; break;
 3640         case 0x07: name = "Spanish (Dominican Republic)"; break;
 3641         case 0x08: name = "Spanish (Venezuela)"; break;
 3642         case 0x09: name = "Spanish (Colombia)"; break;
 3643         case 0x0a: name = "Spanish (Peru)"; break;
 3644         case 0x0b: name = "Spanish (Argentina)"; break;
 3645         case 0x0c: name = "Spanish (Ecuador)"; break;
 3646         case 0x0d: name = "Spanish (Chile)"; break;
 3647         case 0x0e: name = "Spanish (Uruguay)"; break;
 3648         case 0x0f: name = "Spanish (Paraguay)"; break;
 3649         case 0x10: name = "Spanish (Bolivia)"; break;
 3650         case 0x11: name = "Spanish (El Salvador)"; break;
 3651         case 0x12: name = "Spanish (Honduras)"; break;
 3652         case 0x13: name = "Spanish (Nicaragua)"; break;
 3653         case 0x14: name = "Spanish (Puerto Rico)"; break;
 3654         case 0x15: name = "Spanish (United States)"; break;
 3655         }
 3656         break;
 3657     case 0x0b:
 3658         switch (secondary) {
 3659         case 0x00: name = "Finnish (Finland)"; break;
 3660         case 0x01: name = "Finnish"; break;
 3661         }
 3662         break;
 3663     case 0x0c:
 3664         switch (secondary) {
 3665         case 0x00:
 3666         case 0x01: name = "French (France)"; break;
 3667         case 0x02: name = "French (Belgium)"; break;
 3668         case 0x03: name = "French (Canada)"; break;
 3669         case 0x04: name = "French (Switzerland)"; break;
 3670         case 0x05: name = "French (Luxembourg)"; break;
 3671         case 0x06: name = "French (Monaco)"; break;
 3672         }
 3673         break;
 3674     case 0x0d:
 3675         switch (secondary) {
 3676         case 0x00: name = "Hebrew (Israel)"; break;
 3677         case 0x01: name = "Hebrew"; break;
 3678         }
 3679         break;
 3680     case 0x0e:
 3681         switch (secondary) {
 3682         case 0x00: name = "Hungarian (Hungary)"; break;
 3683         case 0x01: name = "Hungarian"; break;
 3684         }
 3685         break;
 3686     case 0x0f:
 3687         switch (secondary) {
 3688         case 0x00: name = "Icelandic (Iceland)"; break;
 3689         case 0x01: name = "Icelandic"; break;
 3690         }
 3691         break;
 3692     case 0x10:
 3693         switch (secondary) {
 3694         case 0x00:
 3695         case 0x01: name = "Italian (Italy)"; break;
 3696         case 0x02: name = "Italian (Switzerland)"; break;
 3697         }
 3698         break;
 3699     case 0x11:
 3700         switch (secondary) {
 3701         case 0x00: name = "Japanese (Japan)"; break;
 3702         case 0x01: name = "Japanese"; break;
 3703         }
 3704         break;
 3705     case 0x12:
 3706         switch (secondary) {
 3707         case 0x00: name = "Korean (Korea)"; break;
 3708         case 0x01: name = "Korean"; break;
 3709         }
 3710         break;
 3711     case 0x13:
 3712         switch (secondary) {
 3713         case 0x00:
 3714         case 0x01: name = "Dutch (Netherlands)"; break;
 3715         case 0x02: name = "Dutch (Belgium)"; break;
 3716         }
 3717         break;
 3718     case 0x14:
 3719         switch (secondary) {
 3720         case 0x00:
 3721         case 0x01: name = "Norwegian (Bokmal)"; break;
 3722         case 0x02: name = "Norwegian (Nynorsk)"; break;
 3723         }
 3724         break;
 3725     case 0x15:
 3726         switch (secondary) {
 3727         case 0x00: name = "Polish (Poland)"; break;
 3728         case 0x01: name = "Polish"; break;
 3729         }
 3730         break;
 3731     case 0x16:
 3732         switch (secondary) {
 3733         case 0x00:
 3734         case 0x01: name = "Portuguese (Brazil)"; break;
 3735         case 0x02: name = "Portuguese (Portugal)"; break;
 3736         }
 3737         break;
 3738     case 0x17:
 3739         switch (secondary) {
 3740         case 0x01: name = "Romansh (Switzerland)"; break;
 3741         }
 3742         break;
 3743     case 0x18:
 3744         switch (secondary) {
 3745         case 0x00: name = "Romanian (Romania)"; break;
 3746         case 0x01: name = "Romanian"; break;
 3747         }
 3748         break;
 3749     case 0x19:
 3750         switch (secondary) {
 3751         case 0x00: name = "Russian (Russia)"; break;
 3752         case 0x01: name = "Russian"; break;
 3753         }
 3754         break;
 3755     case 0x1a:
 3756         switch (secondary) {
 3757         case 0x00: name = "Croatian (Croatia)"; break;
 3758         case 0x01: name = "Croatian"; break;
 3759         case 0x02: name = "Serbian (Latin)"; break;
 3760         case 0x03: name = "Serbian (Cyrillic)"; break;
 3761         case 0x04: name = "Croatian (Bosnia and Herzegovina)"; break;
 3762         case 0x05: name = "Bosnian (Latin, Bosnia and Herzegovina)"; break;
 3763         case 0x06: name = "Serbian (Latin, Bosnia and Herzegovina)"; break;
 3764         case 0x07: name = "Serbian (Cyrillic, Bosnia and Herzegovina)"; break;
 3765         case 0x08: name = "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break;
 3766         }
 3767         break;
 3768     case 0x1b:
 3769         switch (secondary) {
 3770         case 0x00: name = "Slovak (Slovakia)"; break;
 3771         case 0x01: name = "Slovak"; break;
 3772         }
 3773         break;
 3774     case 0x1c:
 3775         switch (secondary) {
 3776         case 0x00: name = "Albanian (Albania)"; break;
 3777         case 0x01: name = "Albanian"; break;
 3778         }
 3779         break;
 3780     case 0x1d:
 3781         switch (secondary) {
 3782         case 0x00: name = "Swedish (Sweden)"; break;
 3783         case 0x01: name = "Swedish"; break;
 3784         case 0x02: name = "Swedish (Finland)"; break;
 3785         }
 3786         break;
 3787     case 0x1e:
 3788         switch (secondary) {
 3789         case 0x00: name = "Thai (Thailand)"; break;
 3790         case 0x01: name = "Thai"; break;
 3791         }
 3792         break;
 3793     case 0x1f:
 3794         switch (secondary) {
 3795         case 0x00: name = "Turkish (Turkey)"; break;
 3796         case 0x01: name = "Turkish"; break;
 3797         }
 3798         break;
 3799     case 0x20:
 3800         switch (secondary) {
 3801         case 0x00: name = "Urdu (Islamic Republic of Pakistan)"; break;
 3802         case 0x01: name = "Urdu"; break;
 3803         }
 3804         break;
 3805     case 0x21:
 3806         switch (secondary) {
 3807         case 0x00: name = "Indonesian (Indonesia)"; break;
 3808         case 0x01: name = "Indonesian"; break;
 3809         }
 3810         break;
 3811     case 0x22:
 3812         switch (secondary) {
 3813         case 0x00: name = "Ukrainian (Ukraine)"; break;
 3814         case 0x01: name = "Ukrainian"; break;
 3815         }
 3816         break;
 3817     case 0x23:
 3818         switch (secondary) {
 3819         case 0x00: name = "Belarusian (Belarus)"; break;
 3820         case 0x01: name = "Belarusian"; break;
 3821         }
 3822         break;
 3823     case 0x24:
 3824         switch (secondary) {
 3825         case 0x00: name = "Slovenian (Slovenia)"; break;
 3826         case 0x01: name = "Slovenian"; break;
 3827         }
 3828         break;
 3829     case 0x25:
 3830         switch (secondary) {
 3831         case 0x00: name = "Estonian (Estonia)"; break;
 3832         case 0x01: name = "Estonian"; break;
 3833         }
 3834         break;
 3835     case 0x26:
 3836         switch (secondary) {
 3837         case 0x00: name = "Latvian (Latvia)"; break;
 3838         case 0x01: name = "Latvian"; break;
 3839         }
 3840         break;
 3841     case 0x27:
 3842         switch (secondary) {
 3843         case 0x00: name = "Lithuanian (Lithuania)"; break;
 3844         case 0x01: name = "Lithuanian"; break;
 3845         }
 3846         break;
 3847     case 0x28:
 3848         switch (secondary) {
 3849         case 0x01: name = "Tajik (Tajikistan)"; break;
 3850         }
 3851         break;
 3852     case 0x29:
 3853         switch (secondary) {
 3854         case 0x00: name = "Farsi (Iran)"; break;
 3855         case 0x01: name = "Farsi"; break;
 3856         }
 3857         break;
 3858     case 0x2a:
 3859         switch (secondary) {
 3860         case 0x00: name = "Vietnamese (Viet Nam)"; break;
 3861         case 0x01: name = "Vietnamese"; break;
 3862         }
 3863         break;
 3864     case 0x2b:
 3865         switch (secondary) {
 3866         case 0x00: name = "Armenian (Armenia)"; break;
 3867         case 0x01: name = "Armenian"; break;
 3868         }
 3869         break;
 3870     case 0x2c:
 3871         switch (secondary) {
 3872         case 0x00: name = "Azeri (Latin) (Azerbaijan)"; break;
 3873         case 0x01: name = "Azeri (Latin)"; break;
 3874         case 0x02: name = "Azeri (Cyrillic)"; break;
 3875         }
 3876         break;
 3877     case 0x2d:
 3878         switch (secondary) {
 3879         case 0x00: name = "Basque (Spain)"; break;
 3880         case 0x01: name = "Basque"; break;
 3881         }
 3882         break;
 3883     case 0x2e:
 3884         switch (secondary) {
 3885         case 0x01: name = "Upper Sorbian (Germany)"; break;
 3886         case 0x02: name = "Lower Sorbian (Germany)"; break;
 3887         }
 3888         break;
 3889     case 0x2f:
 3890         switch (secondary) {
 3891         case 0x00: name = "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
 3892         case 0x01: name = "FYRO Macedonian"; break;
 3893         }
 3894         break;
 3895     case 0x32:
 3896         switch (secondary) {
 3897         case 0x00: name = "Tswana (South Africa)"; break;
 3898         case 0x01: name = "Tswana"; break;
 3899         }
 3900         break;
 3901     case 0x34:
 3902         switch (secondary) {
 3903         case 0x00: name = "Xhosa (South Africa)"; break;
 3904         case 0x01: name = "Xhosa"; break;
 3905         }
 3906         break;
 3907     case 0x35:
 3908         switch (secondary) {
 3909         case 0x00: name = "Zulu (South Africa)"; break;
 3910         case 0x01: name = "Zulu"; break;
 3911         }
 3912         break;
 3913     case 0x36:
 3914         switch (secondary) {
 3915         case 0x00: name = "Afrikaans (South Africa)"; break;
 3916         case 0x01: name = "Afrikaans"; break;
 3917         }
 3918         break;
 3919     case 0x37:
 3920         switch (secondary) {
 3921         case 0x00: name = "Georgian (Georgia)"; break;
 3922         case 0x01: name = "Georgian"; break;
 3923         }
 3924         break;
 3925     case 0x38:
 3926         switch (secondary) {
 3927         case 0x00: name = "Faroese (Faroe Islands)"; break;
 3928         case 0x01: name = "Faroese"; break;
 3929         }
 3930         break;
 3931     case 0x39:
 3932         switch (secondary) {
 3933         case 0x00: name = "Hindi (India)"; break;
 3934         case 0x01: name = "Hindi"; break;
 3935         }
 3936         break;
 3937     case 0x3a:
 3938         switch (secondary) {
 3939         case 0x00: name = "Maltese (Malta)"; break;
 3940         case 0x01: name = "Maltese"; break;
 3941         }
 3942         break;
 3943     case 0x3b:
 3944         switch (secondary) {
 3945         case 0x00: name = "Sami (Northern) (Norway)"; break;
 3946         case 0x01: name = "Sami, Northern (Norway)"; break;
 3947         case 0x02: name = "Sami, Northern (Sweden)"; break;
 3948         case 0x03: name = "Sami, Northern (Finland)"; break;
 3949         case 0x04: name = "Sami, Lule (Norway)"; break;
 3950         case 0x05: name = "Sami, Lule (Sweden)"; break;
 3951         case 0x06: name = "Sami, Southern (Norway)"; break;
 3952         case 0x07: name = "Sami, Southern (Sweden)"; break;
 3953         case 0x08: name = "Sami, Skolt (Finland)"; break;
 3954         case 0x09: name = "Sami, Inari (Finland)"; break;
 3955         }
 3956         break;
 3957     case 0x3c:
 3958         switch (secondary) {
 3959         case 0x02: name = "Irish (Ireland)"; break;
 3960         }
 3961         break;
 3962     case 0x3e:
 3963         switch (secondary) {
 3964         case 0x00:
 3965         case 0x01: name = "Malay (Malaysia)"; break;
 3966         case 0x02: name = "Malay (Brunei Darussalam)"; break;
 3967         }
 3968         break;
 3969     case 0x3f:
 3970         switch (secondary) {
 3971         case 0x00: name = "Kazakh (Kazakhstan)"; break;
 3972         case 0x01: name = "Kazakh"; break;
 3973         }
 3974         break;
 3975     case 0x40:
 3976         switch (secondary) {
 3977         case 0x00: name = "Kyrgyz (Kyrgyzstan)"; break;
 3978         case 0x01: name = "Kyrgyz (Cyrillic)"; break;
 3979         }
 3980         break;
 3981     case 0x41:
 3982         switch (secondary) {
 3983         case 0x00: name = "Swahili (Kenya)"; break;
 3984         case 0x01: name = "Swahili"; break;
 3985         }
 3986         break;
 3987     case 0x42:
 3988         switch (secondary) {
 3989         case 0x01: name = "Turkmen (Turkmenistan)"; break;
 3990         }
 3991         break;
 3992     case 0x43:
 3993         switch (secondary) {
 3994         case 0x00: name = "Uzbek (Latin) (Uzbekistan)"; break;
 3995         case 0x01: name = "Uzbek (Latin)"; break;
 3996         case 0x02: name = "Uzbek (Cyrillic)"; break;
 3997         }
 3998         break;
 3999     case 0x44:
 4000         switch (secondary) {
 4001         case 0x00: name = "Tatar (Russia)"; break;
 4002         case 0x01: name = "Tatar"; break;
 4003         }
 4004         break;
 4005     case 0x45:
 4006         switch (secondary) {
 4007         case 0x00:
 4008         case 0x01: name = "Bengali (India)"; break;
 4009         }
 4010         break;
 4011     case 0x46:
 4012         switch (secondary) {
 4013         case 0x00: name = "Punjabi (India)"; break;
 4014         case 0x01: name = "Punjabi"; break;
 4015         }
 4016         break;
 4017     case 0x47:
 4018         switch (secondary) {
 4019         case 0x00: name = "Gujarati (India)"; break;
 4020         case 0x01: name = "Gujarati"; break;
 4021         }
 4022         break;
 4023     case 0x49:
 4024         switch (secondary) {
 4025         case 0x00: name = "Tamil (India)"; break;
 4026         case 0x01: name = "Tamil"; break;
 4027         }
 4028         break;
 4029     case 0x4a:
 4030         switch (secondary) {
 4031         case 0x00: name = "Telugu (India)"; break;
 4032         case 0x01: name = "Telugu"; break;
 4033         }
 4034         break;
 4035     case 0x4b:
 4036         switch (secondary) {
 4037         case 0x00: name = "Kannada (India)"; break;
 4038         case 0x01: name = "Kannada"; break;
 4039         }
 4040         break;
 4041     case 0x4c:
 4042         switch (secondary) {
 4043         case 0x00:
 4044         case 0x01: name = "Malayalam (India)"; break;
 4045         }
 4046         break;
 4047     case 0x4d:
 4048         switch (secondary) {
 4049         case 0x01: name = "Assamese (India)"; break;
 4050         }
 4051         break;
 4052     case 0x4e:
 4053         switch (secondary) {
 4054         case 0x00: name = "Marathi (India)"; break;
 4055         case 0x01: name = "Marathi"; break;
 4056         }
 4057         break;
 4058     case 0x4f:
 4059         switch (secondary) {
 4060         case 0x00: name = "Sanskrit (India)"; break;
 4061         case 0x01: name = "Sanskrit"; break;
 4062         }
 4063         break;
 4064     case 0x50:
 4065         switch (secondary) {
 4066         case 0x00: name = "Mongolian (Mongolia)"; break;
 4067         case 0x01: name = "Mongolian (Cyrillic)"; break;
 4068         case 0x02: name = "Mongolian (PRC)"; break;
 4069         }
 4070         break;
 4071     case 0x51:
 4072         switch (secondary) {
 4073         case 0x01: name = "Tibetan (PRC)"; break;
 4074         case 0x02: name = "Tibetan (Bhutan)"; break;
 4075         }
 4076         break;
 4077     case 0x52:
 4078         switch (secondary) {
 4079         case 0x00: name = "Welsh (United Kingdom)"; break;
 4080         case 0x01: name = "Welsh"; break;
 4081         }
 4082         break;
 4083     case 0x53:
 4084         switch (secondary) {
 4085         case 0x01: name = "Khmer (Cambodia)"; break;
 4086         }
 4087         break;
 4088     case 0x54:
 4089         switch (secondary) {
 4090         case 0x01: name = "Lao (Lao PDR)"; break;
 4091         }
 4092         break;
 4093     case 0x56:
 4094         switch (secondary) {
 4095         case 0x00: name = "Galician (Spain)"; break;
 4096         case 0x01: name = "Galician"; break;
 4097         }
 4098         break;
 4099     case 0x57:
 4100         switch (secondary) {
 4101         case 0x00: name = "Konkani (India)"; break;
 4102         case 0x01: name = "Konkani"; break;
 4103         }
 4104         break;
 4105     case 0x5a:
 4106         switch (secondary) {
 4107         case 0x00: name = "Syriac (Syria)"; break;
 4108         case 0x01: name = "Syriac"; break;
 4109         }
 4110         break;
 4111     case 0x5b:
 4112         switch (secondary) {
 4113         case 0x01: name = "Sinhala (Sri Lanka)"; break;
 4114         }
 4115         break;
 4116     case 0x5d:
 4117         switch (secondary) {
 4118         case 0x01: name = "Inuktitut (Syllabics, Canada)"; break;
 4119         case 0x02: name = "Inuktitut (Latin, Canada)"; break;
 4120         }
 4121         break;
 4122     case 0x5e:
 4123         switch (secondary) {
 4124         case 0x01: name = "Amharic (Ethiopia)"; break;
 4125         }
 4126         break;
 4127     case 0x5f:
 4128         switch (secondary) {
 4129         case 0x02: name = "Tamazight (Algeria, Latin)"; break;
 4130         }
 4131         break;
 4132     case 0x61:
 4133         switch (secondary) {
 4134         case 0x01: name = "Nepali (Nepal)"; break;
 4135         }
 4136         break;
 4137     case 0x62:
 4138         switch (secondary) {
 4139         case 0x01: name = "Frisian (Netherlands)"; break;
 4140         }
 4141         break;
 4142     case 0x63:
 4143         switch (secondary) {
 4144         case 0x01: name = "Pashto (Afghanistan)"; break;
 4145         }
 4146         break;
 4147     case 0x64:
 4148         switch (secondary) {
 4149         case 0x01: name = "Filipino (Philippines)"; break;
 4150         }
 4151         break;
 4152     case 0x65:
 4153         switch (secondary) {
 4154         case 0x00: name = "Divehi (Maldives)"; break;
 4155         case 0x01: name = "Divehi"; break;
 4156         }
 4157         break;
 4158     case 0x68:
 4159         switch (secondary) {
 4160         case 0x01: name = "Hausa (Nigeria, Latin)"; break;
 4161         }
 4162         break;
 4163     case 0x6a:
 4164         switch (secondary) {
 4165         case 0x01: name = "Yoruba (Nigeria)"; break;
 4166         }
 4167         break;
 4168     case 0x6b:
 4169         switch (secondary) {
 4170         case 0x00:
 4171         case 0x01: name = "Quechua (Bolivia)"; break;
 4172         case 0x02: name = "Quechua (Ecuador)"; break;
 4173         case 0x03: name = "Quechua (Peru)"; break;
 4174         }
 4175         break;
 4176     case 0x6c:
 4177         switch (secondary) {
 4178         case 0x00: name = "Northern Sotho (South Africa)"; break;
 4179         case 0x01: name = "Northern Sotho"; break;
 4180         }
 4181         break;
 4182     case 0x6d:
 4183         switch (secondary) {
 4184         case 0x01: name = "Bashkir (Russia)"; break;
 4185         }
 4186         break;
 4187     case 0x6e:
 4188         switch (secondary) {
 4189         case 0x01: name = "Luxembourgish (Luxembourg)"; break;
 4190         }
 4191         break;
 4192     case 0x6f:
 4193         switch (secondary) {
 4194         case 0x01: name = "Greenlandic (Greenland)"; break;
 4195         }
 4196         break;
 4197     case 0x78:
 4198         switch (secondary) {
 4199         case 0x01: name = "Yi (PRC)"; break;
 4200         }
 4201         break;
 4202     case 0x7a:
 4203         switch (secondary) {
 4204         case 0x01: name = "Mapudungun (Chile)"; break;
 4205         }
 4206         break;
 4207     case 0x7c:
 4208         switch (secondary) {
 4209         case 0x01: name = "Mohawk (Mohawk)"; break;
 4210         }
 4211         break;
 4212     case 0x7e:
 4213         switch (secondary) {
 4214         case 0x01: name = "Breton (France)"; break;
 4215         }
 4216         break;
 4217     case 0x7f:
 4218         switch (secondary) {
 4219         case 0x00: name = "Invariant Language (Invariant Country)"; break;
 4220         }
 4221         break;
 4222     case 0x80:
 4223         switch (secondary) {
 4224         case 0x01: name = "Uighur (PRC)"; break;
 4225         }
 4226         break;
 4227     case 0x81:
 4228         switch (secondary) {
 4229         case 0x00: name = "Maori (New Zealand)"; break;
 4230         case 0x01: name = "Maori"; break;
 4231         }
 4232         break;
 4233     case 0x83:
 4234         switch (secondary) {
 4235         case 0x01: name = "Corsican (France)"; break;
 4236         }
 4237         break;
 4238     case 0x84:
 4239         switch (secondary) {
 4240         case 0x01: name = "Alsatian (France)"; break;
 4241         }
 4242         break;
 4243     case 0x85:
 4244         switch (secondary) {
 4245         case 0x01: name = "Yakut (Russia)"; break;
 4246         }
 4247         break;
 4248     case 0x86:
 4249         switch (secondary) {
 4250         case 0x01: name = "K'iche (Guatemala)"; break;
 4251         }
 4252         break;
 4253     case 0x87:
 4254         switch (secondary) {
 4255         case 0x01: name = "Kinyarwanda (Rwanda)"; break;
 4256         }
 4257         break;
 4258     case 0x88:
 4259         switch (secondary) {
 4260         case 0x01: name = "Wolof (Senegal)"; break;
 4261         }
 4262         break;
 4263     case 0x8c:
 4264         switch (secondary) {
 4265         case 0x01: name = "Dari (Afghanistan)"; break;
 4266         }
 4267         break;
 4268 
 4269     default:
 4270         name = "Language Neutral";
 4271 
 4272     }
 4273 
 4274     if (!name)
 4275         name = "Language Neutral";
 4276 
 4277     return copy_lang (lang_out, lang_len, name);
 4278 }
 4279 
 4280 #else /* ENABLE_NETCORE */
 4281 
 4282 void
 4283 mono_w32process_init (void)
 4284 {
 4285 }
 4286 
 4287 void
 4288 mono_w32process_cleanup (void)
 4289 {
 4290 }
 4291 
 4292 void
 4293 mono_w32process_set_cli_launcher (gchar *path)
 4294 {
 4295 }
 4296 
 4297 void
 4298 mono_w32process_signal_finished (void)
 4299 {
 4300 }
 4301 
 4302 guint32
 4303 mono_w32process_ver_language_name (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
 4304 {
 4305     return 0;
 4306 }
 4307 
 4308 gboolean
 4309 mono_w32process_get_fileversion_info (const gunichar2 *filename, gpointer *data)
 4310 {
 4311     return FALSE;
 4312 }
 4313 
 4314 gboolean
 4315 mono_w32process_module_get_information (gpointer handle, gpointer module, MODULEINFO *modinfo, guint32 size)
 4316 {
 4317     return FALSE;
 4318 }
 4319 
 4320 gboolean
 4321 mono_w32process_ver_query_value (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len)
 4322 {
 4323     return FALSE;
 4324 }
 4325 
 4326 #endif /* ENABLE_NETCORE */