--- /dev/null
+/**
+ * collectd - src/lua.c
+ * Copyright (C) 2010 Julien Ammous
+ * Copyright (C) 2010 Florian Forster
+ * Copyright (C) 2016 Ruben Kerkhof
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Julien Ammous
+ * Florian Forster <octo at collectd.org>
+ * Ruben Kerkhof <ruben at rubenkerkhof.com>
+ **/
+
+/* <lua5.1/luaconf.h> defines a macro using "sprintf". Although not used here,
+ * GCC will complain about the macro definition. */
+#define DONT_POISON_SPRINTF_YET
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+/* Include the Lua API header files. */
+#include "utils_lua.h"
+#include <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+
+#include <pthread.h>
+
+#if COLLECT_DEBUG && __GNUC__
+#undef sprintf
+#pragma GCC poison sprintf
+#endif
+
+typedef struct lua_script_s {
+ char *script_path;
+ lua_State *lua_state;
+ struct lua_script_s *next;
+} lua_script_t;
+
+typedef struct {
+ lua_State *lua_state;
+ const char *lua_function_name;
+ pthread_mutex_t lock;
+ int callback_id;
+} clua_callback_data_t;
+
+typedef struct {
+ const char *name;
+ lua_CFunction func;
+} lua_c_function_t;
+
+static char base_path[PATH_MAX];
+static lua_script_t *scripts;
+
+static int clua_store_callback(lua_State *L, int idx) /* {{{ */
+{
+ /* Copy the function pointer */
+ lua_pushvalue(L, idx);
+
+ return luaL_ref(L, LUA_REGISTRYINDEX);
+} /* }}} int clua_store_callback */
+
+static int clua_load_callback(lua_State *L, int callback_ref) /* {{{ */
+{
+ lua_rawgeti(L, LUA_REGISTRYINDEX, callback_ref);
+
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 1);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int clua_load_callback */
+
+/* Store the threads in a global variable so they are not cleaned up by the
+ * garbage collector. */
+static int clua_store_thread(lua_State *L, int idx) /* {{{ */
+{
+ if (idx < 0)
+ idx += lua_gettop(L) + 1;
+
+ /* Copy the thread pointer */
+ lua_pushvalue(L, idx); /* +1 = 3 */
+ if (!lua_isthread(L, -1)) {
+ lua_pop(L, 3); /* -3 = 0 */
+ return (-1);
+ }
+
+ luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_pop(L, 1); /* -1 = 0 */
+ return (0);
+} /* }}} int clua_store_thread */
+
+static int clua_read(user_data_t *ud) /* {{{ */
+{
+ clua_callback_data_t *cb = ud->data;
+
+ pthread_mutex_lock(&cb->lock);
+
+ lua_State *L = cb->lua_state;
+
+ int status = clua_load_callback(L, cb->callback_id);
+ if (status != 0) {
+ ERROR("Lua plugin: Unable to load callback \"%s\" (id %i).",
+ cb->lua_function_name, cb->callback_id);
+ pthread_mutex_unlock(&cb->lock);
+ return (-1);
+ }
+ /* +1 = 1 */
+
+ status = lua_pcall(L, 0, 1, 0);
+ if (status != 0) {
+ const char *errmsg = lua_tostring(L, -1);
+ if (errmsg == NULL)
+ ERROR("Lua plugin: Calling a read callback failed. "
+ "In addition, retrieving the error message failed.");
+ else
+ ERROR("Lua plugin: Calling a read callback failed: %s", errmsg);
+ lua_pop(L, 1);
+ pthread_mutex_unlock(&cb->lock);
+ return (-1);
+ }
+
+ if (!lua_isnumber(L, -1)) {
+ ERROR("Lua plugin: Read function \"%s\" (id %i) did not return a numeric "
+ "status.",
+ cb->lua_function_name, cb->callback_id);
+ status = -1;
+ } else {
+ status = (int)lua_tointeger(L, -1);
+ }
+
+ /* pop return value and function */
+ lua_pop(L, 1); /* -1 = 0 */
+
+ pthread_mutex_unlock(&cb->lock);
+ return (status);
+} /* }}} int clua_read */
+
+static int clua_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
+ user_data_t *ud) {
+ clua_callback_data_t *cb = ud->data;
+
+ pthread_mutex_lock(&cb->lock);
+
+ lua_State *L = cb->lua_state;
+
+ int status = clua_load_callback(L, cb->callback_id);
+ if (status != 0) {
+ ERROR("Lua plugin: Unable to load callback \"%s\" (id %i).",
+ cb->lua_function_name, cb->callback_id);
+ pthread_mutex_unlock(&cb->lock);
+ return (-1);
+ }
+ /* +1 = 1 */
+
+ status = luaC_pushvaluelist(L, ds, vl);
+ if (status != 0) {
+ lua_pop(L, 1); /* -1 = 0 */
+ pthread_mutex_unlock(&cb->lock);
+ ERROR("Lua plugin: luaC_pushvaluelist failed.");
+ return (-1);
+ }
+ /* +1 = 2 */
+
+ status = lua_pcall(L, 1, 1, 0); /* -2+1 = 1 */
+ if (status != 0) {
+ const char *errmsg = lua_tostring(L, -1);
+ if (errmsg == NULL)
+ ERROR("Lua plugin: Calling the write callback failed. "
+ "In addition, retrieving the error message failed.");
+ else
+ ERROR("Lua plugin: Calling the write callback failed:\n%s", errmsg);
+ lua_pop(L, 1); /* -1 = 0 */
+ pthread_mutex_unlock(&cb->lock);
+ return (-1);
+ }
+
+ if (!lua_isnumber(L, -1)) {
+ ERROR("Lua plugin: Write function \"%s\" (id %i) did not return a numeric "
+ "value.",
+ cb->lua_function_name, cb->callback_id);
+ status = -1;
+ } else {
+ status = (int)lua_tointeger(L, -1);
+ }
+
+ lua_pop(L, 1); /* -1 = 0 */
+ pthread_mutex_unlock(&cb->lock);
+ return (status);
+} /* }}} int clua_write */
+
+/*
+ * Exported functions
+ */
+
+static int lua_cb_log_debug(lua_State *L) /* {{{ */
+{
+ const char *msg = luaL_checkstring(L, 1);
+ plugin_log(LOG_DEBUG, "%s", msg);
+ return 0;
+} /* }}} int lua_cb_log_debug */
+
+static int lua_cb_log_error(lua_State *L) /* {{{ */
+{
+ const char *msg = luaL_checkstring(L, 1);
+ plugin_log(LOG_ERR, "%s", msg);
+ return 0;
+} /* }}} int lua_cb_log_error */
+
+static int lua_cb_log_info(lua_State *L) /* {{{ */
+{
+ const char *msg = luaL_checkstring(L, 1);
+ plugin_log(LOG_INFO, "%s", msg);
+ return 0;
+} /* }}} int lua_cb_log_info */
+
+static int lua_cb_log_notice(lua_State *L) /* {{{ */
+{
+ const char *msg = luaL_checkstring(L, 1);
+ plugin_log(LOG_NOTICE, "%s", msg);
+ return 0;
+} /* }}} int lua_cb_log_notice */
+
+static int lua_cb_log_warning(lua_State *L) /* {{{ */
+{
+ const char *msg = luaL_checkstring(L, 1);
+ plugin_log(LOG_WARNING, "%s", msg);
+ return 0;
+} /* }}} int lua_cb_log_warning */
+
+static int lua_cb_dispatch_values(lua_State *L) /* {{{ */
+{
+ int nargs = lua_gettop(L);
+
+ if (nargs != 1)
+ return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ value_list_t *vl = luaC_tovaluelist(L, -1);
+ if (vl == NULL)
+ return luaL_error(L, "%s", "luaC_tovaluelist failed");
+
+ char identifier[6 * DATA_MAX_NAME_LEN];
+ FORMAT_VL(identifier, sizeof(identifier), vl);
+
+ DEBUG("Lua plugin: collectd.dispatch_values(): Received value list \"%s\", "
+ "time %.3f, interval %.3f.",
+ identifier, CDTIME_T_TO_DOUBLE(vl->time),
+ CDTIME_T_TO_DOUBLE(vl->interval));
+
+ plugin_dispatch_values(vl);
+
+ sfree(vl->values);
+ sfree(vl);
+ return 0;
+} /* }}} lua_cb_dispatch_values */
+
+static int lua_cb_register_read(lua_State *L) /* {{{ */
+{
+ int nargs = lua_gettop(L);
+
+ if (nargs != 1)
+ return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
+
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+
+ char function_name[DATA_MAX_NAME_LEN];
+ ssnprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
+
+ int callback_id = clua_store_callback(L, 1);
+ if (callback_id < 0)
+ return luaL_error(L, "%s", "Storing callback function failed");
+
+ lua_State *thread = lua_newthread(L);
+ if (thread == NULL)
+ return luaL_error(L, "%s", "lua_newthread failed");
+ clua_store_thread(L, -1);
+ lua_pop(L, 1);
+
+ clua_callback_data_t *cb = calloc(1, sizeof(*cb));
+ if (cb == NULL)
+ return luaL_error(L, "%s", "calloc failed");
+
+ cb->lua_state = thread;
+ cb->callback_id = callback_id;
+ cb->lua_function_name = strdup(function_name);
+ pthread_mutex_init(&cb->lock, NULL);
+
+ user_data_t ud = {
+ .data = cb
+ };
+
+ int status = plugin_register_complex_read(/* group = */ "lua",
+ /* name = */ function_name,
+ /* callback = */ clua_read,
+ /* interval = */ 0,
+ /* user_data = */ &ud);
+
+ if (status != 0)
+ return luaL_error(L, "%s", "plugin_register_complex_read failed");
+ return 0;
+} /* }}} int lua_cb_register_read */
+
+static int lua_cb_register_write(lua_State *L) /* {{{ */
+{
+ int nargs = lua_gettop(L);
+
+ if (nargs != 1)
+ return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
+
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+
+ char function_name[DATA_MAX_NAME_LEN] = "";
+ ssnprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
+
+ int callback_id = clua_store_callback(L, 1);
+ if (callback_id < 0)
+ return luaL_error(L, "%s", "Storing callback function failed");
+
+ lua_State *thread = lua_newthread(L);
+ if (thread == NULL)
+ return luaL_error(L, "%s", "lua_newthread failed");
+ clua_store_thread(L, -1);
+ lua_pop(L, 1);
+
+ clua_callback_data_t *cb = calloc(1, sizeof(*cb));
+ if (cb == NULL)
+ return luaL_error(L, "%s", "calloc failed");
+
+ cb->lua_state = thread;
+ cb->callback_id = callback_id;
+ cb->lua_function_name = strdup(function_name);
+ pthread_mutex_init(&cb->lock, NULL);
+
+ user_data_t ud = {
+ .data = cb
+ };
+
+ int status = plugin_register_write(/* name = */ function_name,
+ /* callback = */ clua_write,
+ /* user_data = */ &ud);
+
+ if (status != 0)
+ return luaL_error(L, "%s", "plugin_register_write failed");
+ return 0;
+} /* }}} int lua_cb_register_write */
+
+static lua_c_function_t lua_c_functions[] = {
+ {"log_debug", lua_cb_log_debug},
+ {"log_error", lua_cb_log_error},
+ {"log_info", lua_cb_log_info},
+ {"log_notice", lua_cb_log_notice},
+ {"log_warning", lua_cb_log_warning},
+ {"dispatch_values", lua_cb_dispatch_values},
+ {"register_read", lua_cb_register_read},
+ {"register_write", lua_cb_register_write}};
+
+static void lua_script_free(lua_script_t *script) /* {{{ */
+{
+ if (script == NULL)
+ return;
+
+ lua_script_t *next = script->next;
+
+ if (script->lua_state != NULL) {
+ lua_close(script->lua_state);
+ script->lua_state = NULL;
+ }
+
+ sfree(script->script_path);
+ sfree(script);
+
+ lua_script_free(next);
+} /* }}} void lua_script_free */
+
+static int lua_script_init(lua_script_t *script) /* {{{ */
+{
+ memset(script, 0, sizeof(*script));
+
+ /* initialize the lua context */
+ script->lua_state = luaL_newstate();
+ if (script->lua_state == NULL) {
+ ERROR("Lua plugin: luaL_newstate() failed.");
+ return (-1);
+ }
+
+ /* Open up all the standard Lua libraries. */
+ luaL_openlibs(script->lua_state);
+
+ /* Register all the functions we implement in C */
+ lua_newtable(script->lua_state);
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(lua_c_functions); i++) {
+ lua_pushcfunction(script->lua_state, lua_c_functions[i].func);
+ lua_setfield(script->lua_state, -2, lua_c_functions[i].name);
+ }
+ lua_setglobal(script->lua_state, "collectd");
+
+ /* Prepend BasePath to package.path */
+ if (base_path[0] != '\0') {
+ lua_getglobal(script->lua_state, "package");
+ lua_getfield(script->lua_state, -1, "path");
+
+ const char *cur_path = lua_tostring(script->lua_state, -1);
+ char *new_path = ssnprintf_alloc("%s/?.lua;%s", base_path, cur_path);
+
+ lua_pop(script->lua_state, 1);
+ lua_pushstring(script->lua_state, new_path);
+
+ free(new_path);
+
+ lua_setfield(script->lua_state, -2, "path");
+ lua_pop(script->lua_state, 1);
+ }
+
+ return (0);
+} /* }}} int lua_script_init */
+
+static int lua_script_load(const char *script_path) /* {{{ */
+{
+ lua_script_t *script = malloc(sizeof(*script));
+ if (script == NULL) {
+ ERROR("Lua plugin: malloc failed.");
+ return (-1);
+ }
+
+ int status = lua_script_init(script);
+ if (status != 0) {
+ lua_script_free(script);
+ return (status);
+ }
+
+ script->script_path = strdup(script_path);
+ if (script->script_path == NULL) {
+ ERROR("Lua plugin: strdup failed.");
+ lua_script_free(script);
+ return (-1);
+ }
+
+ status = luaL_loadfile(script->lua_state, script->script_path);
+ if (status != 0) {
+ ERROR("Lua plugin: luaL_loadfile failed: %s",
+ lua_tostring(script->lua_state, -1));
+ lua_pop(script->lua_state, 1);
+ lua_script_free(script);
+ return (-1);
+ }
+
+ status = lua_pcall(script->lua_state,
+ /* nargs = */ 0,
+ /* nresults = */ LUA_MULTRET,
+ /* errfunc = */ 0);
+ if (status != 0) {
+ const char *errmsg = lua_tostring(script->lua_state, -1);
+
+ if (errmsg == NULL)
+ ERROR("Lua plugin: lua_pcall failed with status %i. "
+ "In addition, no error message could be retrieved from the stack.",
+ status);
+ else
+ ERROR("Lua plugin: Executing script \"%s\" failed:\n%s",
+ script->script_path, errmsg);
+
+ lua_script_free(script);
+ return (-1);
+ }
+
+ /* Append this script to the global list of scripts. */
+ if (scripts) {
+ lua_script_t *last = scripts;
+ while (last->next)
+ last = last->next;
+
+ last->next = script;
+ } else {
+ scripts = script;
+ }
+
+ return (0);
+} /* }}} int lua_script_load */
+
+static int lua_config_base_path(const oconfig_item_t *ci) /* {{{ */
+{
+ int status = cf_util_get_string_buffer(ci, base_path, sizeof(base_path));
+ if (status != 0)
+ return (status);
+
+ size_t len = strlen(base_path);
+ while ((len > 0) && (base_path[len - 1] == '/')) {
+ len--;
+ base_path[len] = '\0';
+ }
+
+ DEBUG("Lua plugin: base_path = \"%s\";", base_path);
+
+ return (0);
+} /* }}} int lua_config_base_path */
+
+static int lua_config_script(const oconfig_item_t *ci) /* {{{ */
+{
+ char rel_path[PATH_MAX];
+
+ int status = cf_util_get_string_buffer(ci, rel_path, sizeof(rel_path));
+ if (status != 0)
+ return (status);
+
+ char abs_path[PATH_MAX];
+
+ if (base_path[0] == '\0')
+ sstrncpy(abs_path, rel_path, sizeof(abs_path));
+ else
+ ssnprintf(abs_path, sizeof(abs_path), "%s/%s", base_path, rel_path);
+
+ DEBUG("Lua plugin: abs_path = \"%s\";", abs_path);
+
+ status = lua_script_load(abs_path);
+ if (status != 0)
+ return (status);
+
+ INFO("Lua plugin: File \"%s\" loaded succesfully", abs_path);
+
+ return 0;
+} /* }}} int lua_config_script */
+
+/*
+ * <Plugin lua>
+ * BasePath "/"
+ * Script "script1.lua"
+ * Script "script2.lua"
+ * </Plugin>
+ */
+static int lua_config(oconfig_item_t *ci) /* {{{ */
+{
+ int status = 0;
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("BasePath", child->key) == 0) {
+ status = lua_config_base_path(child);
+ } else if (strcasecmp("Script", child->key) == 0) {
+ status = lua_config_script(child);
+ } else {
+ ERROR("Lua plugin: Option `%s' is not allowed here.", child->key);
+ status = 1;
+ }
+ }
+
+ return status;
+} /* }}} int lua_config */
+
+static int lua_shutdown(void) /* {{{ */
+{
+ lua_script_free(scripts);
+
+ return (0);
+} /* }}} int lua_shutdown */
+
+void module_register() {
+ plugin_register_complex_config("lua", lua_config);
+ plugin_register_shutdown("lua", lua_shutdown);
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/**
+ * collectd - src/utils_lua.c
+ * Copyright (C) 2010 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+/* <lua5.1/luaconf.h> defines a macro using "sprintf". Although not used here,
+ * GCC will complain about the macro definition. */
+#define DONT_POISON_SPRINTF_YET
+
+#include "utils_lua.h"
+#include "common.h"
+
+static int ltoc_values(lua_State *L, /* {{{ */
+ const data_set_t *ds, value_t *ret_values) {
+ if (!lua_istable(L, -1)) {
+ WARNING("ltoc_values: not a table");
+ return (-1);
+ }
+
+ /* Push initial key */
+ lua_pushnil(L); /* +1 = 1 */
+ size_t i = 0;
+ while (lua_next(L, -2) != 0) /* -1+2 = 2 || -1 = 0 */
+ {
+ if (i >= ds->ds_num) {
+ lua_pop(L, 2); /* -2 = 0 */
+ i++;
+ break;
+ }
+
+ ret_values[i] = luaC_tovalue(L, -1, ds->ds[i].type);
+
+ /* Pop the value */
+ lua_pop(L, 1); /* -1 = 1 */
+ i++;
+ } /* while (lua_next) */
+
+ if (i != ds->ds_num) {
+ WARNING("ltoc_values: invalid size for datasource \"%s\": expected %zu, "
+ "got %zu",
+ ds->type, ds->ds_num, i);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int ltoc_values */
+
+static int ltoc_table_values(lua_State *L, int idx, /* {{{ */
+ const data_set_t *ds, value_list_t *vl) {
+ /* We're only called from "luaC_tovaluelist", which ensures that "idx" is an
+ * absolute index (i.e. a positive number) */
+ assert(idx > 0);
+
+ lua_getfield(L, idx, "values");
+ if (!lua_istable(L, -1)) {
+ WARNING("utils_lua: ltoc_table_values: The \"values\" member is a %s "
+ "value, not a table.",
+ lua_typename(L, lua_type(L, -1)));
+ lua_pop(L, 1);
+ return (-1);
+ }
+
+ vl->values_len = ds->ds_num;
+ vl->values = calloc(vl->values_len, sizeof(*vl->values));
+ if (vl->values == NULL) {
+ ERROR("utils_lua: calloc failed.");
+ vl->values_len = 0;
+ lua_pop(L, 1);
+ return (-1);
+ }
+
+ int status = ltoc_values(L, ds, vl->values);
+
+ lua_pop(L, 1);
+
+ if (status != 0) {
+ vl->values_len = 0;
+ sfree(vl->values);
+ }
+
+ return (status);
+} /* }}} int ltoc_table_values */
+
+static int luaC_pushvalues(lua_State *L, const data_set_t *ds,
+ const value_list_t *vl) /* {{{ */
+{
+ assert(vl->values_len == ds->ds_num);
+
+ lua_newtable(L);
+ for (size_t i = 0; i < vl->values_len; i++) {
+ lua_pushinteger(L, (lua_Integer)i + 1);
+ luaC_pushvalue(L, vl->values[i], ds->ds[i].type);
+ lua_settable(L, -3);
+ }
+
+ return (0);
+} /* }}} int luaC_pushvalues */
+
+static int luaC_pushdstypes(lua_State *L, const data_set_t *ds) /* {{{ */
+{
+ lua_newtable(L);
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ lua_pushinteger(L, (lua_Integer)i);
+ lua_pushstring(L, DS_TYPE_TO_STRING(ds->ds[i].type));
+ lua_settable(L, -3);
+ }
+
+ return (0);
+} /* }}} int luaC_pushdstypes */
+
+static int luaC_pushdsnames(lua_State *L, const data_set_t *ds) /* {{{ */
+{
+ lua_newtable(L);
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ lua_pushinteger(L, (lua_Integer)i);
+ lua_pushstring(L, ds->ds[i].name);
+ lua_settable(L, -3);
+ }
+
+ return (0);
+} /* }}} int luaC_pushdsnames */
+
+/*
+ * Public functions
+ */
+cdtime_t luaC_tocdtime(lua_State *L, int idx) /* {{{ */
+{
+ if (!lua_isnumber(L, /* stack pos = */ idx))
+ return (0);
+
+ double d = lua_tonumber(L, idx);
+
+ return (DOUBLE_TO_CDTIME_T(d));
+} /* }}} int ltoc_table_cdtime */
+
+int luaC_tostringbuffer(lua_State *L, int idx, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ const char *str = lua_tostring(L, idx);
+ if (str == NULL)
+ return (-1);
+
+ sstrncpy(buffer, str, buffer_size);
+ return (0);
+} /* }}} int luaC_tostringbuffer */
+
+value_t luaC_tovalue(lua_State *L, int idx, int ds_type) /* {{{ */
+{
+ value_t v = { 0 };
+
+ if (!lua_isnumber(L, idx))
+ return (v);
+
+ if (ds_type == DS_TYPE_GAUGE)
+ v.gauge = (gauge_t)lua_tonumber(L, /* stack pos = */ -1);
+ else if (ds_type == DS_TYPE_DERIVE)
+ v.derive = (derive_t)lua_tointeger(L, /* stack pos = */ -1);
+ else if (ds_type == DS_TYPE_COUNTER)
+ v.counter = (counter_t)lua_tointeger(L, /* stack pos = */ -1);
+ else if (ds_type == DS_TYPE_ABSOLUTE)
+ v.absolute = (absolute_t)lua_tointeger(L, /* stack pos = */ -1);
+
+ return (v);
+} /* }}} value_t luaC_tovalue */
+
+value_list_t *luaC_tovaluelist(lua_State *L, int idx) /* {{{ */
+{
+#if COLLECT_DEBUG
+ int stack_top_before = lua_gettop(L);
+#endif
+
+ /* Convert relative indexes to absolute indexes, so it doesn't change when we
+ * push / pop stuff. */
+ if (idx < 1)
+ idx += lua_gettop(L) + 1;
+
+ /* Check that idx is in the valid range */
+ if ((idx < 1) || (idx > lua_gettop(L))) {
+ DEBUG("luaC_tovaluelist: idx(%d), top(%d)", idx, stack_top_before);
+ return (NULL);
+ }
+
+ value_list_t *vl = calloc(1, sizeof(*vl));
+ if (vl == NULL) {
+ DEBUG("luaC_tovaluelist: calloc failed");
+ return (NULL);
+ }
+
+ /* Push initial key */
+ lua_pushnil(L);
+ while (lua_next(L, idx) != 0) {
+ const char *key = lua_tostring(L, -2);
+
+ if (key == NULL) {
+ DEBUG("luaC_tovaluelist: Ignoring non-string key.");
+ } else if (strcasecmp("host", key) == 0)
+ luaC_tostringbuffer(L, -1, vl->host, sizeof(vl->host));
+ else if (strcasecmp("plugin", key) == 0)
+ luaC_tostringbuffer(L, -1, vl->plugin, sizeof(vl->plugin));
+ else if (strcasecmp("plugin_instance", key) == 0)
+ luaC_tostringbuffer(L, -1, vl->plugin_instance,
+ sizeof(vl->plugin_instance));
+ else if (strcasecmp("type", key) == 0)
+ luaC_tostringbuffer(L, -1, vl->type, sizeof(vl->type));
+ else if (strcasecmp("type_instance", key) == 0)
+ luaC_tostringbuffer(L, -1, vl->type_instance,
+ sizeof(vl->type_instance));
+ else if (strcasecmp("time", key) == 0)
+ vl->time = luaC_tocdtime(L, -1);
+ else if (strcasecmp("interval", key) == 0)
+ vl->interval = luaC_tocdtime(L, -1);
+ else if (strcasecmp("values", key) == 0) {
+ /* This key is not handled here, because we have to assure "type" is read
+ * first. */
+ } else {
+ DEBUG("luaC_tovaluelist: Ignoring unknown key \"%s\".", key);
+ }
+
+ /* Pop the value */
+ lua_pop(L, 1);
+ }
+
+ const data_set_t *ds = plugin_get_ds(vl->type);
+ if (ds == NULL) {
+ INFO("utils_lua: Unable to lookup type \"%s\".", vl->type);
+ sfree(vl);
+ return (NULL);
+ }
+
+ int status = ltoc_table_values(L, idx, ds, vl);
+ if (status != 0) {
+ WARNING("utils_lua: ltoc_table_values failed.");
+ sfree(vl);
+ return (NULL);
+ }
+
+#if COLLECT_DEBUG
+ assert(stack_top_before == lua_gettop(L));
+#endif
+ return (vl);
+} /* }}} value_list_t *luaC_tovaluelist */
+
+int luaC_pushcdtime(lua_State *L, cdtime_t t) /* {{{ */
+{
+ double d = CDTIME_T_TO_DOUBLE(t);
+
+ lua_pushnumber(L, (lua_Number)d);
+ return (0);
+} /* }}} int luaC_pushcdtime */
+
+int luaC_pushvalue(lua_State *L, value_t v, int ds_type) /* {{{ */
+{
+ if (ds_type == DS_TYPE_GAUGE)
+ lua_pushnumber(L, (lua_Number)v.gauge);
+ else if (ds_type == DS_TYPE_DERIVE)
+ lua_pushinteger(L, (lua_Integer)v.derive);
+ else if (ds_type == DS_TYPE_COUNTER)
+ lua_pushinteger(L, (lua_Integer)v.counter);
+ else if (ds_type == DS_TYPE_ABSOLUTE)
+ lua_pushinteger(L, (lua_Integer)v.absolute);
+ else
+ return (-1);
+ return (0);
+} /* }}} int luaC_pushvalue */
+
+int luaC_pushvaluelist(lua_State *L, const data_set_t *ds,
+ const value_list_t *vl) /* {{{ */
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, vl->host);
+ lua_setfield(L, -2, "host");
+
+ lua_pushstring(L, vl->plugin);
+ lua_setfield(L, -2, "plugin");
+ lua_pushstring(L, vl->plugin_instance);
+ lua_setfield(L, -2, "plugin_instance");
+
+ lua_pushstring(L, vl->type);
+ lua_setfield(L, -2, "type");
+ lua_pushstring(L, vl->type_instance);
+ lua_setfield(L, -2, "type_instance");
+
+ luaC_pushvalues(L, ds, vl);
+ lua_setfield(L, -2, "values");
+
+ luaC_pushdstypes(L, ds);
+ lua_setfield(L, -2, "dstypes");
+
+ luaC_pushdsnames(L, ds);
+ lua_setfield(L, -2, "dsnames");
+
+ luaC_pushcdtime(L, vl->time);
+ lua_setfield(L, -2, "time");
+
+ luaC_pushcdtime(L, vl->interval);
+ lua_setfield(L, -2, "interval");
+
+ return (0);
+} /* }}} int luaC_pushvaluelist */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */