lua plugin: Move the "collectd_log()" function to src/lua.c.
[collectd.git] / src / lua.c
1 /**
2  * collectd - src/lua.c
3  * Copyright (C) 2010       Julien Ammous
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; only version 2.1 of the License is
8  * applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors:
20  *   Julien Ammous
21  **/
22
23 #include "collectd.h"
24 #include "plugin.h"
25 #include "common.h"
26 #include "configfile.h"
27 #include "utils_cache.h"
28
29 /* Include the Lua API header files. */
30 #include <lua.h>
31 #include <lauxlib.h>
32 #include <lualib.h>
33
34 typedef struct lua_script_s {
35   char          *script_path;
36   lua_State     *lua_state;
37   
38   struct lua_script_s  *next;
39 } lua_script_t;
40
41 struct lua_c_functions_s
42 {
43   char         *name;
44   lua_CFunction func;
45 };
46 typedef struct lua_c_functions_s lua_c_functions_t;
47
48 static char           base_path[PATH_MAX + 1] = "";
49 static lua_script_t  *scripts = NULL;
50
51 /* Cleans up the stack, pushes the return value as a number onto the stack and
52  * returns the number of values returned (1). */
53 #define RETURN_LUA(l,status) do {                    \
54   lua_State *_l_state = (l);                         \
55   lua_settop (_l_state, 0);                          \
56   lua_pushnumber (_l_state, (lua_Number) (status));  \
57   return (1);                                        \
58 } while (0)
59
60 /*
61  * Exported functions
62  */
63 static int lua_cb_log (lua_State *l) /* {{{ */
64 {
65   int nargs = lua_gettop (l); /* number of arguments */
66   int severity;
67   const char *msg;
68
69   if (nargs != 2)
70   {
71     WARNING ("lua plugin: collectd_log() called with an invalid number of arguments (%i).",
72         nargs);
73     RETURN_LUA (l, -1);
74   }
75
76   if (!lua_isnumber (l, 1))
77   {
78     WARNING ("lua plugin: The first argument to collectd_log() must be a number.");
79     RETURN_LUA (l, -1);
80   }
81
82   if (!lua_isstring (l, 2))
83   {
84     WARNING ("lua plugin: The second argument to collectd_log() must be a string.");
85     RETURN_LUA (l, -1);
86   }
87
88   severity = (int) lua_tonumber (l, /* stack pos = */ 1);
89   if ((severity != LOG_ERR)
90       && (severity != LOG_WARNING)
91       && (severity != LOG_NOTICE)
92       && (severity != LOG_INFO)
93       && (severity != LOG_DEBUG))
94     severity = LOG_ERR;
95
96   msg = lua_tostring (l, 2);
97   if (msg == NULL)
98   {
99     ERROR ("lua plugin: lua_tostring failed.");
100     RETURN_LUA (l, -1);
101   }
102
103   plugin_log (severity, "%s", msg);
104
105   RETURN_LUA (l, 0);
106 } /* }}} int lua_cb_log */
107
108 static lua_c_functions_t lua_c_functions[] =
109 {
110   { "collectd_log", lua_cb_log }
111 };
112
113 /* Declare the Lua libraries we wish to use.
114  * Note: If you are opening and running a file containing Lua code using
115  * 'lua_dofile(l, "myfile.lua") - you must delcare all the libraries used in
116  * that file here also. */
117 static const luaL_reg lua_load_libs[] =
118 {
119   { LUA_COLIBNAME,   luaopen_base   },
120   /* { "luaopen_loadlib", luaopen_loadlib }, */
121 #if COLLECT_DEBUG
122   { LUA_DBLIBNAME,   luaopen_debug  },
123 #endif
124   { LUA_TABLIBNAME,  luaopen_table  },
125   { LUA_IOLIBNAME,   luaopen_io     },
126   { LUA_STRLIBNAME,  luaopen_string },
127   { LUA_MATHLIBNAME, luaopen_math   }
128 };
129
130 static void lua_script_free (lua_script_t *script) /* {{{ */
131 {
132   lua_script_t *next;
133
134   if (script == NULL)
135     return;
136
137   next = script->next;
138
139   if (script->lua_state != NULL)
140   {
141     lua_close (script->lua_state);
142     script->lua_state = NULL;
143   }
144
145   sfree (script->script_path);
146   sfree (script);
147
148   lua_script_free (next);
149 } /* }}} void lua_script_free */
150
151 static int lua_script_init (lua_script_t *script) /* {{{ */
152 {
153   size_t i;
154
155   memset (script, 0, sizeof (*script));
156   script->script_path = NULL;
157   script->next = NULL;
158
159   /* initialize the lua context */
160   script->lua_state = lua_open();
161   if (script->lua_state == NULL)
162   {
163     ERROR ("lua plugin: lua_open failed.");
164     return (-1);
165   }
166
167   /* Open up all the Lua libraries declared above. */
168   for (i = 0; i < STATIC_ARRAY_SIZE (lua_load_libs); i++)
169   {
170     int status;
171
172     status = (*lua_load_libs[i].func) (script->lua_state);
173     if (status != 0)
174       WARNING ("lua plugin: Loading library \"%s\" failed.",
175           lua_load_libs[i].name);
176   }
177
178   /* Register all the functions we implement in C */
179   for (i = 0; i < STATIC_ARRAY_SIZE (lua_c_functions); i++)
180     lua_register (script->lua_state,
181         lua_c_functions[i].name, lua_c_functions[i].func);
182
183   return (0);
184 } /* }}} int lua_script_init */
185
186 static int lua_script_load (const char *script_path) /* {{{ */
187 {
188   lua_script_t *script;
189   int status;
190
191   script = malloc (sizeof (*script));
192   if (script == NULL)
193   {
194     ERROR ("lua plugin: malloc failed.");
195     return (-1);
196   }
197
198   status = lua_script_init (script);
199   if (status != 0)
200   {
201     lua_script_free (script);
202     return (status);
203   }
204
205   script->script_path = strdup (script_path);
206   if (script->script_path == NULL)
207   {
208     ERROR ("lua plugin: strdup failed.");
209     lua_script_free (script);
210     return (-1);
211   }
212
213   status = lua_dofile (script->lua_state, script->script_path);
214   if (status != 0)
215   {
216     const char *errmsg;
217
218     switch (status)
219     {
220       case LUA_ERRSYNTAX: errmsg = "Syntax error"; break;
221       case LUA_ERRFILE:   errmsg = "File I/O error"; break;
222       case LUA_ERRMEM:    errmsg = "Memory allocation error"; break;
223       default:            errmsg = "Unexpected error";
224     }
225
226     ERROR ("lua plugin: Loading script \"%s\" failed: %s",
227         script->script_path, errmsg);
228
229     lua_script_free (script);
230     return (-1);
231   }
232
233   /* Append this script to the global list of scripts. */
234   if (scripts == NULL)
235   {
236     scripts = script;
237   }
238   else
239   {
240     lua_script_t *last;
241
242     last = scripts;
243     while (last->next != NULL)
244       last = last->next;
245
246     last->next = script;
247   }
248
249   return (0);
250 } /* }}} int lua_script_load */
251
252 static int lua_config_base_path (const oconfig_item_t *ci) /* {{{ */
253 {
254   int status;
255   size_t len;
256
257   status = cf_util_get_string_buffer (ci, base_path, sizeof (base_path));
258   if (status != 0)
259     return (status);
260
261   len = strlen (base_path);
262   while ((len > 0) && (base_path[len - 1] == '/'))
263   {
264     len--;
265     base_path[len] = 0;
266   }
267
268   DEBUG ("lua plugin: base_path = \"%s\";", base_path);
269
270   return (0);
271 } /* }}} int lua_config_base_path */
272
273 static int lua_config_script (const oconfig_item_t *ci) /* {{{ */
274 {
275   char rel_path[PATH_MAX + 1];
276   char abs_path[PATH_MAX + 1];
277   int status;
278
279   status = cf_util_get_string_buffer (ci, rel_path, sizeof (rel_path));
280   if (status != 0)
281     return (status);
282
283   if (base_path[0] == 0)
284     sstrncpy (abs_path, rel_path, sizeof (abs_path));
285   else
286     ssnprintf (abs_path, sizeof (abs_path), "%s/%s", base_path, rel_path);
287
288   DEBUG ("lua plugin: abs_path = \"%s\";", abs_path);
289
290   status = lua_script_load (abs_path);
291   if (status != 0)
292     return (status);
293
294   INFO("lua plugin: File \"%s\" loaded succesfully", abs_path);
295   
296   return 0;
297 } /* }}} int lua_config_script */
298
299 /*
300  * <Plugin lua>
301  *   BasePath "/"
302  *   Script "script1.lua"
303  *   Script "script2.lua"
304  * </Plugin>
305  */
306 static int lua_config (oconfig_item_t *ci) /* {{{ */
307 {
308   int i;
309
310   for (i = 0; i < ci->children_num; i++)
311   {
312     oconfig_item_t *child = ci->children + i;
313
314     if (strcasecmp ("BasePath", child->key) == 0) {
315       lua_config_base_path(child);
316     }
317     else if (strcasecmp ("Script", child->key) == 0){
318       lua_config_script(child);
319     }
320     else
321     {
322       WARNING ("network plugin: Option `%s' is not allowed here.",
323           child->key);
324     }
325   }
326   
327   return 0;
328 } /* }}} int lua_config */
329
330 static int lua_shutdown (void) /* {{{ */
331 {
332   lua_script_free (scripts);
333   scripts = NULL;
334
335   return (0);
336 } /* }}} int lua_shutdown */
337
338 void module_register()
339 {
340   plugin_register_complex_config("lua", lua_config);
341   plugin_register_shutdown("lua", lua_shutdown);
342 }
343
344 /* vim: set sw=2 sts=2 et fdm=marker : */