You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_mempool.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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. static const struct luaL_reg mempoollib_m[] = {
  109. LUA_INTERFACE_DEF (mempool, add_destructor),
  110. LUA_INTERFACE_DEF (mempool, stat),
  111. LUA_INTERFACE_DEF (mempool, suggest_size),
  112. LUA_INTERFACE_DEF (mempool, set_variable),
  113. LUA_INTERFACE_DEF (mempool, set_bucket),
  114. LUA_INTERFACE_DEF (mempool, get_variable),
  115. LUA_INTERFACE_DEF (mempool, has_variable),
  116. LUA_INTERFACE_DEF (mempool, delete_variable),
  117. LUA_INTERFACE_DEF (mempool, delete),
  118. {"destroy", lua_mempool_delete},
  119. {"__tostring", rspamd_lua_class_tostring},
  120. {NULL, NULL}
  121. };
  122. static const struct luaL_reg mempoollib_f[] = {
  123. LUA_INTERFACE_DEF (mempool, create),
  124. {NULL, NULL}
  125. };
  126. /*
  127. * Struct for lua destructor
  128. */
  129. struct lua_mempool_udata {
  130. lua_State *L;
  131. gint cbref;
  132. rspamd_mempool_t *mempool;
  133. };
  134. struct memory_pool_s *
  135. rspamd_lua_check_mempool (lua_State * L, gint pos)
  136. {
  137. void *ud = rspamd_lua_check_udata (L, pos, "rspamd{mempool}");
  138. luaL_argcheck (L, ud != NULL, pos, "'mempool' expected");
  139. return ud ? *((struct memory_pool_s **)ud) : NULL;
  140. }
  141. static int
  142. lua_mempool_create (lua_State *L)
  143. {
  144. LUA_TRACE_POINT;
  145. struct memory_pool_s *mempool = rspamd_mempool_new (
  146. rspamd_mempool_suggest_size (), "lua"), **pmempool;
  147. if (mempool) {
  148. pmempool = lua_newuserdata (L, sizeof (struct memory_pool_s *));
  149. rspamd_lua_setclass (L, "rspamd{mempool}", -1);
  150. *pmempool = mempool;
  151. }
  152. else {
  153. lua_pushnil (L);
  154. }
  155. return 1;
  156. }
  157. static void
  158. lua_mempool_destructor_func (gpointer p)
  159. {
  160. struct lua_mempool_udata *ud = p;
  161. lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref);
  162. if (lua_pcall (ud->L, 0, 0, 0) != 0) {
  163. msg_info ("call to destructor failed: %s", lua_tostring (ud->L, -1));
  164. lua_pop (ud->L, 1);
  165. }
  166. luaL_unref (ud->L, LUA_REGISTRYINDEX, ud->cbref);
  167. }
  168. static int
  169. lua_mempool_add_destructor (lua_State *L)
  170. {
  171. LUA_TRACE_POINT;
  172. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  173. struct lua_mempool_udata *ud;
  174. if (mempool) {
  175. if (lua_isfunction (L, 2)) {
  176. ud = rspamd_mempool_alloc (mempool,
  177. sizeof (struct lua_mempool_udata));
  178. lua_pushvalue (L, 2);
  179. /* Get a reference */
  180. ud->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  181. ud->L = L;
  182. ud->mempool = mempool;
  183. rspamd_mempool_add_destructor (mempool,
  184. lua_mempool_destructor_func,
  185. ud);
  186. }
  187. else {
  188. msg_err ("trying to add destructor without function");
  189. }
  190. }
  191. else {
  192. lua_pushnil (L);
  193. }
  194. return 1;
  195. }
  196. static int
  197. lua_mempool_delete (lua_State *L)
  198. {
  199. LUA_TRACE_POINT;
  200. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  201. if (mempool) {
  202. rspamd_mempool_delete (mempool);
  203. return 0;
  204. }
  205. else {
  206. lua_pushnil (L);
  207. }
  208. return 1;
  209. }
  210. static int
  211. lua_mempool_stat (lua_State *L)
  212. {
  213. LUA_TRACE_POINT;
  214. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  215. if (mempool) {
  216. }
  217. else {
  218. lua_pushnil (L);
  219. }
  220. return 1;
  221. }
  222. static int
  223. lua_mempool_suggest_size (lua_State *L)
  224. {
  225. LUA_TRACE_POINT;
  226. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  227. if (mempool) {
  228. lua_pushinteger (L, rspamd_mempool_suggest_size ());
  229. return 0;
  230. }
  231. else {
  232. lua_pushnil (L);
  233. }
  234. return 1;
  235. }
  236. struct lua_numbers_bucket {
  237. guint nelts;
  238. gdouble elts[0];
  239. };
  240. static int
  241. lua_mempool_set_bucket (lua_State *L)
  242. {
  243. LUA_TRACE_POINT;
  244. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  245. const gchar *var = luaL_checkstring (L, 2);
  246. struct lua_numbers_bucket *bucket;
  247. gint nelts = luaL_checknumber (L, 3), i;
  248. if (var && nelts > 0) {
  249. bucket = rspamd_mempool_alloc (mempool,
  250. sizeof (*bucket) + sizeof (gdouble) * nelts);
  251. bucket->nelts = nelts;
  252. if (lua_type (L, 4) == LUA_TTABLE) {
  253. /* Table version */
  254. for (i = 1; i <= nelts; i ++) {
  255. lua_rawgeti (L, 4, i);
  256. bucket->elts[i - 1] = lua_tonumber (L, -1);
  257. lua_pop (L, 1);
  258. }
  259. }
  260. else {
  261. for (i = 0; i <= nelts; i ++) {
  262. bucket->elts[i] = lua_tonumber (L, 4 + i);
  263. }
  264. }
  265. rspamd_mempool_set_variable (mempool, var, bucket, NULL);
  266. }
  267. else {
  268. return luaL_error (L, "invalid arguments");
  269. }
  270. return 0;
  271. }
  272. static int
  273. lua_mempool_set_variable (lua_State *L)
  274. {
  275. LUA_TRACE_POINT;
  276. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  277. const gchar *var = luaL_checkstring (L, 2);
  278. gpointer value;
  279. struct lua_numbers_bucket *bucket;
  280. gchar *vp;
  281. union {
  282. gdouble d;
  283. const gchar *s;
  284. gboolean b;
  285. } val;
  286. gsize slen;
  287. gint i, j, len = 0, type;
  288. if (mempool && var) {
  289. for (i = 3; i <= lua_gettop (L); i ++) {
  290. type = lua_type (L, i);
  291. if (type == LUA_TNUMBER) {
  292. /* We have some ambiguity here between integer and double */
  293. len += sizeof (gdouble);
  294. }
  295. else if (type == LUA_TBOOLEAN) {
  296. len += sizeof (gboolean);
  297. }
  298. else if (type == LUA_TSTRING) {
  299. (void)lua_tolstring (L, i, &slen);
  300. len += slen + 1;
  301. }
  302. else if (type == LUA_TTABLE) {
  303. /* We assume it as a bucket of numbers so far */
  304. slen = rspamd_lua_table_size (L, i);
  305. len += sizeof (gdouble) * slen + sizeof (*bucket);
  306. }
  307. else {
  308. msg_err ("cannot handle lua type %s", lua_typename (L, type));
  309. }
  310. }
  311. if (len == 0) {
  312. msg_err ("no values specified");
  313. }
  314. else {
  315. value = rspamd_mempool_alloc (mempool, len);
  316. vp = value;
  317. for (i = 3; i <= lua_gettop (L); i ++) {
  318. type = lua_type (L, i);
  319. if (type == LUA_TNUMBER) {
  320. val.d = lua_tonumber (L, i);
  321. memcpy (vp, &val, sizeof (gdouble));
  322. vp += sizeof (gdouble);
  323. }
  324. else if (type == LUA_TBOOLEAN) {
  325. val.b = lua_toboolean (L, i);
  326. memcpy (vp, &val, sizeof (gboolean));
  327. vp += sizeof (gboolean);
  328. }
  329. else if (type == LUA_TSTRING) {
  330. val.s = lua_tolstring (L, i, &slen);
  331. memcpy (vp, val.s, slen + 1);
  332. vp += slen + 1;
  333. }
  334. else if (type == LUA_TTABLE) {
  335. slen = rspamd_lua_table_size (L, i);
  336. /* XXX: Ret, ret, ret: alignment issues */
  337. bucket = (struct lua_numbers_bucket *)vp;
  338. bucket->nelts = slen;
  339. for (j = 0; j < slen; j ++) {
  340. lua_rawgeti (L, i, j + 1);
  341. bucket->elts[j] = lua_tonumber (L, -1);
  342. lua_pop (L, 1);
  343. }
  344. vp += sizeof (gdouble) * slen + sizeof (*bucket);
  345. }
  346. else {
  347. msg_err ("cannot handle lua type %s", lua_typename (L, type));
  348. }
  349. }
  350. rspamd_mempool_set_variable (mempool, var, value, NULL);
  351. }
  352. return 0;
  353. }
  354. else {
  355. lua_pushnil (L);
  356. }
  357. return 1;
  358. }
  359. static int
  360. lua_mempool_get_variable (lua_State *L)
  361. {
  362. LUA_TRACE_POINT;
  363. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  364. const gchar *var = luaL_checkstring (L, 2);
  365. const gchar *type = NULL, *pt;
  366. struct lua_numbers_bucket bucket;
  367. const gchar *value, *pv;
  368. guint len, nvar, slen, i;
  369. if (mempool && var) {
  370. value = rspamd_mempool_get_variable (mempool, var);
  371. if (lua_gettop (L) >= 3) {
  372. type = luaL_checkstring (L, 3);
  373. }
  374. if (value) {
  375. if (type) {
  376. pt = type;
  377. pv = value;
  378. nvar = 0;
  379. while ((len = strcspn (pt, ", ")) > 0) {
  380. if (len == sizeof ("double") - 1 &&
  381. g_ascii_strncasecmp (pt, "double", len) == 0) {
  382. gdouble num;
  383. memcpy (&num, pv, sizeof (gdouble));
  384. lua_pushnumber (L, num);
  385. pv += sizeof (gdouble);
  386. }
  387. else if (len == sizeof ("int") - 1 &&
  388. g_ascii_strncasecmp (pt, "int", len) == 0) {
  389. gint num;
  390. memcpy (&num, pv, sizeof (gint));
  391. lua_pushinteger (L, num);
  392. pv += sizeof (gint);
  393. }
  394. else if (len == sizeof ("int64") - 1 &&
  395. g_ascii_strncasecmp (pt, "int64", len) == 0) {
  396. gint64 num;
  397. memcpy (&num, pv, sizeof (gint64));
  398. lua_pushinteger (L, num);
  399. pv += sizeof (gint64);
  400. }
  401. else if (len == sizeof ("bool") - 1 &&
  402. g_ascii_strncasecmp (pt, "bool", len) == 0) {
  403. gboolean num;
  404. memcpy (&num, pv, sizeof (gboolean));
  405. lua_pushboolean (L, num);
  406. pv += sizeof (gboolean);
  407. }
  408. else if (len == sizeof ("string") - 1 &&
  409. g_ascii_strncasecmp (pt, "string", len) == 0) {
  410. slen = strlen ((const gchar *)pv);
  411. lua_pushlstring (L, (const gchar *)pv, slen);
  412. pv += slen + 1;
  413. }
  414. else if (len == sizeof ("gstring") - 1 &&
  415. g_ascii_strncasecmp (pt, "gstring", len) == 0) {
  416. GString *st = (GString *)pv;
  417. lua_pushlstring (L, st->str, st->len);
  418. pv += sizeof (GString *);
  419. }
  420. else if (len == sizeof ("bucket") - 1 &&
  421. g_ascii_strncasecmp (pt, "bucket", len) == 0) {
  422. memcpy (&bucket, pv, sizeof (bucket));
  423. lua_createtable (L, bucket.nelts, 0);
  424. pv += sizeof (struct lua_numbers_bucket);
  425. for (i = 0; i < bucket.nelts; i ++) {
  426. gdouble num;
  427. memcpy (&num, pv, sizeof (num));
  428. lua_pushnumber (L, num);
  429. lua_rawseti (L, -1, i + 1);
  430. pv += sizeof (num);
  431. }
  432. }
  433. else if (len == sizeof ("fstrings") - 1 &&
  434. g_ascii_strncasecmp (pt, "fstrings", len) == 0) {
  435. GList *cur;
  436. rspamd_fstring_t *fstr;
  437. cur = (GList *)pv;
  438. lua_newtable (L);
  439. i = 1;
  440. while (cur != NULL) {
  441. fstr = cur->data;
  442. lua_pushlstring (L, fstr->str, fstr->len);
  443. lua_rawseti (L, -2, i);
  444. i ++;
  445. cur = g_list_next (cur);
  446. }
  447. pv += sizeof (GList *);
  448. }
  449. else {
  450. msg_err ("unknown type for get_variable: %s", pt);
  451. lua_pushnil (L);
  452. }
  453. pt += len;
  454. pt += strspn (pt, ", ");
  455. nvar ++;
  456. }
  457. return nvar;
  458. }
  459. lua_pushstring (L, value);
  460. }
  461. else {
  462. lua_pushnil (L);
  463. }
  464. }
  465. else {
  466. lua_pushnil (L);
  467. }
  468. return 1;
  469. }
  470. static int
  471. lua_mempool_has_variable (lua_State *L)
  472. {
  473. LUA_TRACE_POINT;
  474. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  475. const gchar *var = luaL_checkstring (L, 2);
  476. gboolean ret = FALSE;
  477. if (mempool && var) {
  478. if (rspamd_mempool_get_variable (mempool, var) != NULL) {
  479. ret = TRUE;
  480. }
  481. }
  482. lua_pushboolean (L, ret);
  483. return 1;
  484. }
  485. static int
  486. lua_mempool_delete_variable (lua_State *L)
  487. {
  488. LUA_TRACE_POINT;
  489. struct memory_pool_s *mempool = rspamd_lua_check_mempool (L, 1);
  490. const gchar *var = luaL_checkstring (L, 2);
  491. gboolean ret = FALSE;
  492. if (mempool && var) {
  493. if (rspamd_mempool_get_variable (mempool, var) != NULL) {
  494. ret = TRUE;
  495. rspamd_mempool_remove_variable (mempool, var);
  496. }
  497. }
  498. lua_pushboolean (L, ret);
  499. return 1;
  500. }
  501. static gint
  502. lua_load_mempool (lua_State * L)
  503. {
  504. lua_newtable (L);
  505. luaL_register (L, NULL, mempoollib_f);
  506. return 1;
  507. }
  508. void
  509. luaopen_mempool (lua_State * L)
  510. {
  511. luaL_newmetatable (L, "rspamd{mempool}");
  512. lua_pushstring (L, "__index");
  513. lua_pushvalue (L, -2);
  514. lua_settable (L, -3);
  515. lua_pushstring (L, "class");
  516. lua_pushstring (L, "rspamd{mempool}");
  517. lua_rawset (L, -3);
  518. luaL_register (L, NULL, mempoollib_m);
  519. rspamd_lua_add_preload (L, "rspamd_mempool", lua_load_mempool);
  520. lua_pop (L, 1); /* remove metatable from stack */
  521. }