3 * Copyright (C) 2010 Julien Ammous
4 * Copyright (C) 2010 Florian Forster
5 * Copyright (C) 2016 Ruben Kerkhof
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * Florian Forster <octo at collectd.org>
28 * Ruben Kerkhof <ruben at rubenkerkhof.com>
33 #include "utils/common/common.h"
34 #include "utils_lua.h"
36 /* Include the Lua API header files. */
43 typedef struct lua_script_s {
45 struct lua_script_s *next;
50 char *lua_function_name;
53 } clua_callback_data_t;
55 static char base_path[PATH_MAX];
56 static lua_script_t *scripts;
58 static int clua_store_callback(lua_State *L, int idx) /* {{{ */
60 /* Copy the function pointer */
61 lua_pushvalue(L, idx);
63 return luaL_ref(L, LUA_REGISTRYINDEX);
64 } /* }}} int clua_store_callback */
66 static int clua_load_callback(lua_State *L, int callback_ref) /* {{{ */
68 lua_rawgeti(L, LUA_REGISTRYINDEX, callback_ref);
70 if (!lua_isfunction(L, -1)) {
76 } /* }}} int clua_load_callback */
78 /* Store the threads in a global variable so they are not cleaned up by the
79 * garbage collector. */
80 static int clua_store_thread(lua_State *L, int idx) /* {{{ */
82 if (!lua_isthread(L, idx)) {
86 /* Copy the thread pointer */
87 lua_pushvalue(L, idx);
89 luaL_ref(L, LUA_REGISTRYINDEX);
91 } /* }}} int clua_store_thread */
93 static int clua_read(user_data_t *ud) /* {{{ */
95 clua_callback_data_t *cb = ud->data;
97 pthread_mutex_lock(&cb->lock);
99 lua_State *L = cb->lua_state;
101 int status = clua_load_callback(L, cb->callback_id);
103 ERROR("Lua plugin: Unable to load callback \"%s\" (id %i).",
104 cb->lua_function_name, cb->callback_id);
105 pthread_mutex_unlock(&cb->lock);
110 status = lua_pcall(L, 0, 1, 0);
112 const char *errmsg = lua_tostring(L, -1);
114 ERROR("Lua plugin: Calling a read callback failed. "
115 "In addition, retrieving the error message failed.");
117 ERROR("Lua plugin: Calling a read callback failed: %s", errmsg);
119 pthread_mutex_unlock(&cb->lock);
123 if (!lua_isnumber(L, -1)) {
124 ERROR("Lua plugin: Read function \"%s\" (id %i) did not return a numeric "
126 cb->lua_function_name, cb->callback_id);
129 status = (int)lua_tointeger(L, -1);
132 /* pop return value and function */
133 lua_pop(L, 1); /* -1 = 0 */
135 pthread_mutex_unlock(&cb->lock);
137 } /* }}} int clua_read */
139 static int clua_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
141 clua_callback_data_t *cb = ud->data;
143 pthread_mutex_lock(&cb->lock);
145 lua_State *L = cb->lua_state;
147 int status = clua_load_callback(L, cb->callback_id);
149 ERROR("Lua plugin: Unable to load callback \"%s\" (id %i).",
150 cb->lua_function_name, cb->callback_id);
151 pthread_mutex_unlock(&cb->lock);
156 status = luaC_pushvaluelist(L, ds, vl);
158 lua_pop(L, 1); /* -1 = 0 */
159 pthread_mutex_unlock(&cb->lock);
160 ERROR("Lua plugin: luaC_pushvaluelist failed.");
165 status = lua_pcall(L, 1, 1, 0); /* -2+1 = 1 */
167 const char *errmsg = lua_tostring(L, -1);
169 ERROR("Lua plugin: Calling the write callback failed. "
170 "In addition, retrieving the error message failed.");
172 ERROR("Lua plugin: Calling the write callback failed:\n%s", errmsg);
173 lua_pop(L, 1); /* -1 = 0 */
174 pthread_mutex_unlock(&cb->lock);
178 if (!lua_isnumber(L, -1)) {
179 ERROR("Lua plugin: Write function \"%s\" (id %i) did not return a numeric "
181 cb->lua_function_name, cb->callback_id);
184 status = (int)lua_tointeger(L, -1);
187 lua_pop(L, 1); /* -1 = 0 */
188 pthread_mutex_unlock(&cb->lock);
190 } /* }}} int clua_write */
196 static int lua_cb_log_debug(lua_State *L) /* {{{ */
198 const char *msg = luaL_checkstring(L, 1);
199 plugin_log(LOG_DEBUG, "%s", msg);
201 } /* }}} int lua_cb_log_debug */
203 static int lua_cb_log_error(lua_State *L) /* {{{ */
205 const char *msg = luaL_checkstring(L, 1);
206 plugin_log(LOG_ERR, "%s", msg);
208 } /* }}} int lua_cb_log_error */
210 static int lua_cb_log_info(lua_State *L) /* {{{ */
212 const char *msg = luaL_checkstring(L, 1);
213 plugin_log(LOG_INFO, "%s", msg);
215 } /* }}} int lua_cb_log_info */
217 static int lua_cb_log_notice(lua_State *L) /* {{{ */
219 const char *msg = luaL_checkstring(L, 1);
220 plugin_log(LOG_NOTICE, "%s", msg);
222 } /* }}} int lua_cb_log_notice */
224 static int lua_cb_log_warning(lua_State *L) /* {{{ */
226 const char *msg = luaL_checkstring(L, 1);
227 plugin_log(LOG_WARNING, "%s", msg);
229 } /* }}} int lua_cb_log_warning */
231 static int lua_cb_dispatch_values(lua_State *L) /* {{{ */
233 int nargs = lua_gettop(L);
236 return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
238 luaL_checktype(L, 1, LUA_TTABLE);
240 value_list_t *vl = luaC_tovaluelist(L, -1);
242 return luaL_error(L, "%s", "luaC_tovaluelist failed");
245 char identifier[6 * DATA_MAX_NAME_LEN];
246 FORMAT_VL(identifier, sizeof(identifier), vl);
248 DEBUG("Lua plugin: collectd.dispatch_values(): Received value list \"%s\", "
249 "time %.3f, interval %.3f.",
250 identifier, CDTIME_T_TO_DOUBLE(vl->time),
251 CDTIME_T_TO_DOUBLE(vl->interval));
254 plugin_dispatch_values(vl);
259 } /* }}} lua_cb_dispatch_values */
261 static void lua_cb_free(void *data) {
262 clua_callback_data_t *cb = data;
263 free(cb->lua_function_name);
264 pthread_mutex_destroy(&cb->lock);
268 static int lua_cb_register_read(lua_State *L) /* {{{ */
270 int nargs = lua_gettop(L);
273 return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
275 luaL_checktype(L, 1, LUA_TFUNCTION);
277 char function_name[DATA_MAX_NAME_LEN];
278 snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
280 int callback_id = clua_store_callback(L, 1);
282 return luaL_error(L, "%s", "Storing callback function failed");
284 lua_State *thread = lua_newthread(L);
286 return luaL_error(L, "%s", "lua_newthread failed");
287 clua_store_thread(L, -1);
290 clua_callback_data_t *cb = calloc(1, sizeof(*cb));
292 return luaL_error(L, "%s", "calloc failed");
294 cb->lua_state = thread;
295 cb->callback_id = callback_id;
296 cb->lua_function_name = strdup(function_name);
297 pthread_mutex_init(&cb->lock, NULL);
300 plugin_register_complex_read(/* group = */ "lua",
301 /* name = */ function_name,
302 /* callback = */ clua_read,
305 .data = cb, .free_func = lua_cb_free,
309 return luaL_error(L, "%s", "plugin_register_complex_read failed");
311 } /* }}} int lua_cb_register_read */
313 static int lua_cb_register_write(lua_State *L) /* {{{ */
315 int nargs = lua_gettop(L);
318 return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
320 luaL_checktype(L, 1, LUA_TFUNCTION);
322 char function_name[DATA_MAX_NAME_LEN] = "";
323 snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
325 int callback_id = clua_store_callback(L, 1);
327 return luaL_error(L, "%s", "Storing callback function failed");
329 lua_State *thread = lua_newthread(L);
331 return luaL_error(L, "%s", "lua_newthread failed");
332 clua_store_thread(L, -1);
335 clua_callback_data_t *cb = calloc(1, sizeof(*cb));
337 return luaL_error(L, "%s", "calloc failed");
339 cb->lua_state = thread;
340 cb->callback_id = callback_id;
341 cb->lua_function_name = strdup(function_name);
342 pthread_mutex_init(&cb->lock, NULL);
344 int status = plugin_register_write(/* name = */ function_name,
345 /* callback = */ clua_write,
347 .data = cb, .free_func = lua_cb_free,
351 return luaL_error(L, "%s", "plugin_register_write failed");
353 } /* }}} int lua_cb_register_write */
355 static const luaL_Reg collectdlib[] = {
356 {"log_debug", lua_cb_log_debug},
357 {"log_error", lua_cb_log_error},
358 {"log_info", lua_cb_log_info},
359 {"log_notice", lua_cb_log_notice},
360 {"log_warning", lua_cb_log_warning},
361 {"dispatch_values", lua_cb_dispatch_values},
362 {"register_read", lua_cb_register_read},
363 {"register_write", lua_cb_register_write},
366 static int open_collectd(lua_State *L) /* {{{ */
368 #if LUA_VERSION_NUM < 502
369 luaL_register(L, "collectd", collectdlib);
371 luaL_newlib(L, collectdlib);
376 static void lua_script_free(lua_script_t *script) /* {{{ */
381 lua_script_t *next = script->next;
383 if (script->lua_state != NULL) {
384 lua_close(script->lua_state);
385 script->lua_state = NULL;
390 lua_script_free(next);
391 } /* }}} void lua_script_free */
393 static int lua_script_init(lua_script_t *script) /* {{{ */
395 memset(script, 0, sizeof(*script));
397 /* initialize the lua context */
398 script->lua_state = luaL_newstate();
399 if (script->lua_state == NULL) {
400 ERROR("Lua plugin: luaL_newstate() failed.");
404 /* Open up all the standard Lua libraries. */
405 luaL_openlibs(script->lua_state);
407 /* Load the 'collectd' library */
408 #if LUA_VERSION_NUM < 502
409 lua_pushcfunction(script->lua_state, open_collectd);
410 lua_pushstring(script->lua_state, "collectd");
411 lua_call(script->lua_state, 1, 0);
413 luaL_requiref(script->lua_state, "collectd", open_collectd, 1);
414 lua_pop(script->lua_state, 1);
417 /* Prepend BasePath to package.path */
418 if (base_path[0] != '\0') {
419 lua_getglobal(script->lua_state, "package");
420 lua_getfield(script->lua_state, -1, "path");
422 const char *cur_path = lua_tostring(script->lua_state, -1);
423 char *new_path = ssnprintf_alloc("%s/?.lua;%s", base_path, cur_path);
425 lua_pop(script->lua_state, 1);
426 lua_pushstring(script->lua_state, new_path);
430 lua_setfield(script->lua_state, -2, "path");
431 lua_pop(script->lua_state, 1);
435 } /* }}} int lua_script_init */
437 static int lua_script_load(const char *script_path) /* {{{ */
439 lua_script_t *script = malloc(sizeof(*script));
440 if (script == NULL) {
441 ERROR("Lua plugin: malloc failed.");
445 int status = lua_script_init(script);
447 lua_script_free(script);
451 status = luaL_loadfile(script->lua_state, script_path);
453 ERROR("Lua plugin: luaL_loadfile failed: %s",
454 lua_tostring(script->lua_state, -1));
455 lua_pop(script->lua_state, 1);
456 lua_script_free(script);
460 status = lua_pcall(script->lua_state,
462 /* nresults = */ LUA_MULTRET,
465 const char *errmsg = lua_tostring(script->lua_state, -1);
468 ERROR("Lua plugin: lua_pcall failed with status %i. "
469 "In addition, no error message could be retrieved from the stack.",
472 ERROR("Lua plugin: Executing script \"%s\" failed: %s",
473 script_path, errmsg);
476 /* Append this script to the global list of scripts. */
478 lua_script_t *last = scripts;
491 } /* }}} int lua_script_load */
493 static int lua_config_base_path(const oconfig_item_t *ci) /* {{{ */
495 int status = cf_util_get_string_buffer(ci, base_path, sizeof(base_path));
499 size_t len = strlen(base_path);
500 while ((len > 0) && (base_path[len - 1] == '/')) {
502 base_path[len] = '\0';
505 DEBUG("Lua plugin: base_path = \"%s\";", base_path);
508 } /* }}} int lua_config_base_path */
510 static int lua_config_script(const oconfig_item_t *ci) /* {{{ */
512 char rel_path[PATH_MAX];
514 int status = cf_util_get_string_buffer(ci, rel_path, sizeof(rel_path));
518 char abs_path[PATH_MAX];
520 if (base_path[0] == '\0')
521 sstrncpy(abs_path, rel_path, sizeof(abs_path));
523 snprintf(abs_path, sizeof(abs_path), "%s/%s", base_path, rel_path);
525 DEBUG("Lua plugin: abs_path = \"%s\";", abs_path);
527 status = lua_script_load(abs_path);
531 INFO("Lua plugin: File \"%s\" loaded successfully", abs_path);
534 } /* }}} int lua_config_script */
539 * Script "script1.lua"
540 * Script "script2.lua"
543 static int lua_config(oconfig_item_t *ci) /* {{{ */
546 for (int i = 0; i < ci->children_num; i++) {
547 oconfig_item_t *child = ci->children + i;
549 if (strcasecmp("BasePath", child->key) == 0) {
550 status = lua_config_base_path(child);
551 } else if (strcasecmp("Script", child->key) == 0) {
552 status = lua_config_script(child);
554 ERROR("Lua plugin: Option `%s' is not allowed here.", child->key);
560 } /* }}} int lua_config */
562 static int lua_shutdown(void) /* {{{ */
564 lua_script_free(scripts);
567 } /* }}} int lua_shutdown */
569 void module_register(void) {
570 plugin_register_complex_config("lua", lua_config);
571 plugin_register_shutdown("lua", lua_shutdown);