dotnet plugin: Implement registering read callbacks.
[collectd.git] / src / dotnet.c
1 /**
2  * collectd - src/dotnet.c
3  * Copyright (C) 2010  Florian Forster
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
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.
22  *
23  * Authors:
24  *   Florian Forster <ff at octo.it>
25  **/
26
27 #include "collectd.h"
28 #include "plugin.h"
29 #include "common.h"
30 #include "configfile.h"
31
32 #include <glib.h>
33 #include <mono/jit/jit.h>
34 #include <mono/metadata/assembly.h>
35
36 struct dotnet_callback_info_s
37 {
38   gint32 domain_id;
39   guint32 obj_handle;
40 };
41 typedef struct dotnet_callback_info_s dotnet_callback_info_t;
42
43 static MonoDomain *domain = NULL;
44
45 static int dotnet_read (user_data_t *ud) /* {{{ */
46 {
47   dotnet_callback_info_t *ci = ud->data;
48   MonoClass  *class;
49   MonoObject *object;
50   MonoMethod *method;
51   MonoObject *ret;
52
53 #if 0
54   MonoDomain *domain;
55   DEBUG ("dotnet plugin: mono_domain_get_by_id (%"PRIi32") ...",
56       (int32_t) ci->domain_id);
57   domain = mono_domain_get_by_id (ci->domain_id);
58   if (domain == NULL)
59   {
60     ERROR ("dotnet plugin: mono_domain_get_by_id failed.");
61     return (-1);
62   }
63   mono_domain_set_internal (domain);
64 #endif
65
66   DEBUG ("dotnet plugin: mono_gchandle_get_target ...");
67   object = mono_gchandle_get_target (ci->obj_handle);
68   if (object == NULL)
69   {
70     ERROR ("dotnet plugin: mono_gchandle_get_target failed.");
71     return (-1);
72   }
73
74   DEBUG ("dotnet plugin: mono_object_get_class ...");
75   class = mono_object_get_class (object);
76   if (class == NULL)
77   {
78     ERROR ("dotnet plugin: mono_object_get_class failed.");
79     return (-1);
80   }
81
82   DEBUG ("dotnet plugin: mono_class_get_method_from_name ...");
83   method = mono_class_get_method_from_name (class,
84       /* name = */ "read", /* nargs = */ 0);
85   if (method == NULL)
86   {
87     ERROR ("dotnet plugin: mono_class_get_method_from_name failed.");
88     return (-1);
89   }
90
91   DEBUG ("dotnet plugin: mono_runtime_invoke ...");
92   ret = mono_runtime_invoke (method, object, /* params = */ NULL,
93       /* exception ptr = */ NULL);
94   DEBUG ("dotnet plugin: mono_runtime_invoke returned %p.", (void *) ret);
95
96   return (0);
97 } /* }}} int dotnet_read */
98
99 static void dotnet_callback_info_free (void *ptr) /* {{{ */
100 {
101   dotnet_callback_info_t *ci = ptr;
102
103   if (ci == NULL)
104     return;
105
106   mono_gchandle_free (ci->obj_handle);
107   sfree (ci);
108 } /* }}} void dotnet_callback_info_free */
109
110 static int dotnet_log (int severity, MonoString *message) /* {{{ */
111 {
112   char *tmp = mono_string_to_utf8 (message);
113
114   DEBUG ("dotnet_log (severity = %i, message = \"%s\");", severity, tmp);
115
116   return (0);
117 } /* }}} int dotnet_log */
118
119 static int dotnet_register_read (MonoString *name, MonoObject *obj) /* {{{ */
120 {
121   user_data_t ud;
122   dotnet_callback_info_t *ci;
123
124   MonoClass *class;
125   MonoMethod *method;
126
127   /* Sanity checks: Make sure this object actually has the required method. */
128   class = mono_object_get_class (obj);
129   if (class == NULL)
130   {
131     ERROR ("dotnet plugin: mono_object_get_class failed.");
132     return (-1);
133   }
134
135   method = mono_class_get_method_from_name (class,
136       /* name = */ "read", /* param count = */ 0);
137   if (method == NULL)
138   {
139     ERROR ("dotnet plugin: mono_class_get_method_from_name failed.");
140     return (-1);
141   }
142
143   ci = malloc (sizeof (*ci));
144   if (ci == NULL)
145   {
146     ERROR ("dotnet plugin: malloc failed.");
147     return (-1);
148   }
149   memset (ci, 0, sizeof (*ci));
150
151   ci->domain_id = mono_domain_get_id (mono_domain_get ());
152   ci->obj_handle = mono_gchandle_new (obj, /* pinned = */ 1);
153
154   ud.data = ci;
155   ud.free_func = dotnet_callback_info_free;
156
157   plugin_register_complex_read (/* group = */ "dotnet",
158       /* name      = */ mono_string_to_utf8 (name),
159       /* callback  = */ dotnet_read,
160       /* interval  = */ NULL,
161       /* user data = */ &ud);
162
163   return (0);
164 } /* }}} int dotnet_register_read */
165
166 static int dotnet_load_class (const char *assembly_name, /* {{{ */
167     const char *name_space, const char *class_name)
168 {
169   MonoAssembly *assembly;
170   MonoImage *image;
171   MonoClass *class;
172   MonoObject *obj;
173
174 #if 0
175   MonoDomain *domain;
176   domain = mono_domain_get ();
177   if (domain == NULL)
178   {
179     ERROR ("dotnet plugin: mono_domain_get failed.");
180     return (-1);
181   }
182 #endif
183
184   assembly = mono_domain_assembly_open (domain, assembly_name);
185   if (assembly == NULL)
186   {
187     ERROR ("dotnet plugin: mono_domain_assembly_open (\"%s\") failed.",
188       assembly_name);
189     mono_jit_cleanup (domain);
190     return (-1);
191   }
192
193   image = mono_assembly_get_image (assembly);
194   if (image == NULL)
195   {
196     ERROR ("mono_assembly_get_image failed.");
197     mono_jit_cleanup (domain);
198     return (-1);
199   }
200
201   class = mono_class_from_name (image,
202       (name_space != NULL) ? name_space : "",
203       class_name);
204   if (class == NULL)
205   {
206     ERROR ("dotnet plugin: Looking up class \"%s\" in assembly \"%s\" failed.",
207         class_name, assembly_name);
208     return (-1);
209   }
210
211   obj = mono_object_new (domain, class);
212   if (obj == NULL)
213   {
214     ERROR ("Creating a \"%s\" object failed.", class_name);
215     return (-1);
216   }
217
218   mono_runtime_object_init (obj);
219
220   DEBUG ("dotnet plugin: Successfully created a \"%s\" object.", class_name);
221
222   return (0);
223 } /* }}} int dotnet_load_class */
224
225 /*
226  * <Plugin dotnet>
227  *   <LoadPlugin "Foobar">
228  *     NameSpace "MyCompany"
229  *     Assembly "path/to/file.dll"
230  *   </LoadPlugin>
231  *
232  *   <Plugin "Foobar">
233  *     ...
234  *   </Plugin>
235  * </Plugin>
236  */
237 static int dotnet_config_loadplugin (oconfig_item_t *ci) /* {{{ */
238 {
239   char *class_name = NULL;
240   char *name_space = NULL;
241   char *assembly_name = NULL;
242   int status;
243   int i;
244
245   status = cf_util_get_string (ci, &class_name);
246   if (status != 0)
247     return (status);
248   assert (class_name != NULL);
249
250   for (i = 0; i < ci->children_num; i++)
251   {
252     oconfig_item_t *child = ci->children + i;
253
254     if (strcasecmp ("NameSpace", child->key) == 0)
255       cf_util_get_string (child, &name_space);
256     else if (strcasecmp ("Assembly", child->key) == 0)
257       cf_util_get_string (child, &assembly_name);
258     else
259       WARNING ("dotnet plugin: Config option \"%s\" is not allowed here.",
260           child->key);
261   }
262
263   if (assembly_name == NULL)
264   {
265     ERROR ("dotnet plugin: No \"Assembly\" option within this \"LoadPlugin\" "
266         "block (class \"%s\").", class_name);
267     sfree (class_name);
268     sfree (name_space);
269   }
270
271   status = dotnet_load_class (assembly_name, name_space, class_name);
272   if (status != 0)
273     return (status);
274
275   return (0);
276 } /* }}} int dotnet_config_loadplugin */
277
278 static int dotnet_config (oconfig_item_t *ci) /* {{{ */
279 {
280   int i;
281
282   for (i = 0; i < ci->children_num; i++)
283   {
284     oconfig_item_t *child = ci->children + i;
285
286     if (strcasecmp ("LoadPlugin", child->key) == 0)
287       dotnet_config_loadplugin (child);
288     else
289       WARNING ("dotnet plugin: Ignoring unknown config option \"%s\".",
290           child->key);
291   }
292
293   return (0);
294 } /* }}} int dotnet_config */
295
296 void module_register (void)
297 {
298   domain = mono_jit_init (PACKAGE_NAME);
299   if (domain == NULL)
300   {
301     ERROR ("dotnet plugin: mono_jit_init failed.");
302     return;
303   }
304
305   mono_add_internal_call ("CollectdAPI.Collectd::log", dotnet_log);
306   mono_add_internal_call ("CollectdAPI.Collectd::registerRead", dotnet_register_read);
307
308   plugin_register_complex_config ("dotnet", dotnet_config);
309 } /* void module_register */
310
311 /* vim: set sw=2 sts=2 et fdm=marker : */