mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-29 17:42:54 +00:00
CONTRIB: move spoa_server out of the tree
As previously mentioned SPOA code has nothing to do in the haproxy core since they're not dependent on haproxy's version. This one was moved to its own repository here with complete history: https://github.com/haproxy/spoa-server
This commit is contained in:
parent
e558043e13
commit
da72723189
@ -1,77 +0,0 @@
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
CC = gcc
|
||||
LD = $(CC)
|
||||
|
||||
CFLAGS = -g -O2 -Wall -Werror -pthread
|
||||
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
|
||||
|
||||
ifneq ($(USE_PYTHON),)
|
||||
OBJS += ps_python.o
|
||||
|
||||
# "--embed" flag is supported (and required) only from python 3.8+
|
||||
check_python_config := $(shell if python3-config --embed > /dev/null 2>&1 ; then echo "python3.8+"; \
|
||||
elif hash python3-config > /dev/null 2>&1 ; then echo "python3"; \
|
||||
elif hash python-config > /dev/null 2>&1 ; then echo "python2"; fi)
|
||||
|
||||
ifeq ($(check_python_config), python3.8+)
|
||||
PYTHON_DEFAULT_INC := $(shell python3-config --includes)
|
||||
PYTHON_DEFAULT_LIB := $(shell python3-config --libs --embed)
|
||||
else ifeq ($(check_python_config), python3)
|
||||
PYTHON_DEFAULT_INC := $(shell python3-config --includes)
|
||||
PYTHON_DEFAULT_LIB := $(shell python3-config --libs)
|
||||
else ifeq ($(check_python_config), python2)
|
||||
PYTHON_DEFAULT_INC := $(shell python-config --includes)
|
||||
PYTHON_DEFAULT_LIB := $(shell python-config --libs)
|
||||
endif
|
||||
|
||||
|
||||
# Add default path
|
||||
ifneq ($(PYTHON_DEFAULT_INC),)
|
||||
CFLAGS += $(PYTHON_DEFAULT_INC)
|
||||
else
|
||||
CFLAGS += -I/usr/include/python2.7
|
||||
endif
|
||||
ifneq ($(PYTHON_DEFAULT_LIB),)
|
||||
LDLIBS += $(PYTHON_DEFAULT_LIB)
|
||||
else
|
||||
LDLIBS += -lpython2.7
|
||||
endif
|
||||
|
||||
# Add user additional paths if any
|
||||
ifneq ($(PYTHON_INC),)
|
||||
CFLAGS += -I$(PYTHON_INC)
|
||||
endif
|
||||
ifneq ($(PYTHON_LIB),)
|
||||
LDLIBS += -L$(PYTHON_LIB)
|
||||
endif
|
||||
|
||||
LDLIBS +=-Wl,--export-dynamic
|
||||
endif
|
||||
|
||||
spoa: $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
install: spoa
|
||||
install spoa $(DESTDIR)$(BINDIR)
|
||||
|
||||
clean:
|
||||
rm -f spoa $(OBJS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
@ -1,92 +0,0 @@
|
||||
Multi script langyage Stream Processing Offload Agent
|
||||
-----------------------------------------------------
|
||||
|
||||
This agent receive SPOP message and process it with script languages. The
|
||||
language register callback with a message. Each callback receive the list
|
||||
of arguments with types according with the language capabilities. The
|
||||
callback write variables which are sent as response when the processing
|
||||
is done.
|
||||
|
||||
|
||||
Prerequirement
|
||||
----------------
|
||||
|
||||
You have to install the development packages, either from the
|
||||
distribution repositories or from the source.
|
||||
|
||||
CentOS/RHEL: sudo yum install python3-devel
|
||||
|
||||
The current minimal python version compatible with this library is 2.7.
|
||||
It's recommended to use python version 3 where possible due to python 2 deprecation.
|
||||
|
||||
|
||||
Compilation
|
||||
---------------
|
||||
|
||||
The server currently supports Lua and Python. Type "make" with the options:
|
||||
USE_LUA=1 and/or USE_PYTHON=1.
|
||||
|
||||
You can add LUA_INC=.. LUA_LIB=.. to the make command to set the paths to
|
||||
the lua header files and lua libraries.
|
||||
|
||||
Similarly, you can add PYTHON_INC=.. PYTHON_LIB=.. to the make command to set the paths to
|
||||
the python header files and python libraries.
|
||||
By default, it will try to compile by detecting the default python 3 parameters.
|
||||
It will fall back to python 2 if python 3 is not available.
|
||||
|
||||
Start the service
|
||||
---------------------
|
||||
|
||||
After you have compiled it, to start the service, you just need to use "spoa"
|
||||
binary:
|
||||
|
||||
$> ./spoa -h
|
||||
Usage: ./spoa [-h] [-d] [-p <port>] [-n <num-workers>]
|
||||
-h Print this message
|
||||
-d Enable the debug mode
|
||||
-p <port> Specify the port to listen on (default: 12345)
|
||||
-n <num-workers> Specify the number of workers (default: 5)
|
||||
-f <file> Load script according with the supported languages
|
||||
|
||||
The file processor is recognized using the extension. .lua or .luac for lua and
|
||||
.py for python. Start example:
|
||||
|
||||
$> ./spoa -d -f ps_lua.lua
|
||||
|
||||
$> ./spoa -d -f ps_python.py
|
||||
|
||||
|
||||
Configure
|
||||
-------------
|
||||
|
||||
Sample configuration are join to this server:
|
||||
|
||||
spoa-server.conf : The HAProxy configuration file using SPOE server
|
||||
spoa-server.spoe.conf : The SPOP description file used by HAProxy
|
||||
ps_lua.lua : Processing Lua example
|
||||
ps_python.py : Processing Python example
|
||||
|
||||
|
||||
Considerations
|
||||
------------------
|
||||
|
||||
This server is a beta version. It works fine, but some improvement will be
|
||||
welcome:
|
||||
|
||||
Main process:
|
||||
|
||||
* Improve log management: Today the log are sent on stdout.
|
||||
* Improve process management: The dead process are ignored.
|
||||
* Implement systemd integration.
|
||||
* Implement threads: It would be fine to implement thread working. Shared
|
||||
memory is welcome for managing database connection pool and something like
|
||||
that.
|
||||
* Add PHP support and some other languages.
|
||||
|
||||
Python:
|
||||
|
||||
* Improve reporting: Catch python error message and report it in the right
|
||||
place. Today the error are dumped on stdout. How using syslog for logging
|
||||
stack traces ?
|
||||
|
||||
Maybe some other things...
|
@ -1,68 +0,0 @@
|
||||
function color(index, str)
|
||||
return "\x1b[" .. index .. "m" .. str .. "\x1b[00m"
|
||||
end
|
||||
|
||||
function nocolor(index, str)
|
||||
return str
|
||||
end
|
||||
|
||||
function sp(count)
|
||||
local spaces = ""
|
||||
while count > 0 do
|
||||
spaces = spaces .. " "
|
||||
count = count - 1
|
||||
end
|
||||
return spaces
|
||||
end
|
||||
|
||||
function print_rr(p, indent, c, wr)
|
||||
local i = 0
|
||||
local nl = ""
|
||||
|
||||
if type(p) == "table" then
|
||||
wr(c("33", "(table)") .. " " .. c("34", tostring(p)) .. " [")
|
||||
|
||||
mt = getmetatable(p)
|
||||
if mt ~= nil then
|
||||
wr("\n" .. sp(indent+1) .. c("31", "METATABLE") .. ": ")
|
||||
print_rr(mt, indent+1, c, wr)
|
||||
end
|
||||
|
||||
for k,v in pairs(p) do
|
||||
if i > 0 then
|
||||
nl = "\n"
|
||||
else
|
||||
wr("\n")
|
||||
end
|
||||
wr(nl .. sp(indent+1))
|
||||
if type(k) == "number" then
|
||||
wr(c("32", tostring(k)))
|
||||
else
|
||||
wr("\"" .. c("32", tostring(k)) .. "\"")
|
||||
end
|
||||
wr(": ")
|
||||
print_rr(v, indent+1, c, wr)
|
||||
i = i + 1
|
||||
end
|
||||
if i == 0 then
|
||||
wr(" " .. c("35", "/* empty */") .. " ]")
|
||||
else
|
||||
wr("\n" .. sp(indent) .. "]")
|
||||
end
|
||||
elseif type(p) == "string" then
|
||||
wr(c("33", "(string)") .. " \"" .. c("34", p) .. "\"")
|
||||
else
|
||||
wr(c("33", "(" .. type(p) .. ")") .. " " .. c("34", tostring(p)))
|
||||
end
|
||||
end
|
||||
|
||||
function print_r(p, col, wr)
|
||||
if col == nil then col = true end
|
||||
if wr == nil then wr = function(msg) io.stdout:write(msg) end end
|
||||
if col == true then
|
||||
print_rr(p, 0, color, wr)
|
||||
else
|
||||
print_rr(p, 0, nocolor, wr)
|
||||
end
|
||||
wr("\n")
|
||||
end
|
@ -1,509 +0,0 @@
|
||||
/* 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 unknown 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 unknown 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);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
require("print_r")
|
||||
require("math")
|
||||
|
||||
print_r("Load lua message processors")
|
||||
|
||||
spoa.register_message("check-client-ip", function(args)
|
||||
print_r(args)
|
||||
spoa.set_var_null("null", spoa.scope.txn)
|
||||
spoa.set_var_boolean("boolean", spoa.scope.txn, true)
|
||||
spoa.set_var_int32("int32", spoa.scope.txn, 1234)
|
||||
spoa.set_var_uint32("uint32", spoa.scope.txn, 1234)
|
||||
spoa.set_var_int64("int64", spoa.scope.txn, 1234)
|
||||
spoa.set_var_uint64("uint64", spoa.scope.txn, 1234)
|
||||
spoa.set_var_ipv4("ipv4", spoa.scope.txn, "127.0.0.1")
|
||||
spoa.set_var_ipv6("ipv6", spoa.scope.txn, "1::f")
|
||||
spoa.set_var_str("str", spoa.scope.txn, "1::f")
|
||||
spoa.set_var_bin("bin", spoa.scope.txn, "1::f")
|
||||
spoa.set_var_int32("ip_score", spoa.scope.sess, math.random(100))
|
||||
end)
|
@ -1,800 +0,0 @@
|
||||
/* spoa-server: processing Python
|
||||
*
|
||||
* Copyright 2018 OZON / Thierry Fournier <thierry.fournier@ozon.io>
|
||||
* Copyright (C) 2020 Gilchrist Dadaglo <gilchrist@dadaglo.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is provided in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define PY_SSIZE_T_CLEAN before including Python.h
|
||||
* as per https://docs.python.org/3/c-api/arg.html and https://docs.python.org/2/c-api/arg.html
|
||||
*/
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "spoa.h"
|
||||
#include "ps_python.h"
|
||||
|
||||
/* Embedding python documentation:
|
||||
*
|
||||
* https://docs.python.org/2/extending/embedding.html
|
||||
* https://docs.python.org/2/extending/extending.html#extending-python-with-c-or-c
|
||||
* https://docs.python.org/2/extending/extending.html#calling-python-functions-from-c
|
||||
*/
|
||||
|
||||
static PyObject *module_ipaddress;
|
||||
static PyObject *ipv4_address;
|
||||
static PyObject *ipv6_address;
|
||||
static PyObject *spoa_error;
|
||||
static PyObject *empty_tuple;
|
||||
static struct worker *worker;
|
||||
|
||||
static int ps_python_start_worker(struct worker *w);
|
||||
static int ps_python_load_file(struct worker *w, const char *file);
|
||||
static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
|
||||
|
||||
static struct ps ps_python_bindings = {
|
||||
.init_worker = ps_python_start_worker,
|
||||
.load_file = ps_python_load_file,
|
||||
.exec_message = ps_python_exec_message,
|
||||
.ext = ".py",
|
||||
};
|
||||
|
||||
static int ps_python_check_overflow(Py_ssize_t len)
|
||||
{
|
||||
/* There might be an overflow when converting from Py_ssize_t to int.
|
||||
* This function will catch those cases.
|
||||
* Also, spoa "struct chunk" is limited to int size.
|
||||
* We should not send data bigger than it can handle.
|
||||
*/
|
||||
if (len >= (Py_ssize_t)INT_MAX) {
|
||||
PyErr_Format(spoa_error,
|
||||
"%zd is over 2GB. Please split in smaller pieces.", \
|
||||
len);
|
||||
return -1;
|
||||
} else {
|
||||
return Py_SAFE_DOWNCAST(len, Py_ssize_t, int);
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_PYTHON_3K
|
||||
static PyObject *module_spoa;
|
||||
static PyObject *PyInit_spoa_module(void);
|
||||
#endif /* IS_PYTHON_3K */
|
||||
|
||||
static PyObject *ps_python_register_message(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
PyObject *ref;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sO!", &name, &PyFunction_Type, &ref))
|
||||
return NULL;
|
||||
Py_XINCREF(ref); /* because the function is internally referenced */
|
||||
|
||||
ps_register_message(&ps_python_bindings, name, (void *)ref);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_null(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int name_len_i;
|
||||
int scope;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#i", &name, &name_len, &scope))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_null(worker, name, name_len_i, scope)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_boolean(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
int value;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#ii", &name, &name_len, &scope, &value))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_bool(worker, name, name_len_i, scope, value)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_int32(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
int32_t value;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#ii", &name, &name_len, &scope, &value))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_int32(worker, name, name_len_i, scope, value)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_uint32(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
uint32_t value;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#iI", &name, &name_len, &scope, &value))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_uint32(worker, name, name_len_i, scope, value)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_int64(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
int64_t value;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#il", &name, &name_len, &scope, &value))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_int64(worker, name, name_len_i, scope, value)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_uint64(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
uint64_t value;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#ik", &name, &name_len, &scope, &value))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_uint64(worker, name, name_len_i, scope, value)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_ipv4(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
PyObject *ipv4;
|
||||
PyObject *value;
|
||||
struct in_addr ip;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#iO", &name, &name_len, &scope, &ipv4))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!PyObject_IsInstance(ipv4, ipv4_address)) {
|
||||
PyErr_Format(spoa_error, "must be 'IPv4Address', not '%s'", ipv4->ob_type->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
/* Execute packed ... I think .. */
|
||||
value = PyObject_GetAttrString(ipv4, "packed");
|
||||
if (value == NULL)
|
||||
return NULL;
|
||||
if (PY_STRING_GET_SIZE(value) != sizeof(ip)) {
|
||||
PyErr_Format(spoa_error, "IPv4 manipulation internal error");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&ip, PY_STRING_AS_STRING(value), PY_STRING_GET_SIZE(value));
|
||||
if (!set_var_ipv4(worker, name, name_len_i, scope, &ip)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
/* Once we set the IP value in the worker, we don't need it anymore... */
|
||||
Py_XDECREF(value);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_ipv6(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
PyObject *ipv6;
|
||||
PyObject *value;
|
||||
struct in6_addr ip;
|
||||
int name_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#iO", &name, &name_len, &scope, &ipv6))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
if (name_len_i == -1)
|
||||
return NULL;
|
||||
if (!PyObject_IsInstance(ipv6, ipv6_address)) {
|
||||
PyErr_Format(spoa_error, "must be 'IPv6Address', not '%s'", ipv6->ob_type->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
/* Execute packed ... I think .. */
|
||||
value = PyObject_GetAttrString(ipv6, "packed");
|
||||
if (value == NULL)
|
||||
return NULL;
|
||||
if (PY_STRING_GET_SIZE(value) != sizeof(ip)) {
|
||||
PyErr_Format(spoa_error, "IPv6 manipulation internal error");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&ip, PY_STRING_AS_STRING(value), PY_STRING_GET_SIZE(value));
|
||||
if (!set_var_ipv6(worker, name, name_len_i, scope, &ip)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
/* Once we set the IP value in the worker, we don't need it anymore... */
|
||||
Py_XDECREF(value);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_str(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
const char *value;
|
||||
Py_ssize_t value_len;
|
||||
int name_len_i;
|
||||
int value_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#is#", &name, &name_len, &scope, &value, &value_len))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
value_len_i = ps_python_check_overflow(value_len);
|
||||
if (name_len_i == -1 || value_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_string(worker, name, name_len_i, scope, value, value_len_i)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ps_python_set_var_bin(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
int scope;
|
||||
const char *value;
|
||||
Py_ssize_t value_len;
|
||||
int name_len_i;
|
||||
int value_len_i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#is#", &name, &name_len, &scope, &value, &value_len))
|
||||
return NULL;
|
||||
name_len_i = ps_python_check_overflow(name_len);
|
||||
value_len_i = ps_python_check_overflow(value_len);
|
||||
if (name_len_i == -1 || value_len_i == -1)
|
||||
return NULL;
|
||||
if (!set_var_bin(worker, name, name_len_i, scope, value, value_len_i)) {
|
||||
PyErr_SetString(spoa_error, "No more memory space available");
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef spoa_methods[] = {
|
||||
{"register_message", ps_python_register_message, METH_VARARGS,
|
||||
"Register binding for SPOA message."},
|
||||
{"set_var_null", ps_python_set_var_null, METH_VARARGS,
|
||||
"Set SPOA NULL variable"},
|
||||
{"set_var_boolean", ps_python_set_var_boolean, METH_VARARGS,
|
||||
"Set SPOA boolean variable"},
|
||||
{"set_var_int32", ps_python_set_var_int32, METH_VARARGS,
|
||||
"Set SPOA int32 variable"},
|
||||
{"set_var_uint32", ps_python_set_var_uint32, METH_VARARGS,
|
||||
"Set SPOA uint32 variable"},
|
||||
{"set_var_int64", ps_python_set_var_int64, METH_VARARGS,
|
||||
"Set SPOA int64 variable"},
|
||||
{"set_var_uint64", ps_python_set_var_uint64, METH_VARARGS,
|
||||
"Set SPOA uint64 variable"},
|
||||
{"set_var_ipv4", ps_python_set_var_ipv4, METH_VARARGS,
|
||||
"Set SPOA ipv4 variable"},
|
||||
{"set_var_ipv6", ps_python_set_var_ipv6, METH_VARARGS,
|
||||
"Set SPOA ipv6 variable"},
|
||||
{"set_var_str", ps_python_set_var_str, METH_VARARGS,
|
||||
"Set SPOA str variable"},
|
||||
{"set_var_bin", ps_python_set_var_bin, METH_VARARGS,
|
||||
"Set SPOA bin variable"},
|
||||
{ /* end */ }
|
||||
};
|
||||
|
||||
#if IS_PYTHON_3K
|
||||
static struct PyModuleDef spoa_module_definition = {
|
||||
PyModuleDef_HEAD_INIT, /* m_base */
|
||||
"spoa", /* m_name */
|
||||
"HAProxy SPOA module for python", /* m_doc */
|
||||
-1, /* m_size */
|
||||
spoa_methods, /* m_methods */
|
||||
NULL, /* m_slots */
|
||||
NULL, /* m_traverse */
|
||||
NULL, /* m_clear */
|
||||
NULL /* m_free */
|
||||
};
|
||||
|
||||
static PyObject *PyInit_spoa_module(void)
|
||||
{
|
||||
return module_spoa;
|
||||
}
|
||||
#endif /* IS_PYTHON_3K */
|
||||
|
||||
static int ps_python_start_worker(struct worker *w)
|
||||
{
|
||||
PyObject *m;
|
||||
PyObject *module_name;
|
||||
PyObject *value;
|
||||
int ret;
|
||||
|
||||
#if IS_PYTHON_27
|
||||
Py_SetProgramName("spoa-server");
|
||||
#endif /* IS_PYTHON_27 */
|
||||
#if IS_PYTHON_3K
|
||||
Py_SetProgramName(Py_DecodeLocale("spoa-server", NULL));
|
||||
PyImport_AppendInittab("spoa", &PyInit_spoa_module);
|
||||
#endif /* IS_PYTHON_3K */
|
||||
|
||||
Py_Initialize();
|
||||
|
||||
module_name = PY_STRING_FROM_STRING("ipaddress");
|
||||
if (module_name == NULL) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_ipaddress = PyImport_Import(module_name);
|
||||
Py_DECREF(module_name);
|
||||
if (module_ipaddress == NULL) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ipv4_address = PyObject_GetAttrString(module_ipaddress, "IPv4Address");
|
||||
if (ipv4_address == NULL) {
|
||||
Py_DECREF(module_ipaddress);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ipv6_address = PyObject_GetAttrString(module_ipaddress, "IPv6Address");
|
||||
if (ipv6_address == NULL) {
|
||||
Py_DECREF(ipv4_address);
|
||||
Py_DECREF(module_ipaddress);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
PY_INIT_MODULE(m, "spoa", spoa_methods, &spoa_module_definition);
|
||||
if (m == NULL) {
|
||||
Py_DECREF(ipv4_address);
|
||||
Py_DECREF(ipv6_address);
|
||||
Py_DECREF(module_ipaddress);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
spoa_error = PyErr_NewException("spoa.error", NULL, NULL);
|
||||
/* PyModule_AddObject will steal the reference to spoa_error
|
||||
* in case of success only
|
||||
* We need to increment the counters to continue using it
|
||||
* but cleanup in case of failure
|
||||
*/
|
||||
Py_INCREF(spoa_error);
|
||||
ret = PyModule_AddObject(m, "error", spoa_error);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(m);
|
||||
Py_DECREF(spoa_error);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
value = PyLong_FromLong(SPOE_SCOPE_PROC);
|
||||
if (value == NULL) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyModule_AddObject(m, "scope_proc", value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(m);
|
||||
Py_DECREF(value);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PyLong_FromLong(SPOE_SCOPE_SESS);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(m);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyModule_AddObject(m, "scope_sess", value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(m);
|
||||
Py_DECREF(value);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PyLong_FromLong(SPOE_SCOPE_TXN);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(m);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyModule_AddObject(m, "scope_txn", value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(m);
|
||||
Py_DECREF(value);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PyLong_FromLong(SPOE_SCOPE_REQ);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(m);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyModule_AddObject(m, "scope_req", value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(m);
|
||||
Py_DECREF(value);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PyLong_FromLong(SPOE_SCOPE_RES);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(m);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyModule_AddObject(m, "scope_res", value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(m);
|
||||
Py_DECREF(value);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
empty_tuple = PyTuple_New(0);
|
||||
if (empty_tuple == NULL) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_PYTHON_3K
|
||||
module_spoa = m;
|
||||
#endif /* IS_PYTHON_3K */
|
||||
worker = w;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ps_python_load_file(struct worker *w, const char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (fp == NULL) {
|
||||
LOG("python: Cannot read file \"%s\": %s", file, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyRun_SimpleFile(fp, file);
|
||||
fclose(fp);
|
||||
if (ret != 0) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
|
||||
{
|
||||
int i;
|
||||
PyObject *python_ref = ref;
|
||||
PyObject *fkw;
|
||||
PyObject *kw_args;
|
||||
PyObject *result;
|
||||
PyObject *ent;
|
||||
PyObject *key;
|
||||
PyObject *value;
|
||||
PyObject *func;
|
||||
int ret;
|
||||
char ipbuf[64];
|
||||
const char *p;
|
||||
PyObject *ip_dict;
|
||||
PyObject *ip_name;
|
||||
PyObject *ip_value;
|
||||
|
||||
/* Dict containing arguments */
|
||||
|
||||
kw_args = PyList_New(0);
|
||||
if (kw_args == NULL) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < nargs; i++) {
|
||||
|
||||
/* New dict containing one argument */
|
||||
|
||||
ent = PyDict_New();
|
||||
if (ent == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the name entry */
|
||||
|
||||
key = PY_STRING_FROM_STRING("name");
|
||||
if (key == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PY_STRING_FROM_STRING_AND_SIZE(args[i].name.str, args[i].name.len);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyDict_SetItem(ent, key, value);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the value entry */
|
||||
|
||||
key = PY_STRING_FROM_STRING("value");
|
||||
if (key == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (args[i].value.type) {
|
||||
case SPOE_DATA_T_NULL:
|
||||
Py_INCREF(Py_None);
|
||||
value = Py_None;
|
||||
break;
|
||||
case SPOE_DATA_T_BOOL:
|
||||
value = PyBool_FromLong(args[i].value.u.boolean);
|
||||
break;
|
||||
case SPOE_DATA_T_INT32:
|
||||
value = PyLong_FromLong(args[i].value.u.sint32);
|
||||
break;
|
||||
case SPOE_DATA_T_UINT32:
|
||||
value = PyLong_FromLong(args[i].value.u.uint32);
|
||||
break;
|
||||
case SPOE_DATA_T_INT64:
|
||||
value = PyLong_FromLong(args[i].value.u.sint64);
|
||||
break;
|
||||
case SPOE_DATA_T_UINT64:
|
||||
value = PyLong_FromUnsignedLong(args[i].value.u.uint64);
|
||||
break;
|
||||
case SPOE_DATA_T_IPV4:
|
||||
case SPOE_DATA_T_IPV6:
|
||||
if (args[i].value.type == SPOE_DATA_T_IPV4)
|
||||
p = inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64);
|
||||
else
|
||||
p = inet_ntop(AF_INET6, &args[i].value.u.ipv6, ipbuf, 64);
|
||||
if (!p)
|
||||
strcpy(ipbuf, "0.0.0.0");
|
||||
|
||||
func = PyObject_GetAttrString(module_ipaddress, "ip_address");
|
||||
if (func == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
ip_dict = PyDict_New();
|
||||
if (ip_dict == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(func);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
ip_name = PY_STRING_FROM_STRING("address");
|
||||
if (ip_name == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(func);
|
||||
Py_DECREF(ip_dict);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
ip_value = PyUnicode_FromString(ipbuf);
|
||||
if (ip_value == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(func);
|
||||
Py_DECREF(ip_dict);
|
||||
Py_DECREF(ip_name);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
ret = PyDict_SetItem(ip_dict, ip_name, ip_value);
|
||||
Py_DECREF(ip_name);
|
||||
Py_DECREF(ip_value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(func);
|
||||
Py_DECREF(ip_dict);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
value = PyObject_Call(func, empty_tuple, ip_dict);
|
||||
Py_DECREF(func);
|
||||
Py_DECREF(ip_dict);
|
||||
break;
|
||||
|
||||
case SPOE_DATA_T_STR:
|
||||
value = PY_STRING_FROM_STRING_AND_SIZE(args[i].value.u.buffer.str, args[i].value.u.buffer.len);
|
||||
break;
|
||||
case SPOE_DATA_T_BIN:
|
||||
value = PY_BYTES_FROM_STRING_AND_SIZE(args[i].value.u.buffer.str, args[i].value.u.buffer.len);
|
||||
break;
|
||||
default:
|
||||
Py_INCREF(Py_None);
|
||||
value = Py_None;
|
||||
break;
|
||||
}
|
||||
if (value == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
Py_DECREF(key);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyDict_SetItem(ent, key, value);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(value);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(ent);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add dict to the list */
|
||||
|
||||
ret = PyList_Append(kw_args, ent);
|
||||
Py_DECREF(ent);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(kw_args);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dictionary { args = <list-of-args> } for the function */
|
||||
|
||||
fkw = PyDict_New();
|
||||
if (fkw == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
key = PY_STRING_FROM_STRING("args");
|
||||
if (key == NULL) {
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(fkw);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = PyDict_SetItem(fkw, key, kw_args);
|
||||
Py_DECREF(kw_args);
|
||||
Py_DECREF(key);
|
||||
if (ret == -1) {
|
||||
Py_DECREF(fkw);
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = PyObject_Call(python_ref, empty_tuple, fkw);
|
||||
Py_DECREF(fkw);
|
||||
if (result == NULL) {
|
||||
PyErr_Print();
|
||||
return 0;
|
||||
}
|
||||
Py_DECREF(result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void __ps_python_init(void)
|
||||
{
|
||||
ps_register(&ps_python_bindings);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/* ps_python.h: SPOA Python processing includes
|
||||
*
|
||||
* Copyright (C) 2020 Gilchrist Dadaglo <gilchrist@dadaglo.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is provided in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PS_PYTHON_H__
|
||||
#define __PS_PYTHON_H__
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define IS_PYTHON_3K 1
|
||||
#define IS_PYTHON_27 0
|
||||
#elif PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7
|
||||
#define IS_PYTHON_3K 0
|
||||
#define IS_PYTHON_27 1
|
||||
#else
|
||||
#error "Unsupported Python Version - Please use Python 3"
|
||||
#endif /* PY_MAJOR_VERSION */
|
||||
|
||||
#if IS_PYTHON_3K
|
||||
#define PY_INIT_MODULE(instance, name, methods, moduledef) \
|
||||
(instance = PyModule_Create(moduledef))
|
||||
#define PY_STRING_FROM_STRING PyUnicode_FromString
|
||||
#define PY_STRING_FROM_STRING_AND_SIZE PyUnicode_FromStringAndSize
|
||||
#define PY_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
#define PY_STRING_GET_SIZE PyBytes_Size
|
||||
#define PY_STRING_AS_STRING PyBytes_AsString
|
||||
#elif IS_PYTHON_27
|
||||
#define PY_INIT_MODULE(instance, name, methods, moduledef) \
|
||||
(instance = Py_InitModule(name, methods))
|
||||
#define PY_STRING_FROM_STRING PyString_FromString
|
||||
#define PY_STRING_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||
#define PY_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||
#define PY_STRING_GET_SIZE PyString_GET_SIZE
|
||||
#define PY_STRING_AS_STRING PyString_AS_STRING
|
||||
#endif /* IS_PYTHON_3K */
|
||||
|
||||
#endif /* __PS_PYTHON_H__ */
|
||||
|
||||
/* EOF */
|
@ -1,22 +0,0 @@
|
||||
from pprint import pprint
|
||||
import spoa
|
||||
import ipaddress
|
||||
import random
|
||||
|
||||
def check_client_ip(args):
|
||||
pprint(args)
|
||||
spoa.set_var_null("null", spoa.scope_txn)
|
||||
spoa.set_var_boolean("boolean", spoa.scope_txn, True)
|
||||
spoa.set_var_int32("int32", spoa.scope_txn, 1234)
|
||||
spoa.set_var_uint32("uint32", spoa.scope_txn, 1234)
|
||||
spoa.set_var_int64("int64", spoa.scope_txn, 1234)
|
||||
spoa.set_var_uint64("uint64", spoa.scope_txn, 1234)
|
||||
spoa.set_var_ipv4("ipv4", spoa.scope_txn, ipaddress.IPv4Address(u"127.0.0.1"))
|
||||
spoa.set_var_ipv6("ipv6", spoa.scope_txn, ipaddress.IPv6Address(u"1::f"))
|
||||
spoa.set_var_str("str", spoa.scope_txn, "1::f")
|
||||
spoa.set_var_bin("bin", spoa.scope_txn, "1:\x01:\x02f\x00\x00")
|
||||
spoa.set_var_int32("ip_score", spoa.scope_sess, random.randint(1,100))
|
||||
return
|
||||
|
||||
|
||||
spoa.register_message("check-client-ip", check_client_ip)
|
@ -1,33 +0,0 @@
|
||||
global
|
||||
debug
|
||||
|
||||
defaults
|
||||
mode http
|
||||
option httplog
|
||||
option dontlognull
|
||||
timeout connect 5000
|
||||
timeout client 5000
|
||||
timeout server 5000
|
||||
|
||||
listen test
|
||||
mode http
|
||||
bind :10001
|
||||
filter spoe engine spoa-server config spoa-server.spoe.conf
|
||||
http-request set-var(req.a) var(txn.iprep.null),debug
|
||||
http-request set-var(req.a) var(txn.iprep.boolean),debug
|
||||
http-request set-var(req.a) var(txn.iprep.int32),debug
|
||||
http-request set-var(req.a) var(txn.iprep.uint32),debug
|
||||
http-request set-var(req.a) var(txn.iprep.int64),debug
|
||||
http-request set-var(req.a) var(txn.iprep.uint64),debug
|
||||
http-request set-var(req.a) var(txn.iprep.ipv4),debug
|
||||
http-request set-var(req.a) var(txn.iprep.ipv6),debug
|
||||
http-request set-var(req.a) var(txn.iprep.str),debug
|
||||
http-request set-var(req.a) var(txn.iprep.bin),debug
|
||||
http-request redirect location /%[var(sess.iprep.ip_score)]
|
||||
|
||||
backend spoe-server
|
||||
mode tcp
|
||||
balance roundrobin
|
||||
timeout connect 5s
|
||||
timeout server 3m
|
||||
server spoe-server 127.0.0.1:12345
|
@ -1,13 +0,0 @@
|
||||
[spoa-server]
|
||||
|
||||
spoe-agent spoa-server
|
||||
messages check-client-ip
|
||||
option var-prefix iprep
|
||||
timeout hello 100ms
|
||||
timeout idle 30s
|
||||
timeout processing 15ms
|
||||
use-backend spoe-server
|
||||
|
||||
spoe-message check-client-ip
|
||||
args always_true int(1234) src ipv6(::55) req.fhdr(host)
|
||||
event on-frontend-http-request
|
File diff suppressed because it is too large
Load Diff
@ -1,167 +0,0 @@
|
||||
/* Main SPOA server includes
|
||||
*
|
||||
* Copyright 2016 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __SPOA_H__
|
||||
#define __SPOA_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifndef MAX_FRAME_SIZE
|
||||
#define MAX_FRAME_SIZE 16384
|
||||
#endif
|
||||
|
||||
#define SPOP_VERSION "2.0"
|
||||
#define SPOA_CAPABILITIES ""
|
||||
|
||||
/* Flags set on the SPOE frame */
|
||||
#define SPOE_FRM_FL_FIN 0x00000001
|
||||
|
||||
/* All supported data types */
|
||||
enum spoe_data_type {
|
||||
SPOE_DATA_T_NULL = 0,
|
||||
SPOE_DATA_T_BOOL,
|
||||
SPOE_DATA_T_INT32,
|
||||
SPOE_DATA_T_UINT32,
|
||||
SPOE_DATA_T_INT64,
|
||||
SPOE_DATA_T_UINT64,
|
||||
SPOE_DATA_T_IPV4,
|
||||
SPOE_DATA_T_IPV6,
|
||||
SPOE_DATA_T_STR,
|
||||
SPOE_DATA_T_BIN,
|
||||
SPOE_DATA_TYPES
|
||||
};
|
||||
|
||||
/* Scopes used for variables set by agents. It is a way to be agnotic to vars
|
||||
* scope. */
|
||||
enum spoe_vars_scope {
|
||||
SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */
|
||||
SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */
|
||||
SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */
|
||||
SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */
|
||||
SPOE_SCOPE_RES, /* <=> SCOPE_RES */
|
||||
};
|
||||
|
||||
struct worker {
|
||||
unsigned int id;
|
||||
char buf[MAX_FRAME_SIZE];
|
||||
unsigned int len;
|
||||
unsigned int size;
|
||||
int status_code;
|
||||
unsigned int stream_id;
|
||||
unsigned int frame_id;
|
||||
bool healthcheck;
|
||||
char ack[MAX_FRAME_SIZE];
|
||||
unsigned int ack_len;
|
||||
};
|
||||
|
||||
struct chunk {
|
||||
char *str; /* beginning of the string itself. Might not be 0-terminated */
|
||||
int len; /* current size of the string from first to last char */
|
||||
};
|
||||
|
||||
union spoe_value {
|
||||
bool boolean; /* use for boolean */
|
||||
int32_t sint32; /* used for signed 32bits integers */
|
||||
uint32_t uint32; /* used for signed 32bits integers */
|
||||
int32_t sint64; /* used for signed 64bits integers */
|
||||
uint32_t uint64; /* used for signed 64bits integers */
|
||||
struct in_addr ipv4; /* used for ipv4 addresses */
|
||||
struct in6_addr ipv6; /* used for ipv6 addresses */
|
||||
struct chunk buffer; /* used for char strings or buffers */
|
||||
};
|
||||
|
||||
/* Used to store sample constant */
|
||||
struct spoe_data {
|
||||
enum spoe_data_type type; /* SPOE_DATA_T_* */
|
||||
union spoe_value u; /* spoe data value */
|
||||
};
|
||||
|
||||
struct spoe_kv {
|
||||
struct chunk name;
|
||||
struct spoe_data value;
|
||||
};
|
||||
|
||||
struct ps {
|
||||
struct ps *next;
|
||||
char *ext;
|
||||
int (*init_worker)(struct worker *w);
|
||||
int (*exec_message)(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
|
||||
int (*load_file)(struct worker *w, const char *file);
|
||||
};
|
||||
|
||||
struct ps_message {
|
||||
struct ps_message *next;
|
||||
const char *name;
|
||||
struct ps *ps;
|
||||
void *ref;
|
||||
};
|
||||
|
||||
extern bool debug;
|
||||
extern pthread_key_t worker_id;
|
||||
|
||||
void ps_register(struct ps *ps);
|
||||
void ps_register_message(struct ps *ps, const char *name, void *ref);
|
||||
|
||||
int set_var_null(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope);
|
||||
int set_var_bool(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope, bool value);
|
||||
int set_var_uint32(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope, uint32_t value);
|
||||
int set_var_int32(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope, int32_t value);
|
||||
int set_var_uint64(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope, uint64_t value);
|
||||
int set_var_int64(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope, int64_t value);
|
||||
int set_var_ipv4(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope,
|
||||
struct in_addr *ipv4);
|
||||
int set_var_ipv6(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope,
|
||||
struct in6_addr *ipv6);
|
||||
int set_var_string(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope,
|
||||
const char *str, int strlen);
|
||||
int set_var_bin(struct worker *w,
|
||||
const char *name, int name_len,
|
||||
unsigned char scope,
|
||||
const char *str, int strlen);
|
||||
|
||||
#define LOG(fmt, args...) \
|
||||
do { \
|
||||
struct timeval now; \
|
||||
int wid = *((int*)pthread_getspecific(worker_id)); \
|
||||
\
|
||||
gettimeofday(&now, NULL); \
|
||||
fprintf(stderr, "%ld.%06ld [%02d] " fmt "\n", \
|
||||
now.tv_sec, now.tv_usec, wid, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG(x...) \
|
||||
do { \
|
||||
if (debug) \
|
||||
LOG(x); \
|
||||
} while (0)
|
||||
|
||||
#endif /* __SPOA_H__ */
|
Loading…
Reference in New Issue
Block a user