haproxy/src/hlua.c
Thierry FOURNIER 65f34c6367 MINOR: lua: txn: create class TXN associated with the transaction.
This class of functions permit to access to all the functions
associated with the transaction like http header, HAProxy internal
fetches, etc ...

This patch puts the skeleton of this class. The class will be
enhanced later.
2015-02-28 23:12:34 +01:00

1101 lines
30 KiB
C

#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <ebpttree.h>
#include <common/cfgparse.h>
#include <types/hlua.h>
#include <types/proto_tcp.h>
#include <types/proxy.h>
#include <proto/arg.h>
#include <proto/task.h>
/* Lua uses longjmp to perform yield or throwing errors. This
* macro is used only for identifying the function that can
* not return because a longjmp is executed.
* __LJMP marks a prototype of hlua file that can use longjmp.
* WILL_LJMP() marks an lua function that will use longjmp.
* MAY_LJMP() marks an lua function that may use longjmp.
*/
#define __LJMP
#define WILL_LJMP(func) func
#define MAY_LJMP(func) func
/* The main Lua execution context. */
struct hlua gL;
/* This is the memory pool containing all the signal structs. These
* struct are used to store each requiered signal between two tasks.
*/
struct pool_head *pool2_hlua_com;
/* List head of the function called at the initialisation time. */
struct list hlua_init_functions = LIST_HEAD_INIT(hlua_init_functions);
/* Store the fast lua context for coroutines. This tree uses the
* Lua stack pointer value as indexed entry, and store the associated
* hlua context.
*/
struct eb_root hlua_ctx = EB_ROOT_UNIQUE;
/* The following variables contains the reference of the different
* Lua classes. These references are useful for identify metadata
* associated with an object.
*/
static int class_core_ref;
static int class_txn_ref;
/* These functions converts types between HAProxy internal args or
* sample and LUA types. Another function permits to check if the
* LUA stack contains arguments according with an required ARG_T
* format.
*/
static int hlua_arg2lua(lua_State *L, const struct arg *arg);
static int hlua_lua2arg(lua_State *L, int ud, struct arg *arg);
__LJMP static int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp, unsigned int mask);
static int hlua_smp2lua(lua_State *L, const struct sample *smp);
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp);
/* Used to check an Lua function type in the stack. It creates and
* returns a reference of the function. This function throws an
* error if the rgument is not a "function".
*/
__LJMP unsigned int hlua_checkfunction(lua_State *L, int argno)
{
if (!lua_isfunction(L, argno)) {
const char *msg = lua_pushfstring(L, "function expected, got %s", luaL_typename(L, -1));
WILL_LJMP(luaL_argerror(L, argno, msg));
}
lua_pushvalue(L, argno);
return luaL_ref(L, LUA_REGISTRYINDEX);
}
/* The three following functions are useful for adding entries
* in a table. These functions takes a string and respectively an
* integer, a string or a function and add it to the table in the
* top of the stack.
*
* These functions throws an error if no more stack size is
* available.
*/
__LJMP static inline void hlua_class_const_int(lua_State *L, const char *name,
unsigned int value)
{
if (!lua_checkstack(L, 2))
WILL_LJMP(luaL_error(L, "full stack"));
lua_pushstring(L, name);
lua_pushunsigned(L, value);
lua_settable(L, -3);
}
__LJMP static inline void hlua_class_const_str(lua_State *L, const char *name,
const char *value)
{
if (!lua_checkstack(L, 2))
WILL_LJMP(luaL_error(L, "full stack"));
lua_pushstring(L, name);
lua_pushstring(L, value);
lua_settable(L, -3);
}
__LJMP static inline void hlua_class_function(lua_State *L, const char *name,
int (*function)(lua_State *L))
{
if (!lua_checkstack(L, 2))
WILL_LJMP(luaL_error(L, "full stack"));
lua_pushstring(L, name);
lua_pushcclosure(L, function, 0);
lua_settable(L, -3);
}
/* This function check the number of arguments available in the
* stack. If the number of arguments available is not the same
* then <nb> an error is throwed.
*/
__LJMP static inline void check_args(lua_State *L, int nb, char *fcn)
{
if (lua_gettop(L) == nb)
return;
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
}
/* Return true if the data in stack[<ud>] is an object of
* type <class_ref>.
*/
static int hlua_udataistype(lua_State *L, int ud, int class_ref)
{
void *p = lua_touserdata(L, ud);
if (!p)
return 0;
if (!lua_getmetatable(L, ud))
return 0;
lua_rawgeti(L, LUA_REGISTRYINDEX, class_ref);
if (!lua_rawequal(L, -1, -2)) {
lua_pop(L, 2);
return 0;
}
lua_pop(L, 2);
return 1;
}
/* Return an object of the expected type, or throws an error. */
__LJMP static void *hlua_checkudata(lua_State *L, int ud, int class_ref)
{
if (!hlua_udataistype(L, ud, class_ref))
WILL_LJMP(luaL_argerror(L, 1, NULL));
return lua_touserdata(L, ud);
}
/* This fucntion push an error string prefixed by the file name
* and the line number where the error is encountered.
*/
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
luaL_where(L, 1);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return 1;
}
/* This function register a new signal. "lua" is the current lua
* execution context. It contains a pointer to the associated task.
* "link" is a list head attached to an other task that must be wake
* the lua task if an event occurs. This is useful with external
* events like TCP I/O or sleep functions. This funcion allocate
* memory for the signal.
*/
static int hlua_com_new(struct hlua *lua, struct list *link)
{
struct hlua_com *com = pool_alloc2(pool2_hlua_com);
if (!com)
return 0;
LIST_ADDQ(&lua->com, &com->purge_me);
LIST_ADDQ(link, &com->wake_me);
com->task = lua->task;
return 1;
}
/* This function purge all the pending signals when the LUA execution
* is finished. This prevent than a coprocess try to wake a deleted
* task. This function remove the memory associated to the signal.
*/
static void hlua_com_purge(struct hlua *lua)
{
struct hlua_com *com, *back;
/* Delete all pending communication signals. */
list_for_each_entry_safe(com, back, &lua->com, purge_me) {
LIST_DEL(&com->purge_me);
LIST_DEL(&com->wake_me);
pool_free2(pool2_hlua_com, com);
}
}
/* This function sends signals. It wakes all the tasks attached
* to a list head, and remove the signal, and free the used
* memory.
*/
static void hlua_com_wake(struct list *wake)
{
struct hlua_com *com, *back;
/* Wake task and delete all pending communication signals. */
list_for_each_entry_safe(com, back, wake, wake_me) {
LIST_DEL(&com->purge_me);
LIST_DEL(&com->wake_me);
task_wakeup(com->task, TASK_WOKEN_MSG);
pool_free2(pool2_hlua_com, com);
}
}
/* This functions is used with sample fetch and converters. It
* converts the HAProxy configuration argument in a lua stack
* values.
*
* It takes an array of "arg", and each entry of the array is
* converted and pushed in the LUA stack.
*/
static int hlua_arg2lua(lua_State *L, const struct arg *arg)
{
switch (arg->type) {
case ARGT_SINT:
lua_pushinteger(L, arg->data.sint);
break;
case ARGT_UINT:
case ARGT_TIME:
case ARGT_SIZE:
lua_pushunsigned(L, arg->data.sint);
break;
case ARGT_STR:
lua_pushlstring(L, arg->data.str.str, arg->data.str.len);
break;
case ARGT_IPV4:
case ARGT_IPV6:
case ARGT_MSK4:
case ARGT_MSK6:
case ARGT_FE:
case ARGT_BE:
case ARGT_TAB:
case ARGT_SRV:
case ARGT_USR:
case ARGT_MAP:
default:
lua_pushnil(L);
break;
}
return 1;
}
/* This function take one entrie in an LUA stack at the index "ud",
* and try to convert it in an HAProxy argument entry. This is useful
* with sample fetch wrappers. The input arguments are gived to the
* lua wrapper and converted as arg list by thi function.
*/
static int hlua_lua2arg(lua_State *L, int ud, struct arg *arg)
{
switch (lua_type(L, ud)) {
case LUA_TNUMBER:
case LUA_TBOOLEAN:
arg->type = ARGT_SINT;
arg->data.sint = lua_tointeger(L, ud);
break;
case LUA_TSTRING:
arg->type = ARGT_STR;
arg->data.str.str = (char *)lua_tolstring(L, ud, (size_t *)&arg->data.str.len);
break;
case LUA_TUSERDATA:
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
arg->type = ARGT_SINT;
arg->data.uint = 0;
break;
}
return 1;
}
/* the following functions are used to convert a struct sample
* in Lua type. This useful to convert the return of the
* fetchs or converters.
*/
static int hlua_smp2lua(lua_State *L, const struct sample *smp)
{
switch (smp->type) {
case SMP_T_SINT:
lua_pushinteger(L, smp->data.sint);
break;
case SMP_T_BOOL:
case SMP_T_UINT:
lua_pushunsigned(L, smp->data.uint);
break;
case SMP_T_BIN:
case SMP_T_STR:
lua_pushlstring(L, smp->data.str.str, smp->data.str.len);
break;
case SMP_T_METH:
switch (smp->data.meth.meth) {
case HTTP_METH_OPTIONS: lua_pushstring(L, "OPTIONS"); break;
case HTTP_METH_GET: lua_pushstring(L, "GET"); break;
case HTTP_METH_HEAD: lua_pushstring(L, "HEAD"); break;
case HTTP_METH_POST: lua_pushstring(L, "POST"); break;
case HTTP_METH_PUT: lua_pushstring(L, "PUT"); break;
case HTTP_METH_DELETE: lua_pushstring(L, "DELETE"); break;
case HTTP_METH_TRACE: lua_pushstring(L, "TRACE"); break;
case HTTP_METH_CONNECT: lua_pushstring(L, "CONNECT"); break;
case HTTP_METH_OTHER:
lua_pushlstring(L, smp->data.meth.str.str, smp->data.meth.str.len);
break;
default:
lua_pushnil(L);
break;
}
break;
case SMP_T_IPV4:
case SMP_T_IPV6:
case SMP_T_ADDR: /* This type is never used to qualify a sample. */
default:
lua_pushnil(L);
break;
}
return 1;
}
/* the following functions are used to convert an Lua type in a
* struct sample. This is useful to provide data from a converter
* to the LUA code.
*/
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp)
{
switch (lua_type(L, ud)) {
case LUA_TNUMBER:
smp->type = SMP_T_SINT;
smp->data.sint = lua_tointeger(L, ud);
break;
case LUA_TBOOLEAN:
smp->type = SMP_T_BOOL;
smp->data.uint = lua_toboolean(L, ud);
break;
case LUA_TSTRING:
smp->type = SMP_T_STR;
smp->flags |= SMP_F_CONST;
smp->data.str.str = (char *)lua_tolstring(L, ud, (size_t *)&smp->data.str.len);
break;
case LUA_TUSERDATA:
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
smp->type = SMP_T_BOOL;
smp->data.uint = 0;
break;
}
return 1;
}
/* This function check the "argp" builded by another conversion function
* is in accord with the expected argp defined by the "mask". The fucntion
* returns true or false. It can be adjust the types if there compatibles.
*/
__LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp, unsigned int mask)
{
int min_arg;
int idx;
idx = 0;
min_arg = ARGM(mask);
mask >>= ARGM_BITS;
while (1) {
/* Check oversize. */
if (idx >= ARGM_NBARGS && argp[idx].type != ARGT_STOP) {
WILL_LJMP(luaL_argerror(L, first + idx, "Malformad argument mask"));
}
/* Check for mandatory arguments. */
if (argp[idx].type == ARGT_STOP) {
if (idx + 1 < min_arg)
WILL_LJMP(luaL_argerror(L, first + idx, "Mandatory argument expected"));
return 0;
}
/* Check for exceed the number of requiered argument. */
if ((mask & ARGT_MASK) == ARGT_STOP &&
argp[idx].type != ARGT_STOP) {
WILL_LJMP(luaL_argerror(L, first + idx, "Last argument expected"));
}
if ((mask & ARGT_MASK) == ARGT_STOP &&
argp[idx].type == ARGT_STOP) {
return 0;
}
/* Compatibility mask. */
switch (argp[idx].type) {
case ARGT_SINT:
switch (mask & ARGT_MASK) {
case ARGT_UINT: argp[idx].type = mask & ARGT_MASK; break;
case ARGT_TIME: argp[idx].type = mask & ARGT_MASK; break;
case ARGT_SIZE: argp[idx].type = mask & ARGT_MASK; break;
}
break;
}
/* Check for type of argument. */
if ((mask & ARGT_MASK) != argp[idx].type) {
const char *msg = lua_pushfstring(L, "'%s' expected, got '%s'",
arg_type_names[(mask & ARGT_MASK)],
arg_type_names[argp[idx].type & ARGT_MASK]);
WILL_LJMP(luaL_argerror(L, first + idx, msg));
}
/* Next argument. */
mask >>= ARGT_BITS;
idx++;
}
}
/*
* The following functions are used to make correspondance between the the
* executed lua pointer and the "struct hlua *" that contain the context.
* They run with the tree head "hlua_ctx", they just perform lookup in the
* tree.
*
* - hlua_gethlua : return the hlua context associated with an lua_State.
* - hlua_delhlua : remove the association between hlua context and lua_state.
* - hlua_sethlua : create the association between hlua context and lua_state.
*/
static inline struct hlua *hlua_gethlua(lua_State *L)
{
struct ebpt_node *node;
node = ebpt_lookup(&hlua_ctx, L);
if (!node)
return NULL;
return ebpt_entry(node, struct hlua, node);
}
static inline void hlua_delhlua(struct hlua *hlua)
{
if (hlua->node.key)
ebpt_delete(&hlua->node);
}
static inline void hlua_sethlua(struct hlua *hlua)
{
hlua->node.key = hlua->T;
ebpt_insert(&hlua_ctx, &hlua->node);
}
/* This function initialises the Lua environment stored in the session.
* It must be called at the start of the session. This function creates
* an LUA coroutine. It can not be use to crete the main LUA context.
*/
int hlua_ctx_init(struct hlua *lua, struct task *task)
{
lua->Mref = LUA_REFNIL;
lua->state = HLUA_STOP;
LIST_INIT(&lua->com);
lua->T = lua_newthread(gL.T);
if (!lua->T) {
lua->Tref = LUA_REFNIL;
return 0;
}
hlua_sethlua(lua);
lua->Tref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
lua->task = task;
return 1;
}
/* Used to destroy the Lua coroutine when the attached session or task
* is destroyed. The destroy also the memory context. The struct "lua"
* is not freed.
*/
void hlua_ctx_destroy(struct hlua *lua)
{
/* Remove context. */
hlua_delhlua(lua);
/* Purge all the pending signals. */
hlua_com_purge(lua);
/* The thread is garbage collected by Lua. */
luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
luaL_unref(gL.T, LUA_REGISTRYINDEX, lua->Tref);
}
/* This function is used to restore the Lua context when a coroutine
* fails. This function copy the common memory between old coroutine
* and the new coroutine. The old coroutine is destroyed, and its
* replaced by the new coroutine.
* If the flag "keep_msg" is set, the last entry of the old is assumed
* as string error message and it is copied in the new stack.
*/
static int hlua_ctx_renew(struct hlua *lua, int keep_msg)
{
lua_State *T;
int new_ref;
/* Renew the main LUA stack doesn't have sense. */
if (lua == &gL)
return 0;
/* Remove context. */
hlua_delhlua(lua);
/* New Lua coroutine. */
T = lua_newthread(gL.T);
if (!T)
return 0;
/* Copy last error message. */
if (keep_msg)
lua_xmove(lua->T, T, 1);
/* Copy data between the coroutines. */
lua_rawgeti(lua->T, LUA_REGISTRYINDEX, lua->Mref);
lua_xmove(lua->T, T, 1);
new_ref = luaL_ref(T, LUA_REGISTRYINDEX); /* Valur poped. */
/* Destroy old data. */
luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
/* The thread is garbage collected by Lua. */
luaL_unref(gL.T, LUA_REGISTRYINDEX, lua->Tref);
/* Fill the struct with the new coroutine values. */
lua->Mref = new_ref;
lua->T = T;
lua->Tref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
/* Set context. */
hlua_sethlua(lua);
return 1;
}
/* This function start or resumes the Lua stack execution. If the flag
* "yield_allowed" if no set and the LUA stack execution returns a yield
* The function return an error.
*
* The function can returns 4 values:
* - HLUA_E_OK : The execution is terminated without any errors.
* - HLUA_E_AGAIN : The execution must continue at the next associated
* task wakeup.
* - HLUA_E_ERRMSG : An error has occured, an error message is set in
* the top of the stack.
* - HLUA_E_ERR : An error has occured without error message.
*
* If an error occured, the stack is renewed and it is ready to run new
* LUA code.
*/
static enum hlua_exec hlua_ctx_resume(struct hlua *lua, int yield_allowed)
{
int ret;
const char *msg;
lua->state = HLUA_RUN;
/* Call the function. */
ret = lua_resume(lua->T, gL.T, lua->nargs);
switch (ret) {
case LUA_OK:
ret = HLUA_E_OK;
break;
case LUA_YIELD:
if (!yield_allowed) {
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "yield not allowed");
ret = HLUA_E_ERRMSG;
break;
}
ret = HLUA_E_AGAIN;
break;
case LUA_ERRRUN:
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
msg = lua_tostring(lua->T, -1);
lua_settop(lua->T, 0); /* Empty the stack. */
lua_pop(lua->T, 1);
if (msg)
lua_pushfstring(lua->T, "runtime error: %s", msg);
else
lua_pushfstring(lua->T, "unknown runtime error");
ret = HLUA_E_ERRMSG;
break;
case LUA_ERRMEM:
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "out of memory error");
ret = HLUA_E_ERRMSG;
break;
case LUA_ERRERR:
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
msg = lua_tostring(lua->T, -1);
lua_settop(lua->T, 0); /* Empty the stack. */
lua_pop(lua->T, 1);
if (msg)
lua_pushfstring(lua->T, "message handler error: %s", msg);
else
lua_pushfstring(lua->T, "message handler error");
ret = HLUA_E_ERRMSG;
break;
default:
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "unknonwn error");
ret = HLUA_E_ERRMSG;
break;
}
switch (ret) {
case HLUA_E_AGAIN:
break;
case HLUA_E_ERRMSG:
hlua_com_purge(lua);
hlua_ctx_renew(lua, 1);
lua->state = HLUA_STOP;
break;
case HLUA_E_ERR:
lua->state = HLUA_STOP;
hlua_com_purge(lua);
hlua_ctx_renew(lua, 0);
break;
case HLUA_E_OK:
lua->state = HLUA_STOP;
hlua_com_purge(lua);
break;
}
return ret;
}
/* A class is a lot of memory that contain data. This data can be a table,
* an integer or user data. This data is associated with a metatable. This
* metatable have an original version registred in the global context with
* the name of the object (_G[<name>] = <metable> ).
*
* A metable is a table that modify the standard behavior of a standard
* access to the associated data. The entries of this new metatable are
* defined as is:
*
* http://lua-users.org/wiki/MetatableEvents
*
* __index
*
* we access an absent field in a table, the result is nil. This is
* true, but it is not the whole truth. Actually, such access triggers
* the interpreter to look for an __index metamethod: If there is no
* such method, as usually happens, then the access results in nil;
* otherwise, the metamethod will provide the result.
*
* Control 'prototype' inheritance. When accessing "myTable[key]" and
* the key does not appear in the table, but the metatable has an __index
* property:
*
* - if the value is a function, the function is called, passing in the
* table and the key; the return value of that function is returned as
* the result.
*
* - if the value is another table, the value of the key in that table is
* asked for and returned (and if it doesn't exist in that table, but that
* table's metatable has an __index property, then it continues on up)
*
* - Use "rawget(myTable,key)" to skip this metamethod.
*
* http://www.lua.org/pil/13.4.1.html
*
* __newindex
*
* Like __index, but control property assignment.
*
* __mode - Control weak references. A string value with one or both
* of the characters 'k' and 'v' which specifies that the the
* keys and/or values in the table are weak references.
*
* __call - Treat a table like a function. When a table is followed by
* parenthesis such as "myTable( 'foo' )" and the metatable has
* a __call key pointing to a function, that function is invoked
* (passing any specified arguments) and the return value is
* returned.
*
* __metatable - Hide the metatable. When "getmetatable( myTable )" is
* called, if the metatable for myTable has a __metatable
* key, the value of that key is returned instead of the
* actual metatable.
*
* __tostring - Control string representation. When the builtin
* "tostring( myTable )" function is called, if the metatable
* for myTable has a __tostring property set to a function,
* that function is invoked (passing myTable to it) and the
* return value is used as the string representation.
*
* __len - Control table length. When the table length is requested using
* the length operator ( '#' ), if the metatable for myTable has
* a __len key pointing to a function, that function is invoked
* (passing myTable to it) and the return value used as the value
* of "#myTable".
*
* __gc - Userdata finalizer code. When userdata is set to be garbage
* collected, if the metatable has a __gc field pointing to a
* function, that function is first invoked, passing the userdata
* to it. The __gc metamethod is not called for tables.
* (See http://lua-users.org/lists/lua-l/2006-11/msg00508.html)
*
* Special metamethods for redefining standard operators:
* http://www.lua.org/pil/13.1.html
*
* __add "+"
* __sub "-"
* __mul "*"
* __div "/"
* __unm "!"
* __pow "^"
* __concat ".."
*
* Special methods for redfining standar relations
* http://www.lua.org/pil/13.2.html
*
* __eq "=="
* __lt "<"
* __le "<="
*/
/*
*
*
* Class TXN
*
*
*/
/* Returns a struct hlua_session if the stack entry "ud" is
* a class session, otherwise it throws an error.
*/
__LJMP static struct hlua_txn *hlua_checktxn(lua_State *L, int ud)
{
return (struct hlua_txn *)MAY_LJMP(hlua_checkudata(L, ud, class_txn_ref));
}
/* Create stack entry containing a class TXN. This function
* return 0 if the stack does not contains free slots,
* otherwise it returns 1.
*/
static int hlua_txn_new(lua_State *L, struct session *s, struct proxy *p, void *l7)
{
struct hlua_txn *hs;
/* Check stack size. */
if (!lua_checkstack(L, 2))
return 0;
/* NOTE: The allocation never fails. The failure
* throw an error, and the function never returns.
* if the throw is not avalaible, the process is aborted.
*/
hs = lua_newuserdata(L, sizeof(struct hlua_txn));
hs->s = s;
hs->p = p;
hs->l7 = l7;
/* Pop a class sesison metatable and affect it to the userdata. */
lua_rawgeti(L, LUA_REGISTRYINDEX, class_txn_ref);
lua_setmetatable(L, -2);
return 1;
}
/* This function is used as a calback of a task. It is called by the
* HAProxy task subsystem when the task is awaked. The LUA runtime can
* return an E_AGAIN signal, the emmiter of this signal must set a
* signal to wake the task.
*/
static struct task *hlua_process_task(struct task *task)
{
struct hlua *hlua = task->context;
enum hlua_exec status;
/* We need to remove the task from the wait queue before executing
* the Lua code because we don't know if it needs to wait for
* another timer or not in the case of E_AGAIN.
*/
task_delete(task);
/* Execute the Lua code. */
status = hlua_ctx_resume(hlua, 1);
switch (status) {
/* finished or yield */
case HLUA_E_OK:
hlua_ctx_destroy(hlua);
task_delete(task);
task_free(task);
break;
case HLUA_E_AGAIN: /* co process wake me later. */
break;
/* finished with error. */
case HLUA_E_ERRMSG:
send_log(NULL, LOG_ERR, "Lua task: %s.", lua_tostring(hlua->T, -1));
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua task: %s.\n", lua_tostring(hlua->T, -1));
hlua_ctx_destroy(hlua);
task_delete(task);
task_free(task);
break;
case HLUA_E_ERR:
default:
send_log(NULL, LOG_ERR, "Lua task: unknown error.");
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua task: unknown error.\n");
hlua_ctx_destroy(hlua);
task_delete(task);
task_free(task);
break;
}
return NULL;
}
/* This function is an LUA binding that register LUA function to be
* executed after the HAProxy configuration parsing and before the
* HAProxy scheduler starts. This function expect only one LUA
* argument that is a function. This function returns nothing, but
* throws if an error is encountered.
*/
__LJMP static int hlua_register_init(lua_State *L)
{
struct hlua_init_function *init;
int ref;
MAY_LJMP(check_args(L, 1, "register_init"));
ref = MAY_LJMP(hlua_checkfunction(L, 1));
init = malloc(sizeof(*init));
if (!init)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
init->function_ref = ref;
LIST_ADDQ(&hlua_init_functions, &init->l);
return 0;
}
/* This functio is an LUA binding. It permits to register a task
* executed in parallel of the main HAroxy activity. The task is
* created and it is set in the HAProxy scheduler. It can be called
* from the "init" section, "post init" or during the runtime.
*
* Lua prototype:
*
* <none> core.register_task(<function>)
*/
static int hlua_register_task(lua_State *L)
{
struct hlua *hlua;
struct task *task;
int ref;
MAY_LJMP(check_args(L, 1, "register_task"));
ref = MAY_LJMP(hlua_checkfunction(L, 1));
hlua = malloc(sizeof(*hlua));
if (!hlua)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
task = task_new();
task->context = hlua;
task->process = hlua_process_task;
if (!hlua_ctx_init(hlua, task))
WILL_LJMP(luaL_error(L, "lua out of memory error."));
/* Restore the function in the stack. */
lua_rawgeti(hlua->T, LUA_REGISTRYINDEX, ref);
hlua->nargs = 0;
/* Schedule task. */
task_schedule(task, now_ms);
return 0;
}
/* This function is called by the main configuration key "lua-load". It loads and
* execute an lua file during the parsing of the HAProxy configuration file. It is
* the main lua entry point.
*
* This funtion runs with the HAProxy keywords API. It returns -1 if an error is
* occured, otherwise it returns 0.
*
* In some error case, LUA set an error message in top of the stack. This function
* returns this error message in the HAProxy logs and pop it from the stack.
*/
static int hlua_load(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
int error;
/* Just load and compile the file. */
error = luaL_loadfile(gL.T, args[1]);
if (error) {
memprintf(err, "error in lua file '%s': %s", args[1], lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
}
/* If no syntax error where detected, execute the code. */
error = lua_pcall(gL.T, 0, LUA_MULTRET, 0);
switch (error) {
case LUA_OK:
break;
case LUA_ERRRUN:
memprintf(err, "lua runtime error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
case LUA_ERRMEM:
memprintf(err, "lua out of memory error\n");
return -1;
case LUA_ERRERR:
memprintf(err, "lua message handler error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
case LUA_ERRGCMM:
memprintf(err, "lua garbage collector error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
default:
memprintf(err, "lua unknonwn error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
}
return 0;
}
/* configuration keywords declaration */
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "lua-load", hlua_load },
{ 0, NULL, NULL },
}};
int hlua_post_init()
{
struct hlua_init_function *init;
const char *msg;
enum hlua_exec ret;
list_for_each_entry(init, &hlua_init_functions, l) {
lua_rawgeti(gL.T, LUA_REGISTRYINDEX, init->function_ref);
ret = hlua_ctx_resume(&gL, 0);
switch (ret) {
case HLUA_E_OK:
lua_pop(gL.T, -1);
return 1;
case HLUA_E_AGAIN:
Alert("lua init: yield not allowed.\n");
return 0;
case HLUA_E_ERRMSG:
msg = lua_tostring(gL.T, -1);
Alert("lua init: %s.\n", msg);
return 0;
case HLUA_E_ERR:
default:
Alert("lua init: unknown runtime error.\n");
return 0;
}
}
return 1;
}
void hlua_init(void)
{
int i;
/* Initialise com signals pool session. */
pool2_hlua_com = create_pool("hlua_com", sizeof(struct hlua_com), MEM_F_SHARED);
/* Register configuration keywords. */
cfg_register_keywords(&cfg_kws);
/* Init main lua stack. */
gL.Mref = LUA_REFNIL;
gL.state = HLUA_STOP;
LIST_INIT(&gL.com);
gL.T = luaL_newstate();
hlua_sethlua(&gL);
gL.Tref = LUA_REFNIL;
gL.task = NULL;
/* Initialise lua. */
luaL_openlibs(gL.T);
/*
*
* Create "core" object.
*
*/
/* This integer entry is just used as base value for the object "core". */
lua_pushinteger(gL.T, 0);
/* Create and fill the metatable. */
lua_newtable(gL.T);
/* Create and fill the __index entry. */
lua_pushstring(gL.T, "__index");
lua_newtable(gL.T);
/* Push the loglevel constants. */
for (i=0; i<NB_LOG_LEVELS; i++)
hlua_class_const_int(gL.T, log_levels[i], i);
/* Register special functions. */
hlua_class_function(gL.T, "register_init", hlua_register_init);
hlua_class_function(gL.T, "register_task", hlua_register_task);
/* Store the table __index in the metable. */
lua_settable(gL.T, -3);
/* Register previous table in the registry with named entry. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_CORE); /* register class session. */
/* Register previous table in the registry with reference. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
class_core_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
/* Create new object with class Core. */
lua_setmetatable(gL.T, -2);
lua_setglobal(gL.T, "core");
/*
*
* Register class TXN
*
*/
/* Create and fill the metatable. */
lua_newtable(gL.T);
/* Create and fille the __index entry. */
lua_pushstring(gL.T, "__index");
lua_newtable(gL.T);
lua_settable(gL.T, -3);
/* Register previous table in the registry with reference and named entry. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_TXN); /* register class session. */
class_txn_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
}