This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mono / metadata / process.c
1 /*
2  * process.c: System.Diagnostics.Process support
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11
12 #include <glib.h>
13 #include <string.h>
14
15 #include <mono/metadata/object.h>
16 #include <mono/metadata/process.h>
17 #include <mono/metadata/assembly.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/image.h>
20 #include <mono/metadata/cil-coff.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/utils/strenc.h>
23 #include <mono/io-layer/io-layer.h>
24 /* FIXME: fix this code to not depend so much on the inetrnals */
25 #include <mono/metadata/class-internals.h>
26
27 #undef DEBUG
28
29 HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
30 {
31         HANDLE handle;
32         
33         MONO_ARCH_SAVE_REGS;
34
35         /* GetCurrentProcess returns a pseudo-handle, so use
36          * OpenProcess instead
37          */
38         handle=OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
39         
40         if(handle==NULL) {
41                 /* FIXME: Throw an exception */
42                 return(NULL);
43         }
44         
45         return(handle);
46 }
47
48 guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void)
49 {
50         MONO_ARCH_SAVE_REGS;
51
52         return(GetCurrentProcessId ());
53 }
54
55 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this,
56                                                                  HANDLE process)
57 {
58         MONO_ARCH_SAVE_REGS;
59
60 #ifdef THREAD_DEBUG
61         g_message (G_GNUC_PRETTY_FUNCTION ": Closing process %p, handle %p",
62                    this, process);
63 #endif
64
65         CloseHandle (process);
66 }
67
68 #define STASH_SYS_ASS(this) \
69         if(system_assembly == NULL) { \
70                 system_assembly=this->vtable->klass->image; \
71         }
72
73 static MonoImage *system_assembly=NULL;
74
75 static guint32 unicode_chars (const gunichar2 *str)
76 {
77         guint32 len=0;
78         
79         do {
80                 if(str[len]=='\0') {
81                         return(len);
82                 }
83                 len++;
84         } while(1);
85 }
86
87 static guint32 unicode_bytes (const gunichar2 *str)
88 {
89         guint32 len=0;
90         
91         do {
92                 if(str[len]=='\0') {
93                         /* Include the terminators */
94                         return((len*2)+2);
95                 }
96                 len++;
97         } while(1);
98 }
99
100 static void process_set_field_object (MonoObject *obj, const guchar *fieldname,
101                                       MonoObject *data)
102 {
103         MonoClassField *field;
104
105 #ifdef DEBUG
106         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to object at %p",
107                    fieldname, data);
108 #endif
109
110         field=mono_class_get_field_from_name (mono_object_class (obj),
111                                               fieldname);
112         *(MonoObject **)(((char *)obj) + field->offset)=data;
113 }
114
115 static void process_set_field_string (MonoObject *obj, const guchar *fieldname,
116                                       const gunichar2 *val, guint32 len)
117 {
118         MonoClassField *field;
119         MonoString *string;
120
121 #ifdef DEBUG
122         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
123                    fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL));
124 #endif
125
126         string=mono_string_new_utf16 (mono_object_domain (obj), val, len);
127         
128         field=mono_class_get_field_from_name (mono_object_class (obj),
129                                               fieldname);
130         *(MonoString **)(((char *)obj) + field->offset)=string;
131 }
132
133 static void process_set_field_string_utf8 (MonoObject *obj,
134                                            const guchar *fieldname,
135                                            const guchar *val)
136 {
137         MonoClassField *field;
138         MonoString *string;
139
140 #ifdef DEBUG
141         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
142                    fieldname, val);
143 #endif
144
145         string=mono_string_new (mono_object_domain (obj), val);
146         
147         field=mono_class_get_field_from_name (mono_object_class (obj),
148                                               fieldname);
149         *(MonoString **)(((char *)obj) + field->offset)=string;
150 }
151
152 static void process_set_field_int (MonoObject *obj, const guchar *fieldname,
153                                    guint32 val)
154 {
155         MonoClassField *field;
156
157 #ifdef DEBUG
158         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
159                    fieldname, val);
160 #endif
161         
162         field=mono_class_get_field_from_name (mono_object_class (obj),
163                                               fieldname);
164         *(guint32 *)(((char *)obj) + field->offset)=val;
165 }
166
167 static void process_set_field_bool (MonoObject *obj, const guchar *fieldname,
168                                     gboolean val)
169 {
170         MonoClassField *field;
171
172 #ifdef DEBUG
173         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %s",
174                    fieldname, val?"TRUE":"FALSE");
175 #endif
176         
177         field=mono_class_get_field_from_name (mono_object_class (obj),
178                                               fieldname);
179         *(guint8 *)(((char *)obj) + field->offset)=val;
180 }
181
182 typedef struct {
183         guint16 data_len;
184         guint16 value_len;
185         guint16 type;
186         gunichar2 *key;
187 } version_data;
188
189 /* Returns a pointer to the value data, because theres no way to know
190  * how big that data is (value_len is set to zero for most blocks :-()
191  */
192 static gpointer process_get_versioninfo_block (gpointer data,
193                                                version_data *block)
194 {
195         block->data_len=*((guint16 *)data);
196         data = (char *)data + sizeof(guint16);
197         block->value_len=*((guint16 *)data);
198         data = (char *)data + sizeof(guint16);
199
200         /* No idea what the type is supposed to indicate */
201         block->type=*((guint16 *)data);
202         data = (char *)data + sizeof(guint16);
203         block->key=((gunichar2 *)data);
204
205         /* skip over the key (including the terminator) */
206         data=((gunichar2 *)data)+(unicode_chars (block->key)+1);
207
208         /* align on a 32-bit boundary */
209         data=(gpointer)(((unsigned)data+3) & (~3));
210         
211         return(data);
212 }
213
214 /* Returns a pointer to the byte following the Var block */
215 static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr,
216                                         guint16 data_len)
217 {
218         /* Not currently interested in the VarFileInfo block.  This
219          * might change if language support is needed for file version
220          * strings (VarFileInfo contains lists of supported
221          * languages.)
222          */
223         version_data block;
224
225         /* data_ptr is pointing at a Var block of length data_len */
226         data_ptr=process_get_versioninfo_block (data_ptr, &block);
227         data_ptr=((guchar *)data_ptr)+block.value_len;
228
229         return(data_ptr);
230 }
231
232 /* Returns a pointer to the byte following the String block, or NULL
233  * if the data read hits padding.  We can't recover from this because
234  * the data length does not include padding bytes, so it's not
235  * possible to just return the start position + length.
236  */
237 static gpointer process_read_string_block (MonoObject *filever,
238                                            gpointer data_ptr,
239                                            guint16 data_len,
240                                            gboolean store)
241 {
242         version_data block;
243         guint16 string_len=0;
244         guchar comments_key[]= {'C', '\0', 'o', '\0', 'm', '\0',
245                                 'm', '\0', 'e', '\0', 'n', '\0',
246                                 't', '\0', 's', '\0', '\0', '\0'};
247         guchar compname_key[]= {'C', '\0', 'o', '\0', 'm', '\0',
248                                 'p', '\0', 'a', '\0', 'n', '\0',
249                                 'y', '\0', 'N', '\0', 'a', '\0',
250                                 'm', '\0', 'e', '\0', '\0', '\0'};
251         guchar filedesc_key[]= {'F', '\0', 'i', '\0', 'l', '\0',
252                                 'e', '\0', 'D', '\0', 'e', '\0',
253                                 's', '\0', 'c', '\0', 'r', '\0',
254                                 'i', '\0', 'p', '\0', 't', '\0',
255                                 'i', '\0', 'o', '\0', 'n', '\0',
256                                 '\0', '\0'};
257         guchar filever_key[]= {'F', '\0', 'i', '\0', 'l', '\0',
258                                'e', '\0', 'V', '\0', 'e', '\0',
259                                'r', '\0', 's', '\0', 'i', '\0',
260                                'o', '\0', 'n', '\0', '\0', '\0'};
261         guchar internal_key[]= {'I', '\0', 'n', '\0', 't', '\0',
262                                 'e', '\0', 'r', '\0', 'n', '\0',
263                                 'a', '\0', 'l', '\0', 'N', '\0',
264                                 'a', '\0', 'm', '\0', 'e', '\0',
265                                 '\0', '\0'};
266         guchar legalcpy_key[]= {'L', '\0', 'e', '\0', 'g', '\0',
267                                 'a', '\0', 'l', '\0', 'C', '\0',
268                                 'o', '\0', 'p', '\0', 'y', '\0',
269                                 'r', '\0', 'i', '\0', 'g', '\0',
270                                 'h', '\0', 't', '\0', '\0', '\0'};
271         guchar legaltrade_key[]= {'L', '\0', 'e', '\0', 'g', '\0',
272                                   'a', '\0', 'l', '\0', 'T', '\0',
273                                   'r', '\0', 'a', '\0', 'd', '\0',
274                                   'e', '\0', 'm', '\0', 'a', '\0',
275                                   'r', '\0', 'k', '\0', 's', '\0',
276                                   '\0', '\0'};
277         guchar origfile_key[]= {'O', '\0', 'r', '\0', 'i', '\0',
278                                 'g', '\0', 'i', '\0', 'n', '\0',
279                                 'a', '\0', 'l', '\0', 'F', '\0',
280                                 'i', '\0', 'l', '\0', 'e', '\0',
281                                 'n', '\0', 'a', '\0', 'm', '\0',
282                                 'e', '\0', '\0', '\0'};
283         guchar privbuild_key[]= {'P', '\0', 'r', '\0', 'i', '\0',
284                                  'v', '\0', 'a', '\0', 't', '\0',
285                                  'e', '\0', 'B', '\0', 'u', '\0',
286                                  'i', '\0', 'l', '\0', 'd', '\0',
287                                  '\0', '\0'};
288         guchar prodname_key[]= {'P', '\0', 'r', '\0', 'o', '\0',
289                                 'd', '\0', 'u', '\0', 'c', '\0',
290                                 't', '\0', 'N', '\0', 'a', '\0',
291                                 'm', '\0', 'e', '\0', '\0', '\0'};
292         guchar prodver_key[]= {'P', '\0', 'r', '\0', 'o', '\0',
293                                'd', '\0', 'u', '\0', 'c', '\0',
294                                't', '\0', 'V', '\0', 'e', '\0',
295                                'r', '\0', 's', '\0', 'i', '\0',
296                                'o', '\0', 'n', '\0', '\0', '\0'};
297         guchar specbuild_key[]= {'S', '\0', 'p', '\0', 'e', '\0',
298                                  'c', '\0', 'i', '\0', 'a', '\0',
299                                  'l', '\0', 'B', '\0', 'u', '\0',
300                                  'i', '\0', 'l', '\0', 'd', '\0',
301                                  '\0', '\0'};
302         
303         /* data_ptr is pointing at an array of one or more String
304          * blocks with total length (not including alignment padding)
305          * of data_len.
306          */
307         while(string_len<data_len) {
308                 gunichar2 *value;
309                 
310                 /* align on a 32-bit boundary */
311                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
312
313                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
314                 if(block.data_len==0) {
315                         /* We must have hit padding, so give up
316                          * processing now
317                          */
318 #ifdef DEBUG
319                         g_message (G_GNUC_PRETTY_FUNCTION
320                                    ": Hit 0-length block, giving up");
321 #endif
322                         return(NULL);
323                 }
324                 
325                 string_len=string_len+block.data_len;
326                 value=(gunichar2 *)data_ptr;
327                 /* Skip over the value */
328                 data_ptr=((gunichar2 *)data_ptr)+block.value_len;
329
330                 if(store==TRUE) {
331                         if(!memcmp (block.key, &comments_key,
332                                     unicode_bytes (block.key))) {
333
334                                 process_set_field_string (filever, "comments", value, unicode_chars (value));
335                         } else if (!memcmp (block.key, &compname_key,
336                                             unicode_bytes (block.key))) {
337                                 process_set_field_string (filever, "companyname", value, unicode_chars (value));
338                         } else if (!memcmp (block.key, &filedesc_key,
339                                             unicode_bytes (block.key))) {
340                                 process_set_field_string (filever, "filedescription", value, unicode_chars (value));
341                         } else if (!memcmp (block.key, &filever_key,
342                                             unicode_bytes (block.key))) {
343                                 process_set_field_string (filever, "fileversion", value, unicode_chars (value));
344                         } else if (!memcmp (block.key, &internal_key,
345                                             unicode_bytes (block.key))) {
346                                 process_set_field_string (filever, "internalname", value, unicode_chars (value));
347                         } else if (!memcmp (block.key, &legalcpy_key,
348                                             unicode_bytes (block.key))) {
349                                 process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
350                         } else if (!memcmp (block.key, &legaltrade_key,
351                                             unicode_bytes (block.key))) {
352                                 process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
353                         } else if (!memcmp (block.key, &origfile_key,
354                                             unicode_bytes (block.key))) {
355                                 process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
356                         } else if (!memcmp (block.key, &privbuild_key,
357                                             unicode_bytes (block.key))) {
358                                 process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
359                         } else if (!memcmp (block.key, &prodname_key,
360                                             unicode_bytes (block.key))) {
361                                 process_set_field_string (filever, "productname", value, unicode_chars (value));
362                         } else if (!memcmp (block.key, &prodver_key,
363                                             unicode_bytes (block.key))) {
364                                 process_set_field_string (filever, "productversion", value, unicode_chars (value));
365                         } else if (!memcmp (block.key, &specbuild_key,
366                                             unicode_bytes (block.key))) {
367                                 process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
368                         } else {
369                                 /* Not an error, just not interesting
370                                  * in this case
371                                  */
372                         }
373                 }
374         }
375         
376         return(data_ptr);
377 }
378
379 /* returns a pointer to the byte following the Stringtable block, or
380  * NULL if the data read hits padding.  We can't recover from this
381  * because the data length does not include padding bytes, so it's not
382  * possible to just return the start position + length
383  */
384 static gpointer process_read_stringtable_block (MonoObject *filever,
385                                                 gpointer data_ptr,
386                                                 guint16 data_len)
387 {
388         version_data block;
389         gchar *language;
390         guint16 string_len=36;  /* length of the StringFileInfo block */
391
392         /* data_ptr is pointing at an array of StringTable blocks,
393          * with total length (not including alignment padding) of
394          * data_len.
395          */
396
397         while(string_len<data_len) {
398                 /* align on a 32-bit boundary */
399                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
400
401                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
402                 if(block.data_len==0) {
403                         /* We must have hit padding, so give up
404                          * processing now
405                          */
406 #ifdef DEBUG
407                         g_message (G_GNUC_PRETTY_FUNCTION
408                                    ": Hit 0-length block, giving up");
409 #endif
410                         return(NULL);
411                 }
412                 string_len=string_len+block.data_len;
413
414                 language = g_utf16_to_utf8 (block.key, unicode_bytes (block.key), NULL, NULL, NULL);
415                 g_strdown (language);
416                 if (!strcmp (language, "007f04b0") || !strcmp (language, "000004b0")) {
417                         /* Got the one we're interested in */
418                         process_set_field_string_utf8 (filever, "language",
419                                                        "Language Neutral");
420                         
421                         data_ptr=process_read_string_block (filever, data_ptr,
422                                                             block.data_len,
423                                                             TRUE);
424                 } else {
425                         /* Some other language.  We might want to do
426                          * something with this in the future.
427                          */
428                         data_ptr=process_read_string_block (filever, data_ptr,
429                                                             block.data_len,
430                                                             FALSE);
431                 }
432                 g_free (language);
433
434                 if(data_ptr==NULL) {
435                         /* Child block hit padding */
436 #ifdef DEBUG
437                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
438 #endif
439                         return(NULL);
440                 }
441         }
442                 
443         return(data_ptr);
444 }
445
446 static void process_read_fixedfileinfo_block (MonoObject *filever,
447                                               VS_FIXEDFILEINFO *ffi)
448 {
449 #ifdef DEBUG
450         g_message (G_GNUC_PRETTY_FUNCTION ": ffi: sig 0x%x, strucver 0x%x, fileverm 0x%x, fileverl 0x%x, prodverm 0x%x, prodverl 0x%x, ffmask 0x%x, ff 0x%x, os 0x%x, type 0x%x, subtype 0x%x, datem 0x%x, datel 0x%x", ffi->dwSignature, ffi->dwStrucVersion, ffi->dwFileVersionMS, ffi->dwFileVersionLS, ffi->dwProductVersionMS, ffi->dwProductVersionLS, ffi->dwFileFlagsMask, ffi->dwFileFlags, ffi->dwFileOS, ffi->dwFileType, ffi->dwFileSubtype, ffi->dwFileDateMS, ffi->dwFileDateLS);
451 #endif
452                 
453         process_set_field_int (filever, "filemajorpart",
454                                HIWORD (ffi->dwFileVersionMS));
455         process_set_field_int (filever, "fileminorpart",
456                                LOWORD (ffi->dwFileVersionMS));
457         process_set_field_int (filever, "filebuildpart",
458                                HIWORD (ffi->dwFileVersionLS));
459         process_set_field_int (filever, "fileprivatepart",
460                                LOWORD (ffi->dwFileVersionLS));
461                 
462         process_set_field_int (filever, "productmajorpart",
463                                HIWORD (ffi->dwProductVersionMS));
464         process_set_field_int (filever, "productminorpart",
465                                LOWORD (ffi->dwProductVersionMS));
466         process_set_field_int (filever, "productbuildpart",
467                                HIWORD (ffi->dwProductVersionLS));
468         process_set_field_int (filever, "productprivatepart",
469                                LOWORD (ffi->dwProductVersionLS));
470         
471         process_set_field_bool (filever, "isdebug",
472                                 ffi->dwFileFlags&VS_FF_DEBUG);
473         process_set_field_bool (filever, "isprerelease",
474                                 ffi->dwFileFlags&VS_FF_PRERELEASE);
475         process_set_field_bool (filever, "ispatched",
476                                 ffi->dwFileFlags&VS_FF_PATCHED);
477         process_set_field_bool (filever, "isprivatebuild",
478                                 ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
479         process_set_field_bool (filever, "isspecialbuild",
480                                 ffi->dwFileFlags&VS_FF_SPECIALBUILD);
481 }
482
483 static void process_get_fileversion (MonoObject *filever, MonoImage *image)
484 {
485         MonoPEResourceDataEntry *version_info;
486         gpointer data;
487         VS_FIXEDFILEINFO *ffi;
488         gpointer data_ptr;
489         version_data block;
490         gint32 data_len; /* signed to guard against underflow */
491         guchar vs_key[]= {'V', '\0', 'S', '\0', '_', '\0', 'V', '\0',
492                           'E', '\0', 'R', '\0', 'S', '\0', 'I', '\0',
493                           'O', '\0', 'N', '\0', '_', '\0', 'I', '\0',
494                           'N', '\0', 'F', '\0', 'O', '\0', '\0', '\0'
495         };
496         guchar var_key[]= {'V', '\0', 'a', '\0', 'r', '\0', 'F', '\0',
497                            'i', '\0', 'l', '\0', 'e', '\0', 'I', '\0',
498                            'n', '\0', 'f', '\0', 'o', '\0', '\0', '\0', 
499         };
500         guchar str_key[]= {'S', '\0', 't', '\0', 'r', '\0', 'i', '\0',
501                            'n', '\0', 'g', '\0', 'F', '\0', 'i', '\0',
502                            'l', '\0', 'e', '\0', 'I', '\0', 'n', '\0',
503                            'f', '\0', 'o', '\0', '\0', '\0', 
504         };
505         
506         version_info=mono_image_lookup_resource (image,
507                                                  MONO_PE_RESOURCE_ID_VERSION,
508                                                  0, NULL);
509 #ifdef DEBUG
510         g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
511                    version_info);
512 #endif
513
514         if(version_info==NULL) {
515                 return;
516         }
517         
518         data=mono_image_rva_map (image,
519                                version_info->rde_data_offset);
520         if(data==NULL) {
521                 return;
522         }
523
524         /* See io-layer/versioninfo.h for the gory details on how this
525          * data is laid out. (data should be pointing to
526          * VS_VERSIONINFO data).
527          */
528
529         data_ptr=process_get_versioninfo_block (data, &block);
530                 
531         data_len=block.data_len;
532                 
533         if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
534 #ifdef DEBUG
535                 g_message (G_GNUC_PRETTY_FUNCTION
536                            ": FIXEDFILEINFO size mismatch");
537 #endif
538                 return;
539         }
540
541         if(memcmp (block.key, &vs_key, unicode_bytes (block.key))) {
542 #ifdef DEBUG
543                 g_message (G_GNUC_PRETTY_FUNCTION
544                            ": VS_VERSION_INFO mismatch");
545 #endif
546                 return;
547         }
548
549         ffi=((VS_FIXEDFILEINFO *)data_ptr);
550         data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
551         if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
552            (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
553 #ifdef DEBUG
554                 g_message (G_GNUC_PRETTY_FUNCTION
555                            ": FIXEDFILEINFO bad signature");
556 #endif
557                 return;
558         }
559         process_read_fixedfileinfo_block (filever, ffi);
560         
561         /* Subtract the 92 bytes we've already seen */
562         data_len -= 92;
563         
564         /* There now follow zero or one StringFileInfo blocks and zero
565          * or one VarFileInfo blocks
566          */
567         while(data_len > 0) {
568                 /* align on a 32-bit boundary */
569                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
570
571                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
572                 if(block.data_len==0) {
573                         /* We must have hit padding, so give up
574                          * processing now
575                          */
576 #ifdef DEBUG
577                         g_message (G_GNUC_PRETTY_FUNCTION
578                                    ": Hit 0-length block, giving up");
579 #endif
580                         return;
581                 }
582                 
583                 data_len=data_len-block.data_len;
584
585                 if(!memcmp (block.key, &var_key, unicode_bytes (block.key))) {
586                         data_ptr=process_read_var_block (filever, data_ptr,
587                                                          block.data_len);
588                 } else if (!memcmp (block.key, &str_key,
589                                     unicode_bytes (block.key))) {
590                         data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
591                 } else {
592                         /* Bogus data */
593 #ifdef DEBUG
594                         g_message (G_GNUC_PRETTY_FUNCTION
595                                    ": Not a valid VERSIONINFO child block");
596                         return;
597 #endif
598                 }
599
600                 if(data_ptr==NULL) {
601                         /* Child block hit padding */
602 #ifdef DEBUG
603                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
604 #endif
605                         return;
606                 }
607         }
608 }
609
610 static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
611 {
612         MonoClass *proc_class, *filever_class;
613         MonoObject *item, *filever;
614         MonoDomain *domain=mono_domain_get ();
615         gchar *modulename;
616         const char* filename;
617         
618         /* Build a System.Diagnostics.ProcessModule with the data.
619          * Leave BaseAddress and EntryPointAddress set to NULL,
620          * FileName is ass->image->name, FileVersionInfo is an object
621          * constructed from the PE image data referenced by
622          * ass->image, ModuleMemorySize set to 0, ModuleName the last
623          * component of FileName.
624          */
625         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
626                                          "ProcessModule");
627         item=mono_object_new (domain, proc_class);
628
629         filever_class=mono_class_from_name (system_assembly,
630                                             "System.Diagnostics",
631                                             "FileVersionInfo");
632         filever=mono_object_new (domain, filever_class);
633         
634 #ifdef DEBUG
635         g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d], ModuleName [%s]", ass->image->name, ass->aname.major, ass->aname.minor, ass->aname.build, ass->aname.revision, ass->image->name);
636 #endif
637
638         process_get_fileversion (filever, mono_assembly_get_image (ass));
639
640         filename = mono_image_get_filename (mono_assembly_get_image (ass));
641         process_set_field_string_utf8 (filever, "filename", filename);
642         process_set_field_string_utf8 (item, "filename", filename);
643         process_set_field_object (item, "version_info", filever);
644
645         modulename=g_path_get_basename (filename);
646         process_set_field_string_utf8 (item, "modulename", modulename);
647         g_free (modulename);
648
649         g_ptr_array_add (modules, item);
650 }
651
652 static void process_scan_modules (gpointer data, gpointer user_data)
653 {
654         MonoAssembly *ass=data;
655         GPtrArray *modules=user_data;
656
657         /* The main assembly is already in the list */
658         if(mono_assembly_get_main () != ass) {
659                 process_add_module (modules, ass);
660         }
661 }
662
663
664 /* Returns an array of System.Diagnostics.ProcessModule */
665 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
666 {
667         /* I was going to use toolhelp for this, but then realised I
668          * was being an idiot :)
669          *
670          * (Toolhelp would give shared libraries open by the runtime,
671          * as well as open assemblies.  On windows my tests didnt find
672          * the assemblies loaded by mono either.)
673          */
674         GPtrArray *modules_list=g_ptr_array_new ();
675         MonoArray *arr;
676         guint32 i;
677         
678         MONO_ARCH_SAVE_REGS;
679
680         STASH_SYS_ASS (this);
681         
682         /* Make sure the first entry is the main module */
683         process_add_module (modules_list, mono_assembly_get_main ());
684         
685         mono_assembly_foreach (process_scan_modules, modules_list);
686
687         /* Build a MonoArray out of modules_list */
688         arr=mono_array_new (mono_domain_get (), mono_get_object_class (),
689                             modules_list->len);
690         
691         for(i=0; i<modules_list->len; i++) {
692                 mono_array_set (arr, MonoObject *, i,
693                                 g_ptr_array_index (modules_list, i));
694         }
695         
696         g_ptr_array_free (modules_list, FALSE);
697         
698         return(arr);
699 }
700
701 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
702 {
703         MonoImage *image;
704         guchar *filename_utf8;
705         
706         MONO_ARCH_SAVE_REGS;
707
708         STASH_SYS_ASS (this);
709         
710         filename_utf8=mono_string_to_utf8 (filename);
711         image=mono_image_open (filename_utf8, NULL);
712         g_free (filename_utf8);
713         
714         if(image==NULL) {
715                 /* FIXME: an exception might be appropriate here */
716 #ifdef DEBUG
717                 g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
718 #endif
719
720                 return;
721         }
722         
723         process_get_fileversion (this, image);
724         process_set_field_string_utf8 (this, "filename", mono_image_get_filename (image));
725         
726         mono_image_close (image);
727 }
728
729 static gboolean
730 complete_path (const gunichar2 *appname, gunichar2 **completed)
731 {
732         gchar *utf8app;
733         gchar *found;
734
735         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
736         if (g_path_is_absolute (utf8app)) {
737                 g_free (utf8app);
738                 return FALSE;
739         }
740
741         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
742                 g_free (utf8app);
743                 return FALSE;
744         }
745         
746         found = g_find_program_in_path (utf8app);
747         if (found == NULL) {
748                 *completed = NULL;
749                 g_free (utf8app);
750                 return FALSE;
751         }
752
753         *completed = g_utf8_to_utf16 (found, -1, NULL, NULL, NULL);
754         g_free (found);
755         g_free (utf8app);
756         return TRUE;
757 }
758
759 MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *appname, MonoString *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
760 {
761         gboolean ret;
762         gunichar2 *dir;
763         STARTUPINFO startinfo={0};
764         PROCESS_INFORMATION procinfo;
765         gunichar2 *shell_path = NULL;
766         gchar *env_vars = NULL;
767         gboolean free_shell_path = TRUE;
768         
769         MONO_ARCH_SAVE_REGS;
770
771         startinfo.cb=sizeof(STARTUPINFO);
772         startinfo.dwFlags=STARTF_USESTDHANDLES;
773         startinfo.hStdInput=stdin_handle;
774         startinfo.hStdOutput=stdout_handle;
775         startinfo.hStdError=stderr_handle;
776         
777         if (process_info->use_shell) {
778                 const gchar *spath;
779                 const gchar *shell_args;
780 #ifdef PLATFORM_WIN32
781                 spath = g_getenv ("COMSPEC");
782                 shell_args = "/c %s";
783 #else
784                 spath = g_getenv ("SHELL");
785                 shell_args = "-c %s";
786 #endif
787                 if (spath != NULL) {
788                         gint dummy;
789                         gchar *newcmd, *tmp;
790                         gchar *quoted;
791
792                         shell_path = mono_unicode_from_external (spath, &dummy);
793                         tmp = mono_string_to_utf8 (cmd);
794                         quoted = g_shell_quote (tmp);
795 #ifdef PLATFORM_WIN32
796                         {
797                                 gchar *q = quoted;
798                                 while (*q) {
799                                         if (*q == '\'')
800                                                 *q = '\"';
801                                         q++;
802                                 }
803                         }
804 #endif
805                         newcmd = g_strdup_printf (shell_args, quoted);
806                         g_free (quoted);
807                         g_free (tmp);
808                         cmd = mono_string_new (mono_domain_get (), newcmd);
809                         g_free (newcmd);
810                 }
811         } else {
812                 shell_path = mono_string_chars (appname);
813                 free_shell_path = complete_path (shell_path, &shell_path);
814                 if (shell_path == NULL) {
815                         process_info->pid = -ERROR_FILE_NOT_FOUND;
816                         return FALSE;
817                 }
818         }
819
820         if (process_info->env_keys != NULL) {
821                 gint i, len; 
822                 MonoString *ms;
823                 MonoString *key, *value;
824                 gunichar2 *str, *ptr;
825                 gunichar2 *equals16;
826
827                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
828                         ms = mono_array_get (process_info->env_values, MonoString *, i);
829                         if (ms == NULL)
830                                 continue;
831
832                         len += mono_string_length (ms) * sizeof (gunichar2);
833                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
834                         len += mono_string_length (ms) * sizeof (gunichar2);
835                         len += 2 * sizeof (gunichar2);
836                 }
837
838                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
839                 ptr = str = g_new0 (gunichar2, len + 1);
840                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
841                         value = mono_array_get (process_info->env_values, MonoString *, i);
842                         if (value == NULL)
843                                 continue;
844
845                         key = mono_array_get (process_info->env_keys, MonoString *, i);
846                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
847                         ptr += mono_string_length (key);
848
849                         memcpy (ptr, equals16, sizeof (gunichar2));
850                         ptr++;
851
852                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
853                         ptr += mono_string_length (value);
854                         ptr++;
855                 }
856
857                 g_free (equals16);
858                 env_vars = (gchar *) str;
859         }
860         
861         /* The default dir name is "".  Turn that into NULL to mean
862          * "current directory"
863          */
864         if(mono_string_length (dirname)==0) {
865                 dir=NULL;
866         } else {
867                 dir=mono_string_chars (dirname);
868         }
869         
870         ret=CreateProcess (shell_path, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo);
871
872         g_free (env_vars);
873         if (free_shell_path)
874                 g_free (shell_path);
875
876         if(ret) {
877                 process_info->process_handle=procinfo.hProcess;
878                 /*process_info->thread_handle=procinfo.hThread;*/
879                 process_info->thread_handle=NULL;
880                 CloseHandle(procinfo.hThread);
881                 process_info->pid=procinfo.dwProcessId;
882                 process_info->tid=procinfo.dwThreadId;
883         } else {
884                 process_info->pid = -GetLastError ();
885         }
886         
887         return(ret);
888 }
889
890 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
891 {
892         guint32 ret;
893         
894         MONO_ARCH_SAVE_REGS;
895
896         if(ms<0) {
897                 /* Wait forever */
898                 ret=WaitForSingleObjectEx (process, INFINITE, TRUE);
899         } else {
900                 ret=WaitForSingleObjectEx (process, ms, TRUE);
901         }
902         if(ret==WAIT_OBJECT_0) {
903                 return(TRUE);
904         } else {
905                 return(FALSE);
906         }
907 }
908
909 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
910 {
911         gboolean ret;
912         gint64 ticks;
913         FILETIME create_time, exit_time, kernel_time, user_time;
914         
915         MONO_ARCH_SAVE_REGS;
916
917         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
918                              &user_time);
919         if(ret==TRUE) {
920                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
921                         exit_time.dwLowDateTime;
922                 
923                 return(ticks);
924         } else {
925                 return(0);
926         }
927 }
928
929 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
930 {
931         gboolean ret;
932         gint64 ticks;
933         FILETIME create_time, exit_time, kernel_time, user_time;
934         
935         MONO_ARCH_SAVE_REGS;
936
937         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
938                              &user_time);
939         if(ret==TRUE) {
940                 ticks=((guint64)create_time.dwHighDateTime << 32) +
941                         create_time.dwLowDateTime;
942                 
943                 return(ticks);
944         } else {
945                 return(0);
946         }
947 }
948
949 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
950 {
951         guint32 code;
952         
953         MONO_ARCH_SAVE_REGS;
954
955         GetExitCodeProcess (process, &code);
956         
957 #ifdef DEBUG
958         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
959 #endif
960         
961         return(code);
962 }
963
964 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
965 {
966         MonoString *string;
967         gboolean ok;
968         HMODULE mod;
969         gunichar2 name[MAX_PATH];
970         guint32 needed;
971         guint32 len;
972         
973         MONO_ARCH_SAVE_REGS;
974
975         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
976         if(ok==FALSE) {
977                 return(NULL);
978         }
979         
980         len=GetModuleBaseName (process, mod, name, sizeof(name));
981         if(len==0) {
982                 return(NULL);
983         }
984         
985 #ifdef DEBUG
986         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
987                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
988 #endif
989         
990         string=mono_string_new_utf16 (mono_domain_get (), name, len);
991         
992         return(string);
993 }
994
995 /* Returns an array of pids */
996 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
997 {
998         MonoArray *procs;
999         gboolean ret;
1000         guint32 needed, count, i;
1001         guint32 pids[1024];
1002
1003         MONO_ARCH_SAVE_REGS;
1004
1005         ret=EnumProcesses (pids, sizeof(pids), &needed);
1006         if(ret==FALSE) {
1007                 /* FIXME: throw an exception */
1008                 return(NULL);
1009         }
1010         
1011         count=needed/sizeof(guint32);
1012         procs=mono_array_new (mono_domain_get (), mono_get_int32_class (),
1013                               count);
1014         for(i=0; i<count; i++) {
1015                 mono_array_set (procs, guint32, i, pids[i]);
1016         }
1017         
1018         return(procs);
1019 }
1020
1021 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
1022 {
1023         gboolean ret;
1024         size_t ws_min, ws_max;
1025         
1026         MONO_ARCH_SAVE_REGS;
1027
1028         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1029         *min=(guint32)ws_min;
1030         *max=(guint32)ws_max;
1031         
1032         return(ret);
1033 }
1034
1035 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
1036 {
1037         gboolean ret;
1038         size_t ws_min;
1039         size_t ws_max;
1040         
1041         MONO_ARCH_SAVE_REGS;
1042
1043         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1044         if(ret==FALSE) {
1045                 return(FALSE);
1046         }
1047         
1048         if(use_min==TRUE) {
1049                 ws_min=min;
1050         } else {
1051                 ws_max=max;
1052         }
1053         
1054         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
1055
1056         return(ret);
1057 }
1058
1059 MonoBoolean
1060 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
1061 {
1062         MONO_ARCH_SAVE_REGS;
1063
1064         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
1065
1066         return TerminateProcess (process, -sig);
1067 }
1068