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