3 * System.Diagnostics.Process support
6 * Dick Porter (dick@ximian.com)
8 * Copyright 2002 Ximian, Inc.
9 * Copyright 2002-2006 Novell, Inc.
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
22 #include <sys/types.h>
30 #ifdef HAVE_SYS_PARAM_H
31 #include <sys/param.h>
35 #ifdef HAVE_SYS_WAIT_H
38 #ifdef HAVE_SYS_RESOURCE_H
39 #include <sys/resource.h>
42 #ifdef HAVE_SYS_MKDEV_H
43 #include <sys/mkdev.h>
50 #include <mono/metadata/w32process.h>
51 #include <mono/metadata/w32process-internals.h>
52 #include <mono/metadata/w32process-unix-internals.h>
53 #include <mono/metadata/w32error.h>
54 #include <mono/metadata/class.h>
55 #include <mono/metadata/class-internals.h>
56 #include <mono/metadata/object.h>
57 #include <mono/metadata/object-internals.h>
58 #include <mono/metadata/metadata.h>
59 #include <mono/metadata/metadata-internals.h>
60 #include <mono/metadata/exception.h>
61 #include <mono/metadata/w32handle.h>
62 #include <mono/metadata/w32file.h>
63 #include <mono/utils/mono-membar.h>
64 #include <mono/utils/mono-logger-internals.h>
65 #include <mono/utils/strenc.h>
66 #include <mono/utils/mono-proclib.h>
67 #include <mono/utils/mono-path.h>
68 #include <mono/utils/mono-lazy-init.h>
69 #include <mono/utils/mono-signal-handler.h>
70 #include <mono/utils/mono-time.h>
71 #include <mono/utils/mono-mmap.h>
72 #include <mono/utils/strenc.h>
73 #include <mono/utils/mono-io-portability.h>
74 #include <mono/utils/w32api.h>
77 #define MAXPATHLEN 242
80 #define STILL_ACTIVE ((int) 0x00000103)
83 /* define LOGDEBUG(...) g_message(__VA_ARGS__) */
85 /* The process' environment strings */
86 #if defined(__APPLE__)
87 #if defined (TARGET_OSX)
88 /* Apple defines this in crt_externs.h but doesn't provide that header for
89 * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
90 * in fact exist on all implementations (so far)
92 gchar
***_NSGetEnviron(void);
93 #define environ (*_NSGetEnviron())
95 static char *mono_environ
[1] = { NULL
};
96 #define environ mono_environ
97 #endif /* defined (TARGET_OSX) */
99 extern char **environ
;
103 STARTF_USESHOWWINDOW
=0x001,
104 STARTF_USESIZE
=0x002,
105 STARTF_USEPOSITION
=0x004,
106 STARTF_USECOUNTCHARS
=0x008,
107 STARTF_USEFILLATTRIBUTE
=0x010,
108 STARTF_RUNFULLSCREEN
=0x020,
109 STARTF_FORCEONFEEDBACK
=0x040,
110 STARTF_FORCEOFFFEEDBACK
=0x080,
111 STARTF_USESTDHANDLES
=0x100
121 #if G_BYTE_ORDER == G_BIG_ENDIAN
122 guint32 highDateTime
;
126 guint32 highDateTime
;
131 * Process describes processes we create.
132 * It contains a semaphore that can be waited on in order to wait
133 * for process termination.
135 typedef struct _Process
{
136 pid_t pid
; /* the pid of the process. This value is only valid until the process has exited. */
137 MonoSemType exit_sem
; /* this semaphore will be released when the process exits */
138 int status
; /* the exit status */
139 gint32 handle_count
; /* the number of handles to this process instance */
140 /* we keep a ref to the creating _WapiHandle_process handle until
141 * the process has exited, so that the information there isn't lost.
145 struct _Process
*next
;
148 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
153 gpointer main_thread
;
157 size_t min_working_set
;
158 size_t max_working_set
;
161 } MonoW32HandleProcess
;
166 * 2 bytes: Length in bytes (this block, and all child blocks. does _not_ include alignment padding between blocks)
167 * 2 bytes: Length in bytes of VS_FIXEDFILEINFO struct
168 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
169 * Variable length unicode string (null terminated): Key (currently "VS_VERSION_INFO")
170 * Variable length padding to align VS_FIXEDFILEINFO on a 32-bit boundary
171 * VS_FIXEDFILEINFO struct
172 * Variable length padding to align Child struct on a 32-bit boundary
173 * Child struct (zero or one StringFileInfo structs, zero or one VarFileInfo structs)
179 * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
180 * 2 bytes: Value length (always zero)
181 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
182 * Variable length unicode string: Key (currently "StringFileInfo")
183 * Variable length padding to align Child struct on a 32-bit boundary
184 * 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.)
190 * 2 bytes: Length in bytes (includes this block as well as all Child blocks, but excludes any padding between String blocks)
191 * 2 bytes: Value length (always zero)
192 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
193 * 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.
194 * Variable length padding to align Child struct on a 32-bit boundary
195 * Child structs (an array of one or more String structs (each aligned on a 32-bit boundary)
201 * 2 bytes: Length in bytes (of this block)
202 * 2 bytes: Value length (the length in words of the Value member)
203 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
204 * Variable length unicode string: Key. arbitrary string, identifies data.
205 * Variable length padding to align Value on a 32-bit boundary
206 * Value: Variable length unicode string, holding data.
212 * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
213 * 2 bytes: Value length (always zero)
214 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
215 * Variable length unicode string: Key (currently "VarFileInfo")
216 * Variable length padding to align Child struct on a 32-bit boundary
217 * Child structs (a Var struct)
223 * 2 bytes: Length in bytes of this block
224 * 2 bytes: Value length in bytes of the Value
225 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
226 * Variable length unicode string: Key ("Translation")
227 * Variable length padding to align Value on a 32-bit boundary
228 * Value: an array of one or more 4 byte values that are language and code page identifier pairs, low-order word containing a language identifier, and the high-order word containing a code page number. Either word can be zero, indicating that the file is language or code page independent.
231 #if G_BYTE_ORDER == G_BIG_ENDIAN
232 #define VS_FFI_SIGNATURE 0xbd04effe
233 #define VS_FFI_STRUCVERSION 0x00000100
235 #define VS_FFI_SIGNATURE 0xfeef04bd
236 #define VS_FFI_STRUCVERSION 0x00010000
239 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
241 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0
242 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1
243 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
245 #define IMAGE_SIZEOF_SHORT_NAME 8
247 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
248 #define IMAGE_DOS_SIGNATURE 0x4d5a
249 #define IMAGE_NT_SIGNATURE 0x50450000
250 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0xb10
251 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0xb20
253 #define IMAGE_DOS_SIGNATURE 0x5a4d
254 #define IMAGE_NT_SIGNATURE 0x00004550
255 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
256 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
283 guint16 NumberOfSections
;
284 guint32 TimeDateStamp
;
285 guint32 PointerToSymbolTable
;
286 guint32 NumberOfSymbols
;
287 guint16 SizeOfOptionalHeader
;
288 guint16 Characteristics
;
292 guint32 VirtualAddress
;
294 } IMAGE_DATA_DIRECTORY
;
298 guint8 MajorLinkerVersion
;
299 guint8 MinorLinkerVersion
;
301 guint32 SizeOfInitializedData
;
302 guint32 SizeOfUninitializedData
;
303 guint32 AddressOfEntryPoint
;
307 guint32 SectionAlignment
;
308 guint32 FileAlignment
;
309 guint16 MajorOperatingSystemVersion
;
310 guint16 MinorOperatingSystemVersion
;
311 guint16 MajorImageVersion
;
312 guint16 MinorImageVersion
;
313 guint16 MajorSubsystemVersion
;
314 guint16 MinorSubsystemVersion
;
315 guint32 Win32VersionValue
;
317 guint32 SizeOfHeaders
;
320 guint16 DllCharacteristics
;
321 guint32 SizeOfStackReserve
;
322 guint32 SizeOfStackCommit
;
323 guint32 SizeOfHeapReserve
;
324 guint32 SizeOfHeapCommit
;
326 guint32 NumberOfRvaAndSizes
;
327 IMAGE_DATA_DIRECTORY DataDirectory
[IMAGE_NUMBEROF_DIRECTORY_ENTRIES
];
328 } IMAGE_OPTIONAL_HEADER32
;
332 guint8 MajorLinkerVersion
;
333 guint8 MinorLinkerVersion
;
335 guint32 SizeOfInitializedData
;
336 guint32 SizeOfUninitializedData
;
337 guint32 AddressOfEntryPoint
;
340 guint32 SectionAlignment
;
341 guint32 FileAlignment
;
342 guint16 MajorOperatingSystemVersion
;
343 guint16 MinorOperatingSystemVersion
;
344 guint16 MajorImageVersion
;
345 guint16 MinorImageVersion
;
346 guint16 MajorSubsystemVersion
;
347 guint16 MinorSubsystemVersion
;
348 guint32 Win32VersionValue
;
350 guint32 SizeOfHeaders
;
353 guint16 DllCharacteristics
;
354 guint64 SizeOfStackReserve
;
355 guint64 SizeOfStackCommit
;
356 guint64 SizeOfHeapReserve
;
357 guint64 SizeOfHeapCommit
;
359 guint32 NumberOfRvaAndSizes
;
360 IMAGE_DATA_DIRECTORY DataDirectory
[IMAGE_NUMBEROF_DIRECTORY_ENTRIES
];
361 } IMAGE_OPTIONAL_HEADER64
;
363 #if SIZEOF_VOID_P == 8
364 typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER
;
366 typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER
;
371 IMAGE_FILE_HEADER FileHeader
;
372 IMAGE_OPTIONAL_HEADER32 OptionalHeader
;
373 } IMAGE_NT_HEADERS32
;
377 IMAGE_FILE_HEADER FileHeader
;
378 IMAGE_OPTIONAL_HEADER64 OptionalHeader
;
379 } IMAGE_NT_HEADERS64
;
381 #if SIZEOF_VOID_P == 8
382 typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS
;
384 typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS
;
388 guint8 Name
[IMAGE_SIZEOF_SHORT_NAME
];
390 guint32 PhysicalAddress
;
393 guint32 VirtualAddress
;
394 guint32 SizeOfRawData
;
395 guint32 PointerToRawData
;
396 guint32 PointerToRelocations
;
397 guint32 PointerToLinenumbers
;
398 guint16 NumberOfRelocations
;
399 guint16 NumberOfLinenumbers
;
400 guint32 Characteristics
;
401 } IMAGE_SECTION_HEADER
;
403 #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)))
405 #define RT_CURSOR 0x01
406 #define RT_BITMAP 0x02
409 #define RT_DIALOG 0x05
410 #define RT_STRING 0x06
411 #define RT_FONTDIR 0x07
413 #define RT_ACCELERATOR 0x09
414 #define RT_RCDATA 0x0a
415 #define RT_MESSAGETABLE 0x0b
416 #define RT_GROUP_CURSOR 0x0c
417 #define RT_GROUP_ICON 0x0e
418 #define RT_VERSION 0x10
419 #define RT_DLGINCLUDE 0x11
420 #define RT_PLUGPLAY 0x13
422 #define RT_ANICURSOR 0x15
423 #define RT_ANIICON 0x16
425 #define RT_MANIFEST 0x18
428 guint32 Characteristics
;
429 guint32 TimeDateStamp
;
430 guint16 MajorVersion
;
431 guint16 MinorVersion
;
432 guint16 NumberOfNamedEntries
;
433 guint16 NumberOfIdEntries
;
434 } IMAGE_RESOURCE_DIRECTORY
;
439 #if G_BYTE_ORDER == G_BIG_ENDIAN
440 guint32 NameIsString
:1;
441 guint32 NameOffset
:31;
443 guint32 NameOffset
:31;
444 guint32 NameIsString
:1;
448 #if G_BYTE_ORDER == G_BIG_ENDIAN
450 guint16 __wapi_big_endian_padding
;
458 guint32 OffsetToData
;
460 #if G_BYTE_ORDER == G_BIG_ENDIAN
461 guint32 DataIsDirectory
:1;
462 guint32 OffsetToDirectory
:31;
464 guint32 OffsetToDirectory
:31;
465 guint32 DataIsDirectory
:1;
469 } IMAGE_RESOURCE_DIRECTORY_ENTRY
;
472 guint32 OffsetToData
;
476 } IMAGE_RESOURCE_DATA_ENTRY
;
478 #define VOS_UNKNOWN 0x00000000
479 #define VOS_DOS 0x00010000
480 #define VOS_OS216 0x00020000
481 #define VOS_OS232 0x00030000
482 #define VOS_NT 0x00040000
483 #define VOS__BASE 0x00000000
484 #define VOS__WINDOWS16 0x00000001
485 #define VOS__PM16 0x00000002
486 #define VOS__PM32 0x00000003
487 #define VOS__WINDOWS32 0x00000004
488 /* Should "embrace and extend" here with some entries for linux etc */
490 #define VOS_DOS_WINDOWS16 0x00010001
491 #define VOS_DOS_WINDOWS32 0x00010004
492 #define VOS_OS216_PM16 0x00020002
493 #define VOS_OS232_PM32 0x00030003
494 #define VOS_NT_WINDOWS32 0x00040004
496 #define VFT_UNKNOWN 0x0000
497 #define VFT_APP 0x0001
498 #define VFT_DLL 0x0002
499 #define VFT_DRV 0x0003
500 #define VFT_FONT 0x0004
501 #define VFT_VXD 0x0005
502 #define VFT_STATIC_LIB 0x0007
504 #define VFT2_UNKNOWN 0x0000
505 #define VFT2_DRV_PRINTER 0x0001
506 #define VFT2_DRV_KEYBOARD 0x0002
507 #define VFT2_DRV_LANGUAGE 0x0003
508 #define VFT2_DRV_DISPLAY 0x0004
509 #define VFT2_DRV_MOUSE 0x0005
510 #define VFT2_DRV_NETWORK 0x0006
511 #define VFT2_DRV_SYSTEM 0x0007
512 #define VFT2_DRV_INSTALLABLE 0x0008
513 #define VFT2_DRV_SOUND 0x0009
514 #define VFT2_DRV_COMM 0x000a
515 #define VFT2_DRV_INPUTMETHOD 0x000b
516 #define VFT2_FONT_RASTER 0x0001
517 #define VFT2_FONT_VECTOR 0x0002
518 #define VFT2_FONT_TRUETYPE 0x0003
520 #define MAKELANGID(primary,secondary) ((guint16)((secondary << 10) | (primary)))
522 #define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
525 static mono_lazy_init_t process_sig_chld_once
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
528 static gchar
*cli_launcher
;
530 static Process
*processes
;
531 static mono_mutex_t processes_mutex
;
533 static pid_t current_pid
;
534 static gpointer current_process
;
536 static const gunichar2 utf16_space_bytes
[2] = { 0x20, 0 };
537 static const gunichar2
*utf16_space
= utf16_space_bytes
;
538 static const gunichar2 utf16_quote_bytes
[2] = { 0x22, 0 };
539 static const gunichar2
*utf16_quote
= utf16_quote_bytes
;
541 /* Check if a pid is valid - i.e. if a process exists with this pid. */
543 process_is_alive (pid_t pid
)
545 #if defined(HOST_WATCHOS)
546 return TRUE
; // TODO: Rewrite using sysctl
547 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
550 if (kill (pid
, 0) == 0)
555 #elif defined(__HAIKU__)
557 if (get_team_info ((team_id
)pid
, &teamInfo
) == B_OK
)
561 gchar
*dir
= g_strdup_printf ("/proc/%d", pid
);
562 gboolean result
= access (dir
, F_OK
) == 0;
569 process_details (gpointer data
)
571 MonoW32HandleProcess
*process_handle
= (MonoW32HandleProcess
*) data
;
572 g_print ("pid: %d, exited: %s, exitstatus: %d",
573 process_handle
->pid
, process_handle
->exited
? "true" : "false", process_handle
->exitstatus
);
577 process_typename (void)
583 process_typesize (void)
585 return sizeof (MonoW32HandleProcess
);
588 static MonoW32HandleWaitRet
589 process_wait (gpointer handle
, guint32 timeout
, gboolean
*alerted
)
591 MonoW32HandleProcess
*process_handle
;
592 pid_t pid G_GNUC_UNUSED
, ret
;
598 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u)", __func__
, handle
, timeout
);
603 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
605 g_warning ("%s: error looking up process handle %p", __func__
, handle
);
606 return MONO_W32HANDLE_WAIT_RET_FAILED
;
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
;
615 pid
= process_handle
->pid
;
617 if (pid
== mono_process_current_pid ()) {
618 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on current process", __func__
, handle
, timeout
);
619 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
622 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): PID: %d", __func__
, handle
, timeout
, pid
);
624 if (!process_handle
->child
) {
625 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on non-child process", __func__
, handle
, timeout
);
627 if (!process_is_alive (pid
)) {
628 /* assume the process has exited */
629 process_handle
->exited
= TRUE
;
630 process_handle
->exitstatus
= -1;
631 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
633 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): non-child process is not alive anymore (2)", __func__
, handle
, timeout
);
634 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
637 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
);
638 return MONO_W32HANDLE_WAIT_RET_FAILED
;
641 /* We don't need to lock processes here, the entry
642 * has a handle_count > 0 which means it will not be freed. */
643 process
= process_handle
->process
;
646 start
= mono_msec_ticks ();
650 if (timeout
!= MONO_INFINITE_WAIT
) {
651 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on semaphore for %li ms...",
652 __func__
, handle
, timeout
, (long)(timeout
- (now
- start
)));
653 ret
= mono_os_sem_timedwait (&process
->exit_sem
, (timeout
- (now
- start
)), alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
655 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on semaphore forever...",
656 __func__
, handle
, timeout
);
657 ret
= mono_os_sem_wait (&process
->exit_sem
, alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
660 if (ret
== MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
661 /* Success, process has exited */
662 mono_os_sem_post (&process
->exit_sem
);
666 if (ret
== MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
) {
667 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait timeout (timeout = 0)", __func__
, handle
, timeout
);
668 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
671 now
= mono_msec_ticks ();
672 if (now
- start
>= timeout
) {
673 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait timeout", __func__
, handle
, timeout
);
674 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
677 if (alerted
&& ret
== MONO_SEM_TIMEDWAIT_RET_ALERTED
) {
678 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait alerted", __func__
, handle
, timeout
);
680 return MONO_W32HANDLE_WAIT_RET_ALERTED
;
684 /* Process must have exited */
685 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Waited successfully", __func__
, handle
, timeout
);
687 status
= process
->status
;
688 if (WIFSIGNALED (status
))
689 process_handle
->exitstatus
= 128 + WTERMSIG (status
);
691 process_handle
->exitstatus
= WEXITSTATUS (status
);
693 process_handle
->exit_time
= mono_100ns_datetime ();
695 process_handle
->exited
= TRUE
;
697 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Setting pid %d signalled, exit status %d",
698 __func__
, handle
, timeout
, process_handle
->pid
, process_handle
->exitstatus
);
700 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
702 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
706 processes_cleanup (void)
708 static gint32 cleaning_up
;
710 Process
*prev
= NULL
;
712 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s", __func__
);
714 /* Ensure we're not in here in multiple threads at once, nor recursive. */
715 if (InterlockedCompareExchange (&cleaning_up
, 1, 0) != 0)
719 * This needs to be done outside the lock but atomically, hence the CAS above.
721 for (process
= processes
; process
; process
= process
->next
) {
722 if (process
->signalled
&& process
->handle
) {
723 /* This process has exited and we need to remove the artifical ref
725 mono_w32handle_unref (process
->handle
);
726 process
->handle
= NULL
;
730 mono_os_mutex_lock (&processes_mutex
);
732 for (process
= processes
; process
;) {
733 Process
*next
= process
->next
;
734 if (process
->handle_count
== 0 && process
->signalled
) {
738 if (process
== processes
)
739 processes
= process
->next
;
741 prev
->next
= process
->next
;
743 mono_os_sem_destroy (&process
->exit_sem
);
751 mono_os_mutex_unlock (&processes_mutex
);
753 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s done", __func__
);
755 InterlockedExchange (&cleaning_up
, 0);
759 process_close (gpointer handle
, gpointer data
)
761 MonoW32HandleProcess
*process_handle
;
763 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s", __func__
);
765 process_handle
= (MonoW32HandleProcess
*) data
;
766 g_free (process_handle
->pname
);
767 process_handle
->pname
= NULL
;
768 if (process_handle
->process
)
769 InterlockedDecrement (&process_handle
->process
->handle_count
);
770 processes_cleanup ();
773 static MonoW32HandleOps process_ops
= {
774 process_close
, /* close_shared */
778 process_wait
, /* special_wait */
780 process_details
, /* details */
781 process_typename
, /* typename */
782 process_typesize
, /* typesize */
786 process_set_defaults (MonoW32HandleProcess
*process_handle
)
788 /* These seem to be the defaults on w2k */
789 process_handle
->min_working_set
= 204800;
790 process_handle
->max_working_set
= 1413120;
792 process_handle
->create_time
= mono_100ns_datetime ();
796 process_set_name (MonoW32HandleProcess
*process_handle
)
798 char *progname
, *utf8_progname
, *slash
;
800 progname
= g_get_prgname ();
801 utf8_progname
= mono_utf8_from_external (progname
);
803 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: using [%s] as prog name", __func__
, progname
);
806 slash
= strrchr (utf8_progname
, '/');
808 process_handle
->pname
= g_strdup (slash
+1);
810 process_handle
->pname
= g_strdup (utf8_progname
);
811 g_free (utf8_progname
);
816 mono_w32process_init (void)
818 MonoW32HandleProcess process_handle
;
820 mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS
, &process_ops
);
822 mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS
,
823 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SPECIAL_WAIT
));
825 current_pid
= getpid ();
827 memset (&process_handle
, 0, sizeof (process_handle
));
828 process_handle
.pid
= current_pid
;
829 process_set_defaults (&process_handle
);
830 process_set_name (&process_handle
);
832 current_process
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
833 g_assert (current_process
);
835 mono_os_mutex_init (&processes_mutex
);
839 mono_w32process_cleanup (void)
841 g_free (cli_launcher
);
845 len16 (const gunichar2
*str
)
856 utf16_concat (const gunichar2
*first
, ...)
864 va_start (args
, first
);
865 total
+= len16 (first
);
866 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg(args
, gunichar2
*))
870 ret
= g_new (gunichar2
, total
+ 1);
876 for (s
= first
; *s
!= 0; s
++)
878 va_start (args
, first
);
879 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg (args
, gunichar2
*)){
880 for (p
= s
; *p
!= 0; p
++)
889 mono_w32process_get_pid (gpointer handle
)
891 MonoW32HandleProcess
*process_handle
;
894 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
896 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
900 return process_handle
->pid
;
906 } GetProcessForeachData
;
909 get_process_foreach_callback (gpointer handle
, gpointer handle_specific
, gpointer user_data
)
911 GetProcessForeachData
*foreach_data
;
912 MonoW32HandleProcess
*process_handle
;
915 foreach_data
= (GetProcessForeachData
*) user_data
;
917 if (mono_w32handle_get_type (handle
) != MONO_W32HANDLE_PROCESS
)
920 process_handle
= (MonoW32HandleProcess
*) handle_specific
;
922 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: looking at process %d", __func__
, process_handle
->pid
);
924 pid
= process_handle
->pid
;
928 /* It's possible to have more than one process handle with the
929 * same pid, but only the one running process can be
931 if (foreach_data
->pid
!= pid
)
933 if (mono_w32handle_issignalled (handle
))
936 mono_w32handle_ref (handle
);
937 foreach_data
->handle
= handle
;
942 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid
)
944 GetProcessForeachData foreach_data
;
947 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: looking for process %d", __func__
, pid
);
949 memset (&foreach_data
, 0, sizeof (foreach_data
));
950 foreach_data
.pid
= pid
;
951 mono_w32handle_foreach (get_process_foreach_callback
, &foreach_data
);
952 handle
= foreach_data
.handle
;
954 /* get_process_foreach_callback already added a ref */
958 if (process_is_alive (pid
)) {
959 /* non-child process */
960 MonoW32HandleProcess process_handle
;
962 memset (&process_handle
, 0, sizeof (process_handle
));
963 process_handle
.pid
= pid
;
964 process_handle
.pname
= mono_w32process_get_name (pid
);
966 handle
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
967 if (handle
== INVALID_HANDLE_VALUE
) {
968 g_warning ("%s: error creating process handle", __func__
);
970 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
977 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find pid %d", __func__
, pid
);
979 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
984 match_procname_to_modulename (char *procname
, char *modulename
)
986 char* lastsep
= NULL
;
987 char* lastsep2
= NULL
;
990 gboolean result
= FALSE
;
992 if (procname
== NULL
|| modulename
== NULL
)
995 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: procname=\"%s\", modulename=\"%s\"", __func__
, procname
, modulename
);
996 pname
= mono_path_resolve_symlinks (procname
);
997 mname
= mono_path_resolve_symlinks (modulename
);
999 if (!strcmp (pname
, mname
))
1003 lastsep
= strrchr (mname
, '/');
1005 if (!strcmp (lastsep
+1, pname
))
1008 lastsep2
= strrchr (pname
, '/');
1011 if (!strcmp (lastsep
+1, lastsep2
+1))
1014 if (!strcmp (mname
, lastsep2
+1))
1024 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: result is %d", __func__
, result
);
1029 mono_w32process_try_get_modules (gpointer process
, gpointer
*modules
, guint32 size
, guint32
*needed
)
1031 MonoW32HandleProcess
*process_handle
;
1032 GSList
*mods
= NULL
, *mods_iter
;
1033 MonoW32ProcessModule
*module
;
1034 guint32 count
, avail
= size
/ sizeof(gpointer
);
1040 /* Store modules in an array of pointers (main module as
1041 * modules[0]), using the load address for each module as a
1042 * token. (Use 'NULL' as an alternative for the main module
1043 * so that the simple implementation can just return one item
1044 * for now.) Get the info from /proc/<pid>/maps on linux,
1045 * /proc/<pid>/map on FreeBSD, other systems will have to
1046 * implement /dev/kmem reading or whatever other horrid
1047 * technique is needed.
1049 if (size
< sizeof(gpointer
))
1052 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
1054 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
1058 pid
= process_handle
->pid
;
1059 pname
= g_strdup (process_handle
->pname
);
1063 *needed
= sizeof(gpointer
);
1067 mods
= mono_w32process_get_modules (pid
);
1070 *needed
= sizeof(gpointer
);
1078 * Use the NULL shortcut, as the first line in
1079 * /proc/<pid>/maps isn't the executable, and we need
1080 * that first in the returned list. Check the module name
1081 * to see if it ends with the proc name and substitute
1082 * the first entry with it. FIXME if this turns out to
1087 for (i
= 0; mods_iter
; i
++) {
1088 if (i
< avail
- 1) {
1089 module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1090 if (modules
[0] != NULL
)
1091 modules
[i
] = module
->address_start
;
1092 else if (match_procname_to_modulename (pname
, module
->filename
))
1093 modules
[0] = module
->address_start
;
1095 modules
[i
+ 1] = module
->address_start
;
1097 mono_w32process_module_free ((MonoW32ProcessModule
*)mods_iter
->data
);
1098 mods_iter
= g_slist_next (mods_iter
);
1102 /* count + 1 to leave slot 0 for the main module */
1103 *needed
= sizeof(gpointer
) * (count
+ 1);
1105 g_slist_free (mods
);
1112 mono_w32process_module_get_filename (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
1117 gunichar2
*proc_path
;
1119 size
*= sizeof (gunichar2
); /* adjust for unicode characters */
1121 if (basename
== NULL
|| size
== 0)
1124 pid
= mono_w32process_get_pid (process
);
1126 path
= mono_w32process_get_path (pid
);
1130 proc_path
= mono_unicode_from_external (path
, &bytes
);
1133 if (proc_path
== NULL
)
1138 /* Add the terminator */
1142 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d smaller than needed (%zd); truncating", __func__
, size
, bytes
);
1143 memcpy (basename
, proc_path
, size
);
1145 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d larger than needed (%zd)", __func__
, size
, bytes
);
1146 memcpy (basename
, proc_path
, bytes
);
1155 mono_w32process_module_get_name (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
1157 MonoW32HandleProcess
*process_handle
;
1159 gunichar2
*procname
;
1160 char *procname_ext
= NULL
;
1163 GSList
*mods
= NULL
, *mods_iter
;
1164 MonoW32ProcessModule
*found_module
;
1168 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Getting module base name, process handle %p module %p basename %p size %d",
1169 __func__
, process
, module
, basename
, size
);
1171 size
= size
* sizeof (gunichar2
); /* adjust for unicode characters */
1173 if (basename
== NULL
|| size
== 0)
1176 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
1178 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
1182 pid
= process_handle
->pid
;
1183 pname
= g_strdup (process_handle
->pname
);
1185 mods
= mono_w32process_get_modules (pid
);
1186 if (!mods
&& module
!= NULL
) {
1187 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't get modules %p", __func__
, process
);
1192 /* If module != NULL compare the address.
1193 * If module == NULL we are looking for the main module.
1194 * The best we can do for now check it the module name end with the process name.
1196 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1197 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1198 if (procname_ext
== NULL
&&
1199 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1200 (module
!= NULL
&& found_module
->address_start
== module
))) {
1201 procname_ext
= g_path_get_basename (found_module
->filename
);
1204 mono_w32process_module_free (found_module
);
1207 if (procname_ext
== NULL
) {
1208 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find procname_ext from procmods %p", __func__
, process
);
1209 /* If it's *still* null, we might have hit the
1210 * case where reading /proc/$pid/maps gives an
1211 * empty file for this user.
1213 procname_ext
= mono_w32process_get_name (pid
);
1215 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find procname_ext from proc_get_name %p pid %d", __func__
, process
, pid
);
1218 g_slist_free (mods
);
1222 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Process name is [%s]", __func__
,
1225 procname
= mono_unicode_from_external (procname_ext
, &bytes
);
1226 if (procname
== NULL
) {
1227 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't get procname %p", __func__
, process
);
1229 g_free (procname_ext
);
1235 /* Add the terminator */
1239 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d smaller than needed (%zd); truncating", __func__
, size
, bytes
);
1241 memcpy (basename
, procname
, size
);
1243 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d larger than needed (%zd)",
1244 __func__
, size
, bytes
);
1246 memcpy (basename
, procname
, bytes
);
1250 g_free (procname_ext
);
1255 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find procname_ext %p", __func__
, process
);
1260 mono_w32process_module_get_information (gpointer process
, gpointer module
, MODULEINFO
*modinfo
, guint32 size
)
1262 MonoW32HandleProcess
*process_handle
;
1264 GSList
*mods
= NULL
, *mods_iter
;
1265 MonoW32ProcessModule
*found_module
;
1266 gboolean ret
= FALSE
;
1270 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Getting module info, process handle %p module %p",
1271 __func__
, process
, module
);
1273 if (modinfo
== NULL
|| size
< sizeof (MODULEINFO
))
1276 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
1278 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
1282 pid
= process_handle
->pid
;
1283 pname
= g_strdup (process_handle
->pname
);
1285 mods
= mono_w32process_get_modules (pid
);
1291 /* If module != NULL compare the address.
1292 * If module == NULL we are looking for the main module.
1293 * The best we can do for now check it the module name end with the process name.
1295 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1296 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1298 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1299 (module
!= NULL
&& found_module
->address_start
== module
))) {
1300 modinfo
->lpBaseOfDll
= found_module
->address_start
;
1301 modinfo
->SizeOfImage
= (gsize
)(found_module
->address_end
) - (gsize
)(found_module
->address_start
);
1302 modinfo
->EntryPoint
= found_module
->address_offset
;
1306 mono_w32process_module_free (found_module
);
1309 g_slist_free (mods
);
1316 switch_dir_separators (char *path
)
1318 size_t i
, pathLength
= strlen(path
);
1320 /* Turn all the slashes round the right way, except for \' */
1321 /* There are probably other characters that need to be excluded as well. */
1322 for (i
= 0; i
< pathLength
; i
++) {
1323 if (path
[i
] == '\\' && i
< pathLength
- 1 && path
[i
+1] != '\'' )
1330 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler
, (int _dummy
, siginfo_t
*info
, void *context
))
1333 * Don't want to do any complicated processing here so just wake up the finalizer thread which will call
1334 * mono_w32process_signal_finished ().
1336 int old_errno
= errno
;
1338 mono_gc_finalize_notify ();
1344 process_add_sigchld_handler (void)
1346 struct sigaction sa
;
1348 sa
.sa_sigaction
= mono_sigchld_signal_handler
;
1349 sigemptyset (&sa
.sa_mask
);
1350 sa
.sa_flags
= SA_NOCLDSTOP
| SA_SIGINFO
| SA_RESTART
;
1351 g_assert (sigaction (SIGCHLD
, &sa
, NULL
) != -1);
1352 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "Added SIGCHLD handler");
1358 * mono_w32process_signal_finished:
1360 * Signal the exit semaphore for processes which have finished.
1363 mono_w32process_signal_finished (void)
1371 pid
= waitpid (-1, &status
, WNOHANG
);
1372 } while (pid
== -1 && errno
== EINTR
);
1377 mono_os_mutex_lock (&processes_mutex
);
1379 for (process
= processes
; process
; process
= process
->next
) {
1380 if (process
->pid
!= pid
)
1382 if (process
->signalled
)
1385 process
->signalled
= TRUE
;
1386 process
->status
= status
;
1387 mono_os_sem_post (&process
->exit_sem
);
1391 mono_os_mutex_unlock (&processes_mutex
);
1396 is_readable_or_executable (const char *prog
)
1399 int a
= access (prog
, R_OK
);
1400 int b
= access (prog
, X_OK
);
1401 if (a
!= 0 && b
!= 0)
1403 if (stat (prog
, &buf
))
1405 if (S_ISREG (buf
.st_mode
))
1411 is_executable (const char *prog
)
1414 if (access (prog
, X_OK
) != 0)
1416 if (stat (prog
, &buf
))
1418 if (S_ISREG (buf
.st_mode
))
1424 is_managed_binary (const char *filename
)
1426 int original_errno
= errno
;
1427 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1428 int file
= open (filename
, O_RDONLY
| O_LARGEFILE
);
1430 int file
= open (filename
, O_RDONLY
);
1433 unsigned char buffer
[8];
1434 off_t file_size
, optional_header_offset
;
1435 off_t pe_header_offset
, clr_header_offset
;
1436 gboolean managed
= FALSE
;
1438 guint32 first_word
, second_word
, magic_number
;
1440 /* If we are unable to open the file, then we definitely
1441 * can't say that it is managed. The child mono process
1442 * probably wouldn't be able to open it anyway.
1445 errno
= original_errno
;
1449 /* Retrieve the length of the file for future sanity checks. */
1450 file_size
= lseek (file
, 0, SEEK_END
);
1451 lseek (file
, 0, SEEK_SET
);
1453 /* We know we need to read a header field at offset 60. */
1457 num_read
= read (file
, buffer
, 2);
1459 if ((num_read
!= 2) || (buffer
[0] != 'M') || (buffer
[1] != 'Z'))
1462 new_offset
= lseek (file
, 60, SEEK_SET
);
1464 if (new_offset
!= 60)
1467 num_read
= read (file
, buffer
, 4);
1471 pe_header_offset
= buffer
[0]
1474 | (buffer
[3] << 24);
1476 if (pe_header_offset
+ 24 > file_size
)
1479 new_offset
= lseek (file
, pe_header_offset
, SEEK_SET
);
1481 if (new_offset
!= pe_header_offset
)
1484 num_read
= read (file
, buffer
, 4);
1486 if ((num_read
!= 4) || (buffer
[0] != 'P') || (buffer
[1] != 'E') || (buffer
[2] != 0) || (buffer
[3] != 0))
1490 * Verify that the header we want in the optional header data
1491 * is present in this binary.
1493 new_offset
= lseek (file
, pe_header_offset
+ 20, SEEK_SET
);
1495 if (new_offset
!= pe_header_offset
+ 20)
1498 num_read
= read (file
, buffer
, 2);
1500 if ((num_read
!= 2) || ((buffer
[0] | (buffer
[1] << 8)) < 216))
1503 optional_header_offset
= pe_header_offset
+ 24;
1505 /* Read the PE magic number */
1506 new_offset
= lseek (file
, optional_header_offset
, SEEK_SET
);
1508 if (new_offset
!= optional_header_offset
)
1511 num_read
= read (file
, buffer
, 2);
1516 magic_number
= (buffer
[0] | (buffer
[1] << 8));
1518 if (magic_number
== 0x10B) // PE32
1519 clr_header_offset
= 208;
1520 else if (magic_number
== 0x20B) // PE32+
1521 clr_header_offset
= 224;
1525 /* Read the CLR header address and size fields. These will be
1526 * zero if the binary is not managed.
1528 new_offset
= lseek (file
, optional_header_offset
+ clr_header_offset
, SEEK_SET
);
1530 if (new_offset
!= optional_header_offset
+ clr_header_offset
)
1533 num_read
= read (file
, buffer
, 8);
1535 /* We are not concerned with endianness, only with
1536 * whether it is zero or not.
1538 first_word
= *(guint32
*)&buffer
[0];
1539 second_word
= *(guint32
*)&buffer
[4];
1541 if ((num_read
!= 8) || (first_word
== 0) || (second_word
== 0))
1548 errno
= original_errno
;
1553 process_create (const gunichar2
*appname
, const gunichar2
*cmdline
,
1554 const gunichar2
*cwd
, StartupHandles
*startup_handles
, MonoW32ProcessInfo
*process_info
)
1556 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1557 char *cmd
= NULL
, *prog
= NULL
, *full_prog
= NULL
, *args
= NULL
, *args_after_prog
= NULL
;
1558 char *dir
= NULL
, **env_strings
= NULL
, **argv
= NULL
;
1560 gboolean ret
= FALSE
;
1561 gpointer handle
= NULL
;
1562 GError
*gerr
= NULL
;
1563 int in_fd
, out_fd
, err_fd
;
1565 int startup_pipe
[2] = {-1, -1};
1570 mono_lazy_initialize (&process_sig_chld_once
, process_add_sigchld_handler
);
1573 /* appname and cmdline specify the executable and its args:
1575 * If appname is not NULL, it is the name of the executable.
1576 * Otherwise the executable is the first token in cmdline.
1578 * Executable searching:
1580 * If appname is not NULL, it can specify the full path and
1581 * file name, or else a partial name and the current directory
1582 * will be used. There is no additional searching.
1584 * If appname is NULL, the first whitespace-delimited token in
1585 * cmdline is used. If the name does not contain a full
1586 * directory path, the search sequence is:
1588 * 1) The directory containing the current process
1589 * 2) The current working directory
1590 * 3) The windows system directory (Ignored)
1591 * 4) The windows directory (Ignored)
1594 * Just to make things more interesting, tokens can contain
1595 * white space if they are surrounded by quotation marks. I'm
1596 * beginning to understand just why windows apps are generally
1597 * so crap, with an API like this :-(
1599 if (appname
!= NULL
) {
1600 cmd
= mono_unicode_to_external (appname
);
1602 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL",
1605 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1609 switch_dir_separators(cmd
);
1612 if (cmdline
!= NULL
) {
1613 args
= mono_unicode_to_external (cmdline
);
1615 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
1617 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1623 dir
= mono_unicode_to_external (cwd
);
1625 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
1627 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1631 /* Turn all the slashes round the right way */
1632 switch_dir_separators(dir
);
1636 /* We can't put off locating the executable any longer :-( */
1639 if (g_ascii_isalpha (cmd
[0]) && (cmd
[1] == ':')) {
1640 /* Strip off the drive letter. I can't
1641 * believe that CP/M holdover is still
1644 g_memmove (cmd
, cmd
+2, strlen (cmd
)-2);
1645 cmd
[strlen (cmd
)-2] = '\0';
1648 unquoted
= g_shell_unquote (cmd
, NULL
);
1649 if (unquoted
[0] == '/') {
1650 /* Assume full path given */
1651 prog
= g_strdup (unquoted
);
1653 /* Executable existing ? */
1654 if (!is_readable_or_executable (prog
)) {
1655 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1658 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1662 /* Search for file named by cmd in the current
1665 char *curdir
= g_get_current_dir ();
1667 prog
= g_strdup_printf ("%s/%s", curdir
, unquoted
);
1670 /* And make sure it's readable */
1671 if (!is_readable_or_executable (prog
)) {
1672 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1675 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1681 args_after_prog
= args
;
1686 /* Dig out the first token from args, taking quotation
1687 * marks into account
1690 /* First, strip off all leading whitespace */
1691 args
= g_strchug (args
);
1693 /* args_after_prog points to the contents of args
1694 * after token has been set (otherwise argv[0] is
1697 args_after_prog
= args
;
1699 /* Assume the opening quote will always be the first
1702 if (args
[0] == '\"' || args
[0] == '\'') {
1704 for (i
= 1; args
[i
] != '\0' && args
[i
] != quote
; i
++);
1705 if (args
[i
+ 1] == '\0' || g_ascii_isspace (args
[i
+1])) {
1706 /* We found the first token */
1707 token
= g_strndup (args
+1, i
-1);
1708 args_after_prog
= g_strchug (args
+ i
+ 1);
1710 /* Quotation mark appeared in the
1711 * middle of the token. Just give the
1712 * whole first token, quotes and all,
1718 if (token
== NULL
) {
1719 /* No quote mark, or malformed */
1720 for (i
= 0; args
[i
] != '\0'; i
++) {
1721 if (g_ascii_isspace (args
[i
])) {
1722 token
= g_strndup (args
, i
);
1723 args_after_prog
= args
+ i
+ 1;
1729 if (token
== NULL
&& args
[0] != '\0') {
1730 /* Must be just one token in the string */
1731 token
= g_strdup (args
);
1732 args_after_prog
= NULL
;
1735 if (token
== NULL
) {
1737 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find what to exec", __func__
);
1739 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1743 /* Turn all the slashes round the right way. Only for
1746 switch_dir_separators(token
);
1748 if (g_ascii_isalpha (token
[0]) && (token
[1] == ':')) {
1749 /* Strip off the drive letter. I can't
1750 * believe that CP/M holdover is still
1753 g_memmove (token
, token
+2, strlen (token
)-2);
1754 token
[strlen (token
)-2] = '\0';
1757 if (token
[0] == '/') {
1758 /* Assume full path given */
1759 prog
= g_strdup (token
);
1761 /* Executable existing ? */
1762 if (!is_readable_or_executable (prog
)) {
1763 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1766 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1770 char *curdir
= g_get_current_dir ();
1772 /* FIXME: Need to record the directory
1773 * containing the current process, and check
1774 * that for the new executable as the first
1778 prog
= g_strdup_printf ("%s/%s", curdir
, token
);
1781 /* I assume X_OK is the criterion to use,
1784 * X_OK is too strict *if* the target is a CLR binary
1786 if (!is_readable_or_executable (prog
)) {
1788 prog
= g_find_program_in_path (token
);
1790 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s", __func__
, token
);
1793 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1802 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Exec prog [%s] args [%s]",
1803 __func__
, prog
, args_after_prog
);
1805 /* Check for CLR binaries; if found, we will try to invoke
1806 * them using the same mono binary that started us.
1808 if (is_managed_binary (prog
)) {
1809 gunichar2
*newapp
, *newcmd
;
1810 gsize bytes_ignored
;
1812 newapp
= mono_unicode_from_external (cli_launcher
? cli_launcher
: "mono", &bytes_ignored
);
1815 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, appname
, utf16_space
, cmdline
, NULL
);
1817 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, cmdline
, NULL
);
1822 ret
= process_create (NULL
, newcmd
, cwd
, startup_handles
, process_info
);
1830 if (!is_executable (prog
)) {
1831 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Executable permisson not set on %s", __func__
, prog
);
1832 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1837 if (args_after_prog
!= NULL
&& *args_after_prog
) {
1840 qprog
= g_shell_quote (prog
);
1841 full_prog
= g_strconcat (qprog
, " ", args_after_prog
, NULL
);
1844 full_prog
= g_shell_quote (prog
);
1847 ret
= g_shell_parse_argv (full_prog
, NULL
, &argv
, &gerr
);
1849 g_message ("process_create: %s\n", gerr
->message
);
1850 g_error_free (gerr
);
1855 if (startup_handles
) {
1856 in_fd
= GPOINTER_TO_UINT (startup_handles
->input
);
1857 out_fd
= GPOINTER_TO_UINT (startup_handles
->output
);
1858 err_fd
= GPOINTER_TO_UINT (startup_handles
->error
);
1860 in_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_input ());
1861 out_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_output ());
1862 err_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_error ());
1866 * process->env_variables is a an array of MonoString*
1868 * If new_environ is not NULL it specifies the entire set of
1869 * environment variables in the new process. Otherwise the
1870 * new process inherits the same environment.
1872 if (process_info
->env_variables
) {
1873 gint i
, str_length
, var_length
;
1877 /* +2: one for the process handle value, and the last one is NULL */
1878 env_strings
= g_new0 (gchar
*, mono_array_length (process_info
->env_variables
) + 2);
1883 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1884 for (i
= 0; i
< mono_array_length (process_info
->env_variables
); ++i
) {
1885 var
= mono_array_get (process_info
->env_variables
, MonoString
*, i
);
1886 var_length
= mono_string_length (var
);
1888 /* str is a null-terminated copy of var */
1890 if (var_length
+ 1 > str_length
) {
1891 str_length
= var_length
+ 1;
1892 str
= g_renew (gunichar2
, str
, str_length
);
1895 memcpy (str
, mono_string_chars (var
), var_length
* sizeof (gunichar2
));
1896 str
[var_length
] = '\0';
1898 env_strings
[i
] = mono_unicode_to_external (str
);
1906 for (i
= 0; environ
[i
] != NULL
; i
++)
1909 /* +2: one for the process handle value, and the last one is NULL */
1910 env_strings
= g_new0 (gchar
*, env_count
+ 2);
1912 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1913 for (i
= 0; i
< env_count
; i
++)
1914 env_strings
[i
] = g_strdup (environ
[i
]);
1917 /* Create a pipe to make sure the child doesn't exit before
1918 * we can add the process to the linked list of processes */
1919 if (pipe (startup_pipe
) == -1) {
1920 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1921 * This is just for a very hard to hit race condition in the first place */
1922 startup_pipe
[0] = startup_pipe
[1] = -1;
1923 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__
);
1926 switch (pid
= fork ()) {
1927 case -1: /* Error */ {
1928 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1932 case 0: /* Child */ {
1933 if (startup_pipe
[0] != -1) {
1934 /* Wait until the parent has updated it's internal data */
1935 ssize_t _i G_GNUC_UNUSED
= read (startup_pipe
[0], &dummy
, 1);
1936 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: child: parent has completed its setup", __func__
);
1937 close (startup_pipe
[0]);
1938 close (startup_pipe
[1]);
1941 /* should we detach from the process group? */
1943 /* Connect stdin, stdout and stderr */
1948 /* Close all file descriptors */
1949 for (i
= mono_w32handle_fd_reserve
- 1; i
> 2; i
--)
1952 #ifdef DEBUG_ENABLED
1953 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: exec()ing [%s] in dir [%s]", __func__
, cmd
,
1954 dir
== NULL
?".":dir
);
1955 for (i
= 0; argv
[i
] != NULL
; i
++)
1956 g_message ("arg %d: [%s]", i
, argv
[i
]);
1958 for (i
= 0; env_strings
[i
] != NULL
; i
++)
1959 g_message ("env %d: [%s]", i
, env_strings
[i
]);
1963 if (dir
!= NULL
&& chdir (dir
) == -1) {
1969 execve (argv
[0], argv
, env_strings
);
1976 default: /* Parent */ {
1977 MonoW32HandleProcess process_handle
;
1979 memset (&process_handle
, 0, sizeof (process_handle
));
1980 process_handle
.pid
= pid
;
1981 process_handle
.child
= TRUE
;
1982 process_handle
.pname
= g_strdup (prog
);
1983 process_set_defaults (&process_handle
);
1985 /* Add our process into the linked list of processes */
1986 process
= (Process
*) g_malloc0 (sizeof (Process
));
1988 process
->handle_count
= 1;
1989 mono_os_sem_init (&process
->exit_sem
, 0);
1991 process_handle
.process
= process
;
1993 handle
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
1994 if (handle
== INVALID_HANDLE_VALUE
) {
1995 g_warning ("%s: error creating process handle", __func__
);
1997 mono_os_sem_destroy (&process
->exit_sem
);
2000 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
2005 /* Keep the process handle artificially alive until the process
2006 * exits so that the information in the handle isn't lost. */
2007 mono_w32handle_ref (handle
);
2008 process
->handle
= handle
;
2010 mono_os_mutex_lock (&processes_mutex
);
2011 process
->next
= processes
;
2012 mono_memory_barrier ();
2013 processes
= process
;
2014 mono_os_mutex_unlock (&processes_mutex
);
2016 if (process_info
!= NULL
) {
2017 process_info
->process_handle
= handle
;
2018 process_info
->pid
= pid
;
2020 /* FIXME: we might need to handle the thread info some day */
2021 process_info
->thread_handle
= INVALID_HANDLE_VALUE
;
2022 process_info
->tid
= 0;
2029 if (startup_pipe
[1] != -1) {
2030 /* Write 1 byte, doesn't matter what */
2031 ssize_t _i G_GNUC_UNUSED
= write (startup_pipe
[1], startup_pipe
, 1);
2032 close (startup_pipe
[0]);
2033 close (startup_pipe
[1]);
2048 g_strfreev (env_strings
);
2052 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: returning handle %p for pid %d", __func__
, handle
, pid
);
2054 /* Check if something needs to be cleaned up. */
2055 processes_cleanup ();
2059 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2061 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
2065 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo
*proc_start_info
, MonoW32ProcessInfo
*process_info
)
2067 const gunichar2
*lpFile
;
2068 const gunichar2
*lpParameters
;
2069 const gunichar2
*lpDirectory
;
2073 if (!proc_start_info
->filename
) {
2074 /* w2k returns TRUE for this, for some reason. */
2079 lpFile
= proc_start_info
->filename
? mono_string_chars (proc_start_info
->filename
) : NULL
;
2080 lpParameters
= proc_start_info
->arguments
? mono_string_chars (proc_start_info
->arguments
) : NULL
;
2081 lpDirectory
= proc_start_info
->working_directory
&& mono_string_length (proc_start_info
->working_directory
) != 0 ?
2082 mono_string_chars (proc_start_info
->working_directory
) : NULL
;
2084 /* Put both executable and parameters into the second argument
2085 * to process_create (), so it searches $PATH. The conversion
2086 * into and back out of utf8 is because there is no
2087 * g_strdup_printf () equivalent for gunichar2 :-(
2089 args
= utf16_concat (utf16_quote
, lpFile
, utf16_quote
, lpParameters
== NULL
? NULL
: utf16_space
, lpParameters
, NULL
);
2091 mono_w32error_set_last (ERROR_INVALID_DATA
);
2095 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2098 if (!ret
&& mono_w32error_get_last () == ERROR_OUTOFMEMORY
)
2102 static char *handler
;
2103 static gunichar2
*handler_utf16
;
2105 if (handler_utf16
== (gunichar2
*)-1) {
2110 #ifdef PLATFORM_MACOSX
2111 handler
= g_strdup ("/usr/bin/open");
2114 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
2115 * if that fails, try to use gnome-open, then kfmclient
2117 handler
= g_find_program_in_path ("xdg-open");
2118 if (handler
== NULL
){
2119 handler
= g_find_program_in_path ("gnome-open");
2120 if (handler
== NULL
){
2121 handler
= g_find_program_in_path ("kfmclient");
2122 if (handler
== NULL
){
2123 handler_utf16
= (gunichar2
*) -1;
2127 /* kfmclient needs exec argument */
2128 char *old
= handler
;
2129 handler
= g_strconcat (old
, " exec",
2136 handler_utf16
= g_utf8_to_utf16 (handler
, -1, NULL
, NULL
, NULL
);
2139 /* Put quotes around the filename, in case it's a url
2140 * that contains #'s (process_create() calls
2141 * g_shell_parse_argv(), which deliberately throws
2142 * away anything after an unquoted #). Fixes bug
2145 args
= utf16_concat (handler_utf16
, utf16_space
, utf16_quote
, lpFile
, utf16_quote
,
2146 lpParameters
== NULL
? NULL
: utf16_space
, lpParameters
, NULL
);
2148 mono_w32error_set_last (ERROR_INVALID_DATA
);
2152 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2155 if (mono_w32error_get_last () != ERROR_OUTOFMEMORY
)
2156 mono_w32error_set_last (ERROR_INVALID_DATA
);
2160 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
2161 mono_w32handle_close (process_info
->process_handle
);
2162 process_info
->process_handle
= NULL
;
2167 process_info
->pid
= -mono_w32error_get_last ();
2169 process_info
->thread_handle
= NULL
;
2170 #if !defined(MONO_CROSS_COMPILE)
2171 process_info
->pid
= mono_w32process_get_pid (process_info
->process_handle
);
2173 process_info
->pid
= 0;
2175 process_info
->tid
= 0;
2181 /* Only used when UseShellExecute is false */
2183 process_get_complete_path (const gunichar2
*appname
, gchar
**completed
)
2188 utf8app
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
2190 if (g_path_is_absolute (utf8app
)) {
2191 *completed
= g_shell_quote (utf8app
);
2196 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
2197 *completed
= g_shell_quote (utf8app
);
2202 found
= g_find_program_in_path (utf8app
);
2203 if (found
== NULL
) {
2209 *completed
= g_shell_quote (found
);
2216 process_get_shell_arguments (MonoW32ProcessStartInfo
*proc_start_info
, gunichar2
**shell_path
)
2218 gchar
*complete_path
= NULL
;
2222 if (process_get_complete_path (mono_string_chars (proc_start_info
->filename
), &complete_path
)) {
2223 *shell_path
= g_utf8_to_utf16 (complete_path
, -1, NULL
, NULL
, NULL
);
2224 g_free (complete_path
);
2227 return *shell_path
!= NULL
;
2231 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo
*proc_start_info
,
2232 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
)
2236 StartupHandles startup_handles
;
2237 gunichar2
*shell_path
= NULL
;
2238 gunichar2
*args
= NULL
;
2240 memset (&startup_handles
, 0, sizeof (startup_handles
));
2241 startup_handles
.input
= stdin_handle
;
2242 startup_handles
.output
= stdout_handle
;
2243 startup_handles
.error
= stderr_handle
;
2245 if (!process_get_shell_arguments (proc_start_info
, &shell_path
)) {
2246 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
2250 args
= proc_start_info
->arguments
&& mono_string_length (proc_start_info
->arguments
) > 0 ?
2251 mono_string_chars (proc_start_info
->arguments
): NULL
;
2253 /* The default dir name is "". Turn that into NULL to mean "current directory" */
2254 dir
= proc_start_info
->working_directory
&& mono_string_length (proc_start_info
->working_directory
) > 0 ?
2255 mono_string_chars (proc_start_info
->working_directory
) : NULL
;
2257 ret
= process_create (shell_path
, args
, dir
, &startup_handles
, process_info
);
2259 if (shell_path
!= NULL
)
2260 g_free (shell_path
);
2263 process_info
->pid
= -mono_w32error_get_last ();
2268 /* Returns an array of pids */
2270 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
2277 pidarray
= mono_process_list (&count
);
2279 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
2282 procs
= mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count
, &error
);
2283 if (mono_error_set_pending_exception (&error
)) {
2287 if (sizeof (guint32
) == sizeof (gpointer
)) {
2288 memcpy (mono_array_addr (procs
, guint32
, 0), pidarray
, count
* sizeof (gint32
));
2290 for (i
= 0; i
< count
; ++i
)
2291 *(mono_array_addr (procs
, guint32
, i
)) = GPOINTER_TO_UINT (pidarray
[i
]);
2299 mono_w32process_set_cli_launcher (gchar
*path
)
2301 g_free (cli_launcher
);
2302 cli_launcher
= g_strdup (path
);
2306 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2308 return current_process
;
2312 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
)
2314 MonoW32HandleProcess
*process_handle
;
2320 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2322 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2326 if (process_handle
->pid
== current_pid
) {
2327 *exitcode
= STILL_ACTIVE
;
2331 /* A process handle is only signalled if the process has exited
2332 * and has been waited for. Make sure any process exit has been
2333 * noticed before checking if the process is signalled.
2334 * Fixes bug 325463. */
2335 mono_w32handle_wait_one (handle
, 0, TRUE
);
2337 *exitcode
= mono_w32handle_issignalled (handle
) ? process_handle
->exitstatus
: STILL_ACTIVE
;
2342 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
)
2344 return mono_w32handle_close (handle
);
2348 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
)
2351 MonoW32HandleProcess
*process_handle
;
2356 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2358 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2359 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2363 pid
= process_handle
->pid
;
2365 ret
= kill (pid
, exitcode
== -1 ? SIGKILL
: SIGTERM
);
2370 case EINVAL
: mono_w32error_set_last (ERROR_INVALID_PARAMETER
); break;
2371 case EPERM
: mono_w32error_set_last (ERROR_ACCESS_DENIED
); break;
2372 case ESRCH
: mono_w32error_set_last (ERROR_PROC_NOT_FOUND
); break;
2373 default: mono_w32error_set_last (ERROR_GEN_FAILURE
); break;
2378 g_error ("kill() is not supported by this platform");
2383 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
)
2385 MonoW32HandleProcess
*process_handle
;
2391 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2393 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2397 if (!process_handle
->child
)
2400 *min
= process_handle
->min_working_set
;
2401 *max
= process_handle
->max_working_set
;
2406 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
)
2408 MonoW32HandleProcess
*process_handle
;
2411 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2413 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2417 if (!process_handle
->child
)
2420 process_handle
->min_working_set
= min
;
2421 process_handle
->max_working_set
= max
;
2426 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
)
2428 #ifdef HAVE_GETPRIORITY
2429 MonoW32HandleProcess
*process_handle
;
2434 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2436 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2440 pid
= process_handle
->pid
;
2443 ret
= getpriority (PRIO_PROCESS
, pid
);
2444 if (ret
== -1 && errno
!= 0) {
2448 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2451 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2454 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2460 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2462 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
;
2464 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH
;
2466 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
2468 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE
;
2470 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
2472 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2474 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2480 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
)
2482 #ifdef HAVE_SETPRIORITY
2483 MonoW32HandleProcess
*process_handle
;
2489 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2491 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2495 pid
= process_handle
->pid
;
2497 switch (priorityClass
) {
2498 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE
:
2501 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
:
2504 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
:
2507 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
:
2510 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH
:
2513 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
:
2517 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2521 ret
= setpriority (PRIO_PROCESS
, pid
, prio
);
2526 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2529 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2532 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2538 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2544 ticks_to_processtime (guint64 ticks
, ProcessTime
*processtime
)
2546 processtime
->lowDateTime
= ticks
& 0xFFFFFFFF;
2547 processtime
->highDateTime
= ticks
>> 32;
2551 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creation_time
, gint64
*exit_time
, gint64
*kernel_time
, gint64
*user_time
)
2553 MonoW32HandleProcess
*process_handle
;
2554 ProcessTime
*creation_processtime
, *exit_processtime
, *kernel_processtime
, *user_processtime
;
2557 if (!creation_time
|| !exit_time
|| !kernel_time
|| !user_time
) {
2558 /* Not sure if w32 allows NULLs here or not */
2562 creation_processtime
= (ProcessTime
*) creation_time
;
2563 exit_processtime
= (ProcessTime
*) exit_time
;
2564 kernel_processtime
= (ProcessTime
*) kernel_time
;
2565 user_processtime
= (ProcessTime
*) user_time
;
2567 memset (creation_processtime
, 0, sizeof (ProcessTime
));
2568 memset (exit_processtime
, 0, sizeof (ProcessTime
));
2569 memset (kernel_processtime
, 0, sizeof (ProcessTime
));
2570 memset (user_processtime
, 0, sizeof (ProcessTime
));
2572 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2574 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2578 if (!process_handle
->child
) {
2579 gint64 start_ticks
, user_ticks
, kernel_ticks
;
2581 mono_process_get_times (GINT_TO_POINTER (process_handle
->pid
),
2582 &start_ticks
, &user_ticks
, &kernel_ticks
);
2584 ticks_to_processtime (start_ticks
, creation_processtime
);
2585 ticks_to_processtime (kernel_ticks
, kernel_processtime
);
2586 ticks_to_processtime (user_ticks
, user_processtime
);
2590 ticks_to_processtime (process_handle
->create_time
, creation_processtime
);
2592 /* A process handle is only signalled if the process has
2593 * exited, otherwise exit_processtime isn't set */
2594 if (mono_w32handle_issignalled (handle
))
2595 ticks_to_processtime (process_handle
->exit_time
, exit_processtime
);
2597 #ifdef HAVE_GETRUSAGE
2598 if (process_handle
->pid
== getpid ()) {
2599 struct rusage time_data
;
2600 if (getrusage (RUSAGE_SELF
, &time_data
) == 0) {
2601 ticks_to_processtime ((guint64
)time_data
.ru_utime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_utime
.tv_usec
* 10, user_processtime
);
2602 ticks_to_processtime ((guint64
)time_data
.ru_stime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_stime
.tv_usec
* 10, kernel_processtime
);
2610 static IMAGE_SECTION_HEADER
*
2611 get_enclosing_section_header (guint32 rva
, IMAGE_NT_HEADERS32
*nt_headers
)
2613 IMAGE_SECTION_HEADER
*section
= IMAGE_FIRST_SECTION32 (nt_headers
);
2616 for (i
= 0; i
< GUINT16_FROM_LE (nt_headers
->FileHeader
.NumberOfSections
); i
++, section
++) {
2617 guint32 size
= GUINT32_FROM_LE (section
->Misc
.VirtualSize
);
2619 size
= GUINT32_FROM_LE (section
->SizeOfRawData
);
2622 if ((rva
>= GUINT32_FROM_LE (section
->VirtualAddress
)) &&
2623 (rva
< (GUINT32_FROM_LE (section
->VirtualAddress
) + size
))) {
2631 /* This works for both 32bit and 64bit files, as the differences are
2632 * all after the section header block
2635 get_ptr_from_rva (guint32 rva
, IMAGE_NT_HEADERS32
*ntheaders
, gpointer file_map
)
2637 IMAGE_SECTION_HEADER
*section_header
;
2640 section_header
= get_enclosing_section_header (rva
, ntheaders
);
2641 if (section_header
== NULL
) {
2645 delta
= (guint32
)(GUINT32_FROM_LE (section_header
->VirtualAddress
) -
2646 GUINT32_FROM_LE (section_header
->PointerToRawData
));
2648 return((guint8
*)file_map
+ rva
- delta
);
2652 scan_resource_dir (IMAGE_RESOURCE_DIRECTORY
*root
, IMAGE_NT_HEADERS32
*nt_headers
, gpointer file_map
,
2653 IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
, int level
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2655 IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry
;
2656 gboolean is_string
, is_dir
;
2657 guint32 name_offset
, dir_offset
, data_offset
;
2659 swapped_entry
.Name
= GUINT32_FROM_LE (entry
->Name
);
2660 swapped_entry
.OffsetToData
= GUINT32_FROM_LE (entry
->OffsetToData
);
2662 is_string
= swapped_entry
.NameIsString
;
2663 is_dir
= swapped_entry
.DataIsDirectory
;
2664 name_offset
= swapped_entry
.NameOffset
;
2665 dir_offset
= swapped_entry
.OffsetToDirectory
;
2666 data_offset
= swapped_entry
.OffsetToData
;
2669 /* Normally holds a directory entry for each type of
2672 if ((is_string
== FALSE
&&
2673 name_offset
!= res_id
) ||
2674 (is_string
== TRUE
)) {
2677 } else if (level
== 1) {
2678 /* Normally holds a directory entry for each resource
2681 } else if (level
== 2) {
2682 /* Normally holds a directory entry for each language
2684 if ((is_string
== FALSE
&&
2685 name_offset
!= lang_id
&&
2687 (is_string
== TRUE
)) {
2691 g_assert_not_reached ();
2694 if (is_dir
== TRUE
) {
2695 IMAGE_RESOURCE_DIRECTORY
*res_dir
= (IMAGE_RESOURCE_DIRECTORY
*)((guint8
*)root
+ dir_offset
);
2696 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entries
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(res_dir
+ 1);
2699 entries
= GUINT16_FROM_LE (res_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (res_dir
->NumberOfIdEntries
);
2701 for (i
= 0; i
< entries
; i
++) {
2702 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entry
= &sub_entries
[i
];
2705 ret
= scan_resource_dir (root
, nt_headers
, file_map
,
2706 sub_entry
, level
+ 1, res_id
,
2715 IMAGE_RESOURCE_DATA_ENTRY
*data_entry
= (IMAGE_RESOURCE_DATA_ENTRY
*)((guint8
*)root
+ data_offset
);
2716 *size
= GUINT32_FROM_LE (data_entry
->Size
);
2718 return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry
->OffsetToData
), nt_headers
, file_map
));
2723 find_pe_file_resources32 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2725 IMAGE_DOS_HEADER
*dos_header
;
2726 IMAGE_NT_HEADERS32
*nt_headers
;
2727 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2728 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2729 guint32 resource_rva
, entries
, i
;
2730 gpointer ret
= NULL
;
2732 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2733 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2734 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2736 mono_w32error_set_last (ERROR_INVALID_DATA
);
2740 if (map_size
< sizeof(IMAGE_NT_HEADERS32
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2741 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: File is too small: %d", __func__
, map_size
);
2743 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2747 nt_headers
= (IMAGE_NT_HEADERS32
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2748 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2749 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad NT signature 0x%x", __func__
, nt_headers
->Signature
);
2751 mono_w32error_set_last (ERROR_INVALID_DATA
);
2755 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2756 /* Do 64-bit stuff */
2757 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2759 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2762 if (resource_rva
== 0) {
2763 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: No resources in file!", __func__
);
2765 mono_w32error_set_last (ERROR_INVALID_DATA
);
2769 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2770 if (resource_dir
== NULL
) {
2771 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find resource directory", __func__
);
2773 mono_w32error_set_last (ERROR_INVALID_DATA
);
2777 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2778 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2780 for (i
= 0; i
< entries
; i
++) {
2781 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2782 ret
= scan_resource_dir (resource_dir
,
2783 (IMAGE_NT_HEADERS32
*)nt_headers
,
2784 file_map
, direntry
, 0, res_id
,
2795 find_pe_file_resources64 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2797 IMAGE_DOS_HEADER
*dos_header
;
2798 IMAGE_NT_HEADERS64
*nt_headers
;
2799 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2800 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2801 guint32 resource_rva
, entries
, i
;
2802 gpointer ret
= NULL
;
2804 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2805 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2806 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2808 mono_w32error_set_last (ERROR_INVALID_DATA
);
2812 if (map_size
< sizeof(IMAGE_NT_HEADERS64
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2813 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: File is too small: %d", __func__
, map_size
);
2815 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2819 nt_headers
= (IMAGE_NT_HEADERS64
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2820 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2821 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad NT signature 0x%x", __func__
,
2822 nt_headers
->Signature
);
2824 mono_w32error_set_last (ERROR_INVALID_DATA
);
2828 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2829 /* Do 64-bit stuff */
2830 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2832 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2835 if (resource_rva
== 0) {
2836 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: No resources in file!", __func__
);
2838 mono_w32error_set_last (ERROR_INVALID_DATA
);
2842 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2843 if (resource_dir
== NULL
) {
2844 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find resource directory", __func__
);
2846 mono_w32error_set_last (ERROR_INVALID_DATA
);
2850 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2851 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2853 for (i
= 0; i
< entries
; i
++) {
2854 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2855 ret
= scan_resource_dir (resource_dir
,
2856 (IMAGE_NT_HEADERS32
*)nt_headers
,
2857 file_map
, direntry
, 0, res_id
,
2868 find_pe_file_resources (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2870 /* Figure this out when we support 64bit PE files */
2872 return find_pe_file_resources32 (file_map
, map_size
, res_id
,
2875 return find_pe_file_resources64 (file_map
, map_size
, res_id
,
2881 map_pe_file (gunichar2
*filename
, gint32
*map_size
, void **handle
)
2883 gchar
*filename_ext
;
2885 struct stat statbuf
;
2888 /* According to the MSDN docs, a search path is applied to
2889 * filename. FIXME: implement this, for now just pass it
2893 filename_ext
= mono_unicode_to_external (filename
);
2894 if (filename_ext
== NULL
) {
2895 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
2897 mono_w32error_set_last (ERROR_INVALID_NAME
);
2901 fd
= open (filename_ext
, O_RDONLY
, 0);
2902 if (fd
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
2904 gchar
*located_filename
;
2906 saved_errno
= errno
;
2908 located_filename
= mono_portability_find_file (filename_ext
, TRUE
);
2909 if (!located_filename
) {
2910 errno
= saved_errno
;
2912 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error opening file %s (1): %s", __func__
, filename_ext
, strerror (errno
));
2914 g_free (filename_ext
);
2916 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2920 fd
= open (located_filename
, O_RDONLY
, 0);
2922 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error opening file %s (2): %s", __func__
, filename_ext
, strerror (errno
));
2924 g_free (filename_ext
);
2925 g_free (located_filename
);
2927 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2931 g_free (located_filename
);
2934 if (fstat (fd
, &statbuf
) == -1) {
2935 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error stat()ing file %s: %s", __func__
, filename_ext
, strerror (errno
));
2937 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2938 g_free (filename_ext
);
2942 *map_size
= statbuf
.st_size
;
2944 /* Check basic file size */
2945 if (statbuf
.st_size
< sizeof(IMAGE_DOS_HEADER
)) {
2946 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: File %s is too small: %lld", __func__
, filename_ext
, statbuf
.st_size
);
2948 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2949 g_free (filename_ext
);
2954 file_map
= mono_file_map (statbuf
.st_size
, MONO_MMAP_READ
| MONO_MMAP_PRIVATE
, fd
, 0, handle
);
2955 if (file_map
== NULL
) {
2956 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error mmap()int file %s: %s", __func__
, filename_ext
, strerror (errno
));
2958 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2959 g_free (filename_ext
);
2964 /* Don't need the fd any more */
2966 g_free (filename_ext
);
2972 unmap_pe_file (gpointer file_map
, void *handle
)
2974 mono_file_unmap (file_map
, handle
);
2978 unicode_chars (const gunichar2
*str
)
2983 if (str
[len
] == '\0') {
2991 unicode_compare (const gunichar2
*str1
, const gunichar2
*str2
)
2993 while (*str1
&& *str2
) {
2994 if (*str1
!= *str2
) {
3001 return(*str1
== *str2
);
3004 /* compare a little-endian null-terminated utf16 string and a normal string.
3005 * Can be used only for ascii or latin1 chars.
3008 unicode_string_equals (const gunichar2
*str1
, const gchar
*str2
)
3010 while (*str1
&& *str2
) {
3011 if (GUINT16_TO_LE (*str1
) != *str2
) {
3018 return(*str1
== *str2
);
3028 /* Returns a pointer to the value data, because there's no way to know
3029 * how big that data is (value_len is set to zero for most blocks :-( )
3031 static gconstpointer
3032 get_versioninfo_block (gconstpointer data
, version_data
*block
)
3034 block
->data_len
= GUINT16_FROM_LE (*((guint16
*)data
));
3035 data
= (char *)data
+ sizeof(guint16
);
3036 block
->value_len
= GUINT16_FROM_LE (*((guint16
*)data
));
3037 data
= (char *)data
+ sizeof(guint16
);
3039 /* No idea what the type is supposed to indicate */
3040 block
->type
= GUINT16_FROM_LE (*((guint16
*)data
));
3041 data
= (char *)data
+ sizeof(guint16
);
3042 block
->key
= ((gunichar2
*)data
);
3044 /* Skip over the key (including the terminator) */
3045 data
= ((gunichar2
*)data
) + (unicode_chars (block
->key
) + 1);
3047 /* align on a 32-bit boundary */
3053 static gconstpointer
3054 get_fixedfileinfo_block (gconstpointer data
, version_data
*block
)
3056 gconstpointer data_ptr
;
3057 VS_FIXEDFILEINFO
*ffi
;
3059 data_ptr
= get_versioninfo_block (data
, block
);
3061 if (block
->value_len
!= sizeof(VS_FIXEDFILEINFO
)) {
3062 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: FIXEDFILEINFO size mismatch", __func__
);
3066 if (!unicode_string_equals (block
->key
, "VS_VERSION_INFO")) {
3067 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: VS_VERSION_INFO mismatch", __func__
);
3072 ffi
= ((VS_FIXEDFILEINFO
*)data_ptr
);
3073 if ((ffi
->dwSignature
!= VS_FFI_SIGNATURE
) ||
3074 (ffi
->dwStrucVersion
!= VS_FFI_STRUCVERSION
)) {
3075 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: FIXEDFILEINFO bad signature", __func__
);
3083 static gconstpointer
3084 get_varfileinfo_block (gconstpointer data_ptr
, version_data
*block
)
3086 /* data is pointing at a Var block
3088 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3093 static gconstpointer
3094 get_string_block (gconstpointer data_ptr
, const gunichar2
*string_key
, gpointer
*string_value
,
3095 guint32
*string_value_len
, version_data
*block
)
3097 guint16 data_len
= block
->data_len
;
3098 guint16 string_len
= 28; /* Length of the StringTable block */
3099 char *orig_data_ptr
= (char *)data_ptr
- 28;
3101 /* data_ptr is pointing at an array of one or more String blocks
3102 * with total length (not including alignment padding) of
3105 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3106 /* align on a 32-bit boundary */
3109 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3110 if (block
->data_len
== 0) {
3111 /* We must have hit padding, so give up
3114 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3119 string_len
= string_len
+ block
->data_len
;
3121 if (string_key
!= NULL
&&
3122 string_value
!= NULL
&&
3123 string_value_len
!= NULL
&&
3124 unicode_compare (string_key
, block
->key
) == TRUE
) {
3125 *string_value
= (gpointer
)data_ptr
;
3126 *string_value_len
= block
->value_len
;
3129 /* Skip over the value */
3130 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3136 /* Returns a pointer to the byte following the Stringtable block, or
3137 * NULL if the data read hits padding. We can't recover from this
3138 * because the data length does not include padding bytes, so it's not
3139 * possible to just return the start position + length
3141 * If lang == NULL it means we're just stepping through this block
3143 static gconstpointer
3144 get_stringtable_block (gconstpointer data_ptr
, gchar
*lang
, const gunichar2
*string_key
, gpointer
*string_value
,
3145 guint32
*string_value_len
, version_data
*block
)
3147 guint16 data_len
= block
->data_len
;
3148 guint16 string_len
= 36; /* length of the StringFileInfo block */
3150 gchar
*lowercase_lang
;
3152 /* data_ptr is pointing at an array of StringTable blocks,
3153 * with total length (not including alignment padding) of
3157 while(string_len
< data_len
) {
3158 /* align on a 32-bit boundary */
3161 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3162 if (block
->data_len
== 0) {
3163 /* We must have hit padding, so give up
3166 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3170 string_len
= string_len
+ block
->data_len
;
3172 found_lang
= g_utf16_to_utf8 (block
->key
, 8, NULL
, NULL
, NULL
);
3173 if (found_lang
== NULL
) {
3174 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid language key, giving up", __func__
);
3178 lowercase_lang
= g_utf8_strdown (found_lang
, -1);
3179 g_free (found_lang
);
3180 found_lang
= lowercase_lang
;
3181 lowercase_lang
= NULL
;
3183 if (lang
!= NULL
&& !strcmp (found_lang
, lang
)) {
3184 /* Got the one we're interested in */
3185 data_ptr
= get_string_block (data_ptr
, string_key
,
3187 string_value_len
, block
);
3189 data_ptr
= get_string_block (data_ptr
, NULL
, NULL
,
3193 g_free (found_lang
);
3195 if (data_ptr
== NULL
) {
3196 /* Child block hit padding */
3197 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3205 #if G_BYTE_ORDER == G_BIG_ENDIAN
3206 static gconstpointer
3207 big_up_string_block (gconstpointer data_ptr
, version_data
*block
)
3209 guint16 data_len
= block
->data_len
;
3210 guint16 string_len
= 28; /* Length of the StringTable block */
3212 char *orig_data_ptr
= (char *)data_ptr
- 28;
3214 /* data_ptr is pointing at an array of one or more String
3215 * blocks with total length (not including alignment padding)
3218 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3219 /* align on a 32-bit boundary */
3222 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3223 if (block
->data_len
== 0) {
3224 /* We must have hit padding, so give up
3227 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3231 string_len
= string_len
+ block
->data_len
;
3233 big_value
= g_convert ((gchar
*)block
->key
,
3234 unicode_chars (block
->key
) * 2,
3235 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3237 if (big_value
== NULL
) {
3238 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid string, giving up", __func__
);
3242 /* The swapped string should be exactly the same
3243 * length as the original little-endian one, but only
3244 * copy the number of original chars just to be on the
3247 memcpy (block
->key
, big_value
, unicode_chars (block
->key
) * 2);
3250 big_value
= g_convert ((gchar
*)data_ptr
,
3251 unicode_chars (data_ptr
) * 2,
3252 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3254 if (big_value
== NULL
) {
3255 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid data string, giving up", __func__
);
3258 memcpy ((gpointer
)data_ptr
, big_value
,
3259 unicode_chars (data_ptr
) * 2);
3262 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3268 /* Returns a pointer to the byte following the Stringtable block, or
3269 * NULL if the data read hits padding. We can't recover from this
3270 * because the data length does not include padding bytes, so it's not
3271 * possible to just return the start position + length
3273 static gconstpointer
3274 big_up_stringtable_block (gconstpointer data_ptr
, version_data
*block
)
3276 guint16 data_len
= block
->data_len
;
3277 guint16 string_len
= 36; /* length of the StringFileInfo block */
3280 /* data_ptr is pointing at an array of StringTable blocks,
3281 * with total length (not including alignment padding) of
3285 while(string_len
< data_len
) {
3286 /* align on a 32-bit boundary */
3289 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3290 if (block
->data_len
== 0) {
3291 /* We must have hit padding, so give up
3294 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3298 string_len
= string_len
+ block
->data_len
;
3300 big_value
= g_convert ((gchar
*)block
->key
, 16, "UTF-16BE",
3301 "UTF-16LE", NULL
, NULL
, 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__
);
3307 memcpy (block
->key
, big_value
, 16);
3310 data_ptr
= big_up_string_block (data_ptr
, block
);
3312 if (data_ptr
== NULL
) {
3313 /* Child block hit padding */
3314 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3322 /* Follows the data structures and turns all UTF-16 strings from the
3323 * LE found in the resource section into UTF-16BE
3326 big_up (gconstpointer datablock
, guint32 size
)
3328 gconstpointer data_ptr
;
3329 gint32 data_len
; /* signed to guard against underflow */
3332 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3333 if (data_ptr
!= NULL
) {
3334 VS_FIXEDFILEINFO
*ffi
= (VS_FIXEDFILEINFO
*)data_ptr
;
3336 /* Byteswap all the fields */
3337 ffi
->dwFileVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionMS
);
3338 ffi
->dwFileVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionLS
);
3339 ffi
->dwProductVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionMS
);
3340 ffi
->dwProductVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionLS
);
3341 ffi
->dwFileFlagsMask
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlagsMask
);
3342 ffi
->dwFileFlags
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlags
);
3343 ffi
->dwFileOS
= GUINT32_SWAP_LE_BE (ffi
->dwFileOS
);
3344 ffi
->dwFileType
= GUINT32_SWAP_LE_BE (ffi
->dwFileType
);
3345 ffi
->dwFileSubtype
= GUINT32_SWAP_LE_BE (ffi
->dwFileSubtype
);
3346 ffi
->dwFileDateMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateMS
);
3347 ffi
->dwFileDateLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateLS
);
3349 /* The FFI and header occupies the first 92 bytes
3351 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3352 data_len
= block
.data_len
- 92;
3354 /* There now follow zero or one StringFileInfo blocks
3355 * and zero or one VarFileInfo blocks
3357 while (data_len
> 0) {
3358 /* align on a 32-bit boundary */
3361 data_ptr
= get_versioninfo_block (data_ptr
, &block
);
3362 if (block
.data_len
== 0) {
3363 /* We must have hit padding, so give
3366 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3370 data_len
= data_len
- block
.data_len
;
3372 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3373 data_ptr
= get_varfileinfo_block (data_ptr
,
3375 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3376 } else if (unicode_string_equals (block
.key
,
3377 "StringFileInfo")) {
3378 data_ptr
= big_up_stringtable_block (data_ptr
,
3382 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Not a valid VERSIONINFO child block", __func__
);
3386 if (data_ptr
== NULL
) {
3387 /* Child block hit padding */
3388 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3397 mono_w32process_get_fileversion_info_size (gunichar2
*filename
, guint32
*handle
)
3400 gpointer versioninfo
;
3405 /* This value is unused, but set to zero */
3408 file_map
= map_pe_file (filename
, &map_size
, &map_handle
);
3409 if (file_map
== NULL
) {
3413 versioninfo
= find_pe_file_resources (file_map
, map_size
, RT_VERSION
, 0, &size
);
3414 if (versioninfo
== NULL
) {
3415 /* Didn't find the resource, so set the return value
3421 unmap_pe_file (file_map
, map_handle
);
3427 mono_w32process_get_fileversion_info (gunichar2
*filename
, guint32 handle G_GNUC_UNUSED
, guint32 len
, gpointer data
)
3430 gpointer versioninfo
;
3434 gboolean ret
= FALSE
;
3436 file_map
= map_pe_file (filename
, &map_size
, &map_handle
);
3437 if (file_map
== NULL
) {
3441 versioninfo
= find_pe_file_resources (file_map
, map_size
, RT_VERSION
,
3443 if (versioninfo
!= NULL
) {
3444 /* This could probably process the data so that
3445 * mono_w32process_ver_query_value() doesn't have to follow the data
3446 * blocks every time. But hey, these functions aren't
3447 * likely to appear in many profiles.
3449 memcpy (data
, versioninfo
, len
< size
?len
:size
);
3452 #if G_BYTE_ORDER == G_BIG_ENDIAN
3453 big_up (data
, size
);
3457 unmap_pe_file (file_map
, map_handle
);
3463 mono_w32process_ver_query_value (gconstpointer datablock
, const gunichar2
*subblock
, gpointer
*buffer
, guint32
*len
)
3465 gchar
*subblock_utf8
, *lang_utf8
= NULL
;
3466 gboolean ret
= FALSE
;
3468 gconstpointer data_ptr
;
3469 gint32 data_len
; /* signed to guard against underflow */
3470 gboolean want_var
= FALSE
;
3471 gboolean want_string
= FALSE
;
3473 const gunichar2
*string_key
= NULL
;
3474 gpointer string_value
= NULL
;
3475 guint32 string_value_len
= 0;
3476 gchar
*lowercase_lang
;
3478 subblock_utf8
= g_utf16_to_utf8 (subblock
, -1, NULL
, NULL
, NULL
);
3479 if (subblock_utf8
== NULL
) {
3483 if (!strcmp (subblock_utf8
, "\\VarFileInfo\\Translation")) {
3485 } else if (!strncmp (subblock_utf8
, "\\StringFileInfo\\", 16)) {
3487 memcpy (lang
, subblock
+ 16, 8 * sizeof(gunichar2
));
3488 lang_utf8
= g_utf16_to_utf8 (lang
, 8, NULL
, NULL
, NULL
);
3489 lowercase_lang
= g_utf8_strdown (lang_utf8
, -1);
3491 lang_utf8
= lowercase_lang
;
3492 lowercase_lang
= NULL
;
3493 string_key
= subblock
+ 25;
3496 if (!strcmp (subblock_utf8
, "\\")) {
3497 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3498 if (data_ptr
!= NULL
) {
3499 *buffer
= (gpointer
)data_ptr
;
3500 *len
= block
.value_len
;
3504 } else if (want_var
|| want_string
) {
3505 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3506 if (data_ptr
!= NULL
) {
3507 /* The FFI and header occupies the first 92
3510 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3511 data_len
= block
.data_len
- 92;
3513 /* There now follow zero or one StringFileInfo
3514 * blocks and zero or one VarFileInfo blocks
3516 while (data_len
> 0) {
3517 /* align on a 32-bit boundary */
3520 data_ptr
= get_versioninfo_block (data_ptr
,
3522 if (block
.data_len
== 0) {
3523 /* We must have hit padding,
3524 * so give up processing now
3526 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3530 data_len
= data_len
- block
.data_len
;
3532 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3533 data_ptr
= get_varfileinfo_block (data_ptr
, &block
);
3535 *buffer
= (gpointer
)data_ptr
;
3536 *len
= block
.value_len
;
3540 /* Skip over the Var block */
3541 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3543 } else if (unicode_string_equals (block
.key
, "StringFileInfo")) {
3544 data_ptr
= get_stringtable_block (data_ptr
, lang_utf8
, string_key
, &string_value
, &string_value_len
, &block
);
3546 string_value
!= NULL
&&
3547 string_value_len
!= 0) {
3548 *buffer
= string_value
;
3549 *len
= unicode_chars ((const gunichar2
*)string_value
) + 1; /* Include trailing null */
3555 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Not a valid VERSIONINFO child block", __func__
);
3559 if (data_ptr
== NULL
) {
3560 /* Child block hit padding */
3561 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3573 g_free (subblock_utf8
);
3578 copy_lang (gunichar2
*lang_out
, guint32 lang_len
, const gchar
*text
)
3581 int chars
= strlen (text
);
3584 unitext
= g_utf8_to_utf16 (text
, -1, NULL
, NULL
, NULL
);
3585 g_assert (unitext
!= NULL
);
3587 if (chars
< (lang_len
- 1)) {
3588 memcpy (lang_out
, (gpointer
)unitext
, chars
* 2);
3589 lang_out
[chars
] = '\0';
3592 memcpy (lang_out
, (gpointer
)unitext
, (lang_len
- 1) * 2);
3593 lang_out
[lang_len
] = '\0';
3603 mono_w32process_ver_language_name (guint32 lang
, gunichar2
*lang_out
, guint32 lang_len
)
3605 int primary
, secondary
;
3606 const char *name
= NULL
;
3608 primary
= lang
& 0x3FF;
3609 secondary
= (lang
>> 10) & 0x3F;
3613 switch (secondary
) {
3614 case 0x01: name
= "Process Default Language"; break;
3618 switch (secondary
) {
3620 case 0x01: name
= "Arabic (Saudi Arabia)"; break;
3621 case 0x02: name
= "Arabic (Iraq)"; break;
3622 case 0x03: name
= "Arabic (Egypt)"; break;
3623 case 0x04: name
= "Arabic (Libya)"; break;
3624 case 0x05: name
= "Arabic (Algeria)"; break;
3625 case 0x06: name
= "Arabic (Morocco)"; break;
3626 case 0x07: name
= "Arabic (Tunisia)"; break;
3627 case 0x08: name
= "Arabic (Oman)"; break;
3628 case 0x09: name
= "Arabic (Yemen)"; break;
3629 case 0x0a: name
= "Arabic (Syria)"; break;
3630 case 0x0b: name
= "Arabic (Jordan)"; break;
3631 case 0x0c: name
= "Arabic (Lebanon)"; break;
3632 case 0x0d: name
= "Arabic (Kuwait)"; break;
3633 case 0x0e: name
= "Arabic (U.A.E.)"; break;
3634 case 0x0f: name
= "Arabic (Bahrain)"; break;
3635 case 0x10: name
= "Arabic (Qatar)"; break;
3639 switch (secondary
) {
3640 case 0x00: name
= "Bulgarian (Bulgaria)"; break;
3641 case 0x01: name
= "Bulgarian"; break;
3645 switch (secondary
) {
3646 case 0x00: name
= "Catalan (Spain)"; break;
3647 case 0x01: name
= "Catalan"; break;
3651 switch (secondary
) {
3653 case 0x01: name
= "Chinese (Taiwan)"; break;
3654 case 0x02: name
= "Chinese (PRC)"; break;
3655 case 0x03: name
= "Chinese (Hong Kong S.A.R.)"; break;
3656 case 0x04: name
= "Chinese (Singapore)"; break;
3657 case 0x05: name
= "Chinese (Macau S.A.R.)"; break;
3661 switch (secondary
) {
3662 case 0x00: name
= "Czech (Czech Republic)"; break;
3663 case 0x01: name
= "Czech"; break;
3667 switch (secondary
) {
3668 case 0x00: name
= "Danish (Denmark)"; break;
3669 case 0x01: name
= "Danish"; break;
3673 switch (secondary
) {
3675 case 0x01: name
= "German (Germany)"; break;
3676 case 0x02: name
= "German (Switzerland)"; break;
3677 case 0x03: name
= "German (Austria)"; break;
3678 case 0x04: name
= "German (Luxembourg)"; break;
3679 case 0x05: name
= "German (Liechtenstein)"; break;
3683 switch (secondary
) {
3684 case 0x00: name
= "Greek (Greece)"; break;
3685 case 0x01: name
= "Greek"; break;
3689 switch (secondary
) {
3691 case 0x01: name
= "English (United States)"; break;
3692 case 0x02: name
= "English (United Kingdom)"; break;
3693 case 0x03: name
= "English (Australia)"; break;
3694 case 0x04: name
= "English (Canada)"; break;
3695 case 0x05: name
= "English (New Zealand)"; break;
3696 case 0x06: name
= "English (Ireland)"; break;
3697 case 0x07: name
= "English (South Africa)"; break;
3698 case 0x08: name
= "English (Jamaica)"; break;
3699 case 0x09: name
= "English (Caribbean)"; break;
3700 case 0x0a: name
= "English (Belize)"; break;
3701 case 0x0b: name
= "English (Trinidad and Tobago)"; break;
3702 case 0x0c: name
= "English (Zimbabwe)"; break;
3703 case 0x0d: name
= "English (Philippines)"; break;
3704 case 0x10: name
= "English (India)"; break;
3705 case 0x11: name
= "English (Malaysia)"; break;
3706 case 0x12: name
= "English (Singapore)"; break;
3710 switch (secondary
) {
3711 case 0x00: name
= "Spanish (Spain)"; break;
3712 case 0x01: name
= "Spanish (Traditional Sort)"; break;
3713 case 0x02: name
= "Spanish (Mexico)"; break;
3714 case 0x03: name
= "Spanish (International Sort)"; break;
3715 case 0x04: name
= "Spanish (Guatemala)"; break;
3716 case 0x05: name
= "Spanish (Costa Rica)"; break;
3717 case 0x06: name
= "Spanish (Panama)"; break;
3718 case 0x07: name
= "Spanish (Dominican Republic)"; break;
3719 case 0x08: name
= "Spanish (Venezuela)"; break;
3720 case 0x09: name
= "Spanish (Colombia)"; break;
3721 case 0x0a: name
= "Spanish (Peru)"; break;
3722 case 0x0b: name
= "Spanish (Argentina)"; break;
3723 case 0x0c: name
= "Spanish (Ecuador)"; break;
3724 case 0x0d: name
= "Spanish (Chile)"; break;
3725 case 0x0e: name
= "Spanish (Uruguay)"; break;
3726 case 0x0f: name
= "Spanish (Paraguay)"; break;
3727 case 0x10: name
= "Spanish (Bolivia)"; break;
3728 case 0x11: name
= "Spanish (El Salvador)"; break;
3729 case 0x12: name
= "Spanish (Honduras)"; break;
3730 case 0x13: name
= "Spanish (Nicaragua)"; break;
3731 case 0x14: name
= "Spanish (Puerto Rico)"; break;
3732 case 0x15: name
= "Spanish (United States)"; break;
3736 switch (secondary
) {
3737 case 0x00: name
= "Finnish (Finland)"; break;
3738 case 0x01: name
= "Finnish"; break;
3742 switch (secondary
) {
3744 case 0x01: name
= "French (France)"; break;
3745 case 0x02: name
= "French (Belgium)"; break;
3746 case 0x03: name
= "French (Canada)"; break;
3747 case 0x04: name
= "French (Switzerland)"; break;
3748 case 0x05: name
= "French (Luxembourg)"; break;
3749 case 0x06: name
= "French (Monaco)"; break;
3753 switch (secondary
) {
3754 case 0x00: name
= "Hebrew (Israel)"; break;
3755 case 0x01: name
= "Hebrew"; break;
3759 switch (secondary
) {
3760 case 0x00: name
= "Hungarian (Hungary)"; break;
3761 case 0x01: name
= "Hungarian"; break;
3765 switch (secondary
) {
3766 case 0x00: name
= "Icelandic (Iceland)"; break;
3767 case 0x01: name
= "Icelandic"; break;
3771 switch (secondary
) {
3773 case 0x01: name
= "Italian (Italy)"; break;
3774 case 0x02: name
= "Italian (Switzerland)"; break;
3778 switch (secondary
) {
3779 case 0x00: name
= "Japanese (Japan)"; break;
3780 case 0x01: name
= "Japanese"; break;
3784 switch (secondary
) {
3785 case 0x00: name
= "Korean (Korea)"; break;
3786 case 0x01: name
= "Korean"; break;
3790 switch (secondary
) {
3792 case 0x01: name
= "Dutch (Netherlands)"; break;
3793 case 0x02: name
= "Dutch (Belgium)"; break;
3797 switch (secondary
) {
3799 case 0x01: name
= "Norwegian (Bokmal)"; break;
3800 case 0x02: name
= "Norwegian (Nynorsk)"; break;
3804 switch (secondary
) {
3805 case 0x00: name
= "Polish (Poland)"; break;
3806 case 0x01: name
= "Polish"; break;
3810 switch (secondary
) {
3812 case 0x01: name
= "Portuguese (Brazil)"; break;
3813 case 0x02: name
= "Portuguese (Portugal)"; break;
3817 switch (secondary
) {
3818 case 0x01: name
= "Romansh (Switzerland)"; break;
3822 switch (secondary
) {
3823 case 0x00: name
= "Romanian (Romania)"; break;
3824 case 0x01: name
= "Romanian"; break;
3828 switch (secondary
) {
3829 case 0x00: name
= "Russian (Russia)"; break;
3830 case 0x01: name
= "Russian"; break;
3834 switch (secondary
) {
3835 case 0x00: name
= "Croatian (Croatia)"; break;
3836 case 0x01: name
= "Croatian"; break;
3837 case 0x02: name
= "Serbian (Latin)"; break;
3838 case 0x03: name
= "Serbian (Cyrillic)"; break;
3839 case 0x04: name
= "Croatian (Bosnia and Herzegovina)"; break;
3840 case 0x05: name
= "Bosnian (Latin, Bosnia and Herzegovina)"; break;
3841 case 0x06: name
= "Serbian (Latin, Bosnia and Herzegovina)"; break;
3842 case 0x07: name
= "Serbian (Cyrillic, Bosnia and Herzegovina)"; break;
3843 case 0x08: name
= "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break;
3847 switch (secondary
) {
3848 case 0x00: name
= "Slovak (Slovakia)"; break;
3849 case 0x01: name
= "Slovak"; break;
3853 switch (secondary
) {
3854 case 0x00: name
= "Albanian (Albania)"; break;
3855 case 0x01: name
= "Albanian"; break;
3859 switch (secondary
) {
3860 case 0x00: name
= "Swedish (Sweden)"; break;
3861 case 0x01: name
= "Swedish"; break;
3862 case 0x02: name
= "Swedish (Finland)"; break;
3866 switch (secondary
) {
3867 case 0x00: name
= "Thai (Thailand)"; break;
3868 case 0x01: name
= "Thai"; break;
3872 switch (secondary
) {
3873 case 0x00: name
= "Turkish (Turkey)"; break;
3874 case 0x01: name
= "Turkish"; break;
3878 switch (secondary
) {
3879 case 0x00: name
= "Urdu (Islamic Republic of Pakistan)"; break;
3880 case 0x01: name
= "Urdu"; break;
3884 switch (secondary
) {
3885 case 0x00: name
= "Indonesian (Indonesia)"; break;
3886 case 0x01: name
= "Indonesian"; break;
3890 switch (secondary
) {
3891 case 0x00: name
= "Ukrainian (Ukraine)"; break;
3892 case 0x01: name
= "Ukrainian"; break;
3896 switch (secondary
) {
3897 case 0x00: name
= "Belarusian (Belarus)"; break;
3898 case 0x01: name
= "Belarusian"; break;
3902 switch (secondary
) {
3903 case 0x00: name
= "Slovenian (Slovenia)"; break;
3904 case 0x01: name
= "Slovenian"; break;
3908 switch (secondary
) {
3909 case 0x00: name
= "Estonian (Estonia)"; break;
3910 case 0x01: name
= "Estonian"; break;
3914 switch (secondary
) {
3915 case 0x00: name
= "Latvian (Latvia)"; break;
3916 case 0x01: name
= "Latvian"; break;
3920 switch (secondary
) {
3921 case 0x00: name
= "Lithuanian (Lithuania)"; break;
3922 case 0x01: name
= "Lithuanian"; break;
3926 switch (secondary
) {
3927 case 0x01: name
= "Tajik (Tajikistan)"; break;
3931 switch (secondary
) {
3932 case 0x00: name
= "Farsi (Iran)"; break;
3933 case 0x01: name
= "Farsi"; break;
3937 switch (secondary
) {
3938 case 0x00: name
= "Vietnamese (Viet Nam)"; break;
3939 case 0x01: name
= "Vietnamese"; break;
3943 switch (secondary
) {
3944 case 0x00: name
= "Armenian (Armenia)"; break;
3945 case 0x01: name
= "Armenian"; break;
3949 switch (secondary
) {
3950 case 0x00: name
= "Azeri (Latin) (Azerbaijan)"; break;
3951 case 0x01: name
= "Azeri (Latin)"; break;
3952 case 0x02: name
= "Azeri (Cyrillic)"; break;
3956 switch (secondary
) {
3957 case 0x00: name
= "Basque (Spain)"; break;
3958 case 0x01: name
= "Basque"; break;
3962 switch (secondary
) {
3963 case 0x01: name
= "Upper Sorbian (Germany)"; break;
3964 case 0x02: name
= "Lower Sorbian (Germany)"; break;
3968 switch (secondary
) {
3969 case 0x00: name
= "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
3970 case 0x01: name
= "FYRO Macedonian"; break;
3974 switch (secondary
) {
3975 case 0x00: name
= "Tswana (South Africa)"; break;
3976 case 0x01: name
= "Tswana"; break;
3980 switch (secondary
) {
3981 case 0x00: name
= "Xhosa (South Africa)"; break;
3982 case 0x01: name
= "Xhosa"; break;
3986 switch (secondary
) {
3987 case 0x00: name
= "Zulu (South Africa)"; break;
3988 case 0x01: name
= "Zulu"; break;
3992 switch (secondary
) {
3993 case 0x00: name
= "Afrikaans (South Africa)"; break;
3994 case 0x01: name
= "Afrikaans"; break;
3998 switch (secondary
) {
3999 case 0x00: name
= "Georgian (Georgia)"; break;
4000 case 0x01: name
= "Georgian"; break;
4004 switch (secondary
) {
4005 case 0x00: name
= "Faroese (Faroe Islands)"; break;
4006 case 0x01: name
= "Faroese"; break;
4010 switch (secondary
) {
4011 case 0x00: name
= "Hindi (India)"; break;
4012 case 0x01: name
= "Hindi"; break;
4016 switch (secondary
) {
4017 case 0x00: name
= "Maltese (Malta)"; break;
4018 case 0x01: name
= "Maltese"; break;
4022 switch (secondary
) {
4023 case 0x00: name
= "Sami (Northern) (Norway)"; break;
4024 case 0x01: name
= "Sami, Northern (Norway)"; break;
4025 case 0x02: name
= "Sami, Northern (Sweden)"; break;
4026 case 0x03: name
= "Sami, Northern (Finland)"; break;
4027 case 0x04: name
= "Sami, Lule (Norway)"; break;
4028 case 0x05: name
= "Sami, Lule (Sweden)"; break;
4029 case 0x06: name
= "Sami, Southern (Norway)"; break;
4030 case 0x07: name
= "Sami, Southern (Sweden)"; break;
4031 case 0x08: name
= "Sami, Skolt (Finland)"; break;
4032 case 0x09: name
= "Sami, Inari (Finland)"; break;
4036 switch (secondary
) {
4037 case 0x02: name
= "Irish (Ireland)"; break;
4041 switch (secondary
) {
4043 case 0x01: name
= "Malay (Malaysia)"; break;
4044 case 0x02: name
= "Malay (Brunei Darussalam)"; break;
4048 switch (secondary
) {
4049 case 0x00: name
= "Kazakh (Kazakhstan)"; break;
4050 case 0x01: name
= "Kazakh"; break;
4054 switch (secondary
) {
4055 case 0x00: name
= "Kyrgyz (Kyrgyzstan)"; break;
4056 case 0x01: name
= "Kyrgyz (Cyrillic)"; break;
4060 switch (secondary
) {
4061 case 0x00: name
= "Swahili (Kenya)"; break;
4062 case 0x01: name
= "Swahili"; break;
4066 switch (secondary
) {
4067 case 0x01: name
= "Turkmen (Turkmenistan)"; break;
4071 switch (secondary
) {
4072 case 0x00: name
= "Uzbek (Latin) (Uzbekistan)"; break;
4073 case 0x01: name
= "Uzbek (Latin)"; break;
4074 case 0x02: name
= "Uzbek (Cyrillic)"; break;
4078 switch (secondary
) {
4079 case 0x00: name
= "Tatar (Russia)"; break;
4080 case 0x01: name
= "Tatar"; break;
4084 switch (secondary
) {
4086 case 0x01: name
= "Bengali (India)"; break;
4090 switch (secondary
) {
4091 case 0x00: name
= "Punjabi (India)"; break;
4092 case 0x01: name
= "Punjabi"; break;
4096 switch (secondary
) {
4097 case 0x00: name
= "Gujarati (India)"; break;
4098 case 0x01: name
= "Gujarati"; break;
4102 switch (secondary
) {
4103 case 0x00: name
= "Tamil (India)"; break;
4104 case 0x01: name
= "Tamil"; break;
4108 switch (secondary
) {
4109 case 0x00: name
= "Telugu (India)"; break;
4110 case 0x01: name
= "Telugu"; break;
4114 switch (secondary
) {
4115 case 0x00: name
= "Kannada (India)"; break;
4116 case 0x01: name
= "Kannada"; break;
4120 switch (secondary
) {
4122 case 0x01: name
= "Malayalam (India)"; break;
4126 switch (secondary
) {
4127 case 0x01: name
= "Assamese (India)"; break;
4131 switch (secondary
) {
4132 case 0x00: name
= "Marathi (India)"; break;
4133 case 0x01: name
= "Marathi"; break;
4137 switch (secondary
) {
4138 case 0x00: name
= "Sanskrit (India)"; break;
4139 case 0x01: name
= "Sanskrit"; break;
4143 switch (secondary
) {
4144 case 0x00: name
= "Mongolian (Mongolia)"; break;
4145 case 0x01: name
= "Mongolian (Cyrillic)"; break;
4146 case 0x02: name
= "Mongolian (PRC)"; break;
4150 switch (secondary
) {
4151 case 0x01: name
= "Tibetan (PRC)"; break;
4152 case 0x02: name
= "Tibetan (Bhutan)"; break;
4156 switch (secondary
) {
4157 case 0x00: name
= "Welsh (United Kingdom)"; break;
4158 case 0x01: name
= "Welsh"; break;
4162 switch (secondary
) {
4163 case 0x01: name
= "Khmer (Cambodia)"; break;
4167 switch (secondary
) {
4168 case 0x01: name
= "Lao (Lao PDR)"; break;
4172 switch (secondary
) {
4173 case 0x00: name
= "Galician (Spain)"; break;
4174 case 0x01: name
= "Galician"; break;
4178 switch (secondary
) {
4179 case 0x00: name
= "Konkani (India)"; break;
4180 case 0x01: name
= "Konkani"; break;
4184 switch (secondary
) {
4185 case 0x00: name
= "Syriac (Syria)"; break;
4186 case 0x01: name
= "Syriac"; break;
4190 switch (secondary
) {
4191 case 0x01: name
= "Sinhala (Sri Lanka)"; break;
4195 switch (secondary
) {
4196 case 0x01: name
= "Inuktitut (Syllabics, Canada)"; break;
4197 case 0x02: name
= "Inuktitut (Latin, Canada)"; break;
4201 switch (secondary
) {
4202 case 0x01: name
= "Amharic (Ethiopia)"; break;
4206 switch (secondary
) {
4207 case 0x02: name
= "Tamazight (Algeria, Latin)"; break;
4211 switch (secondary
) {
4212 case 0x01: name
= "Nepali (Nepal)"; break;
4216 switch (secondary
) {
4217 case 0x01: name
= "Frisian (Netherlands)"; break;
4221 switch (secondary
) {
4222 case 0x01: name
= "Pashto (Afghanistan)"; break;
4226 switch (secondary
) {
4227 case 0x01: name
= "Filipino (Philippines)"; break;
4231 switch (secondary
) {
4232 case 0x00: name
= "Divehi (Maldives)"; break;
4233 case 0x01: name
= "Divehi"; break;
4237 switch (secondary
) {
4238 case 0x01: name
= "Hausa (Nigeria, Latin)"; break;
4242 switch (secondary
) {
4243 case 0x01: name
= "Yoruba (Nigeria)"; break;
4247 switch (secondary
) {
4249 case 0x01: name
= "Quechua (Bolivia)"; break;
4250 case 0x02: name
= "Quechua (Ecuador)"; break;
4251 case 0x03: name
= "Quechua (Peru)"; break;
4255 switch (secondary
) {
4256 case 0x00: name
= "Northern Sotho (South Africa)"; break;
4257 case 0x01: name
= "Northern Sotho"; break;
4261 switch (secondary
) {
4262 case 0x01: name
= "Bashkir (Russia)"; break;
4266 switch (secondary
) {
4267 case 0x01: name
= "Luxembourgish (Luxembourg)"; break;
4271 switch (secondary
) {
4272 case 0x01: name
= "Greenlandic (Greenland)"; break;
4276 switch (secondary
) {
4277 case 0x01: name
= "Yi (PRC)"; break;
4281 switch (secondary
) {
4282 case 0x01: name
= "Mapudungun (Chile)"; break;
4286 switch (secondary
) {
4287 case 0x01: name
= "Mohawk (Mohawk)"; break;
4291 switch (secondary
) {
4292 case 0x01: name
= "Breton (France)"; break;
4296 switch (secondary
) {
4297 case 0x00: name
= "Invariant Language (Invariant Country)"; break;
4301 switch (secondary
) {
4302 case 0x01: name
= "Uighur (PRC)"; break;
4306 switch (secondary
) {
4307 case 0x00: name
= "Maori (New Zealand)"; break;
4308 case 0x01: name
= "Maori"; break;
4312 switch (secondary
) {
4313 case 0x01: name
= "Corsican (France)"; break;
4317 switch (secondary
) {
4318 case 0x01: name
= "Alsatian (France)"; break;
4322 switch (secondary
) {
4323 case 0x01: name
= "Yakut (Russia)"; break;
4327 switch (secondary
) {
4328 case 0x01: name
= "K'iche (Guatemala)"; break;
4332 switch (secondary
) {
4333 case 0x01: name
= "Kinyarwanda (Rwanda)"; break;
4337 switch (secondary
) {
4338 case 0x01: name
= "Wolof (Senegal)"; break;
4342 switch (secondary
) {
4343 case 0x01: name
= "Dari (Afghanistan)"; break;
4348 name
= "Language Neutral";
4353 name
= "Language Neutral";
4355 return copy_lang (lang_out
, lang_len
, name
);