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