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 {
46 struct lua_script_s *next;
51 char *lua_function_name;
54 } clua_callback_data_t;
56 static char base_path[PATH_MAX];
57 static lua_script_t *scripts;
59 static int clua_store_callback(lua_State *L, int idx) /* {{{ */
61 /* Copy the function pointer */
62 lua_pushvalue(L, idx);
64 return luaL_ref(L, LUA_REGISTRYINDEX);
65 } /* }}} int clua_store_callback */
67 static int clua_load_callback(lua_State *L, int callback_ref) /* {{{ */
69 lua_rawgeti(L, LUA_REGISTRYINDEX, callback_ref);
71 if (!lua_isfunction(L, -1)) {
77 } /* }}} int clua_load_callback */
79 /* Store the threads in a global variable so they are not cleaned up by the
80 * garbage collector. */
81 static int clua_store_thread(lua_State *L, int idx) /* {{{ */
84 idx += lua_gettop(L) + 1;
86 /* Copy the thread pointer */
87 lua_pushvalue(L, idx); /* +1 = 3 */
88 if (!lua_isthread(L, -1)) {
89 lua_pop(L, 3); /* -3 = 0 */
93 luaL_ref(L, LUA_REGISTRYINDEX);
94 lua_pop(L, 1); /* -1 = 0 */
96 } /* }}} int clua_store_thread */
98 static int clua_read(user_data_t *ud) /* {{{ */
100 clua_callback_data_t *cb = ud->data;
102 pthread_mutex_lock(&cb->lock);
104 lua_State *L = cb->lua_state;
106 int status = clua_load_callback(L, cb->callback_id);
108 ERROR("Lua plugin: Unable to load callback \"%s\" (id %i).",
109 cb->lua_function_name, cb->callback_id);
110 pthread_mutex_unlock(&cb->lock);
115 status = lua_pcall(L, 0, 1, 0);
117 const char *errmsg = lua_tostring(L, -1);
119 ERROR("Lua plugin: Calling a read callback failed. "
120 "In addition, retrieving the error message failed.");
122 ERROR("Lua plugin: Calling a read callback failed: %s", errmsg);
124 pthread_mutex_unlock(&cb->lock);
128 if (!lua_isnumber(L, -1)) {
129 ERROR("Lua plugin: Read function \"%s\" (id %i) did not return a numeric "
131 cb->lua_function_name, cb->callback_id);
134 status = (int)lua_tointeger(L, -1);
137 /* pop return value and function */
138 lua_pop(L, 1); /* -1 = 0 */
140 pthread_mutex_unlock(&cb->lock);
142 } /* }}} int clua_read */
144 static int clua_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
146 clua_callback_data_t *cb = ud->data;
148 pthread_mutex_lock(&cb->lock);
150 lua_State *L = cb->lua_state;
152 int status = clua_load_callback(L, cb->callback_id);
154 ERROR("Lua plugin: Unable to load callback \"%s\" (id %i).",
155 cb->lua_function_name, cb->callback_id);
156 pthread_mutex_unlock(&cb->lock);
161 status = luaC_pushvaluelist(L, ds, vl);
163 lua_pop(L, 1); /* -1 = 0 */
164 pthread_mutex_unlock(&cb->lock);
165 ERROR("Lua plugin: luaC_pushvaluelist failed.");
170 status = lua_pcall(L, 1, 1, 0); /* -2+1 = 1 */
172 const char *errmsg = lua_tostring(L, -1);
174 ERROR("Lua plugin: Calling the write callback failed. "
175 "In addition, retrieving the error message failed.");
177 ERROR("Lua plugin: Calling the write callback failed:\n%s", errmsg);
178 lua_pop(L, 1); /* -1 = 0 */
179 pthread_mutex_unlock(&cb->lock);
183 if (!lua_isnumber(L, -1)) {
184 ERROR("Lua plugin: Write function \"%s\" (id %i) did not return a numeric "
186 cb->lua_function_name, cb->callback_id);
189 status = (int)lua_tointeger(L, -1);
192 lua_pop(L, 1); /* -1 = 0 */
193 pthread_mutex_unlock(&cb->lock);
195 } /* }}} int clua_write */
201 static int lua_cb_log_debug(lua_State *L) /* {{{ */
203 const char *msg = luaL_checkstring(L, 1);
204 plugin_log(LOG_DEBUG, "%s", msg);
206 } /* }}} int lua_cb_log_debug */
208 static int lua_cb_log_error(lua_State *L) /* {{{ */
210 const char *msg = luaL_checkstring(L, 1);
211 plugin_log(LOG_ERR, "%s", msg);
213 } /* }}} int lua_cb_log_error */
215 static int lua_cb_log_info(lua_State *L) /* {{{ */
217 const char *msg = luaL_checkstring(L, 1);
218 plugin_log(LOG_INFO, "%s", msg);
220 } /* }}} int lua_cb_log_info */
222 static int lua_cb_log_notice(lua_State *L) /* {{{ */
224 const char *msg = luaL_checkstring(L, 1);
225 plugin_log(LOG_NOTICE, "%s", msg);
227 } /* }}} int lua_cb_log_notice */
229 static int lua_cb_log_warning(lua_State *L) /* {{{ */
231 const char *msg = luaL_checkstring(L, 1);
232 plugin_log(LOG_WARNING, "%s", msg);
234 } /* }}} int lua_cb_log_warning */
236 static int lua_cb_dispatch_values(lua_State *L) /* {{{ */
238 int nargs = lua_gettop(L);
241 return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
243 luaL_checktype(L, 1, LUA_TTABLE);
245 value_list_t *vl = luaC_tovaluelist(L, -1);
247 return luaL_error(L, "%s", "luaC_tovaluelist failed");
250 char identifier[6 * DATA_MAX_NAME_LEN];
251 FORMAT_VL(identifier, sizeof(identifier), vl);
253 DEBUG("Lua plugin: collectd.dispatch_values(): Received value list \"%s\", "
254 "time %.3f, interval %.3f.",
255 identifier, CDTIME_T_TO_DOUBLE(vl->time),
256 CDTIME_T_TO_DOUBLE(vl->interval));
259 plugin_dispatch_values(vl);
264 } /* }}} lua_cb_dispatch_values */
266 static void lua_cb_free(void *data) {
267 clua_callback_data_t *cb = data;
268 free(cb->lua_function_name);
272 static int lua_cb_register_read(lua_State *L) /* {{{ */
274 int nargs = lua_gettop(L);
277 return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
279 luaL_checktype(L, 1, LUA_TFUNCTION);
281 char function_name[DATA_MAX_NAME_LEN];
282 snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
284 int callback_id = clua_store_callback(L, 1);
286 return luaL_error(L, "%s", "Storing callback function failed");
288 lua_State *thread = lua_newthread(L);
290 return luaL_error(L, "%s", "lua_newthread failed");
291 clua_store_thread(L, -1);
294 clua_callback_data_t *cb = calloc(1, sizeof(*cb));
296 return luaL_error(L, "%s", "calloc failed");
298 cb->lua_state = thread;
299 cb->callback_id = callback_id;
300 cb->lua_function_name = strdup(function_name);
301 pthread_mutex_init(&cb->lock, NULL);
304 plugin_register_complex_read(/* group = */ "lua",
305 /* name = */ function_name,
306 /* callback = */ clua_read,
309 .data = cb, .free_func = lua_cb_free,
313 return luaL_error(L, "%s", "plugin_register_complex_read failed");
315 } /* }}} int lua_cb_register_read */
317 static int lua_cb_register_write(lua_State *L) /* {{{ */
319 int nargs = lua_gettop(L);
322 return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
324 luaL_checktype(L, 1, LUA_TFUNCTION);
326 char function_name[DATA_MAX_NAME_LEN] = "";
327 snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
329 int callback_id = clua_store_callback(L, 1);
331 return luaL_error(L, "%s", "Storing callback function failed");
333 lua_State *thread = lua_newthread(L);
335 return luaL_error(L, "%s", "lua_newthread failed");
336 clua_store_thread(L, -1);
339 clua_callback_data_t *cb = calloc(1, sizeof(*cb));
341 return luaL_error(L, "%s", "calloc failed");
343 cb->lua_state = thread;
344 cb->callback_id = callback_id;
345 cb->lua_function_name = strdup(function_name);
346 pthread_mutex_init(&cb->lock, NULL);
348 int status = plugin_register_write(/* name = */ function_name,
349 /* callback = */ clua_write,
351 .data = cb, .free_func = lua_cb_free,
355 return luaL_error(L, "%s", "plugin_register_write failed");
357 } /* }}} int lua_cb_register_write */
359 static const luaL_Reg collectdlib[] = {
360 {"log_debug", lua_cb_log_debug},
361 {"log_error", lua_cb_log_error},
362 {"log_info", lua_cb_log_info},
363 {"log_notice", lua_cb_log_notice},
364 {"log_warning", lua_cb_log_warning},
365 {"dispatch_values", lua_cb_dispatch_values},
366 {"register_read", lua_cb_register_read},
367 {"register_write", lua_cb_register_write},
370 static int open_collectd(lua_State *L) /* {{{ */
372 #if LUA_VERSION_NUM < 502
373 luaL_register(L, "collectd", collectdlib);
375 luaL_newlib(L, collectdlib);
380 static void lua_script_free(lua_script_t *script) /* {{{ */
385 lua_script_t *next = script->next;
387 if (script->lua_state != NULL) {
388 lua_close(script->lua_state);
389 script->lua_state = NULL;
392 sfree(script->script_path);
395 lua_script_free(next);
396 } /* }}} void lua_script_free */
398 static int lua_script_init(lua_script_t *script) /* {{{ */
400 memset(script, 0, sizeof(*script));
402 /* initialize the lua context */
403 script->lua_state = luaL_newstate();
404 if (script->lua_state == NULL) {
405 ERROR("Lua plugin: luaL_newstate() failed.");
409 /* Open up all the standard Lua libraries. */
410 luaL_openlibs(script->lua_state);
412 /* Load the 'collectd' library */
413 #if LUA_VERSION_NUM < 502
414 lua_pushcfunction(script->lua_state, open_collectd);
415 lua_pushstring(script->lua_state, "collectd");
416 lua_call(script->lua_state, 1, 0);
418 luaL_requiref(script->lua_state, "collectd", open_collectd, 1);
419 lua_pop(script->lua_state, 1);
422 /* Prepend BasePath to package.path */
423 if (base_path[0] != '\0') {
424 lua_getglobal(script->lua_state, "package");
425 lua_getfield(script->lua_state, -1, "path");
427 const char *cur_path = lua_tostring(script->lua_state, -1);
428 char *new_path = ssnprintf_alloc("%s/?.lua;%s", base_path, cur_path);
430 lua_pop(script->lua_state, 1);
431 lua_pushstring(script->lua_state, new_path);
435 lua_setfield(script->lua_state, -2, "path");
436 lua_pop(script->lua_state, 1);
440 } /* }}} int lua_script_init */
442 static int lua_script_load(const char *script_path) /* {{{ */
444 lua_script_t *script = malloc(sizeof(*script));
445 if (script == NULL) {
446 ERROR("Lua plugin: malloc failed.");
450 int status = lua_script_init(script);
452 lua_script_free(script);
456 script->script_path = strdup(script_path);
457 if (script->script_path == NULL) {
458 ERROR("Lua plugin: strdup failed.");
459 lua_script_free(script);
463 status = luaL_loadfile(script->lua_state, script->script_path);
465 ERROR("Lua plugin: luaL_loadfile failed: %s",
466 lua_tostring(script->lua_state, -1));
467 lua_pop(script->lua_state, 1);
468 lua_script_free(script);
472 status = lua_pcall(script->lua_state,
474 /* nresults = */ LUA_MULTRET,
477 const char *errmsg = lua_tostring(script->lua_state, -1);
480 ERROR("Lua plugin: lua_pcall failed with status %i. "
481 "In addition, no error message could be retrieved from the stack.",
484 ERROR("Lua plugin: Executing script \"%s\" failed:\n%s",
485 script->script_path, errmsg);
487 lua_script_free(script);
491 /* Append this script to the global list of scripts. */
493 lua_script_t *last = scripts;
503 } /* }}} int lua_script_load */
505 static int lua_config_base_path(const oconfig_item_t *ci) /* {{{ */
507 int status = cf_util_get_string_buffer(ci, base_path, sizeof(base_path));
511 size_t len = strlen(base_path);
512 while ((len > 0) && (base_path[len - 1] == '/')) {
514 base_path[len] = '\0';
517 DEBUG("Lua plugin: base_path = \"%s\";", base_path);
520 } /* }}} int lua_config_base_path */
522 static int lua_config_script(const oconfig_item_t *ci) /* {{{ */
524 char rel_path[PATH_MAX];
526 int status = cf_util_get_string_buffer(ci, rel_path, sizeof(rel_path));
530 char abs_path[PATH_MAX];
532 if (base_path[0] == '\0')
533 sstrncpy(abs_path, rel_path, sizeof(abs_path));
535 snprintf(abs_path, sizeof(abs_path), "%s/%s", base_path, rel_path);
537 DEBUG("Lua plugin: abs_path = \"%s\";", abs_path);
539 status = lua_script_load(abs_path);
543 INFO("Lua plugin: File \"%s\" loaded successfully", abs_path);
546 } /* }}} int lua_config_script */
551 * Script "script1.lua"
552 * Script "script2.lua"
555 static int lua_config(oconfig_item_t *ci) /* {{{ */
558 for (int i = 0; i < ci->children_num; i++) {
559 oconfig_item_t *child = ci->children + i;
561 if (strcasecmp("BasePath", child->key) == 0) {
562 status = lua_config_base_path(child);
563 } else if (strcasecmp("Script", child->key) == 0) {
564 status = lua_config_script(child);
566 ERROR("Lua plugin: Option `%s' is not allowed here.", child->key);
572 } /* }}} int lua_config */
574 static int lua_shutdown(void) /* {{{ */
576 lua_script_free(scripts);
579 } /* }}} int lua_shutdown */
581 void module_register(void) {
582 plugin_register_complex_config("lua", lua_config);
583 plugin_register_shutdown("lua", lua_shutdown);