summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 6743e8a)
raw | patch | inline | side by side (parent: 6743e8a)
author | Julien Ammous <j.ammous at gmail.com> | |
Fri, 12 Aug 2016 20:10:59 +0000 (22:10 +0200) | ||
committer | Ruben Kerkhof <ruben@rubenkerkhof.com> | |
Fri, 12 Aug 2016 20:27:11 +0000 (22:27 +0200) |
AUTHORS | patch | blob | history | |
configure.ac | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/collectd.conf.in | patch | blob | history | |
src/daemon/plugin.h | patch | blob | history | |
src/lua.c | [new file with mode: 0644] | patch | blob |
src/utils_lua.c | [new file with mode: 0644] | patch | blob |
src/utils_lua.h | [new file with mode: 0644] | patch | blob |
index f5f6db0eeb84c386fe6be619e30a4d1abd2e3d12..28220e79a6e7e6c723f7dfddcc29bb64990094ed 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
Jiri Tyr <jiri.tyr at gmail.com>
- fhcount plugin.
+Julien Ammous <j.ammous at gmail.com>
+ - Lua plugin.
+
Kevin Bowling <kbowling at llnw.com>
- write_tsdb plugin for http://opentsdb.net/
diff --git a/configure.ac b/configure.ac
index 8dda7104514bd760b0f8373004c09bb99b5b4162..4f5f0783b25eb4611fb34374b0965bf6ae88b095 100644 (file)
--- a/configure.ac
+++ b/configure.ac
AM_CONDITIONAL(BUILD_WITH_LIBLDAP, test "x$with_libldap" = "xyes")
# }}}
+# --with-liblua {{{
+with_liblua_cppflags=""
+with_liblua_ldflags=""
+with_liblua_libs=""
+with_liblua="yes"
+
+AC_ARG_VAR([LIBLUA_PKG_CONFIG_NAME], [Name of liblua used by pkg-config])
+if test "x$LIBLUA_PKG_CONFIG_NAME" = "x"
+then
+ LIBLUA_PKG_CONFIG_NAME="lua"
+fi
+
+if test "x$with_liblua" = "xyes"
+then
+ $PKG_CONFIG --exists $LIBLUA_PKG_CONFIG_NAME 2>/dev/null
+ lua_config_status=$?
+
+ if test 0 -ne $lua_config_status
+ then
+ with_liblua="no"
+ fi
+fi
+
+if test "x$with_liblua" = "xyes"
+then
+ with_liblua_cppflags=`$PKG_CONFIG --cflags-only-I $LIBLUA_PKG_CONFIG_NAME` || with_liblua="no"
+ with_liblua_ldflags=`$PKG_CONFIG --libs-only-L $LIBLUA_PKG_CONFIG_NAME` || with_liblua="no"
+ with_liblua_libs=`$PKG_CONFIG --libs-only-l $LIBLUA_PKG_CONFIG_NAME` || with_liblua="no"
+fi
+if test "x$with_liblua" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_liblua_cppflags"
+
+ AC_CHECK_HEADERS(lua.h lauxlib.h lualib.h, [], [with_liblua="no (header not found)"], [])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_liblua" = "xyes"
+then
+ SAVE_LDFLAGS="$LDFLAGS"
+ SAVE_LIBS="$LIBS"
+ LDFLAGS="$SAVE_LDFLAGS $with_liblua_ldflags"
+ LIBS="$LIBS $with_liblua_libs"
+
+ AC_CHECK_FUNC(lua_settop, [with_liblua="yes"], [with_liblua="no (symbol 'lua_settop' not found)"])
+
+ LDFLAGS="$SAVE_LDFLAGS"
+ LIBS="$SAVE_LIBS"
+fi
+if test "x$with_liblua" = "xyes"
+then
+ BUILD_WITH_LIBLUA_CPPFLAGS="$with_liblua_cppflags"
+ BUILD_WITH_LIBLUA_LDFLAGS="$with_liblua_ldflags"
+ BUILD_WITH_LIBLUA_LIBS="$with_liblua_libs"
+fi
+AC_SUBST(BUILD_WITH_LIBLUA_CPPFLAGS)
+AC_SUBST(BUILD_WITH_LIBLUA_LDFLAGS)
+AC_SUBST(BUILD_WITH_LIBLUA_LIBS)
+# }}}
+
# --with-liblvm2app {{{
with_liblvm2app_cppflags=""
with_liblvm2app_ldflags=""
AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
AC_PLUGIN([logfile], [yes], [File logging plugin])
AC_PLUGIN([lpar], [$with_perfstat], [AIX logical partitions statistics])
+AC_PLUGIN([lua], [$with_liblua], [Lua plugin])
AC_PLUGIN([lvm], [$with_liblvm2app], [LVM statistics])
AC_PLUGIN([madwifi], [$have_linux_wireless_h], [Madwifi wireless statistics])
AC_PLUGIN([match_empty_counter], [yes], [The empty counter match])
AC_MSG_RESULT([ libkstat . . . . . . $with_kstat])
AC_MSG_RESULT([ libkvm . . . . . . . $with_libkvm])
AC_MSG_RESULT([ libldap . . . . . . . $with_libldap])
+AC_MSG_RESULT([ liblua . . . . . . . $with_liblua])
AC_MSG_RESULT([ liblvm2app . . . . . $with_liblvm2app])
AC_MSG_RESULT([ libmemcached . . . . $with_libmemcached])
AC_MSG_RESULT([ libmnl . . . . . . . $with_libmnl])
AC_MSG_RESULT([ logfile . . . . . . . $enable_logfile])
AC_MSG_RESULT([ log_logstash . . . . $enable_log_logstash])
AC_MSG_RESULT([ lpar . . . . . . . . $enable_lpar])
+AC_MSG_RESULT([ lua . . . . . . . . . $enable_lua])
AC_MSG_RESULT([ lvm . . . . . . . . . $enable_lvm])
AC_MSG_RESULT([ madwifi . . . . . . . $enable_madwifi])
AC_MSG_RESULT([ match_empty_counter . $enable_match_empty_counter])
diff --git a/src/Makefile.am b/src/Makefile.am
index 12c7730cc95a5779a170ec9eb42de0a6b492bfe3..9a8e346dbb2c4891b47243adf9bd7dddb2d59016 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
apache_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS)
endif
+
if BUILD_PLUGIN_APCUPS
pkglib_LTLIBRARIES += apcups.la
apcups_la_SOURCES = apcups.c
lpar_la_LIBADD = -lperfstat
endif
+if BUILD_PLUGIN_LUA
+pkglib_LTLIBRARIES += lua.la
+lua_la_SOURCES = lua.c \
+ utils_lua.c utils_lua.h
+lua_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBLUA_CPPFLAGS)
+lua_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBLUA_LDFLAGS)
+lua_la_LIBADD = $(BUILD_WITH_LIBLUA_LIBS)
+endif
+
if BUILD_PLUGIN_LVM
pkglib_LTLIBRARIES += lvm.la
lvm_la_SOURCES = lvm.c
diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index e06465bf44ac82c256c1303abb41df3dd3bb49e8..7a3818dc4d471d218fd78fdfeecdc9721ea2b80a 100644 (file)
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
#@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java
@BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
#@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
+#@BUILD_PLUGIN_LUA_TRUE@LoadPlugin lua
#@BUILD_PLUGIN_LVM_TRUE@LoadPlugin lvm
#@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
#@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
# ReportBySerial false
#</Plugin>
+#<Plugin lua>
+# BasePath "@prefix@/share/@PACKAGE_NAME@/lua"
+# Script "script1.lua"
+# Script "script2.lua"
+#</Plugin>
+
#<Plugin madwifi>
# Interface "wlan0"
# IgnoreSelected false
diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h
index 49edba254ea13f5742864f24eb62d4048e9a1258..de42c068f536bf462d86659b30d677b9efaaeb1f 100644 (file)
--- a/src/daemon/plugin.h
+++ b/src/daemon/plugin.h
typedef int (*plugin_shutdown_cb) (void);
typedef int (*plugin_notification_cb) (const notification_t *,
user_data_t *);
-
/*
* NAME
* plugin_set_dir
diff --git a/src/lua.c b/src/lua.c
--- /dev/null
+++ b/src/lua.c
@@ -0,0 +1,582 @@
+/**
+ * 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 : */
diff --git a/src/utils_lua.c b/src/utils_lua.c
--- /dev/null
+++ b/src/utils_lua.c
@@ -0,0 +1,322 @@
+/**
+ * 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 : */
diff --git a/src/utils_lua.h b/src/utils_lua.h
--- /dev/null
+++ b/src/utils_lua.h
@@ -0,0 +1,56 @@
+/**
+ * collectd - src/utils_lua.h
+ * 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>
+ **/
+
+#ifndef UTILS_LUA_H
+#define UTILS_LUA_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+#ifndef DONT_POISON_SPRINTF_YET
+#error "Files including utils_lua.h need to define DONT_POISON_SPRINTF_YET."
+#endif
+#include <lua.h>
+
+/*
+ * access functions (stack -> C)
+ */
+cdtime_t luaC_tocdtime(lua_State *L, int idx);
+int luaC_tostringbuffer(lua_State *L, int idx, char *buffer,
+ size_t buffer_size);
+value_t luaC_tovalue(lua_State *L, int idx, int ds_type);
+value_list_t *luaC_tovaluelist(lua_State *L, int idx);
+
+/*
+ * push functions (C -> stack)
+ */
+int luaC_pushcdtime(lua_State *L, cdtime_t t);
+int luaC_pushvalue(lua_State *L, value_t v, int ds_type);
+int luaC_pushvaluelist(lua_State *L, const data_set_t *ds,
+ const value_list_t *vl);
+
+#endif /* UTILS_LUA_H */
+/* vim: set sw=2 sts=2 et fdm=marker : */