4 * Runtime and assembly configuration file support routines.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
9 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
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-internal.h"
22 #if defined(TARGET_PS3)
23 #define CONFIG_OS "CellOS"
24 #elif defined(__linux__)
25 #define CONFIG_OS "linux"
26 #elif defined(__APPLE__)
27 #define CONFIG_OS "osx"
29 #define CONFIG_OS "solaris"
30 #elif defined(__FreeBSD__)
31 #define CONFIG_OS "freebsd"
32 #elif defined(__NetBSD__)
33 #define CONFIG_OS "netbsd"
34 #elif defined(__OpenBSD__)
35 #define CONFIG_OS "openbsd"
36 #elif defined(__WIN32__) || defined(TARGET_WIN32)
37 #define CONFIG_OS "windows"
39 #define CONFIG_OS "aix"
41 #define CONFIG_OS "hpux"
42 #elif defined(__HAIKU__)
43 #define CONFIG_OS "haiku"
45 #warning Unknown operating system
46 #define CONFIG_OS "unknownOS"
50 #if defined(__i386__) || defined(TARGET_X86)
51 #define CONFIG_CPU "x86"
52 #define CONFIG_WORDSIZE "32"
53 #elif defined(__x86_64__) || defined(TARGET_AMD64)
54 #define CONFIG_CPU "x86-64"
55 #define CONFIG_WORDSIZE "64"
56 #elif defined(sparc) || defined(__sparc__)
57 #define CONFIG_CPU "sparc"
58 #define CONFIG_WORDSIZE "32"
59 #elif defined(__ppc64__) || defined(__powerpc64__) || defined(TARGET_POWERPC)
60 #define CONFIG_WORDSIZE "64"
61 #ifdef __mono_ppc_ilp32__
62 # define CONFIG_CPU "ppc64ilp32"
64 # define CONFIG_CPU "ppc64"
66 #elif defined(__ppc__) || defined(__powerpc__)
67 #define CONFIG_CPU "ppc"
68 #define CONFIG_WORDSIZE "32"
69 #elif defined(__s390x__)
70 #define CONFIG_CPU "s390x"
71 #define CONFIG_WORDSIZE "64"
72 #elif defined(__s390__)
73 #define CONFIG_CPU "s390"
74 #define CONFIG_WORDSIZE "32"
75 #elif defined(__arm__)
76 #define CONFIG_CPU "arm"
77 #define CONFIG_WORDSIZE "32"
78 #elif defined(__aarch64__)
79 #define CONFIG_CPU "armv8"
80 #define CONFIG_WORDSIZE "64"
81 #elif defined(__ia64__)
82 #define CONFIG_CPU "ia64"
83 #define CONFIG_WORDSIZE "64"
84 #elif defined(mips) || defined(__mips) || defined(_mips)
85 #define CONFIG_CPU "mips"
86 #define CONFIG_WORDSIZE "32"
89 #define CONFIG_CPU "unknownCPU"
93 static void start_element (GMarkupParseContext
*context
,
94 const gchar
*element_name
,
95 const gchar
**attribute_names
,
96 const gchar
**attribute_values
,
100 static void end_element (GMarkupParseContext
*context
,
101 const gchar
*element_name
,
105 static void parse_text (GMarkupParseContext
*context
,
111 static void passthrough (GMarkupParseContext
*context
,
117 static void parse_error (GMarkupParseContext
*context
,
121 static const GMarkupParser
130 static GHashTable
*config_handlers
;
132 static const char *mono_cfg_dir
= NULL
;
133 static char *mono_cfg_dir_allocated
= NULL
;
135 /* when this interface is stable, export it. */
136 typedef struct MonoParseHandler MonoParseHandler
;
138 struct MonoParseHandler
{
139 const char *element_name
;
140 void*(*init
) (MonoImage
*assembly
);
141 void (*start
) (gpointer user_data
, const gchar
*name
,
142 const gchar
**attributes
,
143 const gchar
**values
);
144 void (*text
) (gpointer user_data
, const char *text
, gsize test_len
);
145 void (*end
) (gpointer user_data
, const char *name
);
146 void (*finish
) (gpointer user_data
);
150 MonoAssemblyBindingInfo
*info
;
151 void (*info_parsed
)(MonoAssemblyBindingInfo
*info
, void *user_data
);
156 MonoParseHandler
*current
;
162 static void start_element (GMarkupParseContext
*context
,
163 const gchar
*element_name
,
164 const gchar
**attribute_names
,
165 const gchar
**attribute_values
,
169 ParseState
*state
= user_data
;
170 if (!state
->current
) {
171 state
->current
= g_hash_table_lookup (config_handlers
, element_name
);
172 if (state
->current
&& state
->current
->init
)
173 state
->user_data
= state
->current
->init (state
->assembly
);
175 if (state
->current
&& state
->current
->start
)
176 state
->current
->start (state
->user_data
, element_name
, attribute_names
, attribute_values
);
179 static void end_element (GMarkupParseContext
*context
,
180 const gchar
*element_name
,
184 ParseState
*state
= user_data
;
185 if (state
->current
) {
186 if (state
->current
->end
)
187 state
->current
->end (state
->user_data
, element_name
);
188 if (strcmp (state
->current
->element_name
, element_name
) == 0) {
189 if (state
->current
->finish
)
190 state
->current
->finish (state
->user_data
);
191 state
->current
= NULL
;
192 state
->user_data
= NULL
;
197 static void parse_text (GMarkupParseContext
*context
,
203 ParseState
*state
= user_data
;
204 if (state
->current
&& state
->current
->text
)
205 state
->current
->text (state
->user_data
, text
, text_len
);
208 static void passthrough (GMarkupParseContext
*context
,
217 static void parse_error (GMarkupParseContext
*context
,
221 ParseState
*state
= user_data
;
223 const gchar
*filename
;
225 filename
= state
&& state
->user_data
? (gchar
*) state
->user_data
: "<unknown>";
226 msg
= error
&& error
->message
? error
->message
: "";
227 g_warning ("Error parsing %s: %s", filename
, msg
);
231 arch_matches (const char* arch
, const char *value
)
233 char **splitted
, **p
;
235 if (value
[0] == '!')
236 return !arch_matches (arch
, value
+ 1);
237 p
= splitted
= g_strsplit (value
, ",", 0);
239 if (strcmp (arch
, *p
) == 0) {
245 g_strfreev (splitted
);
257 dllmap_init (MonoImage
*assembly
) {
258 DllInfo
*info
= g_new0 (DllInfo
, 1);
259 info
->assembly
= assembly
;
264 dllmap_start (gpointer user_data
,
265 const gchar
*element_name
,
266 const gchar
**attribute_names
,
267 const gchar
**attribute_values
)
270 DllInfo
*info
= user_data
;
272 if (strcmp (element_name
, "dllmap") == 0) {
274 g_free (info
->target
);
275 info
->dll
= info
->target
= NULL
;
276 info
->ignore
= FALSE
;
277 for (i
= 0; attribute_names
[i
]; ++i
) {
278 if (strcmp (attribute_names
[i
], "dll") == 0)
279 info
->dll
= g_strdup (attribute_values
[i
]);
280 else if (strcmp (attribute_names
[i
], "target") == 0)
281 info
->target
= g_strdup (attribute_values
[i
]);
282 else if (strcmp (attribute_names
[i
], "os") == 0 && !arch_matches (CONFIG_OS
, attribute_values
[i
]))
284 else if (strcmp (attribute_names
[i
], "cpu") == 0 && !arch_matches (CONFIG_CPU
, attribute_values
[i
]))
286 else if (strcmp (attribute_names
[i
], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE
, attribute_values
[i
]))
290 mono_dllmap_insert (info
->assembly
, info
->dll
, NULL
, info
->target
, NULL
);
291 } else if (strcmp (element_name
, "dllentry") == 0) {
292 const char *name
= NULL
, *target
= NULL
, *dll
= NULL
;
294 for (i
= 0; attribute_names
[i
]; ++i
) {
295 if (strcmp (attribute_names
[i
], "dll") == 0)
296 dll
= attribute_values
[i
];
297 else if (strcmp (attribute_names
[i
], "target") == 0)
298 target
= attribute_values
[i
];
299 else if (strcmp (attribute_names
[i
], "name") == 0)
300 name
= attribute_values
[i
];
301 else if (strcmp (attribute_names
[i
], "os") == 0 && !arch_matches (CONFIG_OS
, attribute_values
[i
]))
303 else if (strcmp (attribute_names
[i
], "cpu") == 0 && !arch_matches (CONFIG_CPU
, attribute_values
[i
]))
305 else if (strcmp (attribute_names
[i
], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE
, attribute_values
[i
]))
310 if (!info
->ignore
&& !ignore
)
311 mono_dllmap_insert (info
->assembly
, info
->dll
, name
, dll
, target
);
316 dllmap_finish (gpointer user_data
)
318 DllInfo
*info
= user_data
;
321 g_free (info
->target
);
325 static const MonoParseHandler
336 legacyUEP_start (gpointer user_data
,
337 const gchar
*element_name
,
338 const gchar
**attribute_names
,
339 const gchar
**attribute_values
) {
340 if ((strcmp (element_name
, "legacyUnhandledExceptionPolicy") == 0) &&
341 (attribute_names
[0] != NULL
) &&
342 (strcmp (attribute_names
[0], "enabled") == 0)) {
343 if ((strcmp (attribute_values
[0], "1") == 0) ||
344 (g_ascii_strcasecmp (attribute_values
[0], "true") == 0)) {
345 mono_runtime_unhandled_exception_policy_set (MONO_UNHANDLED_POLICY_LEGACY
);
350 static const MonoParseHandler
351 legacyUEP_handler
= {
352 "legacyUnhandledExceptionPolicy",
361 aot_cache_start (gpointer user_data
,
362 const gchar
*element_name
,
363 const gchar
**attribute_names
,
364 const gchar
**attribute_values
)
367 MonoAotCacheConfig
*config
;
369 if (strcmp (element_name
, "aotcache") != 0)
372 config
= mono_get_aot_cache_config ();
374 /* Per-app configuration */
375 for (i
= 0; attribute_names
[i
]; ++i
) {
376 if (!strcmp (attribute_names
[i
], "app")) {
377 config
->apps
= g_slist_prepend (config
->apps
, g_strdup (attribute_values
[i
]));
381 /* Global configuration */
382 for (i
= 0; attribute_names
[i
]; ++i
) {
383 if (!strcmp (attribute_names
[i
], "assemblies")) {
387 parts
= g_strsplit (attribute_values
[i
], " ", -1);
388 for (ptr
= parts
; ptr
&& *ptr
; ptr
++) {
390 config
->assemblies
= g_slist_prepend (config
->assemblies
, g_strdup (part
));
393 } else if (!strcmp (attribute_names
[i
], "options")) {
394 config
->aot_options
= g_strdup (attribute_values
[i
]);
399 static const MonoParseHandler
400 aot_cache_handler
= {
409 static int inited
= 0;
412 mono_config_init (void)
415 config_handlers
= g_hash_table_new (g_str_hash
, g_str_equal
);
416 g_hash_table_insert (config_handlers
, (gpointer
) dllmap_handler
.element_name
, (gpointer
) &dllmap_handler
);
417 g_hash_table_insert (config_handlers
, (gpointer
) legacyUEP_handler
.element_name
, (gpointer
) &legacyUEP_handler
);
418 g_hash_table_insert (config_handlers
, (gpointer
) aot_cache_handler
.element_name
, (gpointer
) &aot_cache_handler
);
422 mono_config_cleanup (void)
425 g_hash_table_destroy (config_handlers
);
426 g_free (mono_cfg_dir_allocated
);
429 /* FIXME: error handling */
432 mono_config_parse_xml_with_context (ParseState
*state
, const char *text
, gsize len
)
434 GMarkupParseContext
*context
;
439 context
= g_markup_parse_context_new (&mono_parser
, 0, state
, NULL
);
440 if (g_markup_parse_context_parse (context
, text
, len
, NULL
)) {
441 g_markup_parse_context_end_parse (context
, NULL
);
443 g_markup_parse_context_free (context
);
446 /* If assembly is NULL, parse in the global context */
448 mono_config_parse_file_with_context (ParseState
*state
, const char *filename
)
454 mono_trace (G_LOG_LEVEL_INFO
, MONO_TRACE_CONFIG
,
455 "Config attempting to parse: '%s'.", filename
);
457 if (!g_file_get_contents (filename
, &text
, &len
, NULL
))
461 if (len
> 3 && text
[0] == '\xef' && text
[1] == (gchar
) '\xbb' && text
[2] == '\xbf')
462 offset
= 3; /* Skip UTF-8 BOM */
463 if (state
->user_data
== NULL
)
464 state
->user_data
= (gpointer
) filename
;
465 mono_config_parse_xml_with_context (state
, text
+ offset
, len
- offset
);
471 * mono_config_parse_memory:
472 * @buffer: a pointer to an string XML representation of the configuration
474 * Parses the configuration from a buffer
477 mono_config_parse_memory (const char *buffer
)
479 ParseState state
= {NULL
};
481 state
.user_data
= (gpointer
) "<buffer>";
482 mono_config_parse_xml_with_context (&state
, buffer
, strlen (buffer
));
486 mono_config_parse_file (const char *filename
)
488 ParseState state
= {NULL
};
489 state
.user_data
= (gpointer
) filename
;
490 mono_config_parse_file_with_context (&state
, filename
);
494 * use the equivalent lookup code from the GAC when available.
495 * Depending on state, this should give something like:
496 * aname/version-pubtoken/
501 get_assembly_filename (MonoImage
*image
, int state
)
505 return g_strdup (mono_image_get_name (image
));
511 typedef struct _BundledConfig BundledConfig
;
513 struct _BundledConfig
{
516 const char* config_xml
;
519 static BundledConfig
*bundled_configs
= NULL
;
521 static const char *bundled_machine_config
= NULL
;
524 mono_register_config_for_assembly (const char* assembly_name
, const char* config_xml
)
526 BundledConfig
*bconfig
;
528 bconfig
= g_new0 (BundledConfig
, 1);
529 bconfig
->aname
= assembly_name
;
530 bconfig
->config_xml
= config_xml
;
531 bconfig
->next
= bundled_configs
;
532 bundled_configs
= bconfig
;
536 mono_config_string_for_assembly_file (const char *filename
)
538 BundledConfig
*bconfig
;
540 for (bconfig
= bundled_configs
; bconfig
; bconfig
= bconfig
->next
) {
541 if (bconfig
->aname
&& strcmp (bconfig
->aname
, filename
) == 0)
542 return bconfig
->config_xml
;
548 mono_config_for_assembly (MonoImage
*assembly
)
550 ParseState state
= {NULL
};
552 char *aname
, *cfg
, *cfg_name
;
553 const char *bundled_config
;
555 state
.assembly
= assembly
;
557 bundled_config
= mono_config_string_for_assembly_file (assembly
->module_name
);
558 if (bundled_config
) {
559 state
.user_data
= (gpointer
) "<bundled>";
560 mono_config_parse_xml_with_context (&state
, bundled_config
, strlen (bundled_config
));
563 cfg_name
= g_strdup_printf ("%s.config", mono_image_get_filename (assembly
));
564 mono_config_parse_file_with_context (&state
, cfg_name
);
567 cfg_name
= g_strdup_printf ("%s.config", mono_image_get_name (assembly
));
569 for (i
= 0; (aname
= get_assembly_filename (assembly
, i
)) != NULL
; ++i
) {
570 cfg
= g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname
, cfg_name
, NULL
);
571 got_it
+= mono_config_parse_file_with_context (&state
, cfg
);
575 const char *home
= g_get_home_dir ();
576 cfg
= g_build_filename (home
, ".mono", "assemblies", aname
, cfg_name
, NULL
);
577 got_it
+= mono_config_parse_file_with_context (&state
, cfg
);
589 * @filename: the filename to load the configuration variables from.
591 * Pass a NULL filename to parse the default config files
592 * (or the file in the MONO_CONFIG env var).
595 mono_config_parse (const char *filename
) {
603 mono_config_parse_file (filename
);
607 home
= g_getenv ("MONO_CONFIG");
609 mono_config_parse_file (home
);
613 mono_cfg
= g_build_filename (mono_get_config_dir (), "mono", "config", NULL
);
614 mono_config_parse_file (mono_cfg
);
617 #if !defined(TARGET_WIN32) && !defined(__native_client__)
618 home
= g_get_home_dir ();
619 user_cfg
= g_strconcat (home
, G_DIR_SEPARATOR_S
, ".mono/config", NULL
);
620 mono_config_parse_file (user_cfg
);
625 /* Invoked during startup */
627 mono_set_config_dir (const char *dir
)
629 /* If this variable is set, overrides the directory computed */
630 mono_cfg_dir
= g_getenv ("MONO_CFG_DIR");
631 if (mono_cfg_dir
== NULL
)
632 mono_cfg_dir
= mono_cfg_dir_allocated
= g_strdup (dir
);
636 mono_get_config_dir (void)
638 if (mono_cfg_dir
== NULL
)
639 mono_set_dirs (NULL
, NULL
);
645 mono_register_machine_config (const char *config_xml
)
647 bundled_machine_config
= config_xml
;
651 mono_get_machine_config (void)
653 return bundled_machine_config
;
657 assembly_binding_end (gpointer user_data
, const char *element_name
)
659 ParserUserData
*pud
= user_data
;
661 if (!strcmp (element_name
, "dependentAssembly")) {
662 if (pud
->info_parsed
&& pud
->info
) {
663 pud
->info_parsed (pud
->info
, pud
->user_data
);
664 g_free (pud
->info
->name
);
665 g_free (pud
->info
->culture
);
671 publisher_policy_start (gpointer user_data
,
672 const gchar
*element_name
,
673 const gchar
**attribute_names
,
674 const gchar
**attribute_values
)
677 MonoAssemblyBindingInfo
*info
;
682 if (!strcmp (element_name
, "dependentAssembly")) {
684 info
->culture
= NULL
;
685 info
->has_old_version_bottom
= FALSE
;
686 info
->has_old_version_top
= FALSE
;
687 info
->has_new_version
= FALSE
;
688 info
->is_valid
= FALSE
;
689 memset (&info
->old_version_bottom
, 0, sizeof (info
->old_version_bottom
));
690 memset (&info
->old_version_top
, 0, sizeof (info
->old_version_top
));
691 memset (&info
->new_version
, 0, sizeof (info
->new_version
));
692 } if (!strcmp (element_name
, "assemblyIdentity")) {
693 for (n
= 0; attribute_names
[n
]; n
++) {
694 const gchar
*attribute_name
= attribute_names
[n
];
696 if (!strcmp (attribute_name
, "name"))
697 info
->name
= g_strdup (attribute_values
[n
]);
698 else if (!strcmp (attribute_name
, "publicKeyToken")) {
699 if (strlen (attribute_values
[n
]) == MONO_PUBLIC_KEY_TOKEN_LENGTH
- 1)
700 g_strlcpy ((char *) info
->public_key_token
, attribute_values
[n
], MONO_PUBLIC_KEY_TOKEN_LENGTH
);
701 } else if (!strcmp (attribute_name
, "culture")) {
702 if (!strcmp (attribute_values
[n
], "neutral"))
703 info
->culture
= g_strdup ("");
705 info
->culture
= g_strdup (attribute_values
[n
]);
708 } else if (!strcmp (element_name
, "bindingRedirect")) {
709 for (n
= 0; attribute_names
[n
]; n
++) {
710 const gchar
*attribute_name
= attribute_names
[n
];
712 if (!strcmp (attribute_name
, "oldVersion")) {
713 gchar
**numbers
, **version
, **versions
;
714 gint major
, minor
, build
, revision
;
717 if (!strcmp (attribute_values
[n
], ""))
720 versions
= g_strsplit (attribute_values
[n
], "-", 2);
721 version
= g_strsplit (*versions
, ".", 4);
723 /* We assign the values to gint vars to do the checks */
725 major
= *numbers
? atoi (*numbers
++) : -1;
726 minor
= *numbers
? atoi (*numbers
++) : -1;
727 build
= *numbers
? atoi (*numbers
++) : -1;
728 revision
= *numbers
? atoi (*numbers
) : -1;
729 g_strfreev (version
);
730 if (major
< 0 || minor
< 0 || build
< 0 || revision
< 0) {
731 g_strfreev (versions
);
735 info
->old_version_bottom
.major
= major
;
736 info
->old_version_bottom
.minor
= minor
;
737 info
->old_version_bottom
.build
= build
;
738 info
->old_version_bottom
.revision
= revision
;
739 info
->has_old_version_bottom
= TRUE
;
741 if (!*(versions
+ 1)) {
742 g_strfreev (versions
);
746 numbers
= version
= g_strsplit (*(versions
+ 1), ".", 4);
747 major
= *numbers
? atoi (*numbers
++) : -1;
748 minor
= *numbers
? atoi (*numbers
++) : -1;
749 build
= *numbers
? atoi (*numbers
++) : -1;
750 revision
= *numbers
? atoi (*numbers
) : 1;
751 g_strfreev (version
);
752 if (major
< 0 || minor
< 0 || build
< 0 || revision
< 0) {
753 g_strfreev (versions
);
757 info
->old_version_top
.major
= major
;
758 info
->old_version_top
.minor
= minor
;
759 info
->old_version_top
.build
= build
;
760 info
->old_version_top
.revision
= revision
;
761 info
->has_old_version_top
= TRUE
;
763 g_strfreev (versions
);
764 } else if (!strcmp (attribute_name
, "newVersion")) {
765 gchar
**numbers
, **version
;
768 if (!strcmp (attribute_values
[n
], ""))
771 numbers
= version
= g_strsplit (attribute_values
[n
], ".", 4);
772 info
->new_version
.major
= *numbers
? atoi (*numbers
++) : -1;
773 info
->new_version
.minor
= *numbers
? atoi (*numbers
++) : -1;
774 info
->new_version
.build
= *numbers
? atoi (*numbers
++) : -1;
775 info
->new_version
.revision
= *numbers
? atoi (*numbers
) : -1;
776 info
->has_new_version
= TRUE
;
777 g_strfreev (version
);
783 static MonoParseHandler
784 publisher_policy_parser
= {
785 "", /* We don't need to use declare an xml element */
787 publisher_policy_start
,
794 mono_config_parse_publisher_policy (const gchar
*filename
, MonoAssemblyBindingInfo
*info
)
796 ParserUserData user_data
= {
802 &publisher_policy_parser
, /* MonoParseHandler */
803 &user_data
, /* user_data */
804 NULL
, /* MonoImage (we don't need it right now)*/
805 TRUE
/* We are already inited */
808 mono_config_parse_file_with_context (&state
, filename
);
811 static MonoParseHandler
812 config_assemblybinding_parser
= {
813 "", /* We don't need to use declare an xml element */
815 publisher_policy_start
,
817 assembly_binding_end
,
822 mono_config_parse_assembly_bindings (const char *filename
, int amajor
, int aminor
, void *user_data
, void (*infocb
)(MonoAssemblyBindingInfo
*info
, void *user_data
))
824 MonoAssemblyBindingInfo info
;
832 pud
.info_parsed
= infocb
;
833 pud
.user_data
= user_data
;
835 state
.current
= &config_assemblybinding_parser
; /* MonoParseHandler */
836 state
.user_data
= &pud
;
837 state
.assembly
= NULL
; /* MonoImage (we don't need it right now)*/
838 state
.inited
= TRUE
; /* We are already inited */
840 mono_config_parse_file_with_context (&state
, filename
);
843 static mono_bool mono_server_mode
= FALSE
;
846 mono_config_set_server_mode (mono_bool server_mode
)
848 mono_server_mode
= server_mode
;
852 mono_config_is_server_mode (void)
854 return mono_server_mode
;