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. It's accessed in our SIGCHLD handler,
134 * when status is updated (and pid cleared, to not clash with
135 * subsequent processes that may get executed).
137 typedef struct _Process
{
138 pid_t pid
; /* the pid of the process. This value is only valid until the process has exited. */
139 MonoSemType exit_sem
; /* this semaphore will be released when the process exits */
140 int status
; /* the exit status */
141 gint32 handle_count
; /* the number of handles to this process instance */
142 /* we keep a ref to the creating _WapiHandle_process handle until
143 * the process has exited, so that the information there isn't lost.
148 struct _Process
*next
;
151 /* MonoW32HandleProcess is a structure containing all the required information for process handling. */
156 gpointer main_thread
;
160 size_t min_working_set
;
161 size_t max_working_set
;
164 } MonoW32HandleProcess
;
169 * 2 bytes: Length in bytes (this block, and all child blocks. does _not_ include alignment padding between blocks)
170 * 2 bytes: Length in bytes of VS_FIXEDFILEINFO struct
171 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
172 * Variable length unicode string (null terminated): Key (currently "VS_VERSION_INFO")
173 * Variable length padding to align VS_FIXEDFILEINFO on a 32-bit boundary
174 * VS_FIXEDFILEINFO struct
175 * Variable length padding to align Child struct on a 32-bit boundary
176 * Child struct (zero or one StringFileInfo structs, zero or one VarFileInfo structs)
182 * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
183 * 2 bytes: Value length (always zero)
184 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
185 * Variable length unicode string: Key (currently "StringFileInfo")
186 * Variable length padding to align Child struct on a 32-bit boundary
187 * 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.)
193 * 2 bytes: Length in bytes (includes this block as well as all Child blocks, but excludes any padding between String blocks)
194 * 2 bytes: Value length (always zero)
195 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
196 * 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.
197 * Variable length padding to align Child struct on a 32-bit boundary
198 * Child structs (an array of one or more String structs (each aligned on a 32-bit boundary)
204 * 2 bytes: Length in bytes (of this block)
205 * 2 bytes: Value length (the length in words of the Value member)
206 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
207 * Variable length unicode string: Key. arbitrary string, identifies data.
208 * Variable length padding to align Value on a 32-bit boundary
209 * Value: Variable length unicode string, holding data.
215 * 2 bytes: Length in bytes (includes this block, as well as all Child blocks)
216 * 2 bytes: Value length (always zero)
217 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
218 * Variable length unicode string: Key (currently "VarFileInfo")
219 * Variable length padding to align Child struct on a 32-bit boundary
220 * Child structs (a Var struct)
226 * 2 bytes: Length in bytes of this block
227 * 2 bytes: Value length in bytes of the Value
228 * 2 bytes: Type (contains 1 if version resource contains text data and 0 if version resource contains binary data)
229 * Variable length unicode string: Key ("Translation")
230 * Variable length padding to align Value on a 32-bit boundary
231 * 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.
234 #if G_BYTE_ORDER == G_BIG_ENDIAN
235 #define VS_FFI_SIGNATURE 0xbd04effe
236 #define VS_FFI_STRUCVERSION 0x00000100
238 #define VS_FFI_SIGNATURE 0xfeef04bd
239 #define VS_FFI_STRUCVERSION 0x00010000
242 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
244 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0
245 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1
246 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
248 #define IMAGE_SIZEOF_SHORT_NAME 8
250 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
251 #define IMAGE_DOS_SIGNATURE 0x4d5a
252 #define IMAGE_NT_SIGNATURE 0x50450000
253 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0xb10
254 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0xb20
256 #define IMAGE_DOS_SIGNATURE 0x5a4d
257 #define IMAGE_NT_SIGNATURE 0x00004550
258 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
259 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
286 guint16 NumberOfSections
;
287 guint32 TimeDateStamp
;
288 guint32 PointerToSymbolTable
;
289 guint32 NumberOfSymbols
;
290 guint16 SizeOfOptionalHeader
;
291 guint16 Characteristics
;
295 guint32 VirtualAddress
;
297 } IMAGE_DATA_DIRECTORY
;
301 guint8 MajorLinkerVersion
;
302 guint8 MinorLinkerVersion
;
304 guint32 SizeOfInitializedData
;
305 guint32 SizeOfUninitializedData
;
306 guint32 AddressOfEntryPoint
;
310 guint32 SectionAlignment
;
311 guint32 FileAlignment
;
312 guint16 MajorOperatingSystemVersion
;
313 guint16 MinorOperatingSystemVersion
;
314 guint16 MajorImageVersion
;
315 guint16 MinorImageVersion
;
316 guint16 MajorSubsystemVersion
;
317 guint16 MinorSubsystemVersion
;
318 guint32 Win32VersionValue
;
320 guint32 SizeOfHeaders
;
323 guint16 DllCharacteristics
;
324 guint32 SizeOfStackReserve
;
325 guint32 SizeOfStackCommit
;
326 guint32 SizeOfHeapReserve
;
327 guint32 SizeOfHeapCommit
;
329 guint32 NumberOfRvaAndSizes
;
330 IMAGE_DATA_DIRECTORY DataDirectory
[IMAGE_NUMBEROF_DIRECTORY_ENTRIES
];
331 } IMAGE_OPTIONAL_HEADER32
;
335 guint8 MajorLinkerVersion
;
336 guint8 MinorLinkerVersion
;
338 guint32 SizeOfInitializedData
;
339 guint32 SizeOfUninitializedData
;
340 guint32 AddressOfEntryPoint
;
343 guint32 SectionAlignment
;
344 guint32 FileAlignment
;
345 guint16 MajorOperatingSystemVersion
;
346 guint16 MinorOperatingSystemVersion
;
347 guint16 MajorImageVersion
;
348 guint16 MinorImageVersion
;
349 guint16 MajorSubsystemVersion
;
350 guint16 MinorSubsystemVersion
;
351 guint32 Win32VersionValue
;
353 guint32 SizeOfHeaders
;
356 guint16 DllCharacteristics
;
357 guint64 SizeOfStackReserve
;
358 guint64 SizeOfStackCommit
;
359 guint64 SizeOfHeapReserve
;
360 guint64 SizeOfHeapCommit
;
362 guint32 NumberOfRvaAndSizes
;
363 IMAGE_DATA_DIRECTORY DataDirectory
[IMAGE_NUMBEROF_DIRECTORY_ENTRIES
];
364 } IMAGE_OPTIONAL_HEADER64
;
366 #if SIZEOF_VOID_P == 8
367 typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER
;
369 typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER
;
374 IMAGE_FILE_HEADER FileHeader
;
375 IMAGE_OPTIONAL_HEADER32 OptionalHeader
;
376 } IMAGE_NT_HEADERS32
;
380 IMAGE_FILE_HEADER FileHeader
;
381 IMAGE_OPTIONAL_HEADER64 OptionalHeader
;
382 } IMAGE_NT_HEADERS64
;
384 #if SIZEOF_VOID_P == 8
385 typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS
;
387 typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS
;
391 guint8 Name
[IMAGE_SIZEOF_SHORT_NAME
];
393 guint32 PhysicalAddress
;
396 guint32 VirtualAddress
;
397 guint32 SizeOfRawData
;
398 guint32 PointerToRawData
;
399 guint32 PointerToRelocations
;
400 guint32 PointerToLinenumbers
;
401 guint16 NumberOfRelocations
;
402 guint16 NumberOfLinenumbers
;
403 guint32 Characteristics
;
404 } IMAGE_SECTION_HEADER
;
406 #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)))
408 #define RT_CURSOR 0x01
409 #define RT_BITMAP 0x02
412 #define RT_DIALOG 0x05
413 #define RT_STRING 0x06
414 #define RT_FONTDIR 0x07
416 #define RT_ACCELERATOR 0x09
417 #define RT_RCDATA 0x0a
418 #define RT_MESSAGETABLE 0x0b
419 #define RT_GROUP_CURSOR 0x0c
420 #define RT_GROUP_ICON 0x0e
421 #define RT_VERSION 0x10
422 #define RT_DLGINCLUDE 0x11
423 #define RT_PLUGPLAY 0x13
425 #define RT_ANICURSOR 0x15
426 #define RT_ANIICON 0x16
428 #define RT_MANIFEST 0x18
431 guint32 Characteristics
;
432 guint32 TimeDateStamp
;
433 guint16 MajorVersion
;
434 guint16 MinorVersion
;
435 guint16 NumberOfNamedEntries
;
436 guint16 NumberOfIdEntries
;
437 } IMAGE_RESOURCE_DIRECTORY
;
442 #if G_BYTE_ORDER == G_BIG_ENDIAN
443 guint32 NameIsString
:1;
444 guint32 NameOffset
:31;
446 guint32 NameOffset
:31;
447 guint32 NameIsString
:1;
451 #if G_BYTE_ORDER == G_BIG_ENDIAN
453 guint16 __wapi_big_endian_padding
;
461 guint32 OffsetToData
;
463 #if G_BYTE_ORDER == G_BIG_ENDIAN
464 guint32 DataIsDirectory
:1;
465 guint32 OffsetToDirectory
:31;
467 guint32 OffsetToDirectory
:31;
468 guint32 DataIsDirectory
:1;
472 } IMAGE_RESOURCE_DIRECTORY_ENTRY
;
475 guint32 OffsetToData
;
479 } IMAGE_RESOURCE_DATA_ENTRY
;
481 #define VOS_UNKNOWN 0x00000000
482 #define VOS_DOS 0x00010000
483 #define VOS_OS216 0x00020000
484 #define VOS_OS232 0x00030000
485 #define VOS_NT 0x00040000
486 #define VOS__BASE 0x00000000
487 #define VOS__WINDOWS16 0x00000001
488 #define VOS__PM16 0x00000002
489 #define VOS__PM32 0x00000003
490 #define VOS__WINDOWS32 0x00000004
491 /* Should "embrace and extend" here with some entries for linux etc */
493 #define VOS_DOS_WINDOWS16 0x00010001
494 #define VOS_DOS_WINDOWS32 0x00010004
495 #define VOS_OS216_PM16 0x00020002
496 #define VOS_OS232_PM32 0x00030003
497 #define VOS_NT_WINDOWS32 0x00040004
499 #define VFT_UNKNOWN 0x0000
500 #define VFT_APP 0x0001
501 #define VFT_DLL 0x0002
502 #define VFT_DRV 0x0003
503 #define VFT_FONT 0x0004
504 #define VFT_VXD 0x0005
505 #define VFT_STATIC_LIB 0x0007
507 #define VFT2_UNKNOWN 0x0000
508 #define VFT2_DRV_PRINTER 0x0001
509 #define VFT2_DRV_KEYBOARD 0x0002
510 #define VFT2_DRV_LANGUAGE 0x0003
511 #define VFT2_DRV_DISPLAY 0x0004
512 #define VFT2_DRV_MOUSE 0x0005
513 #define VFT2_DRV_NETWORK 0x0006
514 #define VFT2_DRV_SYSTEM 0x0007
515 #define VFT2_DRV_INSTALLABLE 0x0008
516 #define VFT2_DRV_SOUND 0x0009
517 #define VFT2_DRV_COMM 0x000a
518 #define VFT2_DRV_INPUTMETHOD 0x000b
519 #define VFT2_FONT_RASTER 0x0001
520 #define VFT2_FONT_VECTOR 0x0002
521 #define VFT2_FONT_TRUETYPE 0x0003
523 #define MAKELANGID(primary,secondary) ((guint16)((secondary << 10) | (primary)))
525 #define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
528 static mono_lazy_init_t process_sig_chld_once
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
531 static gchar
*cli_launcher
;
533 /* The signal-safe logic to use processes goes like this:
534 * - The list must be safe to traverse for the signal handler at all times.
535 * It's safe to: prepend an entry (which is a single store to 'processes'),
536 * unlink an entry (assuming the unlinked entry isn't freed and doesn't
537 * change its 'next' pointer so that it can still be traversed).
538 * When cleaning up we first unlink an entry, then we verify that
539 * the read lock isn't locked. Then we can free the entry, since
540 * we know that nobody is using the old version of the list (including
541 * the unlinked entry).
542 * We also need to lock when adding and cleaning up so that those two
543 * operations don't mess with eachother. (This lock is not used in the
545 static Process
*processes
;
546 static mono_mutex_t processes_mutex
;
548 static pid_t current_pid
;
549 static gpointer current_process
;
551 static const gunichar2 utf16_space_bytes
[2] = { 0x20, 0 };
552 static const gunichar2
*utf16_space
= utf16_space_bytes
;
553 static const gunichar2 utf16_quote_bytes
[2] = { 0x22, 0 };
554 static const gunichar2
*utf16_quote
= utf16_quote_bytes
;
556 /* Check if a pid is valid - i.e. if a process exists with this pid. */
558 process_is_alive (pid_t pid
)
560 #if defined(HOST_WATCHOS)
561 return TRUE
; // TODO: Rewrite using sysctl
562 #elif defined(PLATFORM_MACOSX) || defined(__OpenBSD__) || defined(__FreeBSD__)
565 if (kill (pid
, 0) == 0)
570 #elif defined(__HAIKU__)
572 if (get_team_info ((team_id
)pid
, &teamInfo
) == B_OK
)
576 gchar
*dir
= g_strdup_printf ("/proc/%d", pid
);
577 gboolean result
= access (dir
, F_OK
) == 0;
584 process_details (gpointer data
)
586 MonoW32HandleProcess
*process_handle
= (MonoW32HandleProcess
*) data
;
587 g_print ("pid: %d, exited: %s, exitstatus: %d",
588 process_handle
->pid
, process_handle
->exited
? "true" : "false", process_handle
->exitstatus
);
592 process_typename (void)
598 process_typesize (void)
600 return sizeof (MonoW32HandleProcess
);
603 static MonoW32HandleWaitRet
604 process_wait (gpointer handle
, guint32 timeout
, gboolean
*alerted
)
606 MonoW32HandleProcess
*process_handle
;
607 pid_t pid G_GNUC_UNUSED
, ret
;
613 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u)", __func__
, handle
, timeout
);
618 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
620 g_warning ("%s: error looking up process handle %p", __func__
, handle
);
621 return MONO_W32HANDLE_WAIT_RET_FAILED
;
624 if (process_handle
->exited
) {
625 /* We've already done this one */
626 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Process already exited", __func__
, handle
, timeout
);
627 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
630 pid
= process_handle
->pid
;
632 if (pid
== mono_process_current_pid ()) {
633 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on current process", __func__
, handle
, timeout
);
634 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
637 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): PID: %d", __func__
, handle
, timeout
, pid
);
639 if (!process_handle
->child
) {
640 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on non-child process", __func__
, handle
, timeout
);
642 if (!process_is_alive (pid
)) {
643 /* assume the process has exited */
644 process_handle
->exited
= TRUE
;
645 process_handle
->exitstatus
= -1;
646 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
648 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): non-child process is not alive anymore (2)", __func__
, handle
, timeout
);
649 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
652 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
);
653 return MONO_W32HANDLE_WAIT_RET_FAILED
;
656 /* We don't need to lock processes here, the entry
657 * has a handle_count > 0 which means it will not be freed. */
658 process
= process_handle
->process
;
661 start
= mono_msec_ticks ();
665 if (timeout
!= MONO_INFINITE_WAIT
) {
666 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on semaphore for %li ms...",
667 __func__
, handle
, timeout
, (long)(timeout
- (now
- start
)));
668 ret
= mono_os_sem_timedwait (&process
->exit_sem
, (timeout
- (now
- start
)), alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
670 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): waiting on semaphore forever...",
671 __func__
, handle
, timeout
);
672 ret
= mono_os_sem_wait (&process
->exit_sem
, alerted
? MONO_SEM_FLAGS_ALERTABLE
: MONO_SEM_FLAGS_NONE
);
675 if (ret
== MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
676 /* Success, process has exited */
677 mono_os_sem_post (&process
->exit_sem
);
681 if (ret
== MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
) {
682 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait timeout (timeout = 0)", __func__
, handle
, timeout
);
683 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
686 now
= mono_msec_ticks ();
687 if (now
- start
>= timeout
) {
688 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait timeout", __func__
, handle
, timeout
);
689 return MONO_W32HANDLE_WAIT_RET_TIMEOUT
;
692 if (alerted
&& ret
== MONO_SEM_TIMEDWAIT_RET_ALERTED
) {
693 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): wait alerted", __func__
, handle
, timeout
);
695 return MONO_W32HANDLE_WAIT_RET_ALERTED
;
699 /* Process must have exited */
700 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Waited successfully", __func__
, handle
, timeout
);
702 status
= process
->status
;
703 if (WIFSIGNALED (status
))
704 process_handle
->exitstatus
= 128 + WTERMSIG (status
);
706 process_handle
->exitstatus
= WEXITSTATUS (status
);
708 process_handle
->exit_time
= mono_100ns_datetime ();
710 process_handle
->exited
= TRUE
;
712 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s (%p, %u): Setting pid %d signalled, exit status %d",
713 __func__
, handle
, timeout
, process_handle
->pid
, process_handle
->exitstatus
);
715 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
717 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
721 processes_cleanup (void)
723 static gint32 cleaning_up
;
725 Process
*prev
= NULL
;
726 GSList
*finished
= NULL
;
729 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s", __func__
);
731 /* Ensure we're not in here in multiple threads at once, nor recursive. */
732 if (InterlockedCompareExchange (&cleaning_up
, 1, 0) != 0)
735 for (process
= processes
; process
; process
= process
->next
) {
736 if (process
->signalled
&& process
->handle
) {
737 /* This process has exited and we need to remove the artifical ref
739 mono_w32handle_unref (process
->handle
);
740 process
->handle
= NULL
;
745 * Remove processes which exited from the processes list.
746 * We need to synchronize with the sigchld handler here, which runs
747 * asynchronously. The handler requires that the processes list
750 mono_os_mutex_lock (&processes_mutex
);
752 for (process
= processes
; process
; process
= process
->next
) {
753 if (process
->handle_count
== 0 && process
->freeable
) {
756 * This code can run parallel with the sigchld handler, but the
757 * modifications it makes are safe.
759 if (process
== processes
)
760 processes
= process
->next
;
762 prev
->next
= process
->next
;
763 finished
= g_slist_prepend (finished
, process
);
769 mono_memory_barrier ();
771 for (l
= finished
; l
; l
= l
->next
) {
773 * All the entries in the finished list are unlinked from processes, and
774 * they have the 'finished' flag set, which means the sigchld handler is done
777 process
= (Process
*)l
->data
;
778 mono_os_sem_destroy (&process
->exit_sem
);
781 g_slist_free (finished
);
783 mono_os_mutex_unlock (&processes_mutex
);
785 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s done", __func__
);
787 InterlockedExchange (&cleaning_up
, 0);
791 process_close (gpointer handle
, gpointer data
)
793 MonoW32HandleProcess
*process_handle
;
795 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s", __func__
);
797 process_handle
= (MonoW32HandleProcess
*) data
;
798 g_free (process_handle
->pname
);
799 process_handle
->pname
= NULL
;
800 if (process_handle
->process
)
801 InterlockedDecrement (&process_handle
->process
->handle_count
);
802 processes_cleanup ();
805 static MonoW32HandleOps process_ops
= {
806 process_close
, /* close_shared */
810 process_wait
, /* special_wait */
812 process_details
, /* details */
813 process_typename
, /* typename */
814 process_typesize
, /* typesize */
818 process_set_defaults (MonoW32HandleProcess
*process_handle
)
820 /* These seem to be the defaults on w2k */
821 process_handle
->min_working_set
= 204800;
822 process_handle
->max_working_set
= 1413120;
824 process_handle
->create_time
= mono_100ns_datetime ();
828 process_set_name (MonoW32HandleProcess
*process_handle
)
830 char *progname
, *utf8_progname
, *slash
;
832 progname
= g_get_prgname ();
833 utf8_progname
= mono_utf8_from_external (progname
);
835 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: using [%s] as prog name", __func__
, progname
);
838 slash
= strrchr (utf8_progname
, '/');
840 process_handle
->pname
= g_strdup (slash
+1);
842 process_handle
->pname
= g_strdup (utf8_progname
);
843 g_free (utf8_progname
);
848 mono_w32process_init (void)
850 MonoW32HandleProcess process_handle
;
852 mono_w32handle_register_ops (MONO_W32HANDLE_PROCESS
, &process_ops
);
854 mono_w32handle_register_capabilities (MONO_W32HANDLE_PROCESS
,
855 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SPECIAL_WAIT
));
857 current_pid
= getpid ();
859 memset (&process_handle
, 0, sizeof (process_handle
));
860 process_handle
.pid
= current_pid
;
861 process_set_defaults (&process_handle
);
862 process_set_name (&process_handle
);
864 current_process
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
865 g_assert (current_process
);
867 mono_os_mutex_init (&processes_mutex
);
871 mono_w32process_cleanup (void)
873 g_free (cli_launcher
);
877 len16 (const gunichar2
*str
)
888 utf16_concat (const gunichar2
*first
, ...)
896 va_start (args
, first
);
897 total
+= len16 (first
);
898 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg(args
, gunichar2
*))
902 ret
= g_new (gunichar2
, total
+ 1);
908 for (s
= first
; *s
!= 0; s
++)
910 va_start (args
, first
);
911 for (s
= va_arg (args
, gunichar2
*); s
!= NULL
; s
= va_arg (args
, gunichar2
*)){
912 for (p
= s
; *p
!= 0; p
++)
921 mono_w32process_get_pid (gpointer handle
)
923 MonoW32HandleProcess
*process_handle
;
926 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
928 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
932 return process_handle
->pid
;
938 } GetProcessForeachData
;
941 get_process_foreach_callback (gpointer handle
, gpointer handle_specific
, gpointer user_data
)
943 GetProcessForeachData
*foreach_data
;
944 MonoW32HandleProcess
*process_handle
;
947 foreach_data
= (GetProcessForeachData
*) user_data
;
949 if (mono_w32handle_get_type (handle
) != MONO_W32HANDLE_PROCESS
)
952 process_handle
= (MonoW32HandleProcess
*) handle_specific
;
954 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: looking at process %d", __func__
, process_handle
->pid
);
956 pid
= process_handle
->pid
;
960 /* It's possible to have more than one process handle with the
961 * same pid, but only the one running process can be
963 if (foreach_data
->pid
!= pid
)
965 if (mono_w32handle_issignalled (handle
))
968 mono_w32handle_ref (handle
);
969 foreach_data
->handle
= handle
;
974 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid
)
976 GetProcessForeachData foreach_data
;
979 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: looking for process %d", __func__
, pid
);
981 memset (&foreach_data
, 0, sizeof (foreach_data
));
982 foreach_data
.pid
= pid
;
983 mono_w32handle_foreach (get_process_foreach_callback
, &foreach_data
);
984 handle
= foreach_data
.handle
;
986 /* get_process_foreach_callback already added a ref */
990 if (process_is_alive (pid
)) {
991 /* non-child process */
992 MonoW32HandleProcess process_handle
;
994 memset (&process_handle
, 0, sizeof (process_handle
));
995 process_handle
.pid
= pid
;
996 process_handle
.pname
= mono_w32process_get_name (pid
);
998 handle
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
999 if (handle
== INVALID_HANDLE_VALUE
) {
1000 g_warning ("%s: error creating process handle", __func__
);
1002 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1009 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find pid %d", __func__
, pid
);
1011 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
1016 match_procname_to_modulename (char *procname
, char *modulename
)
1018 char* lastsep
= NULL
;
1019 char* lastsep2
= NULL
;
1022 gboolean result
= FALSE
;
1024 if (procname
== NULL
|| modulename
== NULL
)
1027 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: procname=\"%s\", modulename=\"%s\"", __func__
, procname
, modulename
);
1028 pname
= mono_path_resolve_symlinks (procname
);
1029 mname
= mono_path_resolve_symlinks (modulename
);
1031 if (!strcmp (pname
, mname
))
1035 lastsep
= strrchr (mname
, '/');
1037 if (!strcmp (lastsep
+1, pname
))
1040 lastsep2
= strrchr (pname
, '/');
1043 if (!strcmp (lastsep
+1, lastsep2
+1))
1046 if (!strcmp (mname
, lastsep2
+1))
1056 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: result is %d", __func__
, result
);
1061 mono_w32process_try_get_modules (gpointer process
, gpointer
*modules
, guint32 size
, guint32
*needed
)
1063 MonoW32HandleProcess
*process_handle
;
1064 GSList
*mods
= NULL
, *mods_iter
;
1065 MonoW32ProcessModule
*module
;
1066 guint32 count
, avail
= size
/ sizeof(gpointer
);
1072 /* Store modules in an array of pointers (main module as
1073 * modules[0]), using the load address for each module as a
1074 * token. (Use 'NULL' as an alternative for the main module
1075 * so that the simple implementation can just return one item
1076 * for now.) Get the info from /proc/<pid>/maps on linux,
1077 * /proc/<pid>/map on FreeBSD, other systems will have to
1078 * implement /dev/kmem reading or whatever other horrid
1079 * technique is needed.
1081 if (size
< sizeof(gpointer
))
1084 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
1086 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
1090 pid
= process_handle
->pid
;
1091 pname
= g_strdup (process_handle
->pname
);
1095 *needed
= sizeof(gpointer
);
1099 mods
= mono_w32process_get_modules (pid
);
1102 *needed
= sizeof(gpointer
);
1110 * Use the NULL shortcut, as the first line in
1111 * /proc/<pid>/maps isn't the executable, and we need
1112 * that first in the returned list. Check the module name
1113 * to see if it ends with the proc name and substitute
1114 * the first entry with it. FIXME if this turns out to
1119 for (i
= 0; mods_iter
; i
++) {
1120 if (i
< avail
- 1) {
1121 module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1122 if (modules
[0] != NULL
)
1123 modules
[i
] = module
->address_start
;
1124 else if (match_procname_to_modulename (pname
, module
->filename
))
1125 modules
[0] = module
->address_start
;
1127 modules
[i
+ 1] = module
->address_start
;
1129 mono_w32process_module_free ((MonoW32ProcessModule
*)mods_iter
->data
);
1130 mods_iter
= g_slist_next (mods_iter
);
1134 /* count + 1 to leave slot 0 for the main module */
1135 *needed
= sizeof(gpointer
) * (count
+ 1);
1137 g_slist_free (mods
);
1144 mono_w32process_module_get_filename (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
1149 gunichar2
*proc_path
;
1151 size
*= sizeof (gunichar2
); /* adjust for unicode characters */
1153 if (basename
== NULL
|| size
== 0)
1156 pid
= mono_w32process_get_pid (process
);
1158 path
= mono_w32process_get_path (pid
);
1162 proc_path
= mono_unicode_from_external (path
, &bytes
);
1165 if (proc_path
== NULL
)
1170 /* Add the terminator */
1174 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d smaller than needed (%zd); truncating", __func__
, size
, bytes
);
1175 memcpy (basename
, proc_path
, size
);
1177 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d larger than needed (%zd)", __func__
, size
, bytes
);
1178 memcpy (basename
, proc_path
, bytes
);
1187 mono_w32process_module_get_name (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
1189 MonoW32HandleProcess
*process_handle
;
1191 gunichar2
*procname
;
1192 char *procname_ext
= NULL
;
1195 GSList
*mods
= NULL
, *mods_iter
;
1196 MonoW32ProcessModule
*found_module
;
1200 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Getting module base name, process handle %p module %p basename %p size %d",
1201 __func__
, process
, module
, basename
, size
);
1203 size
= size
* sizeof (gunichar2
); /* adjust for unicode characters */
1205 if (basename
== NULL
|| size
== 0)
1208 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
1210 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
1214 pid
= process_handle
->pid
;
1215 pname
= g_strdup (process_handle
->pname
);
1217 mods
= mono_w32process_get_modules (pid
);
1223 /* If module != NULL compare the address.
1224 * If module == NULL we are looking for the main module.
1225 * The best we can do for now check it the module name end with the process name.
1227 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1228 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1229 if (procname_ext
== NULL
&&
1230 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1231 (module
!= NULL
&& found_module
->address_start
== module
))) {
1232 procname_ext
= g_path_get_basename (found_module
->filename
);
1235 mono_w32process_module_free (found_module
);
1238 if (procname_ext
== NULL
) {
1239 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find procname_ext from procmods %p", __func__
, process
);
1240 /* If it's *still* null, we might have hit the
1241 * case where reading /proc/$pid/maps gives an
1242 * empty file for this user.
1244 procname_ext
= mono_w32process_get_name (pid
);
1246 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
);
1249 g_slist_free (mods
);
1253 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Process name is [%s]", __func__
,
1256 procname
= mono_unicode_from_external (procname_ext
, &bytes
);
1257 if (procname
== NULL
) {
1258 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't get procname %p", __func__
, process
);
1260 g_free (procname_ext
);
1266 /* Add the terminator */
1270 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d smaller than needed (%zd); truncating", __func__
, size
, bytes
);
1272 memcpy (basename
, procname
, size
);
1274 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Size %d larger than needed (%zd)",
1275 __func__
, size
, bytes
);
1277 memcpy (basename
, procname
, bytes
);
1281 g_free (procname_ext
);
1286 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find procname_ext %p", __func__
, process
);
1291 mono_w32process_module_get_information (gpointer process
, gpointer module
, MODULEINFO
*modinfo
, guint32 size
)
1293 MonoW32HandleProcess
*process_handle
;
1295 GSList
*mods
= NULL
, *mods_iter
;
1296 MonoW32ProcessModule
*found_module
;
1297 gboolean ret
= FALSE
;
1301 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Getting module info, process handle %p module %p",
1302 __func__
, process
, module
);
1304 if (modinfo
== NULL
|| size
< sizeof (MODULEINFO
))
1307 res
= mono_w32handle_lookup (process
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
1309 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, process
);
1313 pid
= process_handle
->pid
;
1314 pname
= g_strdup (process_handle
->pname
);
1316 mods
= mono_w32process_get_modules (pid
);
1322 /* If module != NULL compare the address.
1323 * If module == NULL we are looking for the main module.
1324 * The best we can do for now check it the module name end with the process name.
1326 for (mods_iter
= mods
; mods_iter
; mods_iter
= g_slist_next (mods_iter
)) {
1327 found_module
= (MonoW32ProcessModule
*)mods_iter
->data
;
1329 ((module
== NULL
&& match_procname_to_modulename (pname
, found_module
->filename
)) ||
1330 (module
!= NULL
&& found_module
->address_start
== module
))) {
1331 modinfo
->lpBaseOfDll
= found_module
->address_start
;
1332 modinfo
->SizeOfImage
= (gsize
)(found_module
->address_end
) - (gsize
)(found_module
->address_start
);
1333 modinfo
->EntryPoint
= found_module
->address_offset
;
1337 mono_w32process_module_free (found_module
);
1340 g_slist_free (mods
);
1347 switch_dir_separators (char *path
)
1349 size_t i
, pathLength
= strlen(path
);
1351 /* Turn all the slashes round the right way, except for \' */
1352 /* There are probably other characters that need to be excluded as well. */
1353 for (i
= 0; i
< pathLength
; i
++) {
1354 if (path
[i
] == '\\' && i
< pathLength
- 1 && path
[i
+1] != '\'' )
1361 MONO_SIGNAL_HANDLER_FUNC (static, mono_sigchld_signal_handler
, (int _dummy
, siginfo_t
*info
, void *context
))
1369 pid
= waitpid (-1, &status
, WNOHANG
);
1370 } while (pid
== -1 && errno
== EINTR
);
1376 * This can run concurrently with the code in the rest of this module.
1378 for (process
= processes
; process
; process
= process
->next
) {
1379 if (process
->pid
!= pid
)
1381 if (process
->signalled
)
1384 process
->signalled
= TRUE
;
1385 process
->status
= status
;
1386 mono_os_sem_post (&process
->exit_sem
);
1387 mono_memory_barrier ();
1388 /* Mark this as freeable, the pointer becomes invalid afterwards */
1389 process
->freeable
= TRUE
;
1396 process_add_sigchld_handler (void)
1398 struct sigaction sa
;
1400 sa
.sa_sigaction
= mono_sigchld_signal_handler
;
1401 sigemptyset (&sa
.sa_mask
);
1402 sa
.sa_flags
= SA_NOCLDSTOP
| SA_SIGINFO
;
1403 g_assert (sigaction (SIGCHLD
, &sa
, NULL
) != -1);
1404 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "Added SIGCHLD handler");
1410 is_readable_or_executable (const char *prog
)
1413 int a
= access (prog
, R_OK
);
1414 int b
= access (prog
, X_OK
);
1415 if (a
!= 0 && b
!= 0)
1417 if (stat (prog
, &buf
))
1419 if (S_ISREG (buf
.st_mode
))
1425 is_executable (const char *prog
)
1428 if (access (prog
, X_OK
) != 0)
1430 if (stat (prog
, &buf
))
1432 if (S_ISREG (buf
.st_mode
))
1438 is_managed_binary (const char *filename
)
1440 int original_errno
= errno
;
1441 #if defined(HAVE_LARGE_FILE_SUPPORT) && defined(O_LARGEFILE)
1442 int file
= open (filename
, O_RDONLY
| O_LARGEFILE
);
1444 int file
= open (filename
, O_RDONLY
);
1447 unsigned char buffer
[8];
1448 off_t file_size
, optional_header_offset
;
1449 off_t pe_header_offset
, clr_header_offset
;
1450 gboolean managed
= FALSE
;
1452 guint32 first_word
, second_word
, magic_number
;
1454 /* If we are unable to open the file, then we definitely
1455 * can't say that it is managed. The child mono process
1456 * probably wouldn't be able to open it anyway.
1459 errno
= original_errno
;
1463 /* Retrieve the length of the file for future sanity checks. */
1464 file_size
= lseek (file
, 0, SEEK_END
);
1465 lseek (file
, 0, SEEK_SET
);
1467 /* We know we need to read a header field at offset 60. */
1471 num_read
= read (file
, buffer
, 2);
1473 if ((num_read
!= 2) || (buffer
[0] != 'M') || (buffer
[1] != 'Z'))
1476 new_offset
= lseek (file
, 60, SEEK_SET
);
1478 if (new_offset
!= 60)
1481 num_read
= read (file
, buffer
, 4);
1485 pe_header_offset
= buffer
[0]
1488 | (buffer
[3] << 24);
1490 if (pe_header_offset
+ 24 > file_size
)
1493 new_offset
= lseek (file
, pe_header_offset
, SEEK_SET
);
1495 if (new_offset
!= pe_header_offset
)
1498 num_read
= read (file
, buffer
, 4);
1500 if ((num_read
!= 4) || (buffer
[0] != 'P') || (buffer
[1] != 'E') || (buffer
[2] != 0) || (buffer
[3] != 0))
1504 * Verify that the header we want in the optional header data
1505 * is present in this binary.
1507 new_offset
= lseek (file
, pe_header_offset
+ 20, SEEK_SET
);
1509 if (new_offset
!= pe_header_offset
+ 20)
1512 num_read
= read (file
, buffer
, 2);
1514 if ((num_read
!= 2) || ((buffer
[0] | (buffer
[1] << 8)) < 216))
1517 optional_header_offset
= pe_header_offset
+ 24;
1519 /* Read the PE magic number */
1520 new_offset
= lseek (file
, optional_header_offset
, SEEK_SET
);
1522 if (new_offset
!= optional_header_offset
)
1525 num_read
= read (file
, buffer
, 2);
1530 magic_number
= (buffer
[0] | (buffer
[1] << 8));
1532 if (magic_number
== 0x10B) // PE32
1533 clr_header_offset
= 208;
1534 else if (magic_number
== 0x20B) // PE32+
1535 clr_header_offset
= 224;
1539 /* Read the CLR header address and size fields. These will be
1540 * zero if the binary is not managed.
1542 new_offset
= lseek (file
, optional_header_offset
+ clr_header_offset
, SEEK_SET
);
1544 if (new_offset
!= optional_header_offset
+ clr_header_offset
)
1547 num_read
= read (file
, buffer
, 8);
1549 /* We are not concerned with endianness, only with
1550 * whether it is zero or not.
1552 first_word
= *(guint32
*)&buffer
[0];
1553 second_word
= *(guint32
*)&buffer
[4];
1555 if ((num_read
!= 8) || (first_word
== 0) || (second_word
== 0))
1562 errno
= original_errno
;
1567 process_create (const gunichar2
*appname
, const gunichar2
*cmdline
,
1568 const gunichar2
*cwd
, StartupHandles
*startup_handles
, MonoW32ProcessInfo
*process_info
)
1570 #if defined (HAVE_FORK) && defined (HAVE_EXECVE)
1571 char *cmd
= NULL
, *prog
= NULL
, *full_prog
= NULL
, *args
= NULL
, *args_after_prog
= NULL
;
1572 char *dir
= NULL
, **env_strings
= NULL
, **argv
= NULL
;
1574 gboolean ret
= FALSE
;
1575 gpointer handle
= NULL
;
1576 GError
*gerr
= NULL
;
1577 int in_fd
, out_fd
, err_fd
;
1579 int startup_pipe
[2] = {-1, -1};
1584 mono_lazy_initialize (&process_sig_chld_once
, process_add_sigchld_handler
);
1587 /* appname and cmdline specify the executable and its args:
1589 * If appname is not NULL, it is the name of the executable.
1590 * Otherwise the executable is the first token in cmdline.
1592 * Executable searching:
1594 * If appname is not NULL, it can specify the full path and
1595 * file name, or else a partial name and the current directory
1596 * will be used. There is no additional searching.
1598 * If appname is NULL, the first whitespace-delimited token in
1599 * cmdline is used. If the name does not contain a full
1600 * directory path, the search sequence is:
1602 * 1) The directory containing the current process
1603 * 2) The current working directory
1604 * 3) The windows system directory (Ignored)
1605 * 4) The windows directory (Ignored)
1608 * Just to make things more interesting, tokens can contain
1609 * white space if they are surrounded by quotation marks. I'm
1610 * beginning to understand just why windows apps are generally
1611 * so crap, with an API like this :-(
1613 if (appname
!= NULL
) {
1614 cmd
= mono_unicode_to_external (appname
);
1616 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL",
1619 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1623 switch_dir_separators(cmd
);
1626 if (cmdline
!= NULL
) {
1627 args
= mono_unicode_to_external (cmdline
);
1629 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
1631 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1637 dir
= mono_unicode_to_external (cwd
);
1639 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
1641 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1645 /* Turn all the slashes round the right way */
1646 switch_dir_separators(dir
);
1650 /* We can't put off locating the executable any longer :-( */
1653 if (g_ascii_isalpha (cmd
[0]) && (cmd
[1] == ':')) {
1654 /* Strip off the drive letter. I can't
1655 * believe that CP/M holdover is still
1658 g_memmove (cmd
, cmd
+2, strlen (cmd
)-2);
1659 cmd
[strlen (cmd
)-2] = '\0';
1662 unquoted
= g_shell_unquote (cmd
, NULL
);
1663 if (unquoted
[0] == '/') {
1664 /* Assume full path given */
1665 prog
= g_strdup (unquoted
);
1667 /* Executable existing ? */
1668 if (!is_readable_or_executable (prog
)) {
1669 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1672 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1676 /* Search for file named by cmd in the current
1679 char *curdir
= g_get_current_dir ();
1681 prog
= g_strdup_printf ("%s/%s", curdir
, unquoted
);
1684 /* And make sure it's readable */
1685 if (!is_readable_or_executable (prog
)) {
1686 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1689 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1695 args_after_prog
= args
;
1700 /* Dig out the first token from args, taking quotation
1701 * marks into account
1704 /* First, strip off all leading whitespace */
1705 args
= g_strchug (args
);
1707 /* args_after_prog points to the contents of args
1708 * after token has been set (otherwise argv[0] is
1711 args_after_prog
= args
;
1713 /* Assume the opening quote will always be the first
1716 if (args
[0] == '\"' || args
[0] == '\'') {
1718 for (i
= 1; args
[i
] != '\0' && args
[i
] != quote
; i
++);
1719 if (args
[i
+ 1] == '\0' || g_ascii_isspace (args
[i
+1])) {
1720 /* We found the first token */
1721 token
= g_strndup (args
+1, i
-1);
1722 args_after_prog
= g_strchug (args
+ i
+ 1);
1724 /* Quotation mark appeared in the
1725 * middle of the token. Just give the
1726 * whole first token, quotes and all,
1732 if (token
== NULL
) {
1733 /* No quote mark, or malformed */
1734 for (i
= 0; args
[i
] != '\0'; i
++) {
1735 if (g_ascii_isspace (args
[i
])) {
1736 token
= g_strndup (args
, i
);
1737 args_after_prog
= args
+ i
+ 1;
1743 if (token
== NULL
&& args
[0] != '\0') {
1744 /* Must be just one token in the string */
1745 token
= g_strdup (args
);
1746 args_after_prog
= NULL
;
1749 if (token
== NULL
) {
1751 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find what to exec", __func__
);
1753 mono_w32error_set_last (ERROR_PATH_NOT_FOUND
);
1757 /* Turn all the slashes round the right way. Only for
1760 switch_dir_separators(token
);
1762 if (g_ascii_isalpha (token
[0]) && (token
[1] == ':')) {
1763 /* Strip off the drive letter. I can't
1764 * believe that CP/M holdover is still
1767 g_memmove (token
, token
+2, strlen (token
)-2);
1768 token
[strlen (token
)-2] = '\0';
1771 if (token
[0] == '/') {
1772 /* Assume full path given */
1773 prog
= g_strdup (token
);
1775 /* Executable existing ? */
1776 if (!is_readable_or_executable (prog
)) {
1777 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s",
1780 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1784 char *curdir
= g_get_current_dir ();
1786 /* FIXME: Need to record the directory
1787 * containing the current process, and check
1788 * that for the new executable as the first
1792 prog
= g_strdup_printf ("%s/%s", curdir
, token
);
1795 /* I assume X_OK is the criterion to use,
1798 * X_OK is too strict *if* the target is a CLR binary
1800 if (!is_readable_or_executable (prog
)) {
1802 prog
= g_find_program_in_path (token
);
1804 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Couldn't find executable %s", __func__
, token
);
1807 mono_w32error_set_last (ERROR_FILE_NOT_FOUND
);
1816 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Exec prog [%s] args [%s]",
1817 __func__
, prog
, args_after_prog
);
1819 /* Check for CLR binaries; if found, we will try to invoke
1820 * them using the same mono binary that started us.
1822 if (is_managed_binary (prog
)) {
1823 gunichar2
*newapp
, *newcmd
;
1824 gsize bytes_ignored
;
1826 newapp
= mono_unicode_from_external (cli_launcher
? cli_launcher
: "mono", &bytes_ignored
);
1829 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, appname
, utf16_space
, cmdline
, NULL
);
1831 newcmd
= utf16_concat (utf16_quote
, newapp
, utf16_quote
, utf16_space
, cmdline
, NULL
);
1836 ret
= process_create (NULL
, newcmd
, cwd
, startup_handles
, process_info
);
1844 if (!is_executable (prog
)) {
1845 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Executable permisson not set on %s", __func__
, prog
);
1846 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
1851 if (args_after_prog
!= NULL
&& *args_after_prog
) {
1854 qprog
= g_shell_quote (prog
);
1855 full_prog
= g_strconcat (qprog
, " ", args_after_prog
, NULL
);
1858 full_prog
= g_shell_quote (prog
);
1861 ret
= g_shell_parse_argv (full_prog
, NULL
, &argv
, &gerr
);
1863 g_message ("process_create: %s\n", gerr
->message
);
1864 g_error_free (gerr
);
1869 if (startup_handles
) {
1870 in_fd
= GPOINTER_TO_UINT (startup_handles
->input
);
1871 out_fd
= GPOINTER_TO_UINT (startup_handles
->output
);
1872 err_fd
= GPOINTER_TO_UINT (startup_handles
->error
);
1874 in_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_input ());
1875 out_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_output ());
1876 err_fd
= GPOINTER_TO_UINT (mono_w32file_get_console_error ());
1880 * process->env_variables is a an array of MonoString*
1882 * If new_environ is not NULL it specifies the entire set of
1883 * environment variables in the new process. Otherwise the
1884 * new process inherits the same environment.
1886 if (process_info
->env_variables
) {
1887 gint i
, str_length
, var_length
;
1891 /* +2: one for the process handle value, and the last one is NULL */
1892 env_strings
= g_new0 (gchar
*, mono_array_length (process_info
->env_variables
) + 2);
1897 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1898 for (i
= 0; i
< mono_array_length (process_info
->env_variables
); ++i
) {
1899 var
= mono_array_get (process_info
->env_variables
, MonoString
*, i
);
1900 var_length
= mono_string_length (var
);
1902 /* str is a null-terminated copy of var */
1904 if (var_length
+ 1 > str_length
) {
1905 str_length
= var_length
+ 1;
1906 str
= g_renew (gunichar2
, str
, str_length
);
1909 memcpy (str
, mono_string_chars (var
), var_length
* sizeof (gunichar2
));
1910 str
[var_length
] = '\0';
1912 env_strings
[i
] = mono_unicode_to_external (str
);
1920 for (i
= 0; environ
[i
] != NULL
; i
++)
1923 /* +2: one for the process handle value, and the last one is NULL */
1924 env_strings
= g_new0 (gchar
*, env_count
+ 2);
1926 /* Copy each environ string into 'strings' turning it into utf8 (or the requested encoding) at the same time */
1927 for (i
= 0; i
< env_count
; i
++)
1928 env_strings
[i
] = g_strdup (environ
[i
]);
1931 /* Create a pipe to make sure the child doesn't exit before
1932 * we can add the process to the linked list of processes */
1933 if (pipe (startup_pipe
) == -1) {
1934 /* Could not create the pipe to synchroniz process startup. We'll just not synchronize.
1935 * This is just for a very hard to hit race condition in the first place */
1936 startup_pipe
[0] = startup_pipe
[1] = -1;
1937 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__
);
1940 switch (pid
= fork ()) {
1941 case -1: /* Error */ {
1942 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
1946 case 0: /* Child */ {
1947 if (startup_pipe
[0] != -1) {
1948 /* Wait until the parent has updated it's internal data */
1949 ssize_t _i G_GNUC_UNUSED
= read (startup_pipe
[0], &dummy
, 1);
1950 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: child: parent has completed its setup", __func__
);
1951 close (startup_pipe
[0]);
1952 close (startup_pipe
[1]);
1955 /* should we detach from the process group? */
1957 /* Connect stdin, stdout and stderr */
1962 /* Close all file descriptors */
1963 for (i
= mono_w32handle_fd_reserve
- 1; i
> 2; i
--)
1966 #ifdef DEBUG_ENABLED
1967 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: exec()ing [%s] in dir [%s]", __func__
, cmd
,
1968 dir
== NULL
?".":dir
);
1969 for (i
= 0; argv
[i
] != NULL
; i
++)
1970 g_message ("arg %d: [%s]", i
, argv
[i
]);
1972 for (i
= 0; env_strings
[i
] != NULL
; i
++)
1973 g_message ("env %d: [%s]", i
, env_strings
[i
]);
1977 if (dir
!= NULL
&& chdir (dir
) == -1) {
1983 execve (argv
[0], argv
, env_strings
);
1990 default: /* Parent */ {
1991 MonoW32HandleProcess process_handle
;
1993 memset (&process_handle
, 0, sizeof (process_handle
));
1994 process_handle
.pid
= pid
;
1995 process_handle
.child
= TRUE
;
1996 process_handle
.pname
= g_strdup (prog
);
1997 process_set_defaults (&process_handle
);
1999 /* Add our process into the linked list of processes */
2000 process
= (Process
*) g_malloc0 (sizeof (Process
));
2002 process
->handle_count
= 1;
2003 mono_os_sem_init (&process
->exit_sem
, 0);
2005 process_handle
.process
= process
;
2007 handle
= mono_w32handle_new (MONO_W32HANDLE_PROCESS
, &process_handle
);
2008 if (handle
== INVALID_HANDLE_VALUE
) {
2009 g_warning ("%s: error creating process handle", __func__
);
2011 mono_os_sem_destroy (&process
->exit_sem
);
2014 mono_w32error_set_last (ERROR_OUTOFMEMORY
);
2019 /* Keep the process handle artificially alive until the process
2020 * exits so that the information in the handle isn't lost. */
2021 mono_w32handle_ref (handle
);
2022 process
->handle
= handle
;
2024 mono_os_mutex_lock (&processes_mutex
);
2025 process
->next
= processes
;
2026 mono_memory_barrier ();
2027 processes
= process
;
2028 mono_os_mutex_unlock (&processes_mutex
);
2030 if (process_info
!= NULL
) {
2031 process_info
->process_handle
= handle
;
2032 process_info
->pid
= pid
;
2034 /* FIXME: we might need to handle the thread info some day */
2035 process_info
->thread_handle
= INVALID_HANDLE_VALUE
;
2036 process_info
->tid
= 0;
2043 if (startup_pipe
[1] != -1) {
2044 /* Write 1 byte, doesn't matter what */
2045 ssize_t _i G_GNUC_UNUSED
= write (startup_pipe
[1], startup_pipe
, 1);
2046 close (startup_pipe
[0]);
2047 close (startup_pipe
[1]);
2062 g_strfreev (env_strings
);
2066 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: returning handle %p for pid %d", __func__
, handle
, pid
);
2068 /* Check if something needs to be cleaned up. */
2069 processes_cleanup ();
2073 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2075 #endif // defined (HAVE_FORK) && defined (HAVE_EXECVE)
2079 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo
*proc_start_info
, MonoW32ProcessInfo
*process_info
)
2081 const gunichar2
*lpFile
;
2082 const gunichar2
*lpParameters
;
2083 const gunichar2
*lpDirectory
;
2087 if (!proc_start_info
->filename
) {
2088 /* w2k returns TRUE for this, for some reason. */
2093 lpFile
= proc_start_info
->filename
? mono_string_chars (proc_start_info
->filename
) : NULL
;
2094 lpParameters
= proc_start_info
->arguments
? mono_string_chars (proc_start_info
->arguments
) : NULL
;
2095 lpDirectory
= proc_start_info
->working_directory
&& mono_string_length (proc_start_info
->working_directory
) != 0 ?
2096 mono_string_chars (proc_start_info
->working_directory
) : NULL
;
2098 /* Put both executable and parameters into the second argument
2099 * to process_create (), so it searches $PATH. The conversion
2100 * into and back out of utf8 is because there is no
2101 * g_strdup_printf () equivalent for gunichar2 :-(
2103 args
= utf16_concat (utf16_quote
, lpFile
, utf16_quote
, lpParameters
== NULL
? NULL
: utf16_space
, lpParameters
, NULL
);
2105 mono_w32error_set_last (ERROR_INVALID_DATA
);
2109 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2112 if (!ret
&& mono_w32error_get_last () == ERROR_OUTOFMEMORY
)
2116 static char *handler
;
2117 static gunichar2
*handler_utf16
;
2119 if (handler_utf16
== (gunichar2
*)-1) {
2124 #ifdef PLATFORM_MACOSX
2125 handler
= g_strdup ("/usr/bin/open");
2128 * On Linux, try: xdg-open, the FreeDesktop standard way of doing it,
2129 * if that fails, try to use gnome-open, then kfmclient
2131 handler
= g_find_program_in_path ("xdg-open");
2132 if (handler
== NULL
){
2133 handler
= g_find_program_in_path ("gnome-open");
2134 if (handler
== NULL
){
2135 handler
= g_find_program_in_path ("kfmclient");
2136 if (handler
== NULL
){
2137 handler_utf16
= (gunichar2
*) -1;
2141 /* kfmclient needs exec argument */
2142 char *old
= handler
;
2143 handler
= g_strconcat (old
, " exec",
2150 handler_utf16
= g_utf8_to_utf16 (handler
, -1, NULL
, NULL
, NULL
);
2153 /* Put quotes around the filename, in case it's a url
2154 * that contains #'s (process_create() calls
2155 * g_shell_parse_argv(), which deliberately throws
2156 * away anything after an unquoted #). Fixes bug
2159 args
= utf16_concat (handler_utf16
, utf16_space
, utf16_quote
, lpFile
, utf16_quote
,
2160 lpParameters
== NULL
? NULL
: utf16_space
, lpParameters
, NULL
);
2162 mono_w32error_set_last (ERROR_INVALID_DATA
);
2166 ret
= process_create (NULL
, args
, lpDirectory
, NULL
, process_info
);
2169 if (mono_w32error_get_last () != ERROR_OUTOFMEMORY
)
2170 mono_w32error_set_last (ERROR_INVALID_DATA
);
2174 /* Shell exec should not return a process handle when it spawned a GUI thing, like a browser. */
2175 mono_w32handle_close (process_info
->process_handle
);
2176 process_info
->process_handle
= NULL
;
2181 process_info
->pid
= -mono_w32error_get_last ();
2183 process_info
->thread_handle
= NULL
;
2184 #if !defined(MONO_CROSS_COMPILE)
2185 process_info
->pid
= mono_w32process_get_pid (process_info
->process_handle
);
2187 process_info
->pid
= 0;
2189 process_info
->tid
= 0;
2195 /* Only used when UseShellExecute is false */
2197 process_get_complete_path (const gunichar2
*appname
, gchar
**completed
)
2202 utf8app
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
2204 if (g_path_is_absolute (utf8app
)) {
2205 *completed
= g_shell_quote (utf8app
);
2210 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
2211 *completed
= g_shell_quote (utf8app
);
2216 found
= g_find_program_in_path (utf8app
);
2217 if (found
== NULL
) {
2223 *completed
= g_shell_quote (found
);
2230 process_get_shell_arguments (MonoW32ProcessStartInfo
*proc_start_info
, gunichar2
**shell_path
)
2232 gchar
*complete_path
= NULL
;
2236 if (process_get_complete_path (mono_string_chars (proc_start_info
->filename
), &complete_path
)) {
2237 *shell_path
= g_utf8_to_utf16 (complete_path
, -1, NULL
, NULL
, NULL
);
2238 g_free (complete_path
);
2241 return *shell_path
!= NULL
;
2245 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo
*proc_start_info
,
2246 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
)
2250 StartupHandles startup_handles
;
2251 gunichar2
*shell_path
= NULL
;
2252 gunichar2
*args
= NULL
;
2254 memset (&startup_handles
, 0, sizeof (startup_handles
));
2255 startup_handles
.input
= stdin_handle
;
2256 startup_handles
.output
= stdout_handle
;
2257 startup_handles
.error
= stderr_handle
;
2259 if (!process_get_shell_arguments (proc_start_info
, &shell_path
)) {
2260 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
2264 args
= proc_start_info
->arguments
&& mono_string_length (proc_start_info
->arguments
) > 0 ?
2265 mono_string_chars (proc_start_info
->arguments
): NULL
;
2267 /* The default dir name is "". Turn that into NULL to mean "current directory" */
2268 dir
= proc_start_info
->working_directory
&& mono_string_length (proc_start_info
->working_directory
) > 0 ?
2269 mono_string_chars (proc_start_info
->working_directory
) : NULL
;
2271 ret
= process_create (shell_path
, args
, dir
, &startup_handles
, process_info
);
2273 if (shell_path
!= NULL
)
2274 g_free (shell_path
);
2277 process_info
->pid
= -mono_w32error_get_last ();
2282 /* Returns an array of pids */
2284 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
2291 pidarray
= mono_process_list (&count
);
2293 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
2296 procs
= mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count
, &error
);
2297 if (mono_error_set_pending_exception (&error
)) {
2301 if (sizeof (guint32
) == sizeof (gpointer
)) {
2302 memcpy (mono_array_addr (procs
, guint32
, 0), pidarray
, count
* sizeof (gint32
));
2304 for (i
= 0; i
< count
; ++i
)
2305 *(mono_array_addr (procs
, guint32
, i
)) = GPOINTER_TO_UINT (pidarray
[i
]);
2313 mono_w32process_set_cli_launcher (gchar
*path
)
2315 g_free (cli_launcher
);
2316 cli_launcher
= g_strdup (path
);
2320 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
2322 mono_w32handle_ref (current_process
);
2323 return current_process
;
2327 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
)
2329 MonoW32HandleProcess
*process_handle
;
2335 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2337 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2341 if (process_handle
->pid
== current_pid
) {
2342 *exitcode
= STILL_ACTIVE
;
2346 /* A process handle is only signalled if the process has exited
2347 * and has been waited for. Make sure any process exit has been
2348 * noticed before checking if the process is signalled.
2349 * Fixes bug 325463. */
2350 mono_w32handle_wait_one (handle
, 0, TRUE
);
2352 *exitcode
= mono_w32handle_issignalled (handle
) ? process_handle
->exitstatus
: STILL_ACTIVE
;
2357 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
)
2359 return mono_w32handle_close (handle
);
2363 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
)
2366 MonoW32HandleProcess
*process_handle
;
2371 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2373 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2374 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2378 pid
= process_handle
->pid
;
2380 ret
= kill (pid
, exitcode
== -1 ? SIGKILL
: SIGTERM
);
2385 case EINVAL
: mono_w32error_set_last (ERROR_INVALID_PARAMETER
); break;
2386 case EPERM
: mono_w32error_set_last (ERROR_ACCESS_DENIED
); break;
2387 case ESRCH
: mono_w32error_set_last (ERROR_PROC_NOT_FOUND
); break;
2388 default: mono_w32error_set_last (ERROR_GEN_FAILURE
); break;
2393 g_error ("kill() is not supported by this platform");
2398 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
)
2400 MonoW32HandleProcess
*process_handle
;
2406 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2408 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2412 if (!process_handle
->child
)
2415 *min
= process_handle
->min_working_set
;
2416 *max
= process_handle
->max_working_set
;
2421 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
)
2423 MonoW32HandleProcess
*process_handle
;
2426 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2428 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2432 if (!process_handle
->child
)
2435 process_handle
->min_working_set
= min
;
2436 process_handle
->max_working_set
= max
;
2441 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
)
2443 #ifdef HAVE_GETPRIORITY
2444 MonoW32HandleProcess
*process_handle
;
2449 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2451 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2455 pid
= process_handle
->pid
;
2458 ret
= getpriority (PRIO_PROCESS
, pid
);
2459 if (ret
== -1 && errno
!= 0) {
2463 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2466 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2469 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2475 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2477 return MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
;
2479 return MONO_W32PROCESS_PRIORITY_CLASS_HIGH
;
2481 return MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
2483 return MONO_W32PROCESS_PRIORITY_CLASS_IDLE
;
2485 return MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
2487 return MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
;
2489 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2495 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
)
2497 #ifdef HAVE_SETPRIORITY
2498 MonoW32HandleProcess
*process_handle
;
2504 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2506 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
2510 pid
= process_handle
->pid
;
2512 switch (priorityClass
) {
2513 case MONO_W32PROCESS_PRIORITY_CLASS_IDLE
:
2516 case MONO_W32PROCESS_PRIORITY_CLASS_BELOW_NORMAL
:
2519 case MONO_W32PROCESS_PRIORITY_CLASS_NORMAL
:
2522 case MONO_W32PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
:
2525 case MONO_W32PROCESS_PRIORITY_CLASS_HIGH
:
2528 case MONO_W32PROCESS_PRIORITY_CLASS_REALTIME
:
2532 mono_w32error_set_last (ERROR_INVALID_PARAMETER
);
2536 ret
= setpriority (PRIO_PROCESS
, pid
, prio
);
2541 mono_w32error_set_last (ERROR_ACCESS_DENIED
);
2544 mono_w32error_set_last (ERROR_PROC_NOT_FOUND
);
2547 mono_w32error_set_last (ERROR_GEN_FAILURE
);
2553 mono_w32error_set_last (ERROR_NOT_SUPPORTED
);
2559 ticks_to_processtime (guint64 ticks
, ProcessTime
*processtime
)
2561 processtime
->lowDateTime
= ticks
& 0xFFFFFFFF;
2562 processtime
->highDateTime
= ticks
>> 32;
2566 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creation_time
, gint64
*exit_time
, gint64
*kernel_time
, gint64
*user_time
)
2568 MonoW32HandleProcess
*process_handle
;
2569 ProcessTime
*creation_processtime
, *exit_processtime
, *kernel_processtime
, *user_processtime
;
2572 if (!creation_time
|| !exit_time
|| !kernel_time
|| !user_time
) {
2573 /* Not sure if w32 allows NULLs here or not */
2577 creation_processtime
= (ProcessTime
*) creation_time
;
2578 exit_processtime
= (ProcessTime
*) exit_time
;
2579 kernel_processtime
= (ProcessTime
*) kernel_time
;
2580 user_processtime
= (ProcessTime
*) user_time
;
2582 memset (creation_processtime
, 0, sizeof (ProcessTime
));
2583 memset (exit_processtime
, 0, sizeof (ProcessTime
));
2584 memset (kernel_processtime
, 0, sizeof (ProcessTime
));
2585 memset (user_processtime
, 0, sizeof (ProcessTime
));
2587 res
= mono_w32handle_lookup (handle
, MONO_W32HANDLE_PROCESS
, (gpointer
*) &process_handle
);
2589 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find process %p", __func__
, handle
);
2593 if (!process_handle
->child
) {
2594 gint64 start_ticks
, user_ticks
, kernel_ticks
;
2596 mono_process_get_times (GINT_TO_POINTER (process_handle
->pid
),
2597 &start_ticks
, &user_ticks
, &kernel_ticks
);
2599 ticks_to_processtime (start_ticks
, creation_processtime
);
2600 ticks_to_processtime (user_ticks
, kernel_processtime
);
2601 ticks_to_processtime (kernel_ticks
, user_processtime
);
2605 ticks_to_processtime (process_handle
->create_time
, creation_processtime
);
2607 /* A process handle is only signalled if the process has
2608 * exited, otherwise exit_processtime isn't set */
2609 if (mono_w32handle_issignalled (handle
))
2610 ticks_to_processtime (process_handle
->exit_time
, exit_processtime
);
2612 #ifdef HAVE_GETRUSAGE
2613 if (process_handle
->pid
== getpid ()) {
2614 struct rusage time_data
;
2615 if (getrusage (RUSAGE_SELF
, &time_data
) == 0) {
2616 ticks_to_processtime ((guint64
)time_data
.ru_utime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_utime
.tv_usec
* 10, user_processtime
);
2617 ticks_to_processtime ((guint64
)time_data
.ru_stime
.tv_sec
* 10000000 + (guint64
)time_data
.ru_stime
.tv_usec
* 10, kernel_processtime
);
2625 static IMAGE_SECTION_HEADER
*
2626 get_enclosing_section_header (guint32 rva
, IMAGE_NT_HEADERS32
*nt_headers
)
2628 IMAGE_SECTION_HEADER
*section
= IMAGE_FIRST_SECTION32 (nt_headers
);
2631 for (i
= 0; i
< GUINT16_FROM_LE (nt_headers
->FileHeader
.NumberOfSections
); i
++, section
++) {
2632 guint32 size
= GUINT32_FROM_LE (section
->Misc
.VirtualSize
);
2634 size
= GUINT32_FROM_LE (section
->SizeOfRawData
);
2637 if ((rva
>= GUINT32_FROM_LE (section
->VirtualAddress
)) &&
2638 (rva
< (GUINT32_FROM_LE (section
->VirtualAddress
) + size
))) {
2646 /* This works for both 32bit and 64bit files, as the differences are
2647 * all after the section header block
2650 get_ptr_from_rva (guint32 rva
, IMAGE_NT_HEADERS32
*ntheaders
, gpointer file_map
)
2652 IMAGE_SECTION_HEADER
*section_header
;
2655 section_header
= get_enclosing_section_header (rva
, ntheaders
);
2656 if (section_header
== NULL
) {
2660 delta
= (guint32
)(GUINT32_FROM_LE (section_header
->VirtualAddress
) -
2661 GUINT32_FROM_LE (section_header
->PointerToRawData
));
2663 return((guint8
*)file_map
+ rva
- delta
);
2667 scan_resource_dir (IMAGE_RESOURCE_DIRECTORY
*root
, IMAGE_NT_HEADERS32
*nt_headers
, gpointer file_map
,
2668 IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
, int level
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2670 IMAGE_RESOURCE_DIRECTORY_ENTRY swapped_entry
;
2671 gboolean is_string
, is_dir
;
2672 guint32 name_offset
, dir_offset
, data_offset
;
2674 swapped_entry
.Name
= GUINT32_FROM_LE (entry
->Name
);
2675 swapped_entry
.OffsetToData
= GUINT32_FROM_LE (entry
->OffsetToData
);
2677 is_string
= swapped_entry
.NameIsString
;
2678 is_dir
= swapped_entry
.DataIsDirectory
;
2679 name_offset
= swapped_entry
.NameOffset
;
2680 dir_offset
= swapped_entry
.OffsetToDirectory
;
2681 data_offset
= swapped_entry
.OffsetToData
;
2684 /* Normally holds a directory entry for each type of
2687 if ((is_string
== FALSE
&&
2688 name_offset
!= res_id
) ||
2689 (is_string
== TRUE
)) {
2692 } else if (level
== 1) {
2693 /* Normally holds a directory entry for each resource
2696 } else if (level
== 2) {
2697 /* Normally holds a directory entry for each language
2699 if ((is_string
== FALSE
&&
2700 name_offset
!= lang_id
&&
2702 (is_string
== TRUE
)) {
2706 g_assert_not_reached ();
2709 if (is_dir
== TRUE
) {
2710 IMAGE_RESOURCE_DIRECTORY
*res_dir
= (IMAGE_RESOURCE_DIRECTORY
*)((guint8
*)root
+ dir_offset
);
2711 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entries
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(res_dir
+ 1);
2714 entries
= GUINT16_FROM_LE (res_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (res_dir
->NumberOfIdEntries
);
2716 for (i
= 0; i
< entries
; i
++) {
2717 IMAGE_RESOURCE_DIRECTORY_ENTRY
*sub_entry
= &sub_entries
[i
];
2720 ret
= scan_resource_dir (root
, nt_headers
, file_map
,
2721 sub_entry
, level
+ 1, res_id
,
2730 IMAGE_RESOURCE_DATA_ENTRY
*data_entry
= (IMAGE_RESOURCE_DATA_ENTRY
*)((guint8
*)root
+ data_offset
);
2731 *size
= GUINT32_FROM_LE (data_entry
->Size
);
2733 return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry
->OffsetToData
), nt_headers
, file_map
));
2738 find_pe_file_resources32 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2740 IMAGE_DOS_HEADER
*dos_header
;
2741 IMAGE_NT_HEADERS32
*nt_headers
;
2742 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2743 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2744 guint32 resource_rva
, entries
, i
;
2745 gpointer ret
= NULL
;
2747 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2748 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2749 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2751 mono_w32error_set_last (ERROR_INVALID_DATA
);
2755 if (map_size
< sizeof(IMAGE_NT_HEADERS32
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2756 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: File is too small: %d", __func__
, map_size
);
2758 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2762 nt_headers
= (IMAGE_NT_HEADERS32
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2763 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2764 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad NT signature 0x%x", __func__
, nt_headers
->Signature
);
2766 mono_w32error_set_last (ERROR_INVALID_DATA
);
2770 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2771 /* Do 64-bit stuff */
2772 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2774 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2777 if (resource_rva
== 0) {
2778 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: No resources in file!", __func__
);
2780 mono_w32error_set_last (ERROR_INVALID_DATA
);
2784 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2785 if (resource_dir
== NULL
) {
2786 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find resource directory", __func__
);
2788 mono_w32error_set_last (ERROR_INVALID_DATA
);
2792 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2793 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2795 for (i
= 0; i
< entries
; i
++) {
2796 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2797 ret
= scan_resource_dir (resource_dir
,
2798 (IMAGE_NT_HEADERS32
*)nt_headers
,
2799 file_map
, direntry
, 0, res_id
,
2810 find_pe_file_resources64 (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2812 IMAGE_DOS_HEADER
*dos_header
;
2813 IMAGE_NT_HEADERS64
*nt_headers
;
2814 IMAGE_RESOURCE_DIRECTORY
*resource_dir
;
2815 IMAGE_RESOURCE_DIRECTORY_ENTRY
*resource_dir_entry
;
2816 guint32 resource_rva
, entries
, i
;
2817 gpointer ret
= NULL
;
2819 dos_header
= (IMAGE_DOS_HEADER
*)file_map
;
2820 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
2821 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad dos signature 0x%x", __func__
, dos_header
->e_magic
);
2823 mono_w32error_set_last (ERROR_INVALID_DATA
);
2827 if (map_size
< sizeof(IMAGE_NT_HEADERS64
) + GUINT32_FROM_LE (dos_header
->e_lfanew
)) {
2828 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: File is too small: %d", __func__
, map_size
);
2830 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2834 nt_headers
= (IMAGE_NT_HEADERS64
*)((guint8
*)file_map
+ GUINT32_FROM_LE (dos_header
->e_lfanew
));
2835 if (nt_headers
->Signature
!= IMAGE_NT_SIGNATURE
) {
2836 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Bad NT signature 0x%x", __func__
,
2837 nt_headers
->Signature
);
2839 mono_w32error_set_last (ERROR_INVALID_DATA
);
2843 if (nt_headers
->OptionalHeader
.Magic
== IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
2844 /* Do 64-bit stuff */
2845 resource_rva
= GUINT32_FROM_LE (((IMAGE_NT_HEADERS64
*)nt_headers
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2847 resource_rva
= GUINT32_FROM_LE (nt_headers
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
].VirtualAddress
);
2850 if (resource_rva
== 0) {
2851 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: No resources in file!", __func__
);
2853 mono_w32error_set_last (ERROR_INVALID_DATA
);
2857 resource_dir
= (IMAGE_RESOURCE_DIRECTORY
*)get_ptr_from_rva (resource_rva
, (IMAGE_NT_HEADERS32
*)nt_headers
, file_map
);
2858 if (resource_dir
== NULL
) {
2859 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Can't find resource directory", __func__
);
2861 mono_w32error_set_last (ERROR_INVALID_DATA
);
2865 entries
= GUINT16_FROM_LE (resource_dir
->NumberOfNamedEntries
) + GUINT16_FROM_LE (resource_dir
->NumberOfIdEntries
);
2866 resource_dir_entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(resource_dir
+ 1);
2868 for (i
= 0; i
< entries
; i
++) {
2869 IMAGE_RESOURCE_DIRECTORY_ENTRY
*direntry
= &resource_dir_entry
[i
];
2870 ret
= scan_resource_dir (resource_dir
,
2871 (IMAGE_NT_HEADERS32
*)nt_headers
,
2872 file_map
, direntry
, 0, res_id
,
2883 find_pe_file_resources (gpointer file_map
, guint32 map_size
, guint32 res_id
, guint32 lang_id
, guint32
*size
)
2885 /* Figure this out when we support 64bit PE files */
2887 return find_pe_file_resources32 (file_map
, map_size
, res_id
,
2890 return find_pe_file_resources64 (file_map
, map_size
, res_id
,
2896 map_pe_file (gunichar2
*filename
, gint32
*map_size
, void **handle
)
2898 gchar
*filename_ext
;
2900 struct stat statbuf
;
2903 /* According to the MSDN docs, a search path is applied to
2904 * filename. FIXME: implement this, for now just pass it
2908 filename_ext
= mono_unicode_to_external (filename
);
2909 if (filename_ext
== NULL
) {
2910 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: unicode conversion returned NULL", __func__
);
2912 mono_w32error_set_last (ERROR_INVALID_NAME
);
2916 fd
= open (filename_ext
, O_RDONLY
, 0);
2917 if (fd
== -1 && (errno
== ENOENT
|| errno
== ENOTDIR
) && IS_PORTABILITY_SET
) {
2919 gchar
*located_filename
;
2921 saved_errno
= errno
;
2923 located_filename
= mono_portability_find_file (filename_ext
, TRUE
);
2924 if (!located_filename
) {
2925 errno
= saved_errno
;
2927 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error opening file %s (1): %s", __func__
, filename_ext
, strerror (errno
));
2929 g_free (filename_ext
);
2931 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2935 fd
= open (located_filename
, O_RDONLY
, 0);
2937 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error opening file %s (2): %s", __func__
, filename_ext
, strerror (errno
));
2939 g_free (filename_ext
);
2940 g_free (located_filename
);
2942 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2946 g_free (located_filename
);
2949 if (fstat (fd
, &statbuf
) == -1) {
2950 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error stat()ing file %s: %s", __func__
, filename_ext
, strerror (errno
));
2952 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2953 g_free (filename_ext
);
2957 *map_size
= statbuf
.st_size
;
2959 /* Check basic file size */
2960 if (statbuf
.st_size
< sizeof(IMAGE_DOS_HEADER
)) {
2961 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: File %s is too small: %lld", __func__
, filename_ext
, statbuf
.st_size
);
2963 mono_w32error_set_last (ERROR_BAD_LENGTH
);
2964 g_free (filename_ext
);
2969 file_map
= mono_file_map (statbuf
.st_size
, MONO_MMAP_READ
| MONO_MMAP_PRIVATE
, fd
, 0, handle
);
2970 if (file_map
== NULL
) {
2971 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Error mmap()int file %s: %s", __func__
, filename_ext
, strerror (errno
));
2973 mono_w32error_set_last (mono_w32error_unix_to_win32 (errno
));
2974 g_free (filename_ext
);
2979 /* Don't need the fd any more */
2981 g_free (filename_ext
);
2987 unmap_pe_file (gpointer file_map
, void *handle
)
2989 mono_file_unmap (file_map
, handle
);
2993 unicode_chars (const gunichar2
*str
)
2998 if (str
[len
] == '\0') {
3006 unicode_compare (const gunichar2
*str1
, const gunichar2
*str2
)
3008 while (*str1
&& *str2
) {
3009 if (*str1
!= *str2
) {
3016 return(*str1
== *str2
);
3019 /* compare a little-endian null-terminated utf16 string and a normal string.
3020 * Can be used only for ascii or latin1 chars.
3023 unicode_string_equals (const gunichar2
*str1
, const gchar
*str2
)
3025 while (*str1
&& *str2
) {
3026 if (GUINT16_TO_LE (*str1
) != *str2
) {
3033 return(*str1
== *str2
);
3043 /* Returns a pointer to the value data, because there's no way to know
3044 * how big that data is (value_len is set to zero for most blocks :-( )
3046 static gconstpointer
3047 get_versioninfo_block (gconstpointer data
, version_data
*block
)
3049 block
->data_len
= GUINT16_FROM_LE (*((guint16
*)data
));
3050 data
= (char *)data
+ sizeof(guint16
);
3051 block
->value_len
= GUINT16_FROM_LE (*((guint16
*)data
));
3052 data
= (char *)data
+ sizeof(guint16
);
3054 /* No idea what the type is supposed to indicate */
3055 block
->type
= GUINT16_FROM_LE (*((guint16
*)data
));
3056 data
= (char *)data
+ sizeof(guint16
);
3057 block
->key
= ((gunichar2
*)data
);
3059 /* Skip over the key (including the terminator) */
3060 data
= ((gunichar2
*)data
) + (unicode_chars (block
->key
) + 1);
3062 /* align on a 32-bit boundary */
3068 static gconstpointer
3069 get_fixedfileinfo_block (gconstpointer data
, version_data
*block
)
3071 gconstpointer data_ptr
;
3072 VS_FIXEDFILEINFO
*ffi
;
3074 data_ptr
= get_versioninfo_block (data
, block
);
3076 if (block
->value_len
!= sizeof(VS_FIXEDFILEINFO
)) {
3077 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: FIXEDFILEINFO size mismatch", __func__
);
3081 if (!unicode_string_equals (block
->key
, "VS_VERSION_INFO")) {
3082 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: VS_VERSION_INFO mismatch", __func__
);
3087 ffi
= ((VS_FIXEDFILEINFO
*)data_ptr
);
3088 if ((ffi
->dwSignature
!= VS_FFI_SIGNATURE
) ||
3089 (ffi
->dwStrucVersion
!= VS_FFI_STRUCVERSION
)) {
3090 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: FIXEDFILEINFO bad signature", __func__
);
3098 static gconstpointer
3099 get_varfileinfo_block (gconstpointer data_ptr
, version_data
*block
)
3101 /* data is pointing at a Var block
3103 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3108 static gconstpointer
3109 get_string_block (gconstpointer data_ptr
, const gunichar2
*string_key
, gpointer
*string_value
,
3110 guint32
*string_value_len
, version_data
*block
)
3112 guint16 data_len
= block
->data_len
;
3113 guint16 string_len
= 28; /* Length of the StringTable block */
3114 char *orig_data_ptr
= (char *)data_ptr
- 28;
3116 /* data_ptr is pointing at an array of one or more String blocks
3117 * with total length (not including alignment padding) of
3120 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3121 /* align on a 32-bit boundary */
3124 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3125 if (block
->data_len
== 0) {
3126 /* We must have hit padding, so give up
3129 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3134 string_len
= string_len
+ block
->data_len
;
3136 if (string_key
!= NULL
&&
3137 string_value
!= NULL
&&
3138 string_value_len
!= NULL
&&
3139 unicode_compare (string_key
, block
->key
) == TRUE
) {
3140 *string_value
= (gpointer
)data_ptr
;
3141 *string_value_len
= block
->value_len
;
3144 /* Skip over the value */
3145 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3151 /* Returns a pointer to the byte following the Stringtable block, or
3152 * NULL if the data read hits padding. We can't recover from this
3153 * because the data length does not include padding bytes, so it's not
3154 * possible to just return the start position + length
3156 * If lang == NULL it means we're just stepping through this block
3158 static gconstpointer
3159 get_stringtable_block (gconstpointer data_ptr
, gchar
*lang
, const gunichar2
*string_key
, gpointer
*string_value
,
3160 guint32
*string_value_len
, version_data
*block
)
3162 guint16 data_len
= block
->data_len
;
3163 guint16 string_len
= 36; /* length of the StringFileInfo block */
3165 gchar
*lowercase_lang
;
3167 /* data_ptr is pointing at an array of StringTable blocks,
3168 * with total length (not including alignment padding) of
3172 while(string_len
< data_len
) {
3173 /* align on a 32-bit boundary */
3176 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3177 if (block
->data_len
== 0) {
3178 /* We must have hit padding, so give up
3181 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3185 string_len
= string_len
+ block
->data_len
;
3187 found_lang
= g_utf16_to_utf8 (block
->key
, 8, NULL
, NULL
, NULL
);
3188 if (found_lang
== NULL
) {
3189 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid language key, giving up", __func__
);
3193 lowercase_lang
= g_utf8_strdown (found_lang
, -1);
3194 g_free (found_lang
);
3195 found_lang
= lowercase_lang
;
3196 lowercase_lang
= NULL
;
3198 if (lang
!= NULL
&& !strcmp (found_lang
, lang
)) {
3199 /* Got the one we're interested in */
3200 data_ptr
= get_string_block (data_ptr
, string_key
,
3202 string_value_len
, block
);
3204 data_ptr
= get_string_block (data_ptr
, NULL
, NULL
,
3208 g_free (found_lang
);
3210 if (data_ptr
== NULL
) {
3211 /* Child block hit padding */
3212 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3220 #if G_BYTE_ORDER == G_BIG_ENDIAN
3221 static gconstpointer
3222 big_up_string_block (gconstpointer data_ptr
, version_data
*block
)
3224 guint16 data_len
= block
->data_len
;
3225 guint16 string_len
= 28; /* Length of the StringTable block */
3227 char *orig_data_ptr
= (char *)data_ptr
- 28;
3229 /* data_ptr is pointing at an array of one or more String
3230 * blocks with total length (not including alignment padding)
3233 while (((char *)data_ptr
- (char *)orig_data_ptr
) < data_len
) {
3234 /* align on a 32-bit boundary */
3237 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3238 if (block
->data_len
== 0) {
3239 /* We must have hit padding, so give up
3242 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3246 string_len
= string_len
+ block
->data_len
;
3248 big_value
= g_convert ((gchar
*)block
->key
,
3249 unicode_chars (block
->key
) * 2,
3250 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3252 if (big_value
== NULL
) {
3253 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid string, giving up", __func__
);
3257 /* The swapped string should be exactly the same
3258 * length as the original little-endian one, but only
3259 * copy the number of original chars just to be on the
3262 memcpy (block
->key
, big_value
, unicode_chars (block
->key
) * 2);
3265 big_value
= g_convert ((gchar
*)data_ptr
,
3266 unicode_chars (data_ptr
) * 2,
3267 "UTF-16BE", "UTF-16LE", NULL
, NULL
,
3269 if (big_value
== NULL
) {
3270 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid data string, giving up", __func__
);
3273 memcpy ((gpointer
)data_ptr
, big_value
,
3274 unicode_chars (data_ptr
) * 2);
3277 data_ptr
= ((gunichar2
*)data_ptr
) + block
->value_len
;
3283 /* Returns a pointer to the byte following the Stringtable block, or
3284 * NULL if the data read hits padding. We can't recover from this
3285 * because the data length does not include padding bytes, so it's not
3286 * possible to just return the start position + length
3288 static gconstpointer
3289 big_up_stringtable_block (gconstpointer data_ptr
, version_data
*block
)
3291 guint16 data_len
= block
->data_len
;
3292 guint16 string_len
= 36; /* length of the StringFileInfo block */
3295 /* data_ptr is pointing at an array of StringTable blocks,
3296 * with total length (not including alignment padding) of
3300 while(string_len
< data_len
) {
3301 /* align on a 32-bit boundary */
3304 data_ptr
= get_versioninfo_block (data_ptr
, block
);
3305 if (block
->data_len
== 0) {
3306 /* We must have hit padding, so give up
3309 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3313 string_len
= string_len
+ block
->data_len
;
3315 big_value
= g_convert ((gchar
*)block
->key
, 16, "UTF-16BE",
3316 "UTF-16LE", NULL
, NULL
, NULL
);
3317 if (big_value
== NULL
) {
3318 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Didn't find a valid string, giving up", __func__
);
3322 memcpy (block
->key
, big_value
, 16);
3325 data_ptr
= big_up_string_block (data_ptr
, block
);
3327 if (data_ptr
== NULL
) {
3328 /* Child block hit padding */
3329 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3337 /* Follows the data structures and turns all UTF-16 strings from the
3338 * LE found in the resource section into UTF-16BE
3341 big_up (gconstpointer datablock
, guint32 size
)
3343 gconstpointer data_ptr
;
3344 gint32 data_len
; /* signed to guard against underflow */
3347 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3348 if (data_ptr
!= NULL
) {
3349 VS_FIXEDFILEINFO
*ffi
= (VS_FIXEDFILEINFO
*)data_ptr
;
3351 /* Byteswap all the fields */
3352 ffi
->dwFileVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionMS
);
3353 ffi
->dwFileVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileVersionLS
);
3354 ffi
->dwProductVersionMS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionMS
);
3355 ffi
->dwProductVersionLS
= GUINT32_SWAP_LE_BE (ffi
->dwProductVersionLS
);
3356 ffi
->dwFileFlagsMask
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlagsMask
);
3357 ffi
->dwFileFlags
= GUINT32_SWAP_LE_BE (ffi
->dwFileFlags
);
3358 ffi
->dwFileOS
= GUINT32_SWAP_LE_BE (ffi
->dwFileOS
);
3359 ffi
->dwFileType
= GUINT32_SWAP_LE_BE (ffi
->dwFileType
);
3360 ffi
->dwFileSubtype
= GUINT32_SWAP_LE_BE (ffi
->dwFileSubtype
);
3361 ffi
->dwFileDateMS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateMS
);
3362 ffi
->dwFileDateLS
= GUINT32_SWAP_LE_BE (ffi
->dwFileDateLS
);
3364 /* The FFI and header occupies the first 92 bytes
3366 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3367 data_len
= block
.data_len
- 92;
3369 /* There now follow zero or one StringFileInfo blocks
3370 * and zero or one VarFileInfo blocks
3372 while (data_len
> 0) {
3373 /* align on a 32-bit boundary */
3376 data_ptr
= get_versioninfo_block (data_ptr
, &block
);
3377 if (block
.data_len
== 0) {
3378 /* We must have hit padding, so give
3381 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3385 data_len
= data_len
- block
.data_len
;
3387 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3388 data_ptr
= get_varfileinfo_block (data_ptr
,
3390 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3391 } else if (unicode_string_equals (block
.key
,
3392 "StringFileInfo")) {
3393 data_ptr
= big_up_stringtable_block (data_ptr
,
3397 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Not a valid VERSIONINFO child block", __func__
);
3401 if (data_ptr
== NULL
) {
3402 /* Child block hit padding */
3403 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3412 mono_w32process_get_fileversion_info_size (gunichar2
*filename
, guint32
*handle
)
3415 gpointer versioninfo
;
3420 /* This value is unused, but set to zero */
3423 file_map
= map_pe_file (filename
, &map_size
, &map_handle
);
3424 if (file_map
== NULL
) {
3428 versioninfo
= find_pe_file_resources (file_map
, map_size
, RT_VERSION
, 0, &size
);
3429 if (versioninfo
== NULL
) {
3430 /* Didn't find the resource, so set the return value
3436 unmap_pe_file (file_map
, map_handle
);
3442 mono_w32process_get_fileversion_info (gunichar2
*filename
, guint32 handle G_GNUC_UNUSED
, guint32 len
, gpointer data
)
3445 gpointer versioninfo
;
3449 gboolean ret
= FALSE
;
3451 file_map
= map_pe_file (filename
, &map_size
, &map_handle
);
3452 if (file_map
== NULL
) {
3456 versioninfo
= find_pe_file_resources (file_map
, map_size
, RT_VERSION
,
3458 if (versioninfo
!= NULL
) {
3459 /* This could probably process the data so that
3460 * mono_w32process_ver_query_value() doesn't have to follow the data
3461 * blocks every time. But hey, these functions aren't
3462 * likely to appear in many profiles.
3464 memcpy (data
, versioninfo
, len
< size
?len
:size
);
3467 #if G_BYTE_ORDER == G_BIG_ENDIAN
3468 big_up (data
, size
);
3472 unmap_pe_file (file_map
, map_handle
);
3478 mono_w32process_ver_query_value (gconstpointer datablock
, const gunichar2
*subblock
, gpointer
*buffer
, guint32
*len
)
3480 gchar
*subblock_utf8
, *lang_utf8
= NULL
;
3481 gboolean ret
= FALSE
;
3483 gconstpointer data_ptr
;
3484 gint32 data_len
; /* signed to guard against underflow */
3485 gboolean want_var
= FALSE
;
3486 gboolean want_string
= FALSE
;
3488 const gunichar2
*string_key
= NULL
;
3489 gpointer string_value
= NULL
;
3490 guint32 string_value_len
= 0;
3491 gchar
*lowercase_lang
;
3493 subblock_utf8
= g_utf16_to_utf8 (subblock
, -1, NULL
, NULL
, NULL
);
3494 if (subblock_utf8
== NULL
) {
3498 if (!strcmp (subblock_utf8
, "\\VarFileInfo\\Translation")) {
3500 } else if (!strncmp (subblock_utf8
, "\\StringFileInfo\\", 16)) {
3502 memcpy (lang
, subblock
+ 16, 8 * sizeof(gunichar2
));
3503 lang_utf8
= g_utf16_to_utf8 (lang
, 8, NULL
, NULL
, NULL
);
3504 lowercase_lang
= g_utf8_strdown (lang_utf8
, -1);
3506 lang_utf8
= lowercase_lang
;
3507 lowercase_lang
= NULL
;
3508 string_key
= subblock
+ 25;
3511 if (!strcmp (subblock_utf8
, "\\")) {
3512 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3513 if (data_ptr
!= NULL
) {
3514 *buffer
= (gpointer
)data_ptr
;
3515 *len
= block
.value_len
;
3519 } else if (want_var
|| want_string
) {
3520 data_ptr
= get_fixedfileinfo_block (datablock
, &block
);
3521 if (data_ptr
!= NULL
) {
3522 /* The FFI and header occupies the first 92
3525 data_ptr
= (char *)data_ptr
+ sizeof(VS_FIXEDFILEINFO
);
3526 data_len
= block
.data_len
- 92;
3528 /* There now follow zero or one StringFileInfo
3529 * blocks and zero or one VarFileInfo blocks
3531 while (data_len
> 0) {
3532 /* align on a 32-bit boundary */
3535 data_ptr
= get_versioninfo_block (data_ptr
,
3537 if (block
.data_len
== 0) {
3538 /* We must have hit padding,
3539 * so give up processing now
3541 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Hit 0-length block, giving up", __func__
);
3545 data_len
= data_len
- block
.data_len
;
3547 if (unicode_string_equals (block
.key
, "VarFileInfo")) {
3548 data_ptr
= get_varfileinfo_block (data_ptr
, &block
);
3550 *buffer
= (gpointer
)data_ptr
;
3551 *len
= block
.value_len
;
3555 /* Skip over the Var block */
3556 data_ptr
= ((guchar
*)data_ptr
) + block
.value_len
;
3558 } else if (unicode_string_equals (block
.key
, "StringFileInfo")) {
3559 data_ptr
= get_stringtable_block (data_ptr
, lang_utf8
, string_key
, &string_value
, &string_value_len
, &block
);
3561 string_value
!= NULL
&&
3562 string_value_len
!= 0) {
3563 *buffer
= string_value
;
3564 *len
= unicode_chars ((const gunichar2
*)string_value
) + 1; /* Include trailing null */
3570 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Not a valid VERSIONINFO child block", __func__
);
3574 if (data_ptr
== NULL
) {
3575 /* Child block hit padding */
3576 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Child block hit 0-length block, giving up", __func__
);
3588 g_free (subblock_utf8
);
3593 copy_lang (gunichar2
*lang_out
, guint32 lang_len
, const gchar
*text
)
3596 int chars
= strlen (text
);
3599 unitext
= g_utf8_to_utf16 (text
, -1, NULL
, NULL
, NULL
);
3600 g_assert (unitext
!= NULL
);
3602 if (chars
< (lang_len
- 1)) {
3603 memcpy (lang_out
, (gpointer
)unitext
, chars
* 2);
3604 lang_out
[chars
] = '\0';
3607 memcpy (lang_out
, (gpointer
)unitext
, (lang_len
- 1) * 2);
3608 lang_out
[lang_len
] = '\0';
3618 mono_w32process_ver_language_name (guint32 lang
, gunichar2
*lang_out
, guint32 lang_len
)
3620 int primary
, secondary
;
3621 const char *name
= NULL
;
3623 primary
= lang
& 0x3FF;
3624 secondary
= (lang
>> 10) & 0x3F;
3628 switch (secondary
) {
3629 case 0x01: name
= "Process Default Language"; break;
3633 switch (secondary
) {
3635 case 0x01: name
= "Arabic (Saudi Arabia)"; break;
3636 case 0x02: name
= "Arabic (Iraq)"; break;
3637 case 0x03: name
= "Arabic (Egypt)"; break;
3638 case 0x04: name
= "Arabic (Libya)"; break;
3639 case 0x05: name
= "Arabic (Algeria)"; break;
3640 case 0x06: name
= "Arabic (Morocco)"; break;
3641 case 0x07: name
= "Arabic (Tunisia)"; break;
3642 case 0x08: name
= "Arabic (Oman)"; break;
3643 case 0x09: name
= "Arabic (Yemen)"; break;
3644 case 0x0a: name
= "Arabic (Syria)"; break;
3645 case 0x0b: name
= "Arabic (Jordan)"; break;
3646 case 0x0c: name
= "Arabic (Lebanon)"; break;
3647 case 0x0d: name
= "Arabic (Kuwait)"; break;
3648 case 0x0e: name
= "Arabic (U.A.E.)"; break;
3649 case 0x0f: name
= "Arabic (Bahrain)"; break;
3650 case 0x10: name
= "Arabic (Qatar)"; break;
3654 switch (secondary
) {
3655 case 0x00: name
= "Bulgarian (Bulgaria)"; break;
3656 case 0x01: name
= "Bulgarian"; break;
3660 switch (secondary
) {
3661 case 0x00: name
= "Catalan (Spain)"; break;
3662 case 0x01: name
= "Catalan"; break;
3666 switch (secondary
) {
3668 case 0x01: name
= "Chinese (Taiwan)"; break;
3669 case 0x02: name
= "Chinese (PRC)"; break;
3670 case 0x03: name
= "Chinese (Hong Kong S.A.R.)"; break;
3671 case 0x04: name
= "Chinese (Singapore)"; break;
3672 case 0x05: name
= "Chinese (Macau S.A.R.)"; break;
3676 switch (secondary
) {
3677 case 0x00: name
= "Czech (Czech Republic)"; break;
3678 case 0x01: name
= "Czech"; break;
3682 switch (secondary
) {
3683 case 0x00: name
= "Danish (Denmark)"; break;
3684 case 0x01: name
= "Danish"; break;
3688 switch (secondary
) {
3690 case 0x01: name
= "German (Germany)"; break;
3691 case 0x02: name
= "German (Switzerland)"; break;
3692 case 0x03: name
= "German (Austria)"; break;
3693 case 0x04: name
= "German (Luxembourg)"; break;
3694 case 0x05: name
= "German (Liechtenstein)"; break;
3698 switch (secondary
) {
3699 case 0x00: name
= "Greek (Greece)"; break;
3700 case 0x01: name
= "Greek"; break;
3704 switch (secondary
) {
3706 case 0x01: name
= "English (United States)"; break;
3707 case 0x02: name
= "English (United Kingdom)"; break;
3708 case 0x03: name
= "English (Australia)"; break;
3709 case 0x04: name
= "English (Canada)"; break;
3710 case 0x05: name
= "English (New Zealand)"; break;
3711 case 0x06: name
= "English (Ireland)"; break;
3712 case 0x07: name
= "English (South Africa)"; break;
3713 case 0x08: name
= "English (Jamaica)"; break;
3714 case 0x09: name
= "English (Caribbean)"; break;
3715 case 0x0a: name
= "English (Belize)"; break;
3716 case 0x0b: name
= "English (Trinidad and Tobago)"; break;
3717 case 0x0c: name
= "English (Zimbabwe)"; break;
3718 case 0x0d: name
= "English (Philippines)"; break;
3719 case 0x10: name
= "English (India)"; break;
3720 case 0x11: name
= "English (Malaysia)"; break;
3721 case 0x12: name
= "English (Singapore)"; break;
3725 switch (secondary
) {
3726 case 0x00: name
= "Spanish (Spain)"; break;
3727 case 0x01: name
= "Spanish (Traditional Sort)"; break;
3728 case 0x02: name
= "Spanish (Mexico)"; break;
3729 case 0x03: name
= "Spanish (International Sort)"; break;
3730 case 0x04: name
= "Spanish (Guatemala)"; break;
3731 case 0x05: name
= "Spanish (Costa Rica)"; break;
3732 case 0x06: name
= "Spanish (Panama)"; break;
3733 case 0x07: name
= "Spanish (Dominican Republic)"; break;
3734 case 0x08: name
= "Spanish (Venezuela)"; break;
3735 case 0x09: name
= "Spanish (Colombia)"; break;
3736 case 0x0a: name
= "Spanish (Peru)"; break;
3737 case 0x0b: name
= "Spanish (Argentina)"; break;
3738 case 0x0c: name
= "Spanish (Ecuador)"; break;
3739 case 0x0d: name
= "Spanish (Chile)"; break;
3740 case 0x0e: name
= "Spanish (Uruguay)"; break;
3741 case 0x0f: name
= "Spanish (Paraguay)"; break;
3742 case 0x10: name
= "Spanish (Bolivia)"; break;
3743 case 0x11: name
= "Spanish (El Salvador)"; break;
3744 case 0x12: name
= "Spanish (Honduras)"; break;
3745 case 0x13: name
= "Spanish (Nicaragua)"; break;
3746 case 0x14: name
= "Spanish (Puerto Rico)"; break;
3747 case 0x15: name
= "Spanish (United States)"; break;
3751 switch (secondary
) {
3752 case 0x00: name
= "Finnish (Finland)"; break;
3753 case 0x01: name
= "Finnish"; break;
3757 switch (secondary
) {
3759 case 0x01: name
= "French (France)"; break;
3760 case 0x02: name
= "French (Belgium)"; break;
3761 case 0x03: name
= "French (Canada)"; break;
3762 case 0x04: name
= "French (Switzerland)"; break;
3763 case 0x05: name
= "French (Luxembourg)"; break;
3764 case 0x06: name
= "French (Monaco)"; break;
3768 switch (secondary
) {
3769 case 0x00: name
= "Hebrew (Israel)"; break;
3770 case 0x01: name
= "Hebrew"; break;
3774 switch (secondary
) {
3775 case 0x00: name
= "Hungarian (Hungary)"; break;
3776 case 0x01: name
= "Hungarian"; break;
3780 switch (secondary
) {
3781 case 0x00: name
= "Icelandic (Iceland)"; break;
3782 case 0x01: name
= "Icelandic"; break;
3786 switch (secondary
) {
3788 case 0x01: name
= "Italian (Italy)"; break;
3789 case 0x02: name
= "Italian (Switzerland)"; break;
3793 switch (secondary
) {
3794 case 0x00: name
= "Japanese (Japan)"; break;
3795 case 0x01: name
= "Japanese"; break;
3799 switch (secondary
) {
3800 case 0x00: name
= "Korean (Korea)"; break;
3801 case 0x01: name
= "Korean"; break;
3805 switch (secondary
) {
3807 case 0x01: name
= "Dutch (Netherlands)"; break;
3808 case 0x02: name
= "Dutch (Belgium)"; break;
3812 switch (secondary
) {
3814 case 0x01: name
= "Norwegian (Bokmal)"; break;
3815 case 0x02: name
= "Norwegian (Nynorsk)"; break;
3819 switch (secondary
) {
3820 case 0x00: name
= "Polish (Poland)"; break;
3821 case 0x01: name
= "Polish"; break;
3825 switch (secondary
) {
3827 case 0x01: name
= "Portuguese (Brazil)"; break;
3828 case 0x02: name
= "Portuguese (Portugal)"; break;
3832 switch (secondary
) {
3833 case 0x01: name
= "Romansh (Switzerland)"; break;
3837 switch (secondary
) {
3838 case 0x00: name
= "Romanian (Romania)"; break;
3839 case 0x01: name
= "Romanian"; break;
3843 switch (secondary
) {
3844 case 0x00: name
= "Russian (Russia)"; break;
3845 case 0x01: name
= "Russian"; break;
3849 switch (secondary
) {
3850 case 0x00: name
= "Croatian (Croatia)"; break;
3851 case 0x01: name
= "Croatian"; break;
3852 case 0x02: name
= "Serbian (Latin)"; break;
3853 case 0x03: name
= "Serbian (Cyrillic)"; break;
3854 case 0x04: name
= "Croatian (Bosnia and Herzegovina)"; break;
3855 case 0x05: name
= "Bosnian (Latin, Bosnia and Herzegovina)"; break;
3856 case 0x06: name
= "Serbian (Latin, Bosnia and Herzegovina)"; break;
3857 case 0x07: name
= "Serbian (Cyrillic, Bosnia and Herzegovina)"; break;
3858 case 0x08: name
= "Bosnian (Cyrillic, Bosnia and Herzegovina)"; break;
3862 switch (secondary
) {
3863 case 0x00: name
= "Slovak (Slovakia)"; break;
3864 case 0x01: name
= "Slovak"; break;
3868 switch (secondary
) {
3869 case 0x00: name
= "Albanian (Albania)"; break;
3870 case 0x01: name
= "Albanian"; break;
3874 switch (secondary
) {
3875 case 0x00: name
= "Swedish (Sweden)"; break;
3876 case 0x01: name
= "Swedish"; break;
3877 case 0x02: name
= "Swedish (Finland)"; break;
3881 switch (secondary
) {
3882 case 0x00: name
= "Thai (Thailand)"; break;
3883 case 0x01: name
= "Thai"; break;
3887 switch (secondary
) {
3888 case 0x00: name
= "Turkish (Turkey)"; break;
3889 case 0x01: name
= "Turkish"; break;
3893 switch (secondary
) {
3894 case 0x00: name
= "Urdu (Islamic Republic of Pakistan)"; break;
3895 case 0x01: name
= "Urdu"; break;
3899 switch (secondary
) {
3900 case 0x00: name
= "Indonesian (Indonesia)"; break;
3901 case 0x01: name
= "Indonesian"; break;
3905 switch (secondary
) {
3906 case 0x00: name
= "Ukrainian (Ukraine)"; break;
3907 case 0x01: name
= "Ukrainian"; break;
3911 switch (secondary
) {
3912 case 0x00: name
= "Belarusian (Belarus)"; break;
3913 case 0x01: name
= "Belarusian"; break;
3917 switch (secondary
) {
3918 case 0x00: name
= "Slovenian (Slovenia)"; break;
3919 case 0x01: name
= "Slovenian"; break;
3923 switch (secondary
) {
3924 case 0x00: name
= "Estonian (Estonia)"; break;
3925 case 0x01: name
= "Estonian"; break;
3929 switch (secondary
) {
3930 case 0x00: name
= "Latvian (Latvia)"; break;
3931 case 0x01: name
= "Latvian"; break;
3935 switch (secondary
) {
3936 case 0x00: name
= "Lithuanian (Lithuania)"; break;
3937 case 0x01: name
= "Lithuanian"; break;
3941 switch (secondary
) {
3942 case 0x01: name
= "Tajik (Tajikistan)"; break;
3946 switch (secondary
) {
3947 case 0x00: name
= "Farsi (Iran)"; break;
3948 case 0x01: name
= "Farsi"; break;
3952 switch (secondary
) {
3953 case 0x00: name
= "Vietnamese (Viet Nam)"; break;
3954 case 0x01: name
= "Vietnamese"; break;
3958 switch (secondary
) {
3959 case 0x00: name
= "Armenian (Armenia)"; break;
3960 case 0x01: name
= "Armenian"; break;
3964 switch (secondary
) {
3965 case 0x00: name
= "Azeri (Latin) (Azerbaijan)"; break;
3966 case 0x01: name
= "Azeri (Latin)"; break;
3967 case 0x02: name
= "Azeri (Cyrillic)"; break;
3971 switch (secondary
) {
3972 case 0x00: name
= "Basque (Spain)"; break;
3973 case 0x01: name
= "Basque"; break;
3977 switch (secondary
) {
3978 case 0x01: name
= "Upper Sorbian (Germany)"; break;
3979 case 0x02: name
= "Lower Sorbian (Germany)"; break;
3983 switch (secondary
) {
3984 case 0x00: name
= "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; break;
3985 case 0x01: name
= "FYRO Macedonian"; break;
3989 switch (secondary
) {
3990 case 0x00: name
= "Tswana (South Africa)"; break;
3991 case 0x01: name
= "Tswana"; break;
3995 switch (secondary
) {
3996 case 0x00: name
= "Xhosa (South Africa)"; break;
3997 case 0x01: name
= "Xhosa"; break;
4001 switch (secondary
) {
4002 case 0x00: name
= "Zulu (South Africa)"; break;
4003 case 0x01: name
= "Zulu"; break;
4007 switch (secondary
) {
4008 case 0x00: name
= "Afrikaans (South Africa)"; break;
4009 case 0x01: name
= "Afrikaans"; break;
4013 switch (secondary
) {
4014 case 0x00: name
= "Georgian (Georgia)"; break;
4015 case 0x01: name
= "Georgian"; break;
4019 switch (secondary
) {
4020 case 0x00: name
= "Faroese (Faroe Islands)"; break;
4021 case 0x01: name
= "Faroese"; break;
4025 switch (secondary
) {
4026 case 0x00: name
= "Hindi (India)"; break;
4027 case 0x01: name
= "Hindi"; break;
4031 switch (secondary
) {
4032 case 0x00: name
= "Maltese (Malta)"; break;
4033 case 0x01: name
= "Maltese"; break;
4037 switch (secondary
) {
4038 case 0x00: name
= "Sami (Northern) (Norway)"; break;
4039 case 0x01: name
= "Sami, Northern (Norway)"; break;
4040 case 0x02: name
= "Sami, Northern (Sweden)"; break;
4041 case 0x03: name
= "Sami, Northern (Finland)"; break;
4042 case 0x04: name
= "Sami, Lule (Norway)"; break;
4043 case 0x05: name
= "Sami, Lule (Sweden)"; break;
4044 case 0x06: name
= "Sami, Southern (Norway)"; break;
4045 case 0x07: name
= "Sami, Southern (Sweden)"; break;
4046 case 0x08: name
= "Sami, Skolt (Finland)"; break;
4047 case 0x09: name
= "Sami, Inari (Finland)"; break;
4051 switch (secondary
) {
4052 case 0x02: name
= "Irish (Ireland)"; break;
4056 switch (secondary
) {
4058 case 0x01: name
= "Malay (Malaysia)"; break;
4059 case 0x02: name
= "Malay (Brunei Darussalam)"; break;
4063 switch (secondary
) {
4064 case 0x00: name
= "Kazakh (Kazakhstan)"; break;
4065 case 0x01: name
= "Kazakh"; break;
4069 switch (secondary
) {
4070 case 0x00: name
= "Kyrgyz (Kyrgyzstan)"; break;
4071 case 0x01: name
= "Kyrgyz (Cyrillic)"; break;
4075 switch (secondary
) {
4076 case 0x00: name
= "Swahili (Kenya)"; break;
4077 case 0x01: name
= "Swahili"; break;
4081 switch (secondary
) {
4082 case 0x01: name
= "Turkmen (Turkmenistan)"; break;
4086 switch (secondary
) {
4087 case 0x00: name
= "Uzbek (Latin) (Uzbekistan)"; break;
4088 case 0x01: name
= "Uzbek (Latin)"; break;
4089 case 0x02: name
= "Uzbek (Cyrillic)"; break;
4093 switch (secondary
) {
4094 case 0x00: name
= "Tatar (Russia)"; break;
4095 case 0x01: name
= "Tatar"; break;
4099 switch (secondary
) {
4101 case 0x01: name
= "Bengali (India)"; break;
4105 switch (secondary
) {
4106 case 0x00: name
= "Punjabi (India)"; break;
4107 case 0x01: name
= "Punjabi"; break;
4111 switch (secondary
) {
4112 case 0x00: name
= "Gujarati (India)"; break;
4113 case 0x01: name
= "Gujarati"; break;
4117 switch (secondary
) {
4118 case 0x00: name
= "Tamil (India)"; break;
4119 case 0x01: name
= "Tamil"; break;
4123 switch (secondary
) {
4124 case 0x00: name
= "Telugu (India)"; break;
4125 case 0x01: name
= "Telugu"; break;
4129 switch (secondary
) {
4130 case 0x00: name
= "Kannada (India)"; break;
4131 case 0x01: name
= "Kannada"; break;
4135 switch (secondary
) {
4137 case 0x01: name
= "Malayalam (India)"; break;
4141 switch (secondary
) {
4142 case 0x01: name
= "Assamese (India)"; break;
4146 switch (secondary
) {
4147 case 0x00: name
= "Marathi (India)"; break;
4148 case 0x01: name
= "Marathi"; break;
4152 switch (secondary
) {
4153 case 0x00: name
= "Sanskrit (India)"; break;
4154 case 0x01: name
= "Sanskrit"; break;
4158 switch (secondary
) {
4159 case 0x00: name
= "Mongolian (Mongolia)"; break;
4160 case 0x01: name
= "Mongolian (Cyrillic)"; break;
4161 case 0x02: name
= "Mongolian (PRC)"; break;
4165 switch (secondary
) {
4166 case 0x01: name
= "Tibetan (PRC)"; break;
4167 case 0x02: name
= "Tibetan (Bhutan)"; break;
4171 switch (secondary
) {
4172 case 0x00: name
= "Welsh (United Kingdom)"; break;
4173 case 0x01: name
= "Welsh"; break;
4177 switch (secondary
) {
4178 case 0x01: name
= "Khmer (Cambodia)"; break;
4182 switch (secondary
) {
4183 case 0x01: name
= "Lao (Lao PDR)"; break;
4187 switch (secondary
) {
4188 case 0x00: name
= "Galician (Spain)"; break;
4189 case 0x01: name
= "Galician"; break;
4193 switch (secondary
) {
4194 case 0x00: name
= "Konkani (India)"; break;
4195 case 0x01: name
= "Konkani"; break;
4199 switch (secondary
) {
4200 case 0x00: name
= "Syriac (Syria)"; break;
4201 case 0x01: name
= "Syriac"; break;
4205 switch (secondary
) {
4206 case 0x01: name
= "Sinhala (Sri Lanka)"; break;
4210 switch (secondary
) {
4211 case 0x01: name
= "Inuktitut (Syllabics, Canada)"; break;
4212 case 0x02: name
= "Inuktitut (Latin, Canada)"; break;
4216 switch (secondary
) {
4217 case 0x01: name
= "Amharic (Ethiopia)"; break;
4221 switch (secondary
) {
4222 case 0x02: name
= "Tamazight (Algeria, Latin)"; break;
4226 switch (secondary
) {
4227 case 0x01: name
= "Nepali (Nepal)"; break;
4231 switch (secondary
) {
4232 case 0x01: name
= "Frisian (Netherlands)"; break;
4236 switch (secondary
) {
4237 case 0x01: name
= "Pashto (Afghanistan)"; break;
4241 switch (secondary
) {
4242 case 0x01: name
= "Filipino (Philippines)"; break;
4246 switch (secondary
) {
4247 case 0x00: name
= "Divehi (Maldives)"; break;
4248 case 0x01: name
= "Divehi"; break;
4252 switch (secondary
) {
4253 case 0x01: name
= "Hausa (Nigeria, Latin)"; break;
4257 switch (secondary
) {
4258 case 0x01: name
= "Yoruba (Nigeria)"; break;
4262 switch (secondary
) {
4264 case 0x01: name
= "Quechua (Bolivia)"; break;
4265 case 0x02: name
= "Quechua (Ecuador)"; break;
4266 case 0x03: name
= "Quechua (Peru)"; break;
4270 switch (secondary
) {
4271 case 0x00: name
= "Northern Sotho (South Africa)"; break;
4272 case 0x01: name
= "Northern Sotho"; break;
4276 switch (secondary
) {
4277 case 0x01: name
= "Bashkir (Russia)"; break;
4281 switch (secondary
) {
4282 case 0x01: name
= "Luxembourgish (Luxembourg)"; break;
4286 switch (secondary
) {
4287 case 0x01: name
= "Greenlandic (Greenland)"; break;
4291 switch (secondary
) {
4292 case 0x01: name
= "Yi (PRC)"; break;
4296 switch (secondary
) {
4297 case 0x01: name
= "Mapudungun (Chile)"; break;
4301 switch (secondary
) {
4302 case 0x01: name
= "Mohawk (Mohawk)"; break;
4306 switch (secondary
) {
4307 case 0x01: name
= "Breton (France)"; break;
4311 switch (secondary
) {
4312 case 0x00: name
= "Invariant Language (Invariant Country)"; break;
4316 switch (secondary
) {
4317 case 0x01: name
= "Uighur (PRC)"; break;
4321 switch (secondary
) {
4322 case 0x00: name
= "Maori (New Zealand)"; break;
4323 case 0x01: name
= "Maori"; break;
4327 switch (secondary
) {
4328 case 0x01: name
= "Corsican (France)"; break;
4332 switch (secondary
) {
4333 case 0x01: name
= "Alsatian (France)"; break;
4337 switch (secondary
) {
4338 case 0x01: name
= "Yakut (Russia)"; break;
4342 switch (secondary
) {
4343 case 0x01: name
= "K'iche (Guatemala)"; break;
4347 switch (secondary
) {
4348 case 0x01: name
= "Kinyarwanda (Rwanda)"; break;
4352 switch (secondary
) {
4353 case 0x01: name
= "Wolof (Senegal)"; break;
4357 switch (secondary
) {
4358 case 0x01: name
= "Dari (Afghanistan)"; break;
4363 name
= "Language Neutral";
4368 name
= "Language Neutral";
4370 return copy_lang (lang_out
, lang_len
, name
);