123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- /*-
- * Copyright 2016 Vsevolod Stakhov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "lua_common.h"
-
- /***
- * @module rspamd_mempool
- * Rspamd memory pool is used to allocate memory attached to specific objects,
- * namely it was initially used for memory allocation for rspamd_task.
- *
- * All memory allocated by the pool is destroyed when the associated object is
- * destroyed. This allows a sort of controlled garbage collection for memory
- * allocated from the pool. Memory pools are extensively used by rspamd internal
- * components and provide some powerful features, such as destructors or
- * persistent variables.
- * @example
- local mempool = require "rspamd_mempool"
- local pool = mempool.create()
-
- pool:set_variable('a', 'bcd', 1, 1.01, false)
- local v1, v2, v3, v4 = pool:get_variable('a', 'string,double,double,bool')
- pool:destroy()
- */
-
- /* Lua bindings */
- /***
- * @function mempool.create([size])
- * Creates a memory pool of a specified `size` or platform dependent optimal size (normally, a page size)
- * @param {number} size size of a page inside pool
- * @return {rspamd_mempool} new pool object (that should be removed by explicit call to `pool:destroy()`)
- */
- LUA_FUNCTION_DEF(mempool, create);
- /***
- * @method mempool:add_destructor(func)
- * Adds new destructor function to the pool
- * @param {function} func function to be called when the pool is destroyed
- */
- LUA_FUNCTION_DEF(mempool, add_destructor);
- /***
- * @method mempool:destroy()
- * Destroys memory pool cleaning all variables and calling all destructors registered (both C and Lua ones)
- */
- LUA_FUNCTION_DEF(mempool, delete);
- LUA_FUNCTION_DEF(mempool, stat);
- LUA_FUNCTION_DEF(mempool, suggest_size);
- /***
- * @method mempool:set_variable(name, [value1[, value2 ...]])
- * Sets a variable that's valid during memory pool lifetime. This function allows
- * to pack multiple values inside a single variable. Currently supported types are:
- *
- * - `string`: packed as null terminated C string (so no `\0` are allowed)
- * - `number`: packed as C double
- * - `boolean`: packed as bool
- * @param {string} name variable's name to set
- */
- LUA_FUNCTION_DEF(mempool, set_variable);
- /***
- * @method mempool:set_bucket(name, num_values, [value1...valuen]|[table])
- * Stores a variable bucket of numbers where the first number is number of elements to pack
- * and then there should be either n numeric values or a plain table of numeric values
- * @param {string} name variable's name to set
- * @param {number} num_values number of variables in the bucket
- * @param {table|list} values values
- */
- LUA_FUNCTION_DEF(mempool, set_bucket);
- /***
- * @method mempool:get_variable(name[, type])
- * Unpacks mempool variable to lua If `type` is not specified, then a variable is
- * assumed to be zero-terminated C string. Otherwise, `type` is a comma separated (spaces are ignored)
- * list of types that should be unpacked from a variable's content. The following types
- * are supported:
- *
- * - `string`: null terminated C string (so no `\0` are allowed)
- * - `double`: returned as lua number
- * - `int`: unpack a single integer
- * - `int64`: unpack 64-bits integer
- * - `boolean`: unpack boolean
- * - `bucket`: bucket of numbers represented as a Lua table
- * - `fstrings`: list of rspamd_fstring_t (GList) represented as a Lua table
- * @param {string} name variable's name to get
- * @param {string} type list of types to be extracted
- * @return {variable list} list of variables extracted (but **not** a table)
- */
- LUA_FUNCTION_DEF(mempool, get_variable);
- /***
- * @method mempool:has_variable(name)
- * Checks if the specified variable `name` exists in the memory pool
- * @param {string} name variable's name to get
- * @return {boolean} `true` if variable exists and `false` otherwise
- */
- LUA_FUNCTION_DEF(mempool, has_variable);
-
- /***
- * @method mempool:delete_variable(name)
- * Removes the specified variable `name` from the memory pool
- * @param {string} name variable's name to remove
- * @return {boolean} `true` if variable exists and has been removed
- */
- LUA_FUNCTION_DEF(mempool, delete_variable);
- /**
- * @method mempool:topointer()
- *
- * Returns raw C pointer (lightuserdata) associated with mempool. This might be
- * broken with luajit and GC64, use with caution.
- */
- LUA_FUNCTION_DEF(mempool, topointer);
-
- static const struct luaL_reg mempoollib_m[] = {
- LUA_INTERFACE_DEF(mempool, add_destructor),
- LUA_INTERFACE_DEF(mempool, stat),
- LUA_INTERFACE_DEF(mempool, suggest_size),
- LUA_INTERFACE_DEF(mempool, set_variable),
- LUA_INTERFACE_DEF(mempool, set_bucket),
- LUA_INTERFACE_DEF(mempool, get_variable),
- LUA_INTERFACE_DEF(mempool, has_variable),
- LUA_INTERFACE_DEF(mempool, delete_variable),
- LUA_INTERFACE_DEF(mempool, topointer),
- LUA_INTERFACE_DEF(mempool, delete),
- {"destroy", lua_mempool_delete},
- {"__tostring", rspamd_lua_class_tostring},
- {NULL, NULL}};
-
- static const struct luaL_reg mempoollib_f[] = {
- LUA_INTERFACE_DEF(mempool, create),
- {NULL, NULL}};
-
- /*
- * Struct for lua destructor
- */
-
- struct lua_mempool_udata {
- lua_State *L;
- gint cbref;
- rspamd_mempool_t *mempool;
- };
-
- struct memory_pool_s *
- rspamd_lua_check_mempool(lua_State *L, gint pos)
- {
- void *ud = rspamd_lua_check_udata(L, pos, rspamd_mempool_classname);
- luaL_argcheck(L, ud != NULL, pos, "'mempool' expected");
- return ud ? *((struct memory_pool_s **) ud) : NULL;
- }
-
-
- static int
- lua_mempool_create(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_mempool_new(
- rspamd_mempool_suggest_size(), "lua", 0),
- **pmempool;
-
- if (mempool) {
- pmempool = lua_newuserdata(L, sizeof(struct memory_pool_s *));
- rspamd_lua_setclass(L, rspamd_mempool_classname, -1);
- *pmempool = mempool;
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- static void
- lua_mempool_destructor_func(gpointer p)
- {
- struct lua_mempool_udata *ud = p;
-
- lua_rawgeti(ud->L, LUA_REGISTRYINDEX, ud->cbref);
- if (lua_pcall(ud->L, 0, 0, 0) != 0) {
- msg_info("call to destructor failed: %s", lua_tostring(ud->L, -1));
- lua_pop(ud->L, 1);
- }
- luaL_unref(ud->L, LUA_REGISTRYINDEX, ud->cbref);
- }
-
- static int
- lua_mempool_add_destructor(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
- struct lua_mempool_udata *ud;
-
- if (mempool) {
- if (lua_isfunction(L, 2)) {
- ud = rspamd_mempool_alloc(mempool,
- sizeof(struct lua_mempool_udata));
- lua_pushvalue(L, 2);
- /* Get a reference */
- ud->cbref = luaL_ref(L, LUA_REGISTRYINDEX);
- ud->L = L;
- ud->mempool = mempool;
- rspamd_mempool_add_destructor(mempool,
- lua_mempool_destructor_func,
- ud);
- }
- else {
- msg_err("trying to add destructor without function");
- }
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- static int
- lua_mempool_delete(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
-
- if (mempool) {
- rspamd_mempool_delete(mempool);
- return 0;
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- static int
- lua_mempool_stat(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
-
- if (mempool) {
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- static int
- lua_mempool_suggest_size(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
-
- if (mempool) {
- lua_pushinteger(L, rspamd_mempool_suggest_size());
- return 0;
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- struct lua_numbers_bucket {
- guint nelts;
- gdouble elts[0];
- };
-
- static int
- lua_mempool_set_bucket(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
- const gchar *var = luaL_checkstring(L, 2);
- struct lua_numbers_bucket *bucket;
- gint nelts = luaL_checknumber(L, 3), i;
-
- if (var && nelts > 0) {
- bucket = rspamd_mempool_alloc(mempool,
- sizeof(*bucket) + sizeof(gdouble) * nelts);
- bucket->nelts = nelts;
-
- if (lua_type(L, 4) == LUA_TTABLE) {
- /* Table version */
- for (i = 1; i <= nelts; i++) {
- lua_rawgeti(L, 4, i);
- bucket->elts[i - 1] = lua_tonumber(L, -1);
- lua_pop(L, 1);
- }
- }
- else {
- for (i = 0; i <= nelts; i++) {
- bucket->elts[i] = lua_tonumber(L, 4 + i);
- }
- }
-
- rspamd_mempool_set_variable(mempool, var, bucket, NULL);
- }
- else {
- return luaL_error(L, "invalid arguments");
- }
-
- return 0;
- }
-
- static int
- lua_mempool_set_variable(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
- const gchar *var = luaL_checkstring(L, 2);
- gpointer value;
- struct lua_numbers_bucket *bucket;
- gchar *vp;
- union {
- gdouble d;
- const gchar *s;
- gboolean b;
- } val;
- gsize slen;
- gint i, j, len = 0, type;
-
- if (mempool && var) {
-
- for (i = 3; i <= lua_gettop(L); i++) {
- type = lua_type(L, i);
-
- if (type == LUA_TNUMBER) {
- /* We have some ambiguity here between integer and double */
- len += sizeof(gdouble);
- }
- else if (type == LUA_TBOOLEAN) {
- len += sizeof(gboolean);
- }
- else if (type == LUA_TSTRING) {
- (void) lua_tolstring(L, i, &slen);
- len += slen + 1;
- }
- else if (type == LUA_TTABLE) {
- /* We assume it as a bucket of numbers so far */
- slen = rspamd_lua_table_size(L, i);
- len += sizeof(gdouble) * slen + sizeof(*bucket);
- }
- else {
- msg_err("cannot handle lua type %s", lua_typename(L, type));
- }
- }
-
- if (len == 0) {
- msg_err("no values specified");
- }
- else {
- value = rspamd_mempool_alloc(mempool, len);
- vp = value;
-
- for (i = 3; i <= lua_gettop(L); i++) {
- type = lua_type(L, i);
-
- if (type == LUA_TNUMBER) {
- val.d = lua_tonumber(L, i);
- memcpy(vp, &val, sizeof(gdouble));
- vp += sizeof(gdouble);
- }
- else if (type == LUA_TBOOLEAN) {
- val.b = lua_toboolean(L, i);
- memcpy(vp, &val, sizeof(gboolean));
- vp += sizeof(gboolean);
- }
- else if (type == LUA_TSTRING) {
- val.s = lua_tolstring(L, i, &slen);
- memcpy(vp, val.s, slen + 1);
- vp += slen + 1;
- }
- else if (type == LUA_TTABLE) {
- slen = rspamd_lua_table_size(L, i);
- /* XXX: Ret, ret, ret: alignment issues */
- bucket = (struct lua_numbers_bucket *) vp;
- bucket->nelts = slen;
-
- for (j = 0; j < slen; j++) {
- lua_rawgeti(L, i, j + 1);
- bucket->elts[j] = lua_tonumber(L, -1);
- lua_pop(L, 1);
- }
-
- vp += sizeof(gdouble) * slen + sizeof(*bucket);
- }
- else {
- msg_err("cannot handle lua type %s", lua_typename(L, type));
- }
- }
-
- rspamd_mempool_set_variable(mempool, var, value, NULL);
- }
-
- return 0;
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
-
- static int
- lua_mempool_get_variable(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
- const gchar *var = luaL_checkstring(L, 2);
- const gchar *type = NULL, *pt;
- struct lua_numbers_bucket bucket;
- const gchar *value, *pv;
- guint len, nvar, slen, i;
-
- if (mempool && var) {
- value = rspamd_mempool_get_variable(mempool, var);
-
- if (lua_gettop(L) >= 3) {
- type = luaL_checkstring(L, 3);
- }
-
- if (value) {
-
- if (type) {
- pt = type;
- pv = value;
- nvar = 0;
-
- while ((len = strcspn(pt, ", ")) > 0) {
- if (len == sizeof("double") - 1 &&
- g_ascii_strncasecmp(pt, "double", len) == 0) {
- gdouble num;
- memcpy(&num, pv, sizeof(gdouble));
- lua_pushnumber(L, num);
- pv += sizeof(gdouble);
- }
- else if (len == sizeof("int") - 1 &&
- g_ascii_strncasecmp(pt, "int", len) == 0) {
- gint num;
- memcpy(&num, pv, sizeof(gint));
- lua_pushinteger(L, num);
- pv += sizeof(gint);
- }
- else if (len == sizeof("int64") - 1 &&
- g_ascii_strncasecmp(pt, "int64", len) == 0) {
- gint64 num;
- memcpy(&num, pv, sizeof(gint64));
- lua_pushinteger(L, num);
- pv += sizeof(gint64);
- }
- else if (len == sizeof("bool") - 1 &&
- g_ascii_strncasecmp(pt, "bool", len) == 0) {
- gboolean num;
- memcpy(&num, pv, sizeof(gboolean));
- lua_pushboolean(L, num);
- pv += sizeof(gboolean);
- }
- else if (len == sizeof("string") - 1 &&
- g_ascii_strncasecmp(pt, "string", len) == 0) {
- slen = strlen((const gchar *) pv);
- lua_pushlstring(L, (const gchar *) pv, slen);
- pv += slen + 1;
- }
- else if (len == sizeof("gstring") - 1 &&
- g_ascii_strncasecmp(pt, "gstring", len) == 0) {
- GString *st = (GString *) pv;
- lua_pushlstring(L, st->str, st->len);
- pv += sizeof(GString *);
- }
- else if (len == sizeof("bucket") - 1 &&
- g_ascii_strncasecmp(pt, "bucket", len) == 0) {
- memcpy(&bucket, pv, sizeof(bucket));
- lua_createtable(L, bucket.nelts, 0);
- pv += sizeof(struct lua_numbers_bucket);
-
- for (i = 0; i < bucket.nelts; i++) {
- gdouble num;
- memcpy(&num, pv, sizeof(num));
- lua_pushnumber(L, num);
- lua_rawseti(L, -2, i + 1);
- pv += sizeof(num);
- }
- }
- else if (len == sizeof("fstrings") - 1 &&
- g_ascii_strncasecmp(pt, "fstrings", len) == 0) {
- GList *cur;
- rspamd_fstring_t *fstr;
-
- cur = (GList *) pv;
- lua_newtable(L);
-
- i = 1;
- while (cur != NULL) {
- fstr = cur->data;
- lua_pushlstring(L, fstr->str, fstr->len);
- lua_rawseti(L, -2, i);
- i++;
- cur = g_list_next(cur);
- }
-
- pv += sizeof(GList *);
- }
- else {
- msg_err("unknown type for get_variable: %s", pt);
- lua_pushnil(L);
- }
-
- pt += len;
- pt += strspn(pt, ", ");
-
- nvar++;
- }
-
- return nvar;
- }
- else {
- /* No type specified, return string */
- lua_pushstring(L, value);
- }
- }
- else {
- lua_pushnil(L);
- }
- }
- else {
- lua_pushnil(L);
- }
-
- return 1;
- }
-
- static int
- lua_mempool_has_variable(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
- const gchar *var = luaL_checkstring(L, 2);
- gboolean ret = FALSE;
-
- if (mempool && var) {
- if (rspamd_mempool_get_variable(mempool, var) != NULL) {
- ret = TRUE;
- }
- }
-
- lua_pushboolean(L, ret);
-
- return 1;
- }
-
- static int
- lua_mempool_delete_variable(lua_State *L)
- {
- LUA_TRACE_POINT;
- struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
- const gchar *var = luaL_checkstring(L, 2);
- gboolean ret = FALSE;
-
- if (mempool && var) {
- if (rspamd_mempool_get_variable(mempool, var) != NULL) {
- ret = TRUE;
-
- rspamd_mempool_remove_variable(mempool, var);
- }
- }
-
- lua_pushboolean(L, ret);
-
- return 1;
- }
-
- static gint
- lua_mempool_topointer(lua_State *L)
- {
- LUA_TRACE_POINT;
- rspamd_mempool_t *pool = rspamd_lua_check_mempool(L, 1);
-
- if (pool) {
- /* XXX: this might cause issues on arm64 and LuaJIT */
- lua_pushlightuserdata(L, pool);
- }
- else {
- return luaL_error(L, "invalid arguments");
- }
-
- return 1;
- }
-
- static gint
- lua_load_mempool(lua_State *L)
- {
- lua_newtable(L);
- luaL_register(L, NULL, mempoollib_f);
-
- return 1;
- }
-
- void luaopen_mempool(lua_State *L)
- {
- rspamd_lua_new_class(L, rspamd_mempool_classname, mempoollib_m);
- lua_pop(L, 1);
- rspamd_lua_add_preload(L, "rspamd_mempool", lua_load_mempool);
- }
|