2 * collectd - src/dotnet.c
3 * Copyright (C) 2010 Florian Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian Forster <ff at octo.it>
30 #include "configfile.h"
33 #include <mono/jit/jit.h>
34 #include <mono/metadata/assembly.h>
35 #include <mono/metadata/threads.h>
37 struct dotnet_callback_info_s
42 typedef struct dotnet_callback_info_s dotnet_callback_info_t;
44 static MonoDomain *_domain = NULL;
46 static int dotnet_read (user_data_t *ud) /* {{{ */
48 dotnet_callback_info_t *ci = ud->data;
56 DEBUG ("dotnet plugin: mono_domain_get_by_id (%"PRIi32") ...",
57 (int32_t) ci->domain_id);
58 domain = mono_domain_get_by_id (ci->domain_id);
61 ERROR ("dotnet plugin: mono_domain_get_by_id failed.");
65 thread = mono_thread_attach (domain);
68 ERROR ("dotnet plugin: mono_thread_attach failed.");
72 if (!mono_domain_set (domain, /* force = */ 0))
74 ERROR ("dotnet plugin: mono_domain_set failed.");
78 DEBUG ("dotnet plugin: mono_gchandle_get_target ...");
79 object = mono_gchandle_get_target (ci->obj_handle);
82 ERROR ("dotnet plugin: mono_gchandle_get_target failed.");
83 mono_thread_detach (thread);
87 DEBUG ("dotnet plugin: mono_object_get_class ...");
88 class = mono_object_get_class (object);
91 ERROR ("dotnet plugin: mono_object_get_class failed.");
92 mono_thread_detach (thread);
96 DEBUG ("dotnet plugin: mono_class_get_method_from_name ...");
97 method = mono_class_get_method_from_name (class,
98 /* name = */ "read", /* nargs = */ 0);
101 ERROR ("dotnet plugin: mono_class_get_method_from_name failed.");
102 mono_thread_detach (thread);
106 DEBUG ("dotnet plugin: mono_runtime_invoke ...");
107 ret = mono_runtime_invoke (method, object, /* params = */ NULL,
108 /* exception ptr = */ NULL);
109 DEBUG ("dotnet plugin: mono_runtime_invoke returned %p.", (void *) ret);
111 mono_thread_detach (thread);
113 } /* }}} int dotnet_read */
115 static void dotnet_callback_info_free (void *ptr) /* {{{ */
117 dotnet_callback_info_t *ci = ptr;
122 mono_gchandle_free (ci->obj_handle);
124 } /* }}} void dotnet_callback_info_free */
126 static MonoMethod *dotnet_object_method_get_from_name (MonoObject *object, /* {{{ */
127 const char *name, int param_count)
131 class = mono_object_get_class (object);
140 method = mono_class_get_method_from_name (class, name, param_count);
144 pclass = mono_class_get_parent (class);
145 if ((pclass == NULL) || (pclass == class))
147 ERROR ("dotnet plugin: Unable to find method \"%s\" in class \"%s\".",
148 name, mono_class_get_name (mono_object_get_class (object)));
155 } /* }}} MonoMethod *dotnet_object_method_get_from_name */
157 static MonoProperty *dotnet_object_get_property_from_name (MonoObject *object, /* {{{ */
162 class = mono_object_get_class (object);
169 prop = mono_class_get_property_from_name (class, name);
173 pclass = mono_class_get_parent (class);
174 if ((pclass == NULL) || (pclass == class))
176 ERROR ("dotnet plugin: Unable to find property \"%s\" in class \"%s\".",
177 name, mono_class_get_name (mono_object_get_class (object)));
184 } /* }}} MonoProperty *dotnet_object_get_property_from_name */
186 static int dotnet_object_method_get_string (MonoObject *obj, /* {{{ */
187 const char *method_name, char *buffer, size_t buffer_size)
193 method = dotnet_object_method_get_from_name (obj, method_name, /* nargs = */ 0);
197 ret = mono_runtime_invoke (method, obj, /* params = */ NULL, /* exception = */ NULL);
201 tmp = mono_string_to_utf8 ((MonoString *) ret);
204 ERROR ("dotnet plugin: mono_string_to_utf8 failed.");
207 DEBUG ("dotnet plugin: Method \"%s\" returned string \"%s\".",
208 mono_method_get_name (method), tmp);
210 sstrncpy (buffer, tmp, buffer_size);
212 } /* }}} int dotnet_object_method_get_string */
214 static int dotnet_object_method_get_cdtime (MonoObject *obj, /* {{{ */
215 const char *method_name, cdtime_t *ret_value)
221 method = dotnet_object_method_get_from_name (obj, method_name, /* nargs = */ 0);
225 ret_obj = mono_runtime_invoke (method, obj, /* params = */ NULL, /* exception = */ NULL);
229 tmp = *((double *) mono_object_unbox (ret_obj));
230 DEBUG ("dotnet plugin: Method \"%s\" returned value %.3f.",
231 mono_method_get_name (method), tmp);
232 *ret_value = DOUBLE_TO_CDTIME_T (tmp);
235 } /* }}} int dotnet_object_method_get_cdtime */
237 static int dotnet_object_method_get_values (MonoObject *obj, /* {{{ */
241 MonoObject *ilist_obj;
243 MonoProperty *count_prop;
244 MonoProperty *item_prop;
247 method = dotnet_object_method_get_from_name (obj, "getValues", /* nargs = */ 0);
251 ilist_obj = mono_runtime_invoke (method, obj, /* params = */ NULL, /* exception = */ NULL);
252 if (ilist_obj == NULL)
255 count_prop = dotnet_object_get_property_from_name (ilist_obj, "Count");
256 if (count_prop == NULL)
258 ERROR ("dotnet plugin: dotnet_object_get_property_from_name (\"Count\") failed.");
262 item_prop = dotnet_object_get_property_from_name (ilist_obj, "Item");
263 if (item_prop == NULL)
265 ERROR ("dotnet plugin: dotnet_object_get_property_from_name (\"Item\") failed.");
269 method = mono_property_get_get_method (count_prop);
270 tmp_obj = mono_runtime_invoke (method, ilist_obj, /* params = */ NULL, /* exception = */ NULL);
273 ERROR ("dotnet plugin: mono_runtime_invoke failed.");
276 DEBUG ("dotnet plugin: Type of the Count property is \"%s\".",
277 mono_class_get_name (mono_object_get_class (tmp_obj)));
279 vl->values_len = (int) *((int32_t *) mono_object_unbox (tmp_obj));
280 DEBUG ("dotnet plugin: vl->values_len = %i;", vl->values_len);
282 if (vl->values_len <= 0)
288 vl->values = calloc ((size_t) vl->values_len, sizeof (*vl->values));
289 if (vl->values == NULL)
295 for (i = 0; i < ((int32_t) vl->values_len); i++)
297 void *args[2] = { &i, NULL };
298 MonoObject *value_obj;
299 const char *class_name;
301 method = mono_property_get_get_method (item_prop);
302 value_obj = mono_runtime_invoke (method, ilist_obj, /* params = */ args, /* exception = */ NULL);
303 if (value_obj == NULL)
305 ERROR ("dotnet plugin: Item(%i) failed.", i);
309 class_name = mono_class_get_name (mono_object_get_class (value_obj));
310 if (strcmp ("gaugeValue", class_name) == 0)
312 MonoObject *gauge_obj;
314 method = dotnet_object_method_get_from_name (value_obj, "toDouble", /* nargs = */ 0);
318 gauge_obj = mono_runtime_invoke (method, value_obj, /* args = */ NULL, /* exception = */ NULL);
319 if (gauge_obj == NULL)
322 vl->values[i].gauge = (gauge_t) *((double *) mono_object_unbox (gauge_obj));
323 DEBUG ("dotnet plugin: Gauge value %"PRIi32" = %g;",
324 i, vl->values[i].gauge);
326 else if (strcmp ("deriveValue", class_name) == 0)
328 MonoObject *derive_obj;
330 method = dotnet_object_method_get_from_name (value_obj, "toLong", /* nargs = */ 0);
334 derive_obj = mono_runtime_invoke (method, value_obj, /* args = */ NULL, /* exception = */ NULL);
335 if (derive_obj == NULL)
338 vl->values[i].derive = (derive_t) *((int64_t *) mono_object_unbox (derive_obj));
339 DEBUG ("dotnet plugin: Derive value %"PRIi32" = %"PRIi64";",
340 i, vl->values[i].derive);
345 } /* }}} int dotnet_object_method_get_values */
348 * Functions exposed to .Net
350 static int dotnet_log (int severity, MonoString *message) /* {{{ */
352 char *tmp = mono_string_to_utf8 (message);
354 DEBUG ("dotnet_log (severity = %i, message = \"%s\");", severity, tmp);
357 } /* }}} int dotnet_log */
359 static int dotnet_register_read (MonoString *name, MonoObject *obj) /* {{{ */
362 dotnet_callback_info_t *ci;
367 /* Sanity checks: Make sure this object actually has the required method. */
368 class = mono_object_get_class (obj);
371 ERROR ("dotnet plugin: mono_object_get_class failed.");
375 method = mono_class_get_method_from_name (class,
376 /* name = */ "read", /* param count = */ 0);
379 ERROR ("dotnet plugin: mono_class_get_method_from_name failed.");
383 ci = malloc (sizeof (*ci));
386 ERROR ("dotnet plugin: malloc failed.");
389 memset (ci, 0, sizeof (*ci));
391 ci->domain_id = mono_domain_get_id (mono_domain_get ());
392 ci->obj_handle = mono_gchandle_new (obj, /* pinned = */ 1);
395 ud.free_func = dotnet_callback_info_free;
397 plugin_register_complex_read (/* group = */ "dotnet",
398 /* name = */ mono_string_to_utf8 (name),
399 /* callback = */ dotnet_read,
400 /* interval = */ NULL,
401 /* user data = */ &ud);
404 } /* }}} int dotnet_register_read */
406 static int dotnet_dispatch_values (MonoObject *obj) /* {{{ */
408 value_list_t vl = VALUE_LIST_INIT;
411 status = dotnet_object_method_get_string (obj, "getHost", vl.host, sizeof (vl.host));
415 status = dotnet_object_method_get_string (obj, "getPlugin", vl.plugin, sizeof (vl.plugin));
419 status = dotnet_object_method_get_string (obj, "getPluginInstance", vl.plugin_instance, sizeof (vl.plugin_instance));
423 status = dotnet_object_method_get_string (obj, "getType", vl.type, sizeof (vl.type));
427 status = dotnet_object_method_get_string (obj, "getTypeInstance", vl.type_instance, sizeof (vl.type_instance));
431 status = dotnet_object_method_get_cdtime (obj, "getInterval", &vl.interval);
435 status = dotnet_object_method_get_cdtime (obj, "getTime", &vl.time);
439 status = dotnet_object_method_get_values (obj, &vl);
443 status = plugin_dispatch_values (&vl);
447 } /* }}} int dotnet_dispatch_values */
450 * Initialization functions
452 static int dotnet_load_class (const char *assembly_name, /* {{{ */
453 const char *name_space, const char *class_name)
457 MonoAssembly *assembly;
462 domain = mono_domain_create ();
465 ERROR ("dotnet plugin: mono_domain_create failed.");
469 thread = mono_thread_attach (domain);
472 ERROR ("dotnet plugin: mono_thread_attach failed.");
473 mono_domain_free (domain, /* force = */ 0);
477 if (!mono_domain_set (domain, /* force = */ 0))
479 ERROR ("dotnet plugin: mono_domain_set failed.");
480 mono_thread_detach (thread);
481 mono_domain_free (domain, /* force = */ 0);
485 assembly = mono_domain_assembly_open (domain, assembly_name);
486 if (assembly == NULL)
488 ERROR ("dotnet plugin: mono_domain_assembly_open (\"%s\") failed.",
490 mono_thread_detach (thread);
491 mono_domain_free (domain, /* force = */ 0);
495 image = mono_assembly_get_image (assembly);
498 ERROR ("dotnet plugin: mono_assembly_get_image failed.");
499 mono_thread_detach (thread);
500 mono_domain_free (domain, /* force = */ 0);
504 class = mono_class_from_name (image,
505 (name_space != NULL) ? name_space : "",
509 ERROR ("dotnet plugin: Looking up class \"%s\" in assembly \"%s\" failed.",
510 class_name, assembly_name);
511 mono_thread_detach (thread);
512 mono_domain_free (domain, /* force = */ 0);
516 obj = mono_object_new (domain, class);
519 ERROR ("dotnet plugin: Creating a \"%s\" object failed.", class_name);
520 mono_thread_detach (thread);
521 mono_domain_free (domain, /* force = */ 0);
525 mono_runtime_object_init (obj);
527 DEBUG ("dotnet plugin: Successfully created a \"%s\" object.", class_name);
529 mono_thread_detach (thread);
531 } /* }}} int dotnet_load_class */
535 * <LoadPlugin "Foobar">
536 * NameSpace "MyCompany"
537 * Assembly "path/to/file.dll"
545 static int dotnet_config_loadplugin (oconfig_item_t *ci) /* {{{ */
547 char *class_name = NULL;
548 char *name_space = NULL;
549 char *assembly_name = NULL;
553 status = cf_util_get_string (ci, &class_name);
556 assert (class_name != NULL);
558 for (i = 0; i < ci->children_num; i++)
560 oconfig_item_t *child = ci->children + i;
562 if (strcasecmp ("NameSpace", child->key) == 0)
563 cf_util_get_string (child, &name_space);
564 else if (strcasecmp ("Assembly", child->key) == 0)
565 cf_util_get_string (child, &assembly_name);
567 WARNING ("dotnet plugin: Config option \"%s\" is not allowed here.",
571 if (assembly_name == NULL)
573 ERROR ("dotnet plugin: No \"Assembly\" option within this \"LoadPlugin\" "
574 "block (class \"%s\").", class_name);
579 status = dotnet_load_class (assembly_name, name_space, class_name);
584 } /* }}} int dotnet_config_loadplugin */
586 static int dotnet_config (oconfig_item_t *ci) /* {{{ */
590 for (i = 0; i < ci->children_num; i++)
592 oconfig_item_t *child = ci->children + i;
594 if (strcasecmp ("LoadPlugin", child->key) == 0)
595 dotnet_config_loadplugin (child);
597 WARNING ("dotnet plugin: Ignoring unknown config option \"%s\".",
602 } /* }}} int dotnet_config */
604 void module_register (void)
606 _domain = mono_jit_init (PACKAGE_NAME);
609 ERROR ("dotnet plugin: mono_jit_init failed.");
613 mono_add_internal_call ("CollectdAPI.Collectd::log", dotnet_log);
614 mono_add_internal_call ("CollectdAPI.Collectd::registerRead", dotnet_register_read);
615 mono_add_internal_call ("CollectdAPI.Collectd::dispatchValues", dotnet_dispatch_values);
617 plugin_register_complex_config ("dotnet", dotnet_config);
618 } /* void module_register */
620 /* vim: set sw=2 sts=2 et fdm=marker : */