2015-09-19 07:11:53 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#ifdef WITH_LUA
|
|
|
|
# include <stdarg.h>
|
|
|
|
# include <stdint.h>
|
|
|
|
# include "util.h"
|
|
|
|
# include "udata.h"
|
|
|
|
# include "hooks.h"
|
2015-09-21 03:20:39 -07:00
|
|
|
# include <lua.h>
|
|
|
|
# include <lualib.h>
|
|
|
|
# include <lauxlib.h>
|
2015-09-25 08:43:17 -07:00
|
|
|
# include "gcache.h"
|
2015-09-19 07:11:53 -07:00
|
|
|
# include "json.h"
|
|
|
|
# include "version.h"
|
|
|
|
|
|
|
|
static int otr_log(lua_State *lua);
|
|
|
|
static int otr_strftime(lua_State *lua);
|
2015-09-25 09:47:26 -07:00
|
|
|
static int otr_putdb(lua_State *lua);
|
|
|
|
static int otr_getdb(lua_State *lua);
|
2015-09-25 08:43:17 -07:00
|
|
|
|
|
|
|
static struct gcache *LuaDB = NULL;
|
2015-09-19 07:11:53 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Invoke the function `name' in the Lua script, which _may_ return
|
|
|
|
* an integer that we, in turn, return to caller.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int l_function(lua_State *L, char *name)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
lua_getglobal(L, name);
|
|
|
|
if (lua_type(L, -1) != LUA_TFUNCTION) {
|
|
|
|
olog(LOG_ERR, "Cannot invoke Lua function %s: %s\n", name, lua_tostring(L, -1));
|
|
|
|
rc = 1;
|
|
|
|
} else {
|
|
|
|
lua_call(L, 0, 1);
|
|
|
|
rc = (int)lua_tonumber(L, -1);
|
2015-09-25 08:01:46 -07:00
|
|
|
lua_pop(L, 1);
|
2015-09-19 07:11:53 -07:00
|
|
|
}
|
|
|
|
lua_settop(L, 0);
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
2015-09-25 08:43:17 -07:00
|
|
|
struct luadata *hooks_init(struct udata *ud, char *script)
|
2015-09-19 07:11:53 -07:00
|
|
|
{
|
|
|
|
struct luadata *ld;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ((ld = malloc(sizeof(struct luadata))) == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
ld->script = strdup(script);
|
|
|
|
// ld->L = lua_open();
|
|
|
|
ld->L = luaL_newstate();
|
|
|
|
luaL_openlibs(ld->L);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up a global with some values.
|
|
|
|
*/
|
|
|
|
|
|
|
|
lua_newtable(ld->L);
|
|
|
|
lua_pushstring(ld->L, VERSION);
|
|
|
|
lua_setfield(ld->L, -2, "version");
|
|
|
|
|
|
|
|
// lua_pushstring(ld->L, "/Users/jpm/Auto/projects/on-github/owntracks/recorder/lua");
|
|
|
|
// lua_setfield(ld->L, -2, "luapath");
|
|
|
|
|
|
|
|
lua_pushcfunction(ld->L, otr_log);
|
|
|
|
lua_setfield(ld->L, -2, "log");
|
|
|
|
|
|
|
|
lua_pushcfunction(ld->L, otr_strftime);
|
|
|
|
lua_setfield(ld->L, -2, "strftime");
|
|
|
|
|
2015-09-25 09:47:26 -07:00
|
|
|
lua_pushcfunction(ld->L, otr_putdb);
|
|
|
|
lua_setfield(ld->L, -2, "putdb");
|
|
|
|
|
|
|
|
lua_pushcfunction(ld->L, otr_getdb);
|
|
|
|
lua_setfield(ld->L, -2, "getdb");
|
2015-09-25 08:43:17 -07:00
|
|
|
|
2015-09-19 07:11:53 -07:00
|
|
|
lua_setglobal(ld->L, "otr");
|
|
|
|
|
2015-09-28 10:04:37 -07:00
|
|
|
#ifdef WITH_LMDB
|
2015-09-25 08:43:17 -07:00
|
|
|
LuaDB = ud->luadb;
|
|
|
|
#endif
|
2015-09-19 07:11:53 -07:00
|
|
|
|
2015-09-29 00:35:35 -07:00
|
|
|
olog(LOG_DEBUG, "initializing Lua hooks from `%s'", script);
|
2015-09-19 07:11:53 -07:00
|
|
|
|
|
|
|
/* Load the Lua script */
|
|
|
|
if (luaL_dofile(ld->L, ld->script)) {
|
|
|
|
olog(LOG_ERR, "Cannot load Lua from %s: %s", ld->script, lua_tostring(ld->L, -1));
|
|
|
|
hooks_exit(ld, "failed to load script");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = l_function(ld->L, "otr_init");
|
|
|
|
if (rc != 0) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After all this work (sigh), the Script has decided we shouldn't use
|
|
|
|
* hooks, so unload the whole Lua stuff and NULL back.
|
|
|
|
*/
|
|
|
|
|
|
|
|
hooks_exit(ld, "otr_init() returned non-zero");
|
|
|
|
ld = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ld);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hooks_exit(struct luadata *ld, char *reason)
|
|
|
|
{
|
|
|
|
if (ld) {
|
2015-09-20 08:01:05 -07:00
|
|
|
l_function(ld->L, "otr_exit");
|
2015-09-29 07:40:19 -07:00
|
|
|
olog(LOG_DEBUG, "unloading Lua: %s", reason);
|
2015-09-19 07:11:53 -07:00
|
|
|
free(ld->script);
|
|
|
|
lua_close(ld->L);
|
|
|
|
free(ld);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-25 05:02:30 -07:00
|
|
|
static void do_hook(char *hookname, struct udata *ud, char *topic, JsonNode *fullo)
|
2015-09-19 07:11:53 -07:00
|
|
|
{
|
|
|
|
struct luadata *ld = ud->luadata;
|
|
|
|
char *_type = "unknown";
|
|
|
|
JsonNode *j;
|
|
|
|
|
2015-09-25 08:01:46 -07:00
|
|
|
lua_settop(ld->L, 0);
|
2015-09-25 05:02:30 -07:00
|
|
|
lua_getglobal(ld->L, hookname);
|
2015-09-19 07:11:53 -07:00
|
|
|
if (lua_type(ld->L, -1) != LUA_TFUNCTION) {
|
2015-09-25 05:02:30 -07:00
|
|
|
olog(LOG_NOTICE, "cannot invoke %s in Lua script", hookname);
|
2015-09-19 07:11:53 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushstring(ld->L, topic); /* arg1: topic */
|
|
|
|
|
|
|
|
if ((j = json_find_member(fullo, "_type")) != NULL) {
|
|
|
|
if (j->tag == JSON_STRING)
|
|
|
|
_type = j->string_;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushstring(ld->L, _type); /* arg2: record type */
|
|
|
|
|
|
|
|
lua_newtable(ld->L); /* arg3: table */
|
|
|
|
json_foreach(j, fullo) {
|
|
|
|
lua_pushstring(ld->L, j->key); /* table key */
|
|
|
|
if (j->tag == JSON_STRING) {
|
|
|
|
lua_pushstring(ld->L, j->string_);
|
|
|
|
} else if (j->tag == JSON_NUMBER) {
|
|
|
|
lua_pushnumber(ld->L, j->number_);
|
|
|
|
} else if (j->tag == JSON_NULL) {
|
|
|
|
lua_pushnil(ld->L);
|
|
|
|
} else if (j->tag == JSON_BOOL) {
|
|
|
|
lua_pushboolean(ld->L, j->bool_);
|
|
|
|
}
|
|
|
|
lua_rawset(ld->L, -3);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Invoke `hook' function in Lua with our args */
|
|
|
|
if (lua_pcall(ld->L, 3, 1, 0)) {
|
|
|
|
olog(LOG_ERR, "Failed to run script: %s", lua_tostring(ld->L, -1));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// rc = (int)lua_tonumber(ld->L, -1);
|
|
|
|
// printf("C: FILTER returns %d\n", rc);
|
|
|
|
}
|
|
|
|
|
2015-09-25 05:02:30 -07:00
|
|
|
static void hooks_hooklet(struct udata *ud, char *topic, JsonNode *fullo)
|
|
|
|
{
|
|
|
|
JsonNode *j;
|
|
|
|
struct luadata *ld = ud->luadata;
|
|
|
|
char hookname[BUFSIZ];
|
|
|
|
|
|
|
|
json_foreach(j, fullo) {
|
|
|
|
snprintf(hookname, sizeof(hookname), "hooklet_%s", j->key);
|
|
|
|
lua_getglobal(ld->L, hookname);
|
|
|
|
if (lua_type(ld->L, -1) == LUA_TFUNCTION) {
|
|
|
|
do_hook(hookname, ud, topic, fullo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-28 09:59:16 -07:00
|
|
|
/*
|
|
|
|
* Invoked from putrec() in storage. If the Lua putrec function is available
|
|
|
|
* and it returns non-zero, do not putrec.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int hooks_norec(struct udata *ud, char *user, char *device, char *payload)
|
|
|
|
{
|
|
|
|
struct luadata *ld = ud->luadata;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (ld == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
lua_settop(ld->L, 0);
|
2015-09-28 23:03:17 -07:00
|
|
|
lua_getglobal(ld->L, "otr_putrec");
|
2015-09-28 09:59:16 -07:00
|
|
|
if (lua_type(ld->L, -1) != LUA_TFUNCTION) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushstring(ld->L, user);
|
|
|
|
lua_pushstring(ld->L, device);
|
|
|
|
lua_pushstring(ld->L, payload);
|
|
|
|
|
2015-09-28 23:03:17 -07:00
|
|
|
/* Invoke Lua function with our args */
|
2015-09-28 09:59:16 -07:00
|
|
|
if (lua_pcall(ld->L, 3, 1, 0)) {
|
|
|
|
olog(LOG_ERR, "Failed to run putrec in Lua: %s", lua_tostring(ld->L, -1));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = (int)lua_tonumber(ld->L, -1);
|
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
2015-09-25 05:02:30 -07:00
|
|
|
void hooks_hook(struct udata *ud, char *topic, JsonNode *fullo)
|
|
|
|
{
|
|
|
|
do_hook("otr_hook", ud, topic, fullo);
|
|
|
|
hooks_hooklet(ud, topic, fullo);
|
|
|
|
}
|
|
|
|
|
2015-09-19 07:11:53 -07:00
|
|
|
/*
|
|
|
|
* --- Here come the functions we provide to Lua scripts.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int otr_log(lua_State *lua)
|
|
|
|
{
|
|
|
|
const char *str;
|
|
|
|
|
|
|
|
if (lua_gettop(lua) >= 1) {
|
|
|
|
str = lua_tostring(lua, 1);
|
|
|
|
olog(LOG_INFO, "%s", str);
|
2015-09-25 08:01:46 -07:00
|
|
|
lua_pop(lua, 1);
|
2015-09-19 07:11:53 -07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* otr.strftime(format, seconds)
|
|
|
|
* Perform a strtime(3) for Lua with the specified format and
|
|
|
|
* seconds, and return the string result to Lua. As a special
|
|
|
|
* case, if `seconds' is negative, use current time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int otr_strftime(lua_State *lua)
|
|
|
|
{
|
|
|
|
const char *fmt;
|
|
|
|
long secs;
|
|
|
|
struct tm *tm;
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
|
|
|
|
if (lua_gettop(lua) >= 1) {
|
|
|
|
fmt = lua_tostring(lua, 1);
|
|
|
|
if ((secs = lua_tonumber(lua, 2)) < 1)
|
|
|
|
secs = time(0);
|
|
|
|
|
|
|
|
if ((tm = gmtime(&secs)) != NULL) {
|
|
|
|
strftime(buf, sizeof(buf), fmt, tm);
|
|
|
|
|
|
|
|
lua_pushstring(lua, buf);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2015-09-25 08:43:17 -07:00
|
|
|
/*
|
|
|
|
* Requires two string arguments: key, value
|
|
|
|
* These are written into the named LMDB database
|
|
|
|
* called `luadb'.
|
|
|
|
*/
|
|
|
|
|
2015-09-25 09:47:26 -07:00
|
|
|
static int otr_putdb(lua_State *lua)
|
2015-09-25 08:43:17 -07:00
|
|
|
{
|
|
|
|
const char *key, *value;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (lua_gettop(lua) >= 1) {
|
|
|
|
key = lua_tostring(lua, 1);
|
|
|
|
value = lua_tostring(lua, 2);
|
|
|
|
|
|
|
|
rc = gcache_put(LuaDB, (char *)key, (char *)value);
|
|
|
|
// olog(LOG_DEBUG, "LUA_PUT (%s, %s) == %d\n", key, value, rc);
|
|
|
|
}
|
|
|
|
return (rc);
|
|
|
|
}
|
2015-09-25 09:47:26 -07:00
|
|
|
|
|
|
|
static int otr_getdb(lua_State *lua)
|
|
|
|
{
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
const char *key;
|
2015-09-26 05:50:47 -07:00
|
|
|
int rc = 0, blen;
|
2015-09-25 09:47:26 -07:00
|
|
|
|
|
|
|
if (lua_gettop(lua) >= 1) {
|
|
|
|
key = lua_tostring(lua, 1);
|
|
|
|
|
2015-09-26 05:50:47 -07:00
|
|
|
blen = gcache_get(LuaDB, (char *)key, buf, sizeof(buf));
|
|
|
|
if (blen < 0)
|
|
|
|
memset(buf, 0, sizeof(buf));
|
2015-09-25 09:47:26 -07:00
|
|
|
// printf("K=[%s], blen=%d\n", key, blen);
|
|
|
|
lua_pushstring(lua, buf);
|
|
|
|
rc = 1;
|
|
|
|
}
|
|
|
|
return (rc);
|
|
|
|
}
|
2015-09-19 07:11:53 -07:00
|
|
|
#endif /* WITH_LUA */
|