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.h"
22 #if defined(__linux__)
23 #define CONFIG_OS "linux"
24 #elif defined(__APPLE__)
25 #define CONFIG_OS "osx"
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"
37 #define CONFIG_OS "aix"
39 #define CONFIG_OS "hpux"
40 #elif defined(SN_TARGET_PS3)
41 #define CONFIG_OS "CellOS"
43 #warning Unknown operating system
44 #define CONFIG_OS "unknownOS"
49 #define CONFIG_CPU "x86"
50 #define CONFIG_WORDSIZE "32"
51 #elif defined(__x86_64__)
52 #define CONFIG_CPU "x86-64"
53 #define CONFIG_WORDSIZE "64"
54 #elif defined(sparc) || defined(__sparc__)
55 #define CONFIG_CPU "sparc"
56 #define CONFIG_WORDSIZE "32"
57 #elif defined(__ppc64__) || defined(__powerpc64__)
58 #define CONFIG_CPU "ppc64"
59 #define CONFIG_WORDSIZE "64"
60 #elif defined(__ppc__) || defined(__powerpc__)
61 #define CONFIG_CPU "ppc"
62 #define CONFIG_WORDSIZE "32"
63 #elif defined(__s390x__)
64 #define CONFIG_CPU "s390x"
65 #define CONFIG_WORDSIZE "64"
66 #elif defined(__s390__)
67 #define CONFIG_CPU "s390"
68 #define CONFIG_WORDSIZE "32"
69 #elif defined(__arm__)
70 #define CONFIG_CPU "arm"
71 #define CONFIG_WORDSIZE "32"
72 #elif defined(__ia64__)
73 #define CONFIG_CPU "ia64"
74 #define CONFIG_WORDSIZE "64"
75 #elif defined(__alpha__)
76 #define CONFIG_CPU "alpha"
77 #define CONFIG_WORDSIZE "64"
78 #elif defined(hppa) || defined(__hppa__)
79 #define CONFIG_CPU "hppa"
80 #define CONFIG_WORDSIZE "32"
81 #elif defined(mips) || defined(__mips) || defined(_mips)
82 #define CONFIG_CPU "mips"
83 #define CONFIG_WORDSIZE "32"
86 #define CONFIG_CPU "unknownCPU"
90 static void start_element (GMarkupParseContext
*context
,
91 const gchar
*element_name
,
92 const gchar
**attribute_names
,
93 const gchar
**attribute_values
,
97 static void end_element (GMarkupParseContext
*context
,
98 const gchar
*element_name
,
102 static void parse_text (GMarkupParseContext
*context
,
108 static void passthrough (GMarkupParseContext
*context
,
114 static void parse_error (GMarkupParseContext
*context
,
118 static const GMarkupParser
127 static GHashTable
*config_handlers
;
129 /* when this interface is stable, export it. */
130 typedef struct MonoParseHandler MonoParseHandler
;
132 struct MonoParseHandler
{
133 const char *element_name
;
134 void*(*init
) (MonoImage
*assembly
);
135 void (*start
) (gpointer user_data
, const gchar
*name
,
136 const gchar
**attributes
,
137 const gchar
**values
);
138 void (*text
) (gpointer user_data
, const char *text
, gsize test_len
);
139 void (*end
) (gpointer user_data
, const char *name
);
140 void (*finish
) (gpointer user_data
);
144 MonoParseHandler
*current
;
150 static void start_element (GMarkupParseContext
*context
,
151 const gchar
*element_name
,
152 const gchar
**attribute_names
,
153 const gchar
**attribute_values
,
157 ParseState
*state
= user_data
;
158 if (!state
->current
) {
159 state
->current
= g_hash_table_lookup (config_handlers
, element_name
);
160 if (state
->current
&& state
->current
->init
)
161 state
->user_data
= state
->current
->init (state
->assembly
);
163 if (state
->current
&& state
->current
->start
)
164 state
->current
->start (state
->user_data
, element_name
, attribute_names
, attribute_values
);
167 static void end_element (GMarkupParseContext
*context
,
168 const gchar
*element_name
,
172 ParseState
*state
= user_data
;
173 if (state
->current
) {
174 if (state
->current
->end
)
175 state
->current
->end (state
->user_data
, element_name
);
176 if (strcmp (state
->current
->element_name
, element_name
) == 0) {
177 if (state
->current
->finish
)
178 state
->current
->finish (state
->user_data
);
179 state
->current
= NULL
;
180 state
->user_data
= NULL
;
185 static void parse_text (GMarkupParseContext
*context
,
191 ParseState
*state
= user_data
;
192 if (state
->current
&& state
->current
->text
)
193 state
->current
->text (state
->user_data
, text
, text_len
);
196 static void passthrough (GMarkupParseContext
*context
,
205 static void parse_error (GMarkupParseContext
*context
,
212 arch_matches (const char* arch
, const char *value
)
214 char **splitted
, **p
;
216 if (value
[0] == '!')
217 return !arch_matches (arch
, value
+ 1);
218 p
= splitted
= g_strsplit (value
, ",", 0);
220 if (strcmp (arch
, *p
) == 0) {
226 g_strfreev (splitted
);
238 dllmap_init (MonoImage
*assembly
) {
239 DllInfo
*info
= g_new0 (DllInfo
, 1);
240 info
->assembly
= assembly
;
245 dllmap_start (gpointer user_data
,
246 const gchar
*element_name
,
247 const gchar
**attribute_names
,
248 const gchar
**attribute_values
)
251 DllInfo
*info
= user_data
;
253 if (strcmp (element_name
, "dllmap") == 0) {
255 g_free (info
->target
);
256 info
->dll
= info
->target
= NULL
;
257 info
->ignore
= FALSE
;
258 for (i
= 0; attribute_names
[i
]; ++i
) {
259 if (strcmp (attribute_names
[i
], "dll") == 0)
260 info
->dll
= g_strdup (attribute_values
[i
]);
261 else if (strcmp (attribute_names
[i
], "target") == 0)
262 info
->target
= g_strdup (attribute_values
[i
]);
263 else if (strcmp (attribute_names
[i
], "os") == 0 && !arch_matches (CONFIG_OS
, attribute_values
[i
]))
265 else if (strcmp (attribute_names
[i
], "cpu") == 0 && !arch_matches (CONFIG_CPU
, attribute_values
[i
]))
267 else if (strcmp (attribute_names
[i
], "wordsize") == 0 && !arch_matches (CONFIG_WORDSIZE
, attribute_values
[i
]))
271 mono_dllmap_insert (info
->assembly
, info
->dll
, NULL
, info
->target
, NULL
);
272 } else if (strcmp (element_name
, "dllentry") == 0) {
273 const char *name
= NULL
, *target
= NULL
, *dll
= NULL
;
275 for (i
= 0; attribute_names
[i
]; ++i
) {
276 if (strcmp (attribute_names
[i
], "dll") == 0)
277 dll
= attribute_values
[i
];
278 else if (strcmp (attribute_names
[i
], "target") == 0)
279 target
= attribute_values
[i
];
280 else if (strcmp (attribute_names
[i
], "name") == 0)
281 name
= 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
]))
291 if (!info
->ignore
&& !ignore
)
292 mono_dllmap_insert (info
->assembly
, info
->dll
, name
, dll
, target
);
297 dllmap_finish (gpointer user_data
)
299 DllInfo
*info
= user_data
;
302 g_free (info
->target
);
306 static const MonoParseHandler
317 legacyUEP_start (gpointer user_data
,
318 const gchar
*element_name
,
319 const gchar
**attribute_names
,
320 const gchar
**attribute_values
) {
321 if ((strcmp (element_name
, "legacyUnhandledExceptionPolicy") == 0) &&
322 (attribute_names
[0] != NULL
) &&
323 (strcmp (attribute_names
[0], "enabled") == 0)) {
324 if ((strcmp (attribute_values
[0], "1") == 0) ||
325 (g_strcasecmp (attribute_values
[0], "true") == 0)) {
326 mono_runtime_unhandled_exception_policy_set (MONO_UNHANDLED_POLICY_LEGACY
);
331 static const MonoParseHandler
332 legacyUEP_handler
= {
333 "legacyUnhandledExceptionPolicy",
341 static int inited
= 0;
344 mono_config_init (void)
347 config_handlers
= g_hash_table_new (g_str_hash
, g_str_equal
);
348 g_hash_table_insert (config_handlers
, (gpointer
) dllmap_handler
.element_name
, (gpointer
) &dllmap_handler
);
349 g_hash_table_insert (config_handlers
, (gpointer
) legacyUEP_handler
.element_name
, (gpointer
) &legacyUEP_handler
);
352 /* FIXME: error handling */
355 mono_config_parse_xml_with_context (ParseState
*state
, const char *text
, gsize len
)
357 GMarkupParseContext
*context
;
362 context
= g_markup_parse_context_new (&mono_parser
, 0, state
, NULL
);
363 if (g_markup_parse_context_parse (context
, text
, len
, NULL
)) {
364 g_markup_parse_context_end_parse (context
, NULL
);
366 g_markup_parse_context_free (context
);
369 /* If assembly is NULL, parse in the global context */
371 mono_config_parse_file_with_context (ParseState
*state
, const char *filename
)
376 mono_trace (G_LOG_LEVEL_INFO
, MONO_TRACE_CONFIG
,
377 "Config attempting to parse: '%s'.", filename
);
379 if (!g_file_get_contents (filename
, &text
, &len
, NULL
))
381 mono_config_parse_xml_with_context (state
, text
, len
);
387 * mono_config_parse_memory:
388 * @buffer: a pointer to an string XML representation of the configuration
390 * Parses the configuration from a buffer
393 mono_config_parse_memory (const char *buffer
)
395 ParseState state
= {NULL
};
397 mono_config_parse_xml_with_context (&state
, buffer
, strlen (buffer
));
401 mono_config_parse_file (const char *filename
)
403 ParseState state
= {NULL
};
404 mono_config_parse_file_with_context (&state
, filename
);
408 * use the equivalent lookup code from the GAC when available.
409 * Depending on state, this should give something like:
410 * aname/version-pubtoken/
415 get_assembly_filename (MonoImage
*image
, int state
)
419 return g_strdup (mono_image_get_name (image
));
425 typedef struct _BundledConfig BundledConfig
;
427 struct _BundledConfig
{
430 const char* config_xml
;
433 static BundledConfig
*bundled_configs
= NULL
;
435 static const char *bundled_machine_config
= NULL
;
438 mono_register_config_for_assembly (const char* assembly_name
, const char* config_xml
)
440 BundledConfig
*bconfig
;
442 bconfig
= g_new0 (BundledConfig
, 1);
443 bconfig
->aname
= assembly_name
;
444 bconfig
->config_xml
= config_xml
;
445 bconfig
->next
= bundled_configs
;
446 bundled_configs
= bconfig
;
450 mono_config_string_for_assembly_file (const char *filename
)
452 BundledConfig
*bconfig
;
454 for (bconfig
= bundled_configs
; bconfig
; bconfig
= bconfig
->next
) {
455 if (bconfig
->aname
&& strcmp (bconfig
->aname
, filename
) == 0)
456 return bconfig
->config_xml
;
462 mono_config_for_assembly (MonoImage
*assembly
)
464 ParseState state
= {NULL
};
466 char *aname
, *cfg
, *cfg_name
;
467 const char *bundled_config
;
470 state
.assembly
= assembly
;
472 bundled_config
= mono_config_string_for_assembly_file (assembly
->module_name
);
474 mono_config_parse_xml_with_context (&state
, bundled_config
, strlen (bundled_config
));
476 cfg_name
= g_strdup_printf ("%s.config", mono_image_get_filename (assembly
));
477 mono_config_parse_file_with_context (&state
, cfg_name
);
480 cfg_name
= g_strdup_printf ("%s.config", mono_image_get_name (assembly
));
482 home
= g_get_home_dir ();
484 for (i
= 0; (aname
= get_assembly_filename (assembly
, i
)) != NULL
; ++i
) {
485 cfg
= g_build_filename (mono_get_config_dir (), "mono", "assemblies", aname
, cfg_name
, NULL
);
486 got_it
+= mono_config_parse_file_with_context (&state
, cfg
);
489 #ifndef PLATFORM_WIN32
490 cfg
= g_build_filename (home
, ".mono", "assemblies", aname
, cfg_name
, NULL
);
491 got_it
+= mono_config_parse_file_with_context (&state
, cfg
);
503 * @filename: the filename to load the configuration variables from.
505 * Pass a NULL filename to parse the default config files
506 * (or the file in the MONO_CONFIG env var).
509 mono_config_parse (const char *filename
) {
512 #ifndef PLATFORM_WIN32
517 mono_config_parse_file (filename
);
521 home
= g_getenv ("MONO_CONFIG");
523 mono_config_parse_file (home
);
527 mono_cfg
= g_build_filename (mono_get_config_dir (), "mono", "config", NULL
);
528 mono_config_parse_file (mono_cfg
);
531 #ifndef PLATFORM_WIN32
532 home
= g_get_home_dir ();
533 user_cfg
= g_strconcat (home
, G_DIR_SEPARATOR_S
, ".mono/config", NULL
);
534 mono_config_parse_file (user_cfg
);
539 static const char *mono_cfg_dir
= NULL
;
541 /* Invoked during startup */
543 mono_set_config_dir (const char *dir
)
545 /* If this variable is set, overrides the directory computed */
546 mono_cfg_dir
= g_getenv ("MONO_CFG_DIR");
547 if (mono_cfg_dir
== NULL
)
548 mono_cfg_dir
= g_strdup (dir
);
552 mono_get_config_dir (void)
554 if (mono_cfg_dir
== NULL
)
555 mono_set_dirs (NULL
, NULL
);
561 mono_register_machine_config (const char *config_xml
)
563 bundled_machine_config
= config_xml
;
567 mono_get_machine_config (void)
569 return bundled_machine_config
;
573 publisher_policy_start (gpointer user_data
,
574 const gchar
*element_name
,
575 const gchar
**attribute_names
,
576 const gchar
**attribute_values
)
578 MonoAssemblyBindingInfo
*info
;
582 if (!strcmp (element_name
, "assemblyIdentity")) {
583 for (n
= 0; attribute_names
[n
]; n
++) {
584 const gchar
*attribute_name
= attribute_names
[n
];
586 if (!strcmp (attribute_name
, "name"))
587 info
->name
= g_strdup (attribute_values
[n
]);
588 else if (!strcmp (attribute_name
, "publicKeyToken")) {
589 if (strlen (attribute_values
[n
]) == MONO_PUBLIC_KEY_TOKEN_LENGTH
- 1)
590 g_strlcpy ((char *) info
->public_key_token
, attribute_values
[n
], MONO_PUBLIC_KEY_TOKEN_LENGTH
);
591 } else if (!strcmp (attribute_name
, "culture")) {
592 if (!strcmp (attribute_values
[n
], "neutral"))
593 info
->culture
= g_strdup ("");
595 info
->culture
= g_strdup (attribute_values
[n
]);
598 } else if (!strcmp (element_name
, "bindingRedirect")) {
599 for (n
= 0; attribute_names
[n
]; n
++) {
600 const gchar
*attribute_name
= attribute_names
[n
];
602 if (!strcmp (attribute_name
, "oldVersion")) {
603 gchar
**numbers
, **version
, **versions
;
604 gint major
, minor
, build
, revision
;
607 if (!strcmp (attribute_values
[n
], ""))
610 versions
= g_strsplit (attribute_values
[n
], "-", 2);
611 version
= g_strsplit (*versions
, ".", 4);
613 /* We assign the values to gint vars to do the checks */
615 major
= *numbers
? atoi (*numbers
++) : -1;
616 minor
= *numbers
? atoi (*numbers
++) : -1;
617 build
= *numbers
? atoi (*numbers
++) : -1;
618 revision
= *numbers
? atoi (*numbers
) : -1;
619 g_strfreev (version
);
620 if (major
< 0 || minor
< 0 || build
< 0 || revision
< 0) {
621 g_strfreev (versions
);
625 info
->old_version_bottom
.major
= major
;
626 info
->old_version_bottom
.minor
= minor
;
627 info
->old_version_bottom
.build
= build
;
628 info
->old_version_bottom
.revision
= revision
;
629 info
->has_old_version_bottom
= TRUE
;
631 if (!*(versions
+ 1)) {
632 g_strfreev (versions
);
636 numbers
= version
= g_strsplit (*(versions
+ 1), ".", 4);
637 major
= *numbers
? atoi (*numbers
++) : -1;
638 minor
= *numbers
? atoi (*numbers
++) : -1;
639 build
= *numbers
? atoi (*numbers
++) : -1;
640 revision
= *numbers
? atoi (*numbers
) : 1;
641 g_strfreev (version
);
642 if (major
< 0 || minor
< 0 || build
< 0 || revision
< 0) {
643 g_strfreev (versions
);
647 info
->old_version_top
.major
= major
;
648 info
->old_version_top
.minor
= minor
;
649 info
->old_version_top
.build
= build
;
650 info
->old_version_top
.revision
= revision
;
651 info
->has_old_version_top
= TRUE
;
653 g_strfreev (versions
);
654 } else if (!strcmp (attribute_name
, "newVersion")) {
655 gchar
**numbers
, **version
;
658 if (!strcmp (attribute_values
[n
], ""))
661 numbers
= version
= g_strsplit (attribute_values
[n
], ".", 4);
662 info
->new_version
.major
= *numbers
? atoi (*numbers
++) : -1;
663 info
->new_version
.minor
= *numbers
? atoi (*numbers
++) : -1;
664 info
->new_version
.build
= *numbers
? atoi (*numbers
++) : -1;
665 info
->new_version
.revision
= *numbers
? atoi (*numbers
) : -1;
666 info
->has_new_version
= TRUE
;
667 g_strfreev (version
);
673 static MonoParseHandler
674 publisher_policy_parser
= {
675 "", /* We don't need to use declare an xml element */
677 publisher_policy_start
,
684 mono_config_parse_publisher_policy (const gchar
*filename
, MonoAssemblyBindingInfo
*info
)
687 &publisher_policy_parser
, /* MonoParseHandler */
688 info
, /* user_data */
689 NULL
, /* MonoImage (we don't need it right now)*/
690 TRUE
/* We are already inited */
693 mono_config_parse_file_with_context (&state
, filename
);