2008-03-24 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / mono-config.c
1 /*
2  * mono-config.c
3  *
4  * Runtime and assembly configuration file support routines.
5  *
6  * Author: Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2002 Ximian, Inc.
9  */
10 #include "config.h"
11 #include <glib.h>
12 #include <string.h>
13
14 #include "mono/metadata/assembly.h"
15 #include "mono/metadata/loader.h"
16 #include "mono/metadata/mono-config.h"
17 #include "mono/metadata/metadata-internals.h"
18 #include "mono/metadata/object-internals.h"
19 #include "mono/utils/mono-logger.h"
20
21 #if defined(__linux__)
22 #define CONFIG_OS "linux"
23 #elif defined(__APPLE__)
24 #define CONFIG_OS "osx"
25 #elif defined(sun)
26 #define CONFIG_OS "solaris"
27 #elif defined(__FreeBSD__)
28 #define CONFIG_OS "freebsd"
29 #elif defined(__NetBSD__)
30 #define CONFIG_OS "netbsd"
31 #elif defined(__OpenBSD__)
32 #define CONFIG_OS "openbsd"
33 #elif defined(__WIN32__)
34 #define CONFIG_OS "windows"
35 #elif defined(_IBMR2)
36 #define CONFIG_OS "aix"
37 #elif defined(__hpux)
38 #define CONFIG_OS "hpux"
39 #else
40 #warning Unknown operating system
41 #define CONFIG_OS "unknownOS"
42 #endif
43
44 #if defined(__i386__)
45 #define CONFIG_CPU "x86"
46 #elif defined(__x86_64__)
47 #define CONFIG_CPU "x86-64"
48 #elif defined(sparc) || defined(__sparc__)
49 #define CONFIG_CPU "sparc"
50 #elif defined(__ppc__) || defined(__powerpc__)
51 #define CONFIG_CPU "ppc"
52 #elif defined(__s390x__)
53 #define CONFIG_CPU "s390x"
54 #elif defined(__s390__)
55 #define CONFIG_CPU "s390"
56 #elif defined(__arm__)
57 #define CONFIG_CPU "arm"
58 #elif defined(__ia64__)
59 #define CONFIG_CPU "ia64"
60 #elif defined(__alpha__)
61 #define CONFIG_CPU "alpha"
62 #elif defined(hppa) || defined(__hppa__)
63 #define CONFIG_CPU "hppa"
64 #elif defined(mips) || defined(__mips) || defined(_mips)
65 #define CONFIG_CPU "mips"
66 #else
67 #warning Unknown CPU
68 #define CONFIG_CPU "unknownCPU"
69 #endif
70
71 static void start_element (GMarkupParseContext *context, 
72                            const gchar         *element_name,
73                            const gchar        **attribute_names,
74                            const gchar        **attribute_values,
75                            gpointer             user_data,
76                            GError             **error);
77
78 static void end_element   (GMarkupParseContext *context,
79                            const gchar         *element_name,
80                            gpointer             user_data,
81                            GError             **error);
82
83 static void parse_text    (GMarkupParseContext *context,
84                            const gchar         *text,
85                            gsize                text_len,
86                            gpointer             user_data,
87                            GError             **error);
88
89 static void passthrough   (GMarkupParseContext *context,
90                            const gchar         *text,
91                            gsize                text_len,
92                            gpointer             user_data,
93                            GError             **error);
94
95 static void parse_error   (GMarkupParseContext *context,
96                            GError              *error,
97                            gpointer             user_data);
98
99 static const GMarkupParser 
100 mono_parser = {
101         start_element,
102         end_element,
103         parse_text,
104         passthrough,
105         parse_error
106 };
107
108 static GHashTable *config_handlers;
109
110 /* when this interface is stable, export it. */
111 typedef struct MonoParseHandler MonoParseHandler;
112
113 struct MonoParseHandler {
114         const char *element_name;
115         void*(*init)   (MonoImage *assembly);
116         void (*start)  (gpointer user_data, const gchar *name,
117                         const gchar **attributes,
118                         const gchar **values);
119         void (*text)   (gpointer user_data, const char *text, gsize test_len);
120         void (*end)    (gpointer user_data, const char *name);
121         void (*finish) (gpointer user_data);
122 };
123
124 typedef struct {
125         MonoParseHandler *current;
126         void *user_data;
127         MonoImage *assembly;
128         int inited;
129 } ParseState;
130
131 static void start_element (GMarkupParseContext *context, 
132                            const gchar         *element_name,
133                            const gchar        **attribute_names,
134                            const gchar        **attribute_values,
135                            gpointer             user_data,
136                            GError             **error)
137 {
138         ParseState *state = user_data;
139         if (!state->current) {
140                 state->current = g_hash_table_lookup (config_handlers, element_name);
141                 if (state->current && state->current->init)
142                         state->user_data = state->current->init (state->assembly);
143         }
144         if (state->current && state->current->start)
145                 state->current->start (state->user_data, element_name, attribute_names, attribute_values);
146 }
147
148 static void end_element   (GMarkupParseContext *context,
149                            const gchar         *element_name,
150                            gpointer             user_data,
151                            GError             **error)
152 {
153         ParseState *state = user_data;
154         if (state->current) {
155                 if (state->current->end)
156                         state->current->end (state->user_data, element_name);
157                 if (strcmp (state->current->element_name, element_name) == 0) {
158                         if (state->current->finish)
159                                 state->current->finish (state->user_data);
160                         state->current = NULL;
161                         state->user_data = NULL;
162                 }
163         }
164 }
165
166 static void parse_text    (GMarkupParseContext *context,
167                            const gchar         *text,
168                            gsize                text_len,
169                            gpointer             user_data,
170                            GError             **error)
171 {
172         ParseState *state = user_data;
173         if (state->current && state->current->text)
174                 state->current->text (state->user_data, text, text_len);
175 }
176
177 static void passthrough   (GMarkupParseContext *context,
178                            const gchar         *text,
179                            gsize                text_len,
180                            gpointer             user_data,
181                            GError             **error)
182 {
183         /* do nothing */
184 }
185
186 static void parse_error   (GMarkupParseContext *context,
187                            GError              *error,
188                            gpointer             user_data)
189 {
190 }
191
192 static int
193 arch_matches (const char* arch, const char *value)
194 {
195         char **splitted, **p;
196         int found = FALSE;
197         if (value [0] == '!')
198                 return !arch_matches (arch, value + 1);
199         p = splitted = g_strsplit (value, ",", 0);
200         while (*p) {
201                 if (strcmp (arch, *p) == 0) {
202                         found = TRUE;
203                         break;
204                 }
205                 p++;
206         }
207         g_strfreev (splitted);
208         return found;
209 }
210
211 typedef struct {
212         char *dll;
213         char *target;
214         int ignore;
215         MonoImage *assembly;
216 } DllInfo;
217
218 static void*
219 dllmap_init (MonoImage *assembly) {
220         DllInfo *info = g_new0 (DllInfo, 1);
221         info->assembly = assembly;
222         return info;
223 }
224
225 static void
226 dllmap_start (gpointer user_data, 
227               const gchar         *element_name,
228               const gchar        **attribute_names,
229               const gchar        **attribute_values)
230 {
231         int i;
232         DllInfo *info = user_data;
233         
234         if (strcmp (element_name, "dllmap") == 0) {
235                 g_free (info->dll);
236                 g_free (info->target);
237                 info->dll = info->target = NULL;
238                 info->ignore = FALSE;
239                 for (i = 0; attribute_names [i]; ++i) {
240                         if (strcmp (attribute_names [i], "dll") == 0)
241                                 info->dll = g_strdup (attribute_values [i]);
242                         else if (strcmp (attribute_names [i], "target") == 0)
243                                 info->target = g_strdup (attribute_values [i]);
244                         else if (strcmp (attribute_names [i], "os") == 0 && !arch_matches (CONFIG_OS, attribute_values [i]))
245                                 info->ignore = TRUE;
246                         else if (strcmp (attribute_names [i], "cpu") == 0 && !arch_matches (CONFIG_CPU, attribute_values [i]))
247                                 info->ignore = TRUE;
248                 }
249                 if (!info->ignore)
250                         mono_dllmap_insert (info->assembly, info->dll, NULL, info->target, NULL);
251         } else if (strcmp (element_name, "dllentry") == 0) {
252                 const char *name = NULL, *target = NULL, *dll = NULL;
253                 int ignore = FALSE;
254                 for (i = 0; attribute_names [i]; ++i) {
255                         if (strcmp (attribute_names [i], "dll") == 0)
256                                 dll = attribute_values [i];
257                         else if (strcmp (attribute_names [i], "target") == 0)
258                                 target = attribute_values [i];
259                         else if (strcmp (attribute_names [i], "name") == 0)
260                                 name = attribute_values [i];
261                         else if (strcmp (attribute_names [i], "os") == 0 && !arch_matches (CONFIG_OS, attribute_values [i]))
262                                 ignore = TRUE;
263                         else if (strcmp (attribute_names [i], "cpu") == 0 && !arch_matches (CONFIG_CPU, attribute_values [i]))
264                                 ignore = TRUE;
265                 }
266                 if (!dll)
267                         dll = info->dll;
268                 if (!info->ignore && !ignore)
269                         mono_dllmap_insert (info->assembly, info->dll, name, dll, target);
270         }
271 }
272
273 static void
274 dllmap_finish (gpointer user_data)
275 {
276         DllInfo *info = user_data;
277
278         g_free (info->dll);
279         g_free (info->target);
280         g_free (info);
281 }
282
283 static const MonoParseHandler
284 dllmap_handler = {
285         "dllmap",
286         dllmap_init,
287         dllmap_start,
288         NULL, /* text */
289         NULL, /* end */
290         dllmap_finish
291 };
292
293 static void
294 legacyUEP_start (gpointer user_data, 
295               const gchar         *element_name,
296               const gchar        **attribute_names,
297               const gchar        **attribute_values) {
298         if ((strcmp (element_name, "legacyUnhandledExceptionPolicy") == 0) &&
299                         (attribute_names [0] != NULL) &&
300                         (strcmp (attribute_names [0], "enabled") == 0)) {
301                 if ((strcmp (attribute_values [0], "1") == 0) ||
302                                 (g_strcasecmp (attribute_values [0], "true") == 0)) {
303                         mono_runtime_unhandled_exception_policy_set (MONO_UNHANLED_POLICY_LEGACY);
304                 }
305         }
306 }
307
308 static const MonoParseHandler
309 legacyUEP_handler = {
310         "legacyUnhandledExceptionPolicy",
311         NULL, /* init */
312         legacyUEP_start,
313         NULL, /* text */
314         NULL, /* end */
315         NULL, /* finish */
316 };
317
318 static int inited = 0;
319
320 static void
321 mono_config_init (void)
322 {
323         inited = 1;
324         config_handlers = g_hash_table_new (g_str_hash, g_str_equal);
325         g_hash_table_insert (config_handlers, (gpointer) dllmap_handler.element_name, (gpointer) &dllmap_handler);
326         g_hash_table_insert (config_handlers, (gpointer) legacyUEP_handler.element_name, (gpointer) &legacyUEP_handler);
327 }
328
329 /* FIXME: error handling */
330
331 static void
332 mono_config_parse_xml_with_context (ParseState *state, const char *text, gsize len)
333 {
334         GMarkupParseContext *context;
335
336         if (!inited)
337                 mono_config_init ();
338
339         context = g_markup_parse_context_new (&mono_parser, 0, state, NULL);
340         if (g_markup_parse_context_parse (context, text, len, NULL)) {
341                 g_markup_parse_context_end_parse (context, NULL);
342         }
343         g_markup_parse_context_free (context);
344 }
345
346 /* If assembly is NULL, parse in the global context */
347 static int
348 mono_config_parse_file_with_context (ParseState *state, const char *filename)
349 {
350         char *text;
351         gsize len;
352
353         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_CONFIG,
354                         "Config attempting to parse: '%s'.", filename);
355
356         if (!g_file_get_contents (filename, &text, &len, NULL))
357                 return 0;
358         mono_config_parse_xml_with_context (state, text, len);
359         g_free (text);
360         return 1;
361 }
362
363 /**
364  * mono_config_parse_memory:
365  * @buffer: a pointer to an string XML representation of the configuration
366  *
367  * Parses the configuration from a buffer
368  */
369 void
370 mono_config_parse_memory (const char *buffer)
371 {
372         ParseState state = {NULL};
373         
374         mono_config_parse_xml_with_context (&state, buffer, strlen (buffer));
375 }
376
377 static void
378 mono_config_parse_file (const char *filename)
379 {
380         ParseState state = {NULL};
381         mono_config_parse_file_with_context (&state, filename);
382 }
383
384 /* 
385  * use the equivalent lookup code from the GAC when available.
386  * Depending on state, this should give something like:
387  *      aname/version-pubtoken/
388  *      aname/version/
389  *      aname
390  */
391 static char*
392 get_assembly_filename (MonoImage *image, int state)
393 {
394         switch (state) {
395         case 0:
396                 return g_strdup (mono_image_get_name (image));
397         default:
398                 return NULL;
399         }
400 }
401
402 typedef struct _BundledConfig BundledConfig;
403
404 struct _BundledConfig {
405         BundledConfig *next;
406         const char* aname;
407         const char* config_xml;
408 };
409
410 static BundledConfig *bundled_configs = NULL;
411
412 static const char *bundled_machine_config = NULL;
413
414 void
415 mono_register_config_for_assembly (const char* assembly_name, const char* config_xml)
416 {
417         BundledConfig *bconfig;
418
419         bconfig = g_new0 (BundledConfig, 1);
420         bconfig->aname = assembly_name;
421         bconfig->config_xml = config_xml;
422         bconfig->next = bundled_configs;
423         bundled_configs = bconfig;
424 }
425
426 const char *
427 mono_config_string_for_assembly_file (const char *filename)
428 {
429         BundledConfig *bconfig;
430         
431         for (bconfig = bundled_configs; bconfig; bconfig = bconfig->next) {
432                 if (bconfig->aname && strcmp (bconfig->aname, filename) == 0)
433                         return bconfig->config_xml;
434         }
435         return NULL;
436 }
437
438 void 
439 mono_config_for_assembly (MonoImage *assembly)
440 {
441         ParseState state = {NULL};
442         int got_it = 0, i;
443         char *aname, *cfg, *cfg_name;
444         const char *bundled_config;
445         const char *home;
446         
447         state.assembly = assembly;
448
449         bundled_config = mono_config_string_for_assembly_file (assembly->module_name);
450         if (bundled_config)
451                 mono_config_parse_xml_with_context (&state, bundled_config, strlen (bundled_config));
452
453         cfg_name = g_strdup_printf ("%s.config", mono_image_get_filename (assembly));
454         mono_config_parse_file_with_context (&state, cfg_name);
455         g_free (cfg_name);
456
457         cfg_name = g_strdup_printf ("%s.config", mono_image_get_name (assembly));
458
459         home = g_get_home_dir ();
460
461         for (i = 0; (aname = get_assembly_filename (assembly, i)) != NULL; ++i) {
462                 cfg = g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname, cfg_name, NULL);
463                 got_it += mono_config_parse_file_with_context (&state, cfg);
464                 g_free (cfg);
465
466 #ifndef PLATFORM_WIN32
467                 cfg = g_build_filename (home, ".mono", "assemblies", aname, cfg_name, NULL);
468                 got_it += mono_config_parse_file_with_context (&state, cfg);
469                 g_free (cfg);
470 #endif
471                 g_free (aname);
472                 if (got_it)
473                         break;
474         }
475         g_free (cfg_name);
476 }
477
478 /**
479  * mono_config_parse:
480  * @filename: the filename to load the configuration variables from.
481  *
482  * Pass a NULL filename to parse the default config files
483  * (or the file in the MONO_CONFIG env var).
484  */
485 void
486 mono_config_parse (const char *filename) {
487         const char *home;
488         char *mono_cfg;
489 #ifndef PLATFORM_WIN32
490         char *user_cfg;
491 #endif
492
493         if (filename) {
494                 mono_config_parse_file (filename);
495                 return;
496         }
497
498         home = g_getenv ("MONO_CONFIG");
499         if (home) {
500                 mono_config_parse_file (home);
501                 return;
502         }
503
504         mono_cfg = g_build_filename (mono_get_config_dir (), "mono", "config", NULL);
505         mono_config_parse_file (mono_cfg);
506         g_free (mono_cfg);
507
508 #ifndef PLATFORM_WIN32
509         home = g_get_home_dir ();
510         user_cfg = g_strconcat (home, G_DIR_SEPARATOR_S, ".mono/config", NULL);
511         mono_config_parse_file (user_cfg);
512         g_free (user_cfg);
513 #endif
514 }
515
516 static const char *mono_cfg_dir = NULL;
517
518 /* Invoked during startup */
519 void
520 mono_set_config_dir (const char *dir)
521 {
522         /* If this variable is set, overrides the directory computed */
523         mono_cfg_dir = g_getenv ("MONO_CFG_DIR");
524         if (mono_cfg_dir == NULL)
525                 mono_cfg_dir = g_strdup (dir);
526 }
527
528 const char* 
529 mono_get_config_dir (void)
530 {
531         if (mono_cfg_dir == NULL)
532                 mono_set_dirs (NULL, NULL);
533
534         return mono_cfg_dir;
535 }
536
537 void
538 mono_register_machine_config (const char *config_xml)
539 {
540         bundled_machine_config = config_xml;
541 }
542
543 const char *
544 mono_get_machine_config (void)
545 {
546         return bundled_machine_config;
547 }
548
549 static void
550 publisher_policy_start (gpointer user_data,
551                 const gchar *element_name,
552                 const gchar **attribute_names,
553                 const gchar **attribute_values)
554 {
555         MonoAssemblyBindingInfo *info;
556         int n;
557
558         info = user_data;
559         if (!strcmp (element_name, "assemblyIdentity")) {
560                 for (n = 0; attribute_names [n]; n++) {
561                         const gchar *attribute_name = attribute_names [n];
562                         
563                         if (!strcmp (attribute_name, "name"))
564                                 info->name = g_strdup (attribute_values [n]);
565                         else if (!strcmp (attribute_name, "publicKeyToken")) {
566                                 if (strlen (attribute_values [n]) == MONO_PUBLIC_KEY_TOKEN_LENGTH - 1)
567                                         g_strlcpy ((char *) info->public_key_token, attribute_values [n], MONO_PUBLIC_KEY_TOKEN_LENGTH);
568                         } else if (!strcmp (attribute_name, "culture")) {
569                                 if (!strcmp (attribute_values [n], "neutral"))
570                                         info->culture = g_strdup ("");
571                                 else
572                                         info->culture = g_strdup (attribute_values [n]);
573                         }
574                 }
575         } else if (!strcmp (element_name, "bindingRedirect")) {
576                 for (n = 0; attribute_names [n]; n++) {
577                         const gchar *attribute_name = attribute_names [n];
578
579                         if (!strcmp (attribute_name, "oldVersion")) {
580                                 gchar **numbers, **version, **versions;
581                                 gint major, minor, build, revision;
582
583                                 /* Invalid value */
584                                 if (!strcmp (attribute_values [n], ""))
585                                         return;
586                                 
587                                 versions = g_strsplit (attribute_values [n], "-", 2);
588                                 version = g_strsplit (*versions, ".", 4);
589
590                                 /* We assign the values to gint vars to do the checks */
591                                 numbers = version;
592                                 major = *numbers ? atoi (*numbers++) : -1;
593                                 minor = *numbers ? atoi (*numbers++) : -1;
594                                 build = *numbers ? atoi (*numbers++) : -1;
595                                 revision = *numbers ? atoi (*numbers) : -1;
596                                 g_strfreev (version);
597                                 if (major < 0 || minor < 0 || build < 0 || revision < 0) {
598                                         g_strfreev (versions);
599                                         return;
600                                 }
601
602                                 info->old_version_bottom.major = major;
603                                 info->old_version_bottom.minor = minor;
604                                 info->old_version_bottom.build = build;
605                                 info->old_version_bottom.revision = revision;
606                                 info->has_old_version_bottom = TRUE;
607
608                                 if (!*(versions + 1)) {
609                                         g_strfreev (versions);
610                                         continue;
611                                 }
612                                 
613                                 numbers = version = g_strsplit (*(versions + 1), ".", 4);
614                                 major = *numbers ? atoi (*numbers++) : -1;
615                                 minor = *numbers ? atoi (*numbers++) : -1;
616                                 build = *numbers ? atoi (*numbers++) : -1;
617                                 revision = *numbers ? atoi (*numbers) : 1;
618                                 g_strfreev (version);
619                                 if (major < 0 || minor < 0 || build < 0 || revision < 0) {
620                                         g_strfreev (versions);
621                                         return;
622                                 }
623
624                                 info->old_version_top.major = major;
625                                 info->old_version_top.minor = minor;
626                                 info->old_version_top.build = build;
627                                 info->old_version_top.revision = revision;
628                                 info->has_old_version_top = TRUE;
629
630                                 g_strfreev (versions);
631                         } else if (!strcmp (attribute_name, "newVersion")) {
632                                 gchar **numbers, **version;
633
634                                 /* Invalid value */
635                                 if (!strcmp (attribute_values [n], ""))
636                                         return;
637
638                                 numbers = version = g_strsplit (attribute_values [n], ".", 4);
639                                 info->new_version.major = *numbers ? atoi (*numbers++) : -1;
640                                 info->new_version.minor = *numbers ? atoi (*numbers++) : -1;
641                                 info->new_version.build = *numbers ? atoi (*numbers++) : -1;
642                                 info->new_version.revision = *numbers ? atoi (*numbers) : -1;
643                                 info->has_new_version = TRUE;
644                                 g_strfreev (version);
645                         }
646                 }
647         }
648 }
649
650 static MonoParseHandler
651 publisher_policy_parser = {
652         "", /* We don't need to use declare an xml element */
653         NULL,
654         publisher_policy_start,
655         NULL,
656         NULL,
657         NULL
658 };
659
660 void
661 mono_config_parse_publisher_policy (const gchar *filename, MonoAssemblyBindingInfo *info)
662 {
663         ParseState state = {
664                 &publisher_policy_parser, /* MonoParseHandler */
665                 info, /* user_data */
666                 NULL, /* MonoImage (we don't need it right now)*/
667                 TRUE /* We are already inited */
668         };
669         
670         mono_config_parse_file_with_context (&state, filename);
671 }
672