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