Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

lua_mempool.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "lua_common.h"
  17. /***
  18. * @module rspamd_mempool
  19. * Rspamd memory pool is used to allocate memory attached to specific objects,
  20. * namely it was initially used for memory allocation for rspamd_task.
  21. *
  22. * All memory allocated by the pool is destroyed when the associated object is
  23. * destroyed. This allows a sort of controlled garbage collection for memory
  24. * allocated from the pool. Memory pools are extensively used by rspamd internal
  25. * components and provide some powerful features, such as destructors or
  26. * persistent variables.
  27. * @example
  28. local mempool = require "rspamd_mempool"
  29. local pool = mempool.create()
  30. pool:set_variable('a', 'bcd', 1, 1.01, false)
  31. local v1, v2, v3, v4 = pool:get_variable('a', 'string,double,double,bool')
  32. pool:destroy()
  33. */
  34. /* Lua bindings */
  35. /***
  36. * @function mempool.create([size])
  37. * Creates a memory pool of a specified `size` or platform dependent optimal size (normally, a page size)
  38. * @param {number} size size of a page inside pool
  39. * @return {rspamd_mempool} new pool object (that should be removed by explicit call to `pool:destroy()`)
  40. */
  41. LUA_FUNCTION_DEF(mempool, create);
  42. /***
  43. * @method mempool:add_destructor(func)
  44. * Adds new destructor function to the pool
  45. * @param {function} func function to be called when the pool is destroyed
  46. */
  47. LUA_FUNCTION_DEF(mempool, add_destructor);
  48. /***
  49. * @method mempool:destroy()
  50. * Destroys memory pool cleaning all variables and calling all destructors registered (both C and Lua ones)
  51. */
  52. LUA_FUNCTION_DEF(mempool, delete);
  53. LUA_FUNCTION_DEF(mempool, stat);
  54. LUA_FUNCTION_DEF(mempool, suggest_size);
  55. /***
  56. * @method mempool:set_variable(name, [value1[, value2 ...]])
  57. * Sets a variable that's valid during memory pool lifetime. This function allows
  58. * to pack multiple values inside a single variable. Currently supported types are:
  59. *
  60. * - `string`: packed as null terminated C string (so no `\0` are allowed)
  61. * - `number`: packed as C double
  62. * - `boolean`: packed as bool
  63. * @param {string} name variable's name to set
  64. */
  65. LUA_FUNCTION_DEF(mempool, set_variable);
  66. /***
  67. * @method mempool:set_bucket(name, num_values, [value1...valuen]|[table])
  68. * Stores a variable bucket of numbers where the first number is number of elements to pack
  69. * and then there should be either n numeric values or a plain table of numeric values
  70. * @param {string} name variable's name to set
  71. * @param {number} num_values number of variables in the bucket
  72. * @param {table|list} values values
  73. */
  74. LUA_FUNCTION_DEF(mempool, set_bucket);
  75. /***
  76. * @method mempool:get_variable(name[, type])
  77. * Unpacks mempool variable to lua If `type` is not specified, then a variable is
  78. * assumed to be zero-terminated C string. Otherwise, `type` is a comma separated (spaces are ignored)
  79. * list of types that should be unpacked from a variable's content. The following types
  80. * are supported:
  81. *
  82. * - `string`: null terminated C string (so no `\0` are allowed)
  83. * - `double`: returned as lua number
  84. * - `int`: unpack a single integer
  85. * - `int64`: unpack 64-bits integer
  86. * - `boolean`: unpack boolean
  87. * - `bucket`: bucket of numbers represented as a Lua table
  88. * - `fstrings`: list of rspamd_fstring_t (GList) represented as a Lua table
  89. * @param {string} name variable's name to get
  90. * @param {string} type list of types to be extracted
  91. * @return {variable list} list of variables extracted (but **not** a table)
  92. */
  93. LUA_FUNCTION_DEF(mempool, get_variable);
  94. /***
  95. * @method mempool:has_variable(name)
  96. * Checks if the specified variable `name` exists in the memory pool
  97. * @param {string} name variable's name to get
  98. * @return {boolean} `true` if variable exists and `false` otherwise
  99. */
  100. LUA_FUNCTION_DEF(mempool, has_variable);
  101. /***
  102. * @method mempool:delete_variable(name)
  103. * Removes the specified variable `name` from the memory pool
  104. * @param {string} name variable's name to remove
  105. * @return {boolean} `true` if variable exists and has been removed
  106. */
  107. LUA_FUNCTION_DEF(mempool, delete_variable);
  108. /**
  109. * @method mempool:topointer()
  110. *
  111. * Returns raw C pointer (lightuserdata) associated with mempool. This might be
  112. * broken with luajit and GC64, use with caution.
  113. */
  114. LUA_FUNCTION_DEF(mempool, topointer);
  115. static const struct luaL_reg mempoollib_m[] = {
  116. LUA_INTERFACE_DEF(mempool, add_destructor),
  117. LUA_INTERFACE_DEF(mempool, stat),
  118. LUA_INTERFACE_DEF(mempool, suggest_size),
  119. LUA_INTERFACE_DEF(mempool, set_variable),
  120. LUA_INTERFACE_DEF(mempool, set_bucket),
  121. LUA_INTERFACE_DEF(mempool, get_variable),
  122. LUA_INTERFACE_DEF(mempool, has_variable),
  123. LUA_INTERFACE_DEF(mempool, delete_variable),
  124. LUA_INTERFACE_DEF(mempool, topointer),
  125. LUA_INTERFACE_DEF(mempool, delete),
  126. {"destroy", lua_mempool_delete},
  127. {"__tostring", rspamd_lua_class_tostring},
  128. {NULL, NULL}};
  129. static const struct luaL_reg mempoollib_f[] = {
  130. LUA_INTERFACE_DEF(mempool, create),
  131. {NULL, NULL}};
  132. /*
  133. * Struct for lua destructor
  134. */
  135. struct lua_mempool_udata {
  136. lua_State *L;
  137. gint cbref;
  138. rspamd_mempool_t *mempool;
  139. };
  140. struct memory_pool_s *
  141. rspamd_lua_check_mempool(lua_State *L, gint pos)
  142. {
  143. void *ud = rspamd_lua_check_udata(L, pos, rspamd_mempool_classname);
  144. luaL_argcheck(L, ud != NULL, pos, "'mempool' expected");
  145. return ud ? *((struct memory_pool_s **) ud) : NULL;
  146. }
  147. static int
  148. lua_mempool_create(lua_State *L)
  149. {
  150. LUA_TRACE_POINT;
  151. struct memory_pool_s *mempool = rspamd_mempool_new(
  152. rspamd_mempool_suggest_size(), "lua", 0),
  153. **pmempool;
  154. if (mempool) {
  155. pmempool = lua_newuserdata(L, sizeof(struct memory_pool_s *));
  156. rspamd_lua_setclass(L, rspamd_mempool_classname, -1);
  157. *pmempool = mempool;
  158. }
  159. else {
  160. lua_pushnil(L);
  161. }
  162. return 1;
  163. }
  164. static void
  165. lua_mempool_destructor_func(gpointer p)
  166. {
  167. struct lua_mempool_udata *ud = p;
  168. lua_rawgeti(ud->L, LUA_REGISTRYINDEX, ud->cbref);
  169. if (lua_pcall(ud->L, 0, 0, 0) != 0) {
  170. msg_info("call to destructor failed: %s", lua_tostring(ud->L, -1));
  171. lua_pop(ud->L, 1);
  172. }
  173. luaL_unref(ud->L, LUA_REGISTRYINDEX, ud->cbref);
  174. }
  175. static int
  176. lua_mempool_add_destructor(lua_State *L)
  177. {
  178. LUA_TRACE_POINT;
  179. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  180. struct lua_mempool_udata *ud;
  181. if (mempool) {
  182. if (lua_isfunction(L, 2)) {
  183. ud = rspamd_mempool_alloc(mempool,
  184. sizeof(struct lua_mempool_udata));
  185. lua_pushvalue(L, 2);
  186. /* Get a reference */
  187. ud->cbref = luaL_ref(L, LUA_REGISTRYINDEX);
  188. ud->L = L;
  189. ud->mempool = mempool;
  190. rspamd_mempool_add_destructor(mempool,
  191. lua_mempool_destructor_func,
  192. ud);
  193. }
  194. else {
  195. msg_err("trying to add destructor without function");
  196. }
  197. }
  198. else {
  199. lua_pushnil(L);
  200. }
  201. return 1;
  202. }
  203. static int
  204. lua_mempool_delete(lua_State *L)
  205. {
  206. LUA_TRACE_POINT;
  207. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  208. if (mempool) {
  209. rspamd_mempool_delete(mempool);
  210. return 0;
  211. }
  212. else {
  213. lua_pushnil(L);
  214. }
  215. return 1;
  216. }
  217. static int
  218. lua_mempool_stat(lua_State *L)
  219. {
  220. LUA_TRACE_POINT;
  221. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  222. if (mempool) {
  223. }
  224. else {
  225. lua_pushnil(L);
  226. }
  227. return 1;
  228. }
  229. static int
  230. lua_mempool_suggest_size(lua_State *L)
  231. {
  232. LUA_TRACE_POINT;
  233. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  234. if (mempool) {
  235. lua_pushinteger(L, rspamd_mempool_suggest_size());
  236. return 0;
  237. }
  238. else {
  239. lua_pushnil(L);
  240. }
  241. return 1;
  242. }
  243. struct lua_numbers_bucket {
  244. guint nelts;
  245. gdouble elts[0];
  246. };
  247. static int
  248. lua_mempool_set_bucket(lua_State *L)
  249. {
  250. LUA_TRACE_POINT;
  251. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  252. const gchar *var = luaL_checkstring(L, 2);
  253. struct lua_numbers_bucket *bucket;
  254. gint nelts = luaL_checknumber(L, 3), i;
  255. if (var && nelts > 0) {
  256. bucket = rspamd_mempool_alloc(mempool,
  257. sizeof(*bucket) + sizeof(gdouble) * nelts);
  258. bucket->nelts = nelts;
  259. if (lua_type(L, 4) == LUA_TTABLE) {
  260. /* Table version */
  261. for (i = 1; i <= nelts; i++) {
  262. lua_rawgeti(L, 4, i);
  263. bucket->elts[i - 1] = lua_tonumber(L, -1);
  264. lua_pop(L, 1);
  265. }
  266. }
  267. else {
  268. for (i = 0; i <= nelts; i++) {
  269. bucket->elts[i] = lua_tonumber(L, 4 + i);
  270. }
  271. }
  272. rspamd_mempool_set_variable(mempool, var, bucket, NULL);
  273. }
  274. else {
  275. return luaL_error(L, "invalid arguments");
  276. }
  277. return 0;
  278. }
  279. static int
  280. lua_mempool_set_variable(lua_State *L)
  281. {
  282. LUA_TRACE_POINT;
  283. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  284. const gchar *var = luaL_checkstring(L, 2);
  285. gpointer value;
  286. struct lua_numbers_bucket *bucket;
  287. gchar *vp;
  288. union {
  289. gdouble d;
  290. const gchar *s;
  291. gboolean b;
  292. } val;
  293. gsize slen;
  294. gint i, j, len = 0, type;
  295. if (mempool && var) {
  296. for (i = 3; i <= lua_gettop(L); i++) {
  297. type = lua_type(L, i);
  298. if (type == LUA_TNUMBER) {
  299. /* We have some ambiguity here between integer and double */
  300. len += sizeof(gdouble);
  301. }
  302. else if (type == LUA_TBOOLEAN) {
  303. len += sizeof(gboolean);
  304. }
  305. else if (type == LUA_TSTRING) {
  306. (void) lua_tolstring(L, i, &slen);
  307. len += slen + 1;
  308. }
  309. else if (type == LUA_TTABLE) {
  310. /* We assume it as a bucket of numbers so far */
  311. slen = rspamd_lua_table_size(L, i);
  312. len += sizeof(gdouble) * slen + sizeof(*bucket);
  313. }
  314. else {
  315. msg_err("cannot handle lua type %s", lua_typename(L, type));
  316. }
  317. }
  318. if (len == 0) {
  319. msg_err("no values specified");
  320. }
  321. else {
  322. value = rspamd_mempool_alloc(mempool, len);
  323. vp = value;
  324. for (i = 3; i <= lua_gettop(L); i++) {
  325. type = lua_type(L, i);
  326. if (type == LUA_TNUMBER) {
  327. val.d = lua_tonumber(L, i);
  328. memcpy(vp, &val, sizeof(gdouble));
  329. vp += sizeof(gdouble);
  330. }
  331. else if (type == LUA_TBOOLEAN) {
  332. val.b = lua_toboolean(L, i);
  333. memcpy(vp, &val, sizeof(gboolean));
  334. vp += sizeof(gboolean);
  335. }
  336. else if (type == LUA_TSTRING) {
  337. val.s = lua_tolstring(L, i, &slen);
  338. memcpy(vp, val.s, slen + 1);
  339. vp += slen + 1;
  340. }
  341. else if (type == LUA_TTABLE) {
  342. slen = rspamd_lua_table_size(L, i);
  343. /* XXX: Ret, ret, ret: alignment issues */
  344. bucket = (struct lua_numbers_bucket *) vp;
  345. bucket->nelts = slen;
  346. for (j = 0; j < slen; j++) {
  347. lua_rawgeti(L, i, j + 1);
  348. bucket->elts[j] = lua_tonumber(L, -1);
  349. lua_pop(L, 1);
  350. }
  351. vp += sizeof(gdouble) * slen + sizeof(*bucket);
  352. }
  353. else {
  354. msg_err("cannot handle lua type %s", lua_typename(L, type));
  355. }
  356. }
  357. rspamd_mempool_set_variable(mempool, var, value, NULL);
  358. }
  359. return 0;
  360. }
  361. else {
  362. lua_pushnil(L);
  363. }
  364. return 1;
  365. }
  366. static int
  367. lua_mempool_get_variable(lua_State *L)
  368. {
  369. LUA_TRACE_POINT;
  370. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  371. const gchar *var = luaL_checkstring(L, 2);
  372. const gchar *type = NULL, *pt;
  373. struct lua_numbers_bucket bucket;
  374. const gchar *value, *pv;
  375. guint len, nvar, slen, i;
  376. if (mempool && var) {
  377. value = rspamd_mempool_get_variable(mempool, var);
  378. if (lua_gettop(L) >= 3) {
  379. type = luaL_checkstring(L, 3);
  380. }
  381. if (value) {
  382. if (type) {
  383. pt = type;
  384. pv = value;
  385. nvar = 0;
  386. while ((len = strcspn(pt, ", ")) > 0) {
  387. if (len == sizeof("double") - 1 &&
  388. g_ascii_strncasecmp(pt, "double", len) == 0) {
  389. gdouble num;
  390. memcpy(&num, pv, sizeof(gdouble));
  391. lua_pushnumber(L, num);
  392. pv += sizeof(gdouble);
  393. }
  394. else if (len == sizeof("int") - 1 &&
  395. g_ascii_strncasecmp(pt, "int", len) == 0) {
  396. gint num;
  397. memcpy(&num, pv, sizeof(gint));
  398. lua_pushinteger(L, num);
  399. pv += sizeof(gint);
  400. }
  401. else if (len == sizeof("int64") - 1 &&
  402. g_ascii_strncasecmp(pt, "int64", len) == 0) {
  403. gint64 num;
  404. memcpy(&num, pv, sizeof(gint64));
  405. lua_pushinteger(L, num);
  406. pv += sizeof(gint64);
  407. }
  408. else if (len == sizeof("bool") - 1 &&
  409. g_ascii_strncasecmp(pt, "bool", len) == 0) {
  410. gboolean num;
  411. memcpy(&num, pv, sizeof(gboolean));
  412. lua_pushboolean(L, num);
  413. pv += sizeof(gboolean);
  414. }
  415. else if (len == sizeof("string") - 1 &&
  416. g_ascii_strncasecmp(pt, "string", len) == 0) {
  417. slen = strlen((const gchar *) pv);
  418. lua_pushlstring(L, (const gchar *) pv, slen);
  419. pv += slen + 1;
  420. }
  421. else if (len == sizeof("gstring") - 1 &&
  422. g_ascii_strncasecmp(pt, "gstring", len) == 0) {
  423. GString *st = (GString *) pv;
  424. lua_pushlstring(L, st->str, st->len);
  425. pv += sizeof(GString *);
  426. }
  427. else if (len == sizeof("bucket") - 1 &&
  428. g_ascii_strncasecmp(pt, "bucket", len) == 0) {
  429. memcpy(&bucket, pv, sizeof(bucket));
  430. lua_createtable(L, bucket.nelts, 0);
  431. pv += sizeof(struct lua_numbers_bucket);
  432. for (i = 0; i < bucket.nelts; i++) {
  433. gdouble num;
  434. memcpy(&num, pv, sizeof(num));
  435. lua_pushnumber(L, num);
  436. lua_rawseti(L, -2, i + 1);
  437. pv += sizeof(num);
  438. }
  439. }
  440. else if (len == sizeof("fstrings") - 1 &&
  441. g_ascii_strncasecmp(pt, "fstrings", len) == 0) {
  442. GList *cur;
  443. rspamd_fstring_t *fstr;
  444. cur = (GList *) pv;
  445. lua_newtable(L);
  446. i = 1;
  447. while (cur != NULL) {
  448. fstr = cur->data;
  449. lua_pushlstring(L, fstr->str, fstr->len);
  450. lua_rawseti(L, -2, i);
  451. i++;
  452. cur = g_list_next(cur);
  453. }
  454. pv += sizeof(GList *);
  455. }
  456. else {
  457. msg_err("unknown type for get_variable: %s", pt);
  458. lua_pushnil(L);
  459. }
  460. pt += len;
  461. pt += strspn(pt, ", ");
  462. nvar++;
  463. }
  464. return nvar;
  465. }
  466. else {
  467. /* No type specified, return string */
  468. lua_pushstring(L, value);
  469. }
  470. }
  471. else {
  472. lua_pushnil(L);
  473. }
  474. }
  475. else {
  476. lua_pushnil(L);
  477. }
  478. return 1;
  479. }
  480. static int
  481. lua_mempool_has_variable(lua_State *L)
  482. {
  483. LUA_TRACE_POINT;
  484. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  485. const gchar *var = luaL_checkstring(L, 2);
  486. gboolean ret = FALSE;
  487. if (mempool && var) {
  488. if (rspamd_mempool_get_variable(mempool, var) != NULL) {
  489. ret = TRUE;
  490. }
  491. }
  492. lua_pushboolean(L, ret);
  493. return 1;
  494. }
  495. static int
  496. lua_mempool_delete_variable(lua_State *L)
  497. {
  498. LUA_TRACE_POINT;
  499. struct memory_pool_s *mempool = rspamd_lua_check_mempool(L, 1);
  500. const gchar *var = luaL_checkstring(L, 2);
  501. gboolean ret = FALSE;
  502. if (mempool && var) {
  503. if (rspamd_mempool_get_variable(mempool, var) != NULL) {
  504. ret = TRUE;
  505. rspamd_mempool_remove_variable(mempool, var);
  506. }
  507. }
  508. lua_pushboolean(L, ret);
  509. return 1;
  510. }
  511. static gint
  512. lua_mempool_topointer(lua_State *L)
  513. {
  514. LUA_TRACE_POINT;
  515. rspamd_mempool_t *pool = rspamd_lua_check_mempool(L, 1);
  516. if (pool) {
  517. /* XXX: this might cause issues on arm64 and LuaJIT */
  518. lua_pushlightuserdata(L, pool);
  519. }
  520. else {
  521. return luaL_error(L, "invalid arguments");
  522. }
  523. return 1;
  524. }
  525. static gint
  526. lua_load_mempool(lua_State *L)
  527. {
  528. lua_newtable(L);
  529. luaL_register(L, NULL, mempoollib_f);
  530. return 1;
  531. }
  532. void luaopen_mempool(lua_State *L)
  533. {
  534. rspamd_lua_new_class(L, rspamd_mempool_classname, mempoollib_m);
  535. lua_pop(L, 1);
  536. rspamd_lua_add_preload(L, "rspamd_mempool", lua_load_mempool);
  537. }