]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: spoa-server: Add Lua processing
authorThierry FOURNIER <thierry.fournier@ozon.io>
Fri, 23 Feb 2018 14:20:55 +0000 (15:20 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 13 May 2019 15:43:47 +0000 (17:43 +0200)
Use the defined binding for registering Lua engine.

contrib/spoa_server/Makefile
contrib/spoa_server/ps_lua.c [new file with mode: 0644]

index e6b7c534f0d8764891248892028f9fba7d08802f..31e3de58748d117ae61dd8f66f1f46d1f8878d99 100644 (file)
@@ -10,9 +10,19 @@ LDFLAGS = -lpthread
 
 OBJS = spoa.o
 
+ifneq ($(USE_LUA),)
+OBJS += ps_lua.o
+ifneq ($(LUA_INC),)
+CFLAGS += -I$(LUA_INC)
+endif
+ifneq ($(LUA_LIB),)
+LDLIBS += -L$(LUA_LIB)
+endif
+LDLIBS += -ldl -Wl,--export-dynamic -llua -lm -Wl,--no-export-dynamic
+endif
 
 spoa: $(OBJS)
-       $(LD) $(LDFLAGS) -o $@ $^
+       $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
 
 install: spoa
        install spoa $(DESTDIR)$(BINDIR)
diff --git a/contrib/spoa_server/ps_lua.c b/contrib/spoa_server/ps_lua.c
new file mode 100644 (file)
index 0000000..e7de2d9
--- /dev/null
@@ -0,0 +1,509 @@
+/* spoa-server: processing Lua
+ *
+ * Copyright 2018 OZON / Thierry Fournier <thierry.fournier@ozon.io>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+
+#include "spoa.h"
+
+static lua_State *L = NULL;
+static struct worker *worker;
+
+static int ps_lua_start_worker(struct worker *w);
+static int ps_lua_load_file(struct worker *w, const char *file);
+static int ps_lua_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
+
+static struct ps ps_lua_bindings_1 = {
+       .init_worker = ps_lua_start_worker,
+       .load_file = ps_lua_load_file,
+       .exec_message = ps_lua_exec_message,
+       .ext = ".lua",
+};
+
+static struct ps ps_lua_bindings_2 = {
+       .init_worker = ps_lua_start_worker,
+       .load_file = ps_lua_load_file,
+       .exec_message = ps_lua_exec_message,
+       .ext = ".luac",
+};
+
+/* Imported from Lua-5.3.4 */
+static int typeerror (lua_State *L, int arg, const char *tname)
+{
+       const char *msg;
+       const char *typearg;  /* name for the type of the actual argument */
+       if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
+               typearg = lua_tostring(L, -1);  /* use the given type name */
+       else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
+               typearg = "light userdata";  /* special name for messages */
+       else
+               typearg = luaL_typename(L, arg);  /* standard name */
+       msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
+       return luaL_argerror(L, arg, msg);
+}
+
+/* Imported from Lua-5.3.4 */
+static void tag_error (lua_State *L, int arg, int tag) {
+       typeerror(L, arg, lua_typename(L, tag));
+}
+
+#ifndef luaL_checkboolean
+static int luaL_checkboolean(lua_State *L, int index)
+{
+       if (!lua_isboolean(L, index)) {
+               tag_error(L, index, LUA_TBOOLEAN);
+       }
+       return lua_toboolean(L, index);
+}
+#endif
+
+static int ps_lua_register_message(lua_State *L)
+{
+       const char *name;
+       long ref;
+
+       /* First argument is a message name */
+       name = luaL_checkstring(L, 1);
+
+       /* Second argument is a function */
+       if (!lua_isfunction(L, 2)) {
+               const char *msg = lua_pushfstring(L, "function expected, got %s", luaL_typename(L, 2));
+               luaL_argerror(L, 2, msg);
+       }
+       lua_pushvalue(L, 2);
+       ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+       /* Register the message processor */
+       ps_register_message(&ps_lua_bindings_1, name, (void *)ref);
+
+       return 1;
+}
+
+static int ps_lua_set_var_null(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+
+       if (!set_var_null(worker, name, name_len, scope)) {
+               luaL_error(L, "No space left available");
+       }
+       return 0;
+}
+
+static int ps_lua_set_var_boolean(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       int64_t value;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkboolean(L, 3);
+
+       if (!set_var_bool(worker, name, name_len, scope, value))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_uint32(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       int64_t value;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkinteger(L, 3);
+
+       if (value < 0 || value > UINT_MAX)
+               luaL_error(L, "Integer '%lld' out of range for 'uint32' type", value);
+
+       if (!set_var_uint32(worker, name, name_len, scope, value))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_int32(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       int64_t value;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkinteger(L, 3);
+
+       if (value < INT_MIN || value > INT_MAX)
+               luaL_error(L, "Integer '%lld' out of range for 'int32' type", value);
+
+       if (!set_var_int32(worker, name, name_len, scope, value))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_uint64(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       int64_t value;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkinteger(L, 3);
+
+       if (value < 0)
+               luaL_error(L, "Integer '%lld' out of range for 'uint64' type", value);
+
+       if (!set_var_uint64(worker, name, name_len, scope, value))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_int64(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       int64_t value;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkinteger(L, 3);
+
+       if (!set_var_int64(worker, name, name_len, scope, value))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_ipv4(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       const char *value;
+       struct in_addr ipv4;
+       int ret;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkstring(L, 3);
+
+       ret = inet_pton(AF_INET, value, &ipv4);
+       if (ret == 0)
+               luaL_error(L, "IPv4 '%s': invalid format", value);
+       if (ret == -1)
+               luaL_error(L, "IPv4 '%s': %s", value, strerror(errno));
+
+       if (!set_var_ipv4(worker, name, name_len, scope, &ipv4))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_ipv6(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       const char *value;
+       struct in6_addr ipv6;
+       int ret;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checkstring(L, 3);
+
+       ret = inet_pton(AF_INET6, value, &ipv6);
+       if (ret == 0)
+               luaL_error(L, "IPv6 '%s': invalid format", value);
+       if (ret == -1)
+               luaL_error(L, "IPv6 '%s': %s", value, strerror(errno));
+
+       if (!set_var_ipv6(worker, name, name_len, scope, &ipv6))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_str(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       const char *value;
+       size_t value_len;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checklstring(L, 3, &value_len);
+
+       if (!set_var_string(worker, name, name_len, scope, value, value_len))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_set_var_bin(lua_State *L)
+{
+       const char *name;
+       size_t name_len;
+       unsigned char scope;
+       const char *value;
+       size_t value_len;
+
+       name = luaL_checklstring(L, 1, &name_len);
+       scope = (unsigned char)luaL_checkinteger(L, 2);
+       value = luaL_checklstring(L, 3, &value_len);
+
+       if (!set_var_bin(worker, name, name_len, scope, value, value_len))
+               luaL_error(L, "No space left available");
+       return 0;
+}
+
+static int ps_lua_start_worker(struct worker *w)
+{
+       if (L != NULL)
+               return 1;
+
+       worker = w;
+
+       L = luaL_newstate();
+       luaL_openlibs(L);
+
+       lua_newtable(L);
+
+       lua_pushstring(L, "register_message");
+       lua_pushcclosure(L, ps_lua_register_message, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_null");
+       lua_pushcclosure(L, ps_lua_set_var_null, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_boolean");
+       lua_pushcclosure(L, ps_lua_set_var_boolean, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_uint32");
+       lua_pushcclosure(L, ps_lua_set_var_uint32, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_int32");
+       lua_pushcclosure(L, ps_lua_set_var_int32, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_uint64");
+       lua_pushcclosure(L, ps_lua_set_var_uint64, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_int64");
+       lua_pushcclosure(L, ps_lua_set_var_int64, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_ipv4");
+       lua_pushcclosure(L, ps_lua_set_var_ipv4, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_ipv6");
+       lua_pushcclosure(L, ps_lua_set_var_ipv6, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_str");
+       lua_pushcclosure(L, ps_lua_set_var_str, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "set_var_bin");
+       lua_pushcclosure(L, ps_lua_set_var_bin, 0);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "scope");
+       lua_newtable(L);
+
+       lua_pushstring(L, "proc");
+       lua_pushinteger(L, SPOE_SCOPE_PROC);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "sess");
+       lua_pushinteger(L, SPOE_SCOPE_SESS);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "txn");
+       lua_pushinteger(L, SPOE_SCOPE_TXN);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "req");
+       lua_pushinteger(L, SPOE_SCOPE_REQ);
+       lua_rawset(L, -3);
+
+       lua_pushstring(L, "res");
+       lua_pushinteger(L, SPOE_SCOPE_RES);
+       lua_rawset(L, -3);
+
+       lua_rawset(L, -3); /* scope */
+
+       lua_setglobal(L, "spoa");
+       return 1;
+}
+
+static int ps_lua_load_file(struct worker *w, const char *file)
+{
+       int error;
+
+       /* Load the file and check syntax */
+       error = luaL_loadfile(L, file);
+       if (error) {
+               fprintf(stderr, "lua syntax error: %s\n", lua_tostring(L, -1));
+               return 0;
+       }
+
+       /* If no syntax error where detected, execute the code. */
+       error = lua_pcall(L, 0, LUA_MULTRET, 0);
+   switch (error) {
+       case LUA_OK:
+               break;
+       case LUA_ERRRUN:
+               fprintf(stderr, "lua runtime error: %s\n", lua_tostring(L, -1));
+               lua_pop(L, 1);
+               return 0;
+       case LUA_ERRMEM:
+               fprintf(stderr, "lua out of memory error\n");
+               return 0;
+       case LUA_ERRERR:
+               fprintf(stderr, "lua message handler error: %s\n", lua_tostring(L, 0));
+               lua_pop(L, 1);
+               return 0;
+       case LUA_ERRGCMM:
+               fprintf(stderr, "lua garbage collector error: %s\n", lua_tostring(L, 0));
+               lua_pop(L, 1);
+               return 0;
+       default:
+               fprintf(stderr, "lua unknonwn error: %s\n", lua_tostring(L, 0));
+               lua_pop(L, 1);
+               return 0;
+       }
+       return 1;
+}
+
+static int ps_lua_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
+{
+       long lua_ref = (long)ref;
+       int ret;
+       char *msg_fmt = NULL;
+       const char *msg;
+       int i;
+       char ipbuf[64];
+
+       /* Restore function in the stack */
+       lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
+
+       /* convert args in lua mode */
+       lua_newtable(L);
+       for (i = 0; i < nargs; i++) {
+               lua_newtable(L);
+               lua_pushstring(L, "name");
+               lua_pushlstring(L, args[i].name.str, args[i].name.len);
+               lua_rawset(L, -3); /* Push name */
+               lua_pushstring(L, "value");
+               switch (args[i].value.type) {
+               case SPOE_DATA_T_NULL:
+                       lua_pushnil(L);
+                       break;
+               case SPOE_DATA_T_BOOL:
+                       lua_pushboolean(L, args[i].value.u.boolean);
+                       break;
+               case SPOE_DATA_T_INT32:
+                       lua_pushinteger(L, args[i].value.u.sint32);
+                       break;
+               case SPOE_DATA_T_UINT32:
+                       lua_pushinteger(L, args[i].value.u.uint32);
+                       break;
+               case SPOE_DATA_T_INT64:
+                       lua_pushinteger(L, args[i].value.u.sint64);
+                       break;
+               case SPOE_DATA_T_UINT64:
+                       if (args[i].value.u.uint64 > LLONG_MAX)
+                               lua_pushnil(L);
+                       else
+                               lua_pushinteger(L, args[i].value.u.uint64);
+                       break;
+               case SPOE_DATA_T_IPV4:
+                       if (inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64) == NULL)
+                               lua_pushnil(L);
+                       else
+                               lua_pushstring(L, ipbuf);
+                       break;
+               case SPOE_DATA_T_IPV6:
+                       if (inet_ntop(AF_INET6, &args[i].value.u.ipv4, ipbuf, 64) == NULL)
+                               lua_pushnil(L);
+                       else
+                               lua_pushstring(L, ipbuf);
+                       break;
+               case SPOE_DATA_T_STR:
+               case SPOE_DATA_T_BIN:
+                       lua_pushlstring(L, args[i].value.u.buffer.str, args[i].value.u.buffer.len);
+                       break;
+               default:
+                       lua_pushnil(L);
+                       break;
+               }
+               lua_rawset(L, -3); /* Push name */
+               lua_rawseti(L, -2, i + 1); /* Pusg table in globale table */
+       }
+
+       /* execute lua function */
+       while (1) {
+               ret = lua_resume(L, L, 1);
+               switch (ret) {
+               case LUA_OK:
+                       return 1;
+               case LUA_YIELD:
+                       DEBUG("Lua yield");
+                       continue;
+               case LUA_ERRMEM:
+                       LOG("Lua: Out of memory error");
+                       return 0;
+               case LUA_ERRRUN:
+                       msg_fmt = "Lua runtime error";
+               case LUA_ERRGCMM:
+                       msg_fmt = msg_fmt ? msg_fmt : "Lua garbage collector error";
+               case LUA_ERRERR:
+                       msg_fmt = msg_fmt ? msg_fmt : "Lua message handler error";
+               default:
+                       msg_fmt = msg_fmt ? msg_fmt : "Lua unknonwn error";
+                       msg = lua_tostring(L, -1);
+                       if (msg == NULL)
+                               msg = "Unknown error";
+                       LOG("%s: %s", msg_fmt, msg);
+                       lua_settop(L, 0);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+__attribute__((constructor))
+static void __ps_lua_init(void)
+{
+       ps_register(&ps_lua_bindings_1);
+       ps_register(&ps_lua_bindings_2);
+}