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_redis.c 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662
  1. /*
  2. * Copyright 2024 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. #include "lua_thread_pool.h"
  18. #include "utlist.h"
  19. #include "contrib/hiredis/hiredis.h"
  20. #include "contrib/hiredis/async.h"
  21. #define REDIS_DEFAULT_TIMEOUT 1.0
  22. static const gchar *M = "rspamd lua redis";
  23. static void *redis_null;
  24. /***
  25. * @module rspamd_redis
  26. * This module implements redis asynchronous client for rspamd LUA API.
  27. * Here is an example of using of this module:
  28. * @example
  29. local rspamd_redis = require "rspamd_redis"
  30. local rspamd_logger = require "rspamd_logger"
  31. local function symbol_callback(task)
  32. local redis_key = 'some_key'
  33. local function redis_cb(err, data)
  34. if not err then
  35. rspamd_logger.infox('redis returned %1=%2', redis_key, data)
  36. end
  37. end
  38. rspamd_redis.make_request(task, "127.0.0.1:6379", redis_cb,
  39. 'GET', {redis_key})
  40. -- or in table form:
  41. -- rspamd_redis.make_request({task=task, host="127.0.0.1:6379,
  42. -- callback=redis_cb, timeout=2.0, cmd='GET', args={redis_key}})
  43. end
  44. */
  45. LUA_FUNCTION_DEF(redis, make_request);
  46. LUA_FUNCTION_DEF(redis, make_request_sync);
  47. LUA_FUNCTION_DEF(redis, connect);
  48. LUA_FUNCTION_DEF(redis, connect_sync);
  49. LUA_FUNCTION_DEF(redis, add_cmd);
  50. LUA_FUNCTION_DEF(redis, exec);
  51. LUA_FUNCTION_DEF(redis, gc);
  52. static const struct luaL_reg redislib_f[] = {
  53. LUA_INTERFACE_DEF(redis, make_request),
  54. LUA_INTERFACE_DEF(redis, make_request_sync),
  55. LUA_INTERFACE_DEF(redis, connect),
  56. LUA_INTERFACE_DEF(redis, connect_sync),
  57. {NULL, NULL}};
  58. static const struct luaL_reg redislib_m[] = {
  59. LUA_INTERFACE_DEF(redis, add_cmd),
  60. LUA_INTERFACE_DEF(redis, exec),
  61. {"__gc", lua_redis_gc},
  62. {"__tostring", rspamd_lua_class_tostring},
  63. {NULL, NULL}};
  64. #undef REDIS_DEBUG_REFS
  65. #ifdef REDIS_DEBUG_REFS
  66. #define REDIS_RETAIN(x) \
  67. do { \
  68. msg_err("retain ref %p, refcount: %d", (x), (x)->ref.refcount); \
  69. REF_RETAIN(x); \
  70. } while (0)
  71. #define REDIS_RELEASE(x) \
  72. do { \
  73. msg_err("release ref %p, refcount: %d", (x), (x)->ref.refcount); \
  74. REF_RELEASE(x); \
  75. } while (0)
  76. #else
  77. #define REDIS_RETAIN REF_RETAIN
  78. #define REDIS_RELEASE REF_RELEASE
  79. #endif
  80. struct lua_redis_request_specific_userdata;
  81. /**
  82. * Struct for userdata representation
  83. */
  84. struct lua_redis_userdata {
  85. redisAsyncContext *ctx;
  86. struct rspamd_task *task;
  87. struct rspamd_symcache_dynamic_item *item;
  88. struct rspamd_async_session *s;
  89. struct ev_loop *event_loop;
  90. struct rspamd_config *cfg;
  91. struct rspamd_redis_pool *pool;
  92. gchar *server;
  93. gchar log_tag[RSPAMD_LOG_ID_LEN + 1];
  94. struct lua_redis_request_specific_userdata *specific;
  95. gdouble timeout;
  96. guint16 port;
  97. guint16 terminated;
  98. };
  99. #define msg_debug_lua_redis(...) rspamd_conditional_debug_fast(NULL, NULL, \
  100. rspamd_lua_redis_log_id, "lua_redis", ud->log_tag, \
  101. G_STRFUNC, \
  102. __VA_ARGS__)
  103. INIT_LOG_MODULE(lua_redis)
  104. #define LUA_REDIS_SPECIFIC_REPLIED (1 << 0)
  105. /* session was finished */
  106. #define LUA_REDIS_SPECIFIC_FINISHED (1 << 1)
  107. #define LUA_REDIS_ASYNC (1 << 0)
  108. #define LUA_REDIS_TEXTDATA (1 << 1)
  109. #define LUA_REDIS_TERMINATED (1 << 2)
  110. #define LUA_REDIS_NO_POOL (1 << 3)
  111. #define LUA_REDIS_SUBSCRIBED (1 << 4)
  112. #define IS_ASYNC(ctx) ((ctx)->flags & LUA_REDIS_ASYNC)
  113. struct lua_redis_request_specific_userdata {
  114. gint cbref;
  115. guint nargs;
  116. gchar **args;
  117. gsize *arglens;
  118. struct lua_redis_userdata *c;
  119. struct lua_redis_ctx *ctx;
  120. struct lua_redis_request_specific_userdata *next;
  121. ev_timer timeout_ev;
  122. guint flags;
  123. };
  124. struct lua_redis_ctx {
  125. guint flags;
  126. struct lua_redis_userdata async;
  127. guint cmds_pending;
  128. ref_entry_t ref;
  129. GQueue *replies; /* for sync connection only */
  130. GQueue *events_cleanup; /* for sync connection only */
  131. struct thread_entry *thread; /* for sync mode, set only if there was yield */
  132. };
  133. struct lua_redis_result {
  134. gboolean is_error;
  135. gint result_ref;
  136. struct rspamd_symcache_dynamic_item *item;
  137. struct rspamd_async_session *s;
  138. struct rspamd_task *task;
  139. struct lua_redis_request_specific_userdata *sp_ud;
  140. };
  141. static struct lua_redis_ctx *
  142. lua_check_redis(lua_State *L, gint pos)
  143. {
  144. void *ud = rspamd_lua_check_udata(L, pos, rspamd_redis_classname);
  145. luaL_argcheck(L, ud != NULL, pos, "'redis' expected");
  146. return ud ? *((struct lua_redis_ctx **) ud) : NULL;
  147. }
  148. static void
  149. lua_redis_free_args(char **args, gsize *arglens, guint nargs)
  150. {
  151. guint i;
  152. if (args) {
  153. for (i = 0; i < nargs; i++) {
  154. g_free(args[i]);
  155. }
  156. g_free(args);
  157. g_free(arglens);
  158. }
  159. }
  160. static void
  161. lua_redis_dtor(struct lua_redis_ctx *ctx)
  162. {
  163. struct lua_redis_userdata *ud;
  164. struct lua_redis_request_specific_userdata *cur, *tmp;
  165. gboolean is_successful = TRUE;
  166. struct redisAsyncContext *ac;
  167. ud = &ctx->async;
  168. msg_debug_lua_redis("destructing %p", ctx);
  169. if (ud->ctx) {
  170. LL_FOREACH_SAFE(ud->specific, cur, tmp)
  171. {
  172. ev_timer_stop(ud->event_loop, &cur->timeout_ev);
  173. if (!(cur->flags & LUA_REDIS_SPECIFIC_REPLIED)) {
  174. is_successful = FALSE;
  175. }
  176. cur->flags |= LUA_REDIS_SPECIFIC_FINISHED;
  177. }
  178. ctx->flags |= LUA_REDIS_TERMINATED;
  179. ud->terminated = 1;
  180. ac = ud->ctx;
  181. ud->ctx = NULL;
  182. if (!is_successful) {
  183. rspamd_redis_pool_release_connection(ud->pool, ac,
  184. RSPAMD_REDIS_RELEASE_FATAL);
  185. }
  186. else {
  187. rspamd_redis_pool_release_connection(ud->pool, ac,
  188. (ctx->flags & LUA_REDIS_NO_POOL) ? RSPAMD_REDIS_RELEASE_ENFORCE : RSPAMD_REDIS_RELEASE_DEFAULT);
  189. }
  190. }
  191. LL_FOREACH_SAFE(ud->specific, cur, tmp)
  192. {
  193. lua_redis_free_args(cur->args, cur->arglens, cur->nargs);
  194. if (cur->cbref != -1) {
  195. luaL_unref(ud->cfg->lua_state, LUA_REGISTRYINDEX, cur->cbref);
  196. }
  197. g_free(cur);
  198. }
  199. if (ctx->events_cleanup) {
  200. g_queue_free(ctx->events_cleanup);
  201. ctx->events_cleanup = NULL;
  202. }
  203. if (ctx->replies) {
  204. g_queue_free(ctx->replies);
  205. ctx->replies = NULL;
  206. }
  207. g_free(ctx);
  208. }
  209. static gint
  210. lua_redis_gc(lua_State *L)
  211. {
  212. struct lua_redis_ctx *ctx = lua_check_redis(L, 1);
  213. if (ctx) {
  214. REDIS_RELEASE(ctx);
  215. }
  216. return 0;
  217. }
  218. static void
  219. lua_redis_fin(void *arg)
  220. {
  221. struct lua_redis_request_specific_userdata *sp_ud = arg;
  222. struct lua_redis_userdata *ud;
  223. struct lua_redis_ctx *ctx;
  224. ctx = sp_ud->ctx;
  225. ud = sp_ud->c;
  226. if (ev_can_stop(&sp_ud->timeout_ev)) {
  227. ev_timer_stop(sp_ud->ctx->async.event_loop, &sp_ud->timeout_ev);
  228. }
  229. msg_debug_lua_redis("finished redis query %p from session %p; refcount=%d",
  230. sp_ud, ctx, ctx->ref.refcount);
  231. sp_ud->flags |= LUA_REDIS_SPECIFIC_FINISHED;
  232. REDIS_RELEASE(ctx);
  233. }
  234. /**
  235. * Push error of redis request to lua callback
  236. * @param code
  237. * @param ud
  238. */
  239. static void
  240. lua_redis_push_error(const gchar *err,
  241. struct lua_redis_ctx *ctx,
  242. struct lua_redis_request_specific_userdata *sp_ud,
  243. gboolean connected)
  244. {
  245. struct lua_redis_userdata *ud = sp_ud->c;
  246. struct lua_callback_state cbs;
  247. lua_State *L;
  248. if (!(sp_ud->flags & (LUA_REDIS_SPECIFIC_REPLIED | LUA_REDIS_SPECIFIC_FINISHED))) {
  249. if (sp_ud->cbref != -1) {
  250. lua_thread_pool_prepare_callback(ud->cfg->lua_thread_pool, &cbs);
  251. L = cbs.L;
  252. lua_pushcfunction(L, &rspamd_lua_traceback);
  253. int err_idx = lua_gettop(L);
  254. /* Push error */
  255. lua_rawgeti(cbs.L, LUA_REGISTRYINDEX, sp_ud->cbref);
  256. /* String of error */
  257. lua_pushstring(cbs.L, err);
  258. /* Data is nil */
  259. lua_pushnil(cbs.L);
  260. if (ud->item) {
  261. rspamd_symcache_set_cur_item(ud->task, ud->item);
  262. }
  263. if (lua_pcall(cbs.L, 2, 0, err_idx) != 0) {
  264. msg_info("call to callback failed: %s", lua_tostring(cbs.L, -1));
  265. }
  266. lua_settop(L, err_idx - 1);
  267. lua_thread_pool_restore_callback(&cbs);
  268. }
  269. sp_ud->flags |= LUA_REDIS_SPECIFIC_REPLIED;
  270. if (connected && ud->s) {
  271. if (ud->item) {
  272. rspamd_symcache_item_async_dec_check(ud->task, ud->item, M);
  273. }
  274. rspamd_session_remove_event(ud->s, lua_redis_fin, sp_ud);
  275. }
  276. else {
  277. lua_redis_fin(sp_ud);
  278. }
  279. }
  280. }
  281. static void
  282. lua_redis_push_reply(lua_State *L, const redisReply *r, gboolean text_data)
  283. {
  284. guint i;
  285. struct rspamd_lua_text *t;
  286. switch (r->type) {
  287. case REDIS_REPLY_INTEGER:
  288. lua_pushinteger(L, r->integer);
  289. break;
  290. case REDIS_REPLY_NIL:
  291. lua_getfield(L, LUA_REGISTRYINDEX, "redis.null");
  292. break;
  293. case REDIS_REPLY_STRING:
  294. case REDIS_REPLY_STATUS:
  295. if (text_data) {
  296. t = lua_newuserdata(L, sizeof(*t));
  297. rspamd_lua_setclass(L, rspamd_text_classname, -1);
  298. t->flags = 0;
  299. t->start = r->str;
  300. t->len = r->len;
  301. }
  302. else {
  303. lua_pushlstring(L, r->str, r->len);
  304. }
  305. break;
  306. case REDIS_REPLY_ARRAY:
  307. lua_createtable(L, r->elements, 0);
  308. for (i = 0; i < r->elements; ++i) {
  309. lua_redis_push_reply(L, r->element[i], text_data);
  310. lua_rawseti(L, -2, i + 1); /* Store sub-reply */
  311. }
  312. break;
  313. default: /* should not happen */
  314. msg_info("unknown reply type: %d", r->type);
  315. break;
  316. }
  317. }
  318. /**
  319. * Push data of redis request to lua callback
  320. * @param r redis reply data
  321. * @param ud
  322. */
  323. static void
  324. lua_redis_push_data(const redisReply *r, struct lua_redis_ctx *ctx,
  325. struct lua_redis_request_specific_userdata *sp_ud)
  326. {
  327. struct lua_redis_userdata *ud = sp_ud->c;
  328. struct lua_callback_state cbs;
  329. lua_State *L;
  330. if (!(sp_ud->flags & (LUA_REDIS_SPECIFIC_REPLIED | LUA_REDIS_SPECIFIC_FINISHED)) ||
  331. (sp_ud->flags & LUA_REDIS_SUBSCRIBED)) {
  332. if (sp_ud->cbref != -1) {
  333. lua_thread_pool_prepare_callback(ud->cfg->lua_thread_pool, &cbs);
  334. L = cbs.L;
  335. lua_pushcfunction(L, &rspamd_lua_traceback);
  336. int err_idx = lua_gettop(L);
  337. /* Push error */
  338. lua_rawgeti(cbs.L, LUA_REGISTRYINDEX, sp_ud->cbref);
  339. /* Error is nil */
  340. lua_pushnil(cbs.L);
  341. /* Data */
  342. lua_redis_push_reply(cbs.L, r, ctx->flags & LUA_REDIS_TEXTDATA);
  343. if (ud->item) {
  344. rspamd_symcache_set_cur_item(ud->task, ud->item);
  345. }
  346. gint ret = lua_pcall(cbs.L, 2, 0, err_idx);
  347. if (ret != 0) {
  348. msg_info("call to lua_redis callback failed (%d): %s",
  349. ret, lua_tostring(cbs.L, -1));
  350. }
  351. lua_settop(L, err_idx - 1);
  352. lua_thread_pool_restore_callback(&cbs);
  353. }
  354. if (sp_ud->flags & LUA_REDIS_SUBSCRIBED) {
  355. if (!(sp_ud->flags & LUA_REDIS_SPECIFIC_REPLIED)) {
  356. if (ev_can_stop(&sp_ud->timeout_ev)) {
  357. ev_timer_stop(sp_ud->ctx->async.event_loop,
  358. &sp_ud->timeout_ev);
  359. }
  360. }
  361. }
  362. sp_ud->flags |= LUA_REDIS_SPECIFIC_REPLIED;
  363. if (!(sp_ud->flags & LUA_REDIS_SUBSCRIBED)) {
  364. if (ud->s) {
  365. if (ud->item) {
  366. rspamd_symcache_item_async_dec_check(ud->task,
  367. ud->item, M);
  368. }
  369. rspamd_session_remove_event(ud->s, lua_redis_fin, sp_ud);
  370. }
  371. else {
  372. lua_redis_fin(sp_ud);
  373. }
  374. }
  375. }
  376. }
  377. /**
  378. * Callback for redis replies
  379. * @param c context of redis connection
  380. * @param r redis reply
  381. * @param priv userdata
  382. */
  383. static void
  384. lua_redis_callback(redisAsyncContext *c, gpointer r, gpointer priv)
  385. {
  386. redisReply *reply = r;
  387. struct lua_redis_request_specific_userdata *sp_ud = priv;
  388. struct lua_redis_ctx *ctx;
  389. struct lua_redis_userdata *ud;
  390. redisAsyncContext *ac;
  391. ctx = sp_ud->ctx;
  392. ud = sp_ud->c;
  393. if (ud->terminated || !rspamd_lua_is_initialised()) {
  394. /* We are already at the termination stage, just go out */
  395. return;
  396. }
  397. msg_debug_lua_redis("got reply from redis %p for query %p", sp_ud->c->ctx,
  398. sp_ud);
  399. REDIS_RETAIN(ctx);
  400. /* If session is finished, we cannot call lua callbacks */
  401. if (!(sp_ud->flags & LUA_REDIS_SPECIFIC_FINISHED) ||
  402. (sp_ud->flags & LUA_REDIS_SUBSCRIBED)) {
  403. if (c->err == 0) {
  404. if (r != NULL) {
  405. if (reply->type != REDIS_REPLY_ERROR) {
  406. lua_redis_push_data(reply, ctx, sp_ud);
  407. }
  408. else {
  409. lua_redis_push_error(reply->str, ctx, sp_ud, TRUE);
  410. }
  411. }
  412. else {
  413. lua_redis_push_error("received no data from server", ctx, sp_ud, TRUE);
  414. }
  415. }
  416. else {
  417. if (c->err == REDIS_ERR_IO) {
  418. lua_redis_push_error(strerror(errno), ctx, sp_ud, TRUE);
  419. }
  420. else {
  421. lua_redis_push_error(c->errstr, ctx, sp_ud, TRUE);
  422. }
  423. }
  424. }
  425. if (!(sp_ud->flags & LUA_REDIS_SUBSCRIBED)) {
  426. ctx->cmds_pending--;
  427. if (ctx->cmds_pending == 0 && !ud->terminated) {
  428. /* Disconnect redis early as we don't need it anymore */
  429. ud->terminated = 1;
  430. ac = ud->ctx;
  431. ud->ctx = NULL;
  432. if (ac) {
  433. msg_debug_lua_redis("release redis connection ud=%p; ctx=%p; refcount=%d",
  434. ud, ctx, ctx->ref.refcount);
  435. rspamd_redis_pool_release_connection(ud->pool, ac,
  436. (ctx->flags & LUA_REDIS_NO_POOL) ? RSPAMD_REDIS_RELEASE_ENFORCE : RSPAMD_REDIS_RELEASE_DEFAULT);
  437. }
  438. }
  439. }
  440. REDIS_RELEASE(ctx);
  441. }
  442. static gint
  443. lua_redis_push_results(struct lua_redis_ctx *ctx, lua_State *L)
  444. {
  445. gint results = g_queue_get_length(ctx->replies);
  446. gint i;
  447. gboolean can_use_lua = TRUE;
  448. results = g_queue_get_length(ctx->replies);
  449. if (!lua_checkstack(L, (results * 2) + 1)) {
  450. luaL_error(L, "cannot resize stack to fit %d commands",
  451. ctx->cmds_pending);
  452. can_use_lua = FALSE;
  453. }
  454. for (i = 0; i < results; i++) {
  455. struct lua_redis_result *result = g_queue_pop_head(ctx->replies);
  456. if (can_use_lua) {
  457. lua_pushboolean(L, !result->is_error);
  458. lua_rawgeti(L, LUA_REGISTRYINDEX, result->result_ref);
  459. }
  460. luaL_unref(L, LUA_REGISTRYINDEX, result->result_ref);
  461. g_queue_push_tail(ctx->events_cleanup, result);
  462. }
  463. return can_use_lua ? results * 2 : 0;
  464. }
  465. static void
  466. lua_redis_cleanup_events(struct lua_redis_ctx *ctx)
  467. {
  468. REDIS_RETAIN(ctx); /* To avoid preliminary destruction */
  469. while (!g_queue_is_empty(ctx->events_cleanup)) {
  470. struct lua_redis_result *result = g_queue_pop_head(ctx->events_cleanup);
  471. if (result->item) {
  472. rspamd_symcache_item_async_dec_check(result->task, result->item, M);
  473. }
  474. if (result->s) {
  475. rspamd_session_remove_event(result->s, lua_redis_fin, result->sp_ud);
  476. }
  477. else {
  478. lua_redis_fin(result->sp_ud);
  479. }
  480. g_free(result);
  481. }
  482. REDIS_RELEASE(ctx);
  483. }
  484. /**
  485. * Callback for redis replies
  486. * @param c context of redis connection
  487. * @param r redis reply
  488. * @param priv userdata
  489. */
  490. static void
  491. lua_redis_callback_sync(redisAsyncContext *ac, gpointer r, gpointer priv)
  492. {
  493. redisReply *reply = r;
  494. struct lua_redis_request_specific_userdata *sp_ud = priv;
  495. struct lua_redis_ctx *ctx;
  496. struct lua_redis_userdata *ud;
  497. struct thread_entry *thread;
  498. gint results;
  499. ctx = sp_ud->ctx;
  500. ud = sp_ud->c;
  501. lua_State *L = ctx->async.cfg->lua_state;
  502. sp_ud->flags |= LUA_REDIS_SPECIFIC_REPLIED;
  503. if (ud->terminated) {
  504. /* We are already at the termination stage, just go out */
  505. /* TODO:
  506. if somebody is waiting for us (ctx->thread), return result,
  507. otherwise, indeed, ignore
  508. */
  509. return;
  510. }
  511. if (ev_can_stop(&sp_ud->timeout_ev)) {
  512. ev_timer_stop(ud->event_loop, &sp_ud->timeout_ev);
  513. }
  514. if (!(sp_ud->flags & LUA_REDIS_SPECIFIC_FINISHED)) {
  515. msg_debug_lua_redis("got reply from redis: %p for query %p", ac, sp_ud);
  516. struct lua_redis_result *result = g_malloc0(sizeof *result);
  517. if (ac->err == 0) {
  518. if (r != NULL) {
  519. if (reply->type != REDIS_REPLY_ERROR) {
  520. result->is_error = FALSE;
  521. lua_redis_push_reply(L, reply, ctx->flags & LUA_REDIS_TEXTDATA);
  522. }
  523. else {
  524. result->is_error = TRUE;
  525. lua_pushstring(L, reply->str);
  526. }
  527. }
  528. else {
  529. result->is_error = TRUE;
  530. lua_pushliteral(L, "received no data from server");
  531. }
  532. }
  533. else {
  534. result->is_error = TRUE;
  535. if (ac->err == REDIS_ERR_IO) {
  536. lua_pushstring(L, strerror(errno));
  537. }
  538. else {
  539. lua_pushstring(L, ac->errstr);
  540. }
  541. }
  542. /* if error happened, we should terminate the connection,
  543. and release it */
  544. if (result->is_error && sp_ud->c->ctx) {
  545. ac = sp_ud->c->ctx;
  546. /* Set to NULL to avoid double free in dtor */
  547. sp_ud->c->ctx = NULL;
  548. ctx->flags |= LUA_REDIS_TERMINATED;
  549. /*
  550. * This will call all callbacks pending so the entire context
  551. * will be destructed
  552. */
  553. rspamd_redis_pool_release_connection(sp_ud->c->pool, ac,
  554. RSPAMD_REDIS_RELEASE_FATAL);
  555. }
  556. result->result_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  557. result->s = ud->s;
  558. result->item = ud->item;
  559. result->task = ud->task;
  560. result->sp_ud = sp_ud;
  561. g_queue_push_tail(ctx->replies, result);
  562. }
  563. ctx->cmds_pending--;
  564. if (ctx->cmds_pending == 0) {
  565. if (ctx->thread) {
  566. if (!(sp_ud->flags & LUA_REDIS_SPECIFIC_FINISHED)) {
  567. /* somebody yielded and waits for results */
  568. thread = ctx->thread;
  569. ctx->thread = NULL;
  570. results = lua_redis_push_results(ctx, thread->lua_state);
  571. if (ud->item) {
  572. rspamd_symcache_set_cur_item(ud->task, ud->item);
  573. }
  574. lua_thread_resume(thread, results);
  575. lua_redis_cleanup_events(ctx);
  576. }
  577. else {
  578. /* We cannot resume the thread as the associated task has gone */
  579. lua_thread_pool_terminate_entry_full(ud->cfg->lua_thread_pool,
  580. ctx->thread, G_STRLOC, true);
  581. ctx->thread = NULL;
  582. }
  583. }
  584. }
  585. }
  586. static void
  587. lua_redis_timeout_sync(EV_P_ ev_timer *w, int revents)
  588. {
  589. struct lua_redis_request_specific_userdata *sp_ud =
  590. (struct lua_redis_request_specific_userdata *) w->data;
  591. struct lua_redis_ctx *ctx;
  592. struct lua_redis_userdata *ud;
  593. redisAsyncContext *ac;
  594. if (sp_ud->flags & LUA_REDIS_SPECIFIC_FINISHED) {
  595. return;
  596. }
  597. ud = sp_ud->c;
  598. ctx = sp_ud->ctx;
  599. msg_debug_lua_redis("timeout while querying redis server: %p, redis: %p", sp_ud,
  600. sp_ud->c->ctx);
  601. if (sp_ud->c->ctx) {
  602. ac = sp_ud->c->ctx;
  603. /* Set to NULL to avoid double free in dtor */
  604. sp_ud->c->ctx = NULL;
  605. ac->err = REDIS_ERR_IO;
  606. errno = ETIMEDOUT;
  607. ctx->flags |= LUA_REDIS_TERMINATED;
  608. /*
  609. * This will call all callbacks pending so the entire context
  610. * will be destructed
  611. */
  612. rspamd_redis_pool_release_connection(sp_ud->c->pool, ac,
  613. RSPAMD_REDIS_RELEASE_FATAL);
  614. }
  615. }
  616. static void
  617. lua_redis_timeout(EV_P_ ev_timer *w, int revents)
  618. {
  619. struct lua_redis_request_specific_userdata *sp_ud =
  620. (struct lua_redis_request_specific_userdata *) w->data;
  621. struct lua_redis_userdata *ud;
  622. struct lua_redis_ctx *ctx;
  623. redisAsyncContext *ac;
  624. if (sp_ud->flags & LUA_REDIS_SPECIFIC_FINISHED) {
  625. return;
  626. }
  627. ctx = sp_ud->ctx;
  628. ud = sp_ud->c;
  629. REDIS_RETAIN(ctx);
  630. msg_debug_lua_redis("timeout while querying redis server: %p, redis: %p", sp_ud,
  631. sp_ud->c->ctx);
  632. lua_redis_push_error("timeout while connecting the server", ctx, sp_ud, TRUE);
  633. if (sp_ud->c->ctx) {
  634. ac = sp_ud->c->ctx;
  635. /* Set to NULL to avoid double free in dtor */
  636. sp_ud->c->ctx = NULL;
  637. ac->err = REDIS_ERR_IO;
  638. errno = ETIMEDOUT;
  639. /*
  640. * This will call all callbacks pending so the entire context
  641. * will be destructed
  642. */
  643. rspamd_redis_pool_release_connection(sp_ud->c->pool, ac,
  644. RSPAMD_REDIS_RELEASE_FATAL);
  645. }
  646. REDIS_RELEASE(ctx);
  647. }
  648. static void
  649. lua_redis_parse_args(lua_State *L, gint idx, const gchar *cmd,
  650. gchar ***pargs, gsize **parglens, guint *nargs)
  651. {
  652. gchar **args = NULL;
  653. gsize *arglens;
  654. gint top;
  655. if (idx != 0 && lua_type(L, idx) == LUA_TTABLE) {
  656. /* Get all arguments */
  657. lua_pushvalue(L, idx);
  658. lua_pushnil(L);
  659. top = 0;
  660. while (lua_next(L, -2) != 0) {
  661. gint type = lua_type(L, -1);
  662. if (type == LUA_TNUMBER || type == LUA_TSTRING ||
  663. type == LUA_TUSERDATA) {
  664. top++;
  665. }
  666. lua_pop(L, 1);
  667. }
  668. args = g_malloc((top + 1) * sizeof(gchar *));
  669. arglens = g_malloc((top + 1) * sizeof(gsize));
  670. arglens[0] = strlen(cmd);
  671. args[0] = g_malloc(arglens[0]);
  672. memcpy(args[0], cmd, arglens[0]);
  673. top = 1;
  674. lua_pushnil(L);
  675. while (lua_next(L, -2) != 0) {
  676. gint type = lua_type(L, -1);
  677. if (type == LUA_TSTRING) {
  678. const gchar *s;
  679. s = lua_tolstring(L, -1, &arglens[top]);
  680. args[top] = g_malloc(arglens[top]);
  681. memcpy(args[top], s, arglens[top]);
  682. top++;
  683. }
  684. else if (type == LUA_TUSERDATA) {
  685. struct rspamd_lua_text *t;
  686. t = lua_check_text(L, -1);
  687. if (t && t->start) {
  688. arglens[top] = t->len;
  689. args[top] = g_malloc(arglens[top]);
  690. memcpy(args[top], t->start, arglens[top]);
  691. top++;
  692. }
  693. }
  694. else if (type == LUA_TNUMBER) {
  695. gdouble val = lua_tonumber(L, -1);
  696. gint r;
  697. gchar numbuf[64];
  698. if (val == (gdouble) ((int64_t) val)) {
  699. r = rspamd_snprintf(numbuf, sizeof(numbuf), "%L",
  700. (int64_t) val);
  701. }
  702. else {
  703. r = rspamd_snprintf(numbuf, sizeof(numbuf), "%f",
  704. val);
  705. }
  706. arglens[top] = r;
  707. args[top] = g_malloc(arglens[top]);
  708. memcpy(args[top], numbuf, arglens[top]);
  709. top++;
  710. }
  711. lua_pop(L, 1);
  712. }
  713. lua_pop(L, 1);
  714. }
  715. else {
  716. /* Use merely cmd */
  717. args = g_malloc(sizeof(gchar *));
  718. arglens = g_malloc(sizeof(gsize));
  719. arglens[0] = strlen(cmd);
  720. args[0] = g_malloc(arglens[0]);
  721. memcpy(args[0], cmd, arglens[0]);
  722. top = 1;
  723. }
  724. *pargs = args;
  725. *parglens = arglens;
  726. *nargs = top;
  727. }
  728. static struct lua_redis_ctx *
  729. rspamd_lua_redis_prepare_connection(lua_State *L, gint *pcbref, gboolean is_async)
  730. {
  731. struct lua_redis_ctx *ctx = NULL;
  732. rspamd_inet_addr_t *ip = NULL;
  733. struct lua_redis_userdata *ud = NULL;
  734. struct rspamd_lua_ip *addr = NULL;
  735. struct rspamd_task *task = NULL;
  736. const gchar *host = NULL;
  737. const gchar *username = NULL, *password = NULL, *dbname = NULL, *log_tag = NULL;
  738. gint cbref = -1;
  739. struct rspamd_config *cfg = NULL;
  740. struct rspamd_async_session *session = NULL;
  741. struct ev_loop *ev_base = NULL;
  742. gboolean ret = FALSE;
  743. guint flags = 0;
  744. if (lua_istable(L, 1)) {
  745. /* Table version */
  746. lua_pushvalue(L, 1);
  747. lua_pushstring(L, "task");
  748. lua_gettable(L, -2);
  749. if (lua_type(L, -1) == LUA_TUSERDATA) {
  750. task = lua_check_task_maybe(L, -1);
  751. }
  752. lua_pop(L, 1);
  753. if (!task) {
  754. /* We need to get ev_base, config and session separately */
  755. lua_pushstring(L, "config");
  756. lua_gettable(L, -2);
  757. if (lua_type(L, -1) == LUA_TUSERDATA) {
  758. cfg = lua_check_config(L, -1);
  759. }
  760. lua_pop(L, 1);
  761. lua_pushstring(L, "session");
  762. lua_gettable(L, -2);
  763. if (lua_type(L, -1) == LUA_TUSERDATA) {
  764. session = lua_check_session(L, -1);
  765. }
  766. lua_pop(L, 1);
  767. lua_pushstring(L, "ev_base");
  768. lua_gettable(L, -2);
  769. if (lua_type(L, -1) == LUA_TUSERDATA) {
  770. ev_base = lua_check_ev_base(L, -1);
  771. }
  772. lua_pop(L, 1);
  773. if (cfg && ev_base) {
  774. ret = TRUE;
  775. }
  776. else if (!cfg) {
  777. msg_err_task_check("config is not passed");
  778. }
  779. else {
  780. msg_err_task_check("ev_base is not set");
  781. }
  782. }
  783. else {
  784. cfg = task->cfg;
  785. session = task->s;
  786. ev_base = task->event_loop;
  787. log_tag = task->task_pool->tag.uid;
  788. ret = TRUE;
  789. }
  790. if (pcbref) {
  791. lua_pushstring(L, "callback");
  792. lua_gettable(L, -2);
  793. if (lua_type(L, -1) == LUA_TFUNCTION) {
  794. /* This also pops function from the stack */
  795. cbref = luaL_ref(L, LUA_REGISTRYINDEX);
  796. *pcbref = cbref;
  797. }
  798. else {
  799. *pcbref = -1;
  800. lua_pop(L, 1);
  801. }
  802. }
  803. lua_pushstring(L, "host");
  804. lua_gettable(L, -2);
  805. if (lua_type(L, -1) == LUA_TUSERDATA) {
  806. addr = lua_check_ip(L, -1);
  807. host = rspamd_inet_address_to_string_pretty(addr->addr);
  808. }
  809. else if (lua_type(L, -1) == LUA_TSTRING) {
  810. host = lua_tostring(L, -1);
  811. if (rspamd_parse_inet_address(&ip,
  812. host, strlen(host), RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) {
  813. addr = g_alloca(sizeof(*addr));
  814. addr->addr = ip;
  815. if (rspamd_inet_address_get_port(ip) == 0) {
  816. rspamd_inet_address_set_port(ip, 6379);
  817. }
  818. }
  819. }
  820. lua_pop(L, 1);
  821. lua_pushstring(L, "username");
  822. lua_gettable(L, -2);
  823. if (lua_type(L, -1) == LUA_TSTRING) {
  824. username = lua_tostring(L, -1);
  825. }
  826. lua_pop(L, 1);
  827. lua_pushstring(L, "password");
  828. lua_gettable(L, -2);
  829. if (lua_type(L, -1) == LUA_TSTRING) {
  830. password = lua_tostring(L, -1);
  831. }
  832. lua_pop(L, 1);
  833. lua_pushstring(L, "dbname");
  834. lua_gettable(L, -2);
  835. if (lua_type(L, -1) == LUA_TSTRING) {
  836. dbname = lua_tostring(L, -1);
  837. }
  838. lua_pop(L, 1);
  839. lua_pushstring(L, "opaque_data");
  840. lua_gettable(L, -2);
  841. if (!!lua_toboolean(L, -1)) {
  842. flags |= LUA_REDIS_TEXTDATA;
  843. }
  844. lua_pop(L, 1);
  845. lua_pushstring(L, "no_pool");
  846. lua_gettable(L, -2);
  847. if (!!lua_toboolean(L, -1)) {
  848. flags |= LUA_REDIS_NO_POOL;
  849. }
  850. lua_pop(L, 1);
  851. lua_pop(L, 1); /* table */
  852. if (session && rspamd_session_blocked(session)) {
  853. msg_err_task_check("Session is being destroying");
  854. ret = FALSE;
  855. }
  856. if (ret && addr != NULL) {
  857. ctx = g_malloc0(sizeof(struct lua_redis_ctx));
  858. REF_INIT_RETAIN(ctx, lua_redis_dtor);
  859. if (is_async) {
  860. ctx->flags |= flags | LUA_REDIS_ASYNC;
  861. ud = &ctx->async;
  862. }
  863. else {
  864. ud = &ctx->async;
  865. ctx->replies = g_queue_new();
  866. ctx->events_cleanup = g_queue_new();
  867. }
  868. ud->s = session;
  869. ud->cfg = cfg;
  870. ud->pool = cfg->redis_pool;
  871. ud->event_loop = ev_base;
  872. ud->task = task;
  873. if (log_tag) {
  874. rspamd_strlcpy(ud->log_tag, log_tag, sizeof(ud->log_tag));
  875. }
  876. else {
  877. /* Use pointer itself as a tag */
  878. rspamd_snprintf(ud->log_tag, sizeof(ud->log_tag),
  879. "%ud",
  880. (int) rspamd_cryptobox_fast_hash(&ud, sizeof(ud), 0));
  881. }
  882. if (task) {
  883. ud->item = rspamd_symcache_get_cur_item(task);
  884. }
  885. ret = TRUE;
  886. }
  887. else {
  888. if (cbref != -1) {
  889. luaL_unref(L, LUA_REGISTRYINDEX, cbref);
  890. }
  891. msg_err_task_check("incorrect function invocation");
  892. ret = FALSE;
  893. }
  894. }
  895. if (ret) {
  896. ud->terminated = 0;
  897. ud->ctx = rspamd_redis_pool_connect(ud->pool,
  898. dbname, username, password,
  899. rspamd_inet_address_to_string(addr->addr),
  900. rspamd_inet_address_get_port(addr->addr));
  901. if (ip) {
  902. rspamd_inet_address_free(ip);
  903. }
  904. if (ud->ctx == NULL || ud->ctx->err) {
  905. if (ud->ctx) {
  906. msg_err_task_check("cannot connect to redis: %s",
  907. ud->ctx->errstr);
  908. rspamd_redis_pool_release_connection(ud->pool, ud->ctx,
  909. RSPAMD_REDIS_RELEASE_FATAL);
  910. ud->ctx = NULL;
  911. }
  912. else {
  913. msg_err_task_check("cannot connect to redis (OS error): %s",
  914. strerror(errno));
  915. }
  916. REDIS_RELEASE(ctx);
  917. return NULL;
  918. }
  919. msg_debug_lua_redis("opened redis connection host=%s; ctx=%p; ud=%p",
  920. host, ctx, ud);
  921. return ctx;
  922. }
  923. if (ip) {
  924. rspamd_inet_address_free(ip);
  925. }
  926. return NULL;
  927. }
  928. /***
  929. * @function rspamd_redis.make_request({params})
  930. * Make request to redis server, params is a table of key=value arguments in any order
  931. * @param {task} task worker task object
  932. * @param {ip|string} host server address
  933. * @param {function} callback callback to be called in form `function (task, err, data)`
  934. * @param {string} cmd command to be sent to redis
  935. * @param {table} args numeric array of strings used as redis arguments
  936. * @param {number} timeout timeout in seconds for request (1.0 by default)
  937. * @return {boolean} `true` if a request has been scheduled
  938. */
  939. static int
  940. lua_redis_make_request(lua_State *L)
  941. {
  942. LUA_TRACE_POINT;
  943. struct lua_redis_request_specific_userdata *sp_ud;
  944. struct lua_redis_userdata *ud;
  945. struct lua_redis_ctx *ctx, **pctx;
  946. const gchar *cmd = NULL;
  947. gdouble timeout = REDIS_DEFAULT_TIMEOUT;
  948. gint cbref = -1;
  949. gboolean ret = FALSE;
  950. ctx = rspamd_lua_redis_prepare_connection(L, &cbref, TRUE);
  951. if (ctx) {
  952. ud = &ctx->async;
  953. sp_ud = g_malloc0(sizeof(*sp_ud));
  954. sp_ud->cbref = cbref;
  955. sp_ud->c = ud;
  956. sp_ud->ctx = ctx;
  957. lua_pushstring(L, "cmd");
  958. lua_gettable(L, -2);
  959. cmd = lua_tostring(L, -1);
  960. lua_pop(L, 1);
  961. lua_pushstring(L, "timeout");
  962. lua_gettable(L, 1);
  963. if (lua_type(L, -1) == LUA_TNUMBER) {
  964. timeout = lua_tonumber(L, -1);
  965. }
  966. lua_pop(L, 1);
  967. ud->timeout = timeout;
  968. lua_pushstring(L, "args");
  969. lua_gettable(L, 1);
  970. lua_redis_parse_args(L, -1, cmd, &sp_ud->args, &sp_ud->arglens,
  971. &sp_ud->nargs);
  972. lua_pop(L, 1);
  973. LL_PREPEND(ud->specific, sp_ud);
  974. ret = redisAsyncCommandArgv(ud->ctx,
  975. lua_redis_callback,
  976. sp_ud,
  977. sp_ud->nargs,
  978. (const gchar **) sp_ud->args,
  979. sp_ud->arglens);
  980. if (ret == REDIS_OK) {
  981. if (ud->s) {
  982. rspamd_session_add_event(ud->s,
  983. lua_redis_fin, sp_ud,
  984. M);
  985. if (ud->item) {
  986. rspamd_symcache_item_async_inc(ud->task, ud->item, M);
  987. }
  988. }
  989. REDIS_RETAIN(ctx); /* Cleared by fin event */
  990. ctx->cmds_pending++;
  991. if (ud->ctx->c.flags & REDIS_SUBSCRIBED) {
  992. msg_debug_lua_redis("subscribe command, never unref/timeout");
  993. sp_ud->flags |= LUA_REDIS_SUBSCRIBED;
  994. }
  995. sp_ud->timeout_ev.data = sp_ud;
  996. ev_now_update_if_cheap((struct ev_loop *) ud->event_loop);
  997. ev_timer_init(&sp_ud->timeout_ev, lua_redis_timeout, timeout, 0.0);
  998. ev_timer_start(ud->event_loop, &sp_ud->timeout_ev);
  999. ret = TRUE;
  1000. }
  1001. else {
  1002. msg_info("call to redis failed: %s", ud->ctx->errstr);
  1003. rspamd_redis_pool_release_connection(ud->pool, ud->ctx,
  1004. RSPAMD_REDIS_RELEASE_FATAL);
  1005. ud->ctx = NULL;
  1006. REDIS_RELEASE(ctx);
  1007. ret = FALSE;
  1008. }
  1009. }
  1010. else {
  1011. lua_pushboolean(L, FALSE);
  1012. lua_pushnil(L);
  1013. return 2;
  1014. }
  1015. lua_pushboolean(L, ret);
  1016. if (ret) {
  1017. pctx = lua_newuserdata(L, sizeof(ctx));
  1018. *pctx = ctx;
  1019. rspamd_lua_setclass(L, rspamd_redis_classname, -1);
  1020. }
  1021. else {
  1022. lua_pushnil(L);
  1023. }
  1024. return 2;
  1025. }
  1026. /***
  1027. * @function rspamd_redis.make_request_sync({params})
  1028. * Make blocking request to redis server, params is a table of key=value arguments in any order
  1029. * @param {ip|string} host server address
  1030. * @param {string} cmd command to be sent to redis
  1031. * @param {table} args numeric array of strings used as redis arguments
  1032. * @param {number} timeout timeout in seconds for request (1.0 by default)
  1033. * @return {boolean + result} `true` and a result if a request has been successful
  1034. */
  1035. static int
  1036. lua_redis_make_request_sync(lua_State *L)
  1037. {
  1038. LUA_TRACE_POINT;
  1039. struct rspamd_lua_ip *addr = NULL;
  1040. rspamd_inet_addr_t *ip = NULL;
  1041. const gchar *cmd = NULL, *host;
  1042. struct timeval tv;
  1043. gboolean ret = FALSE;
  1044. gdouble timeout = REDIS_DEFAULT_TIMEOUT;
  1045. gchar **args = NULL;
  1046. gsize *arglens = NULL;
  1047. guint nargs = 0, flags = 0;
  1048. redisContext *ctx;
  1049. redisReply *r;
  1050. if (lua_istable(L, 1)) {
  1051. lua_pushvalue(L, 1);
  1052. lua_pushstring(L, "cmd");
  1053. lua_gettable(L, -2);
  1054. cmd = lua_tostring(L, -1);
  1055. lua_pop(L, 1);
  1056. lua_pushstring(L, "host");
  1057. lua_gettable(L, -2);
  1058. if (lua_type(L, -1) == LUA_TUSERDATA) {
  1059. addr = lua_check_ip(L, -1);
  1060. }
  1061. else if (lua_type(L, -1) == LUA_TSTRING) {
  1062. host = lua_tostring(L, -1);
  1063. if (rspamd_parse_inet_address(&ip,
  1064. host, strlen(host), RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) {
  1065. addr = g_alloca(sizeof(*addr));
  1066. addr->addr = ip;
  1067. if (rspamd_inet_address_get_port(ip) == 0) {
  1068. rspamd_inet_address_set_port(ip, 6379);
  1069. }
  1070. }
  1071. }
  1072. lua_pop(L, 1);
  1073. lua_pushstring(L, "timeout");
  1074. lua_gettable(L, -2);
  1075. if (lua_type(L, -1) == LUA_TNUMBER) {
  1076. timeout = lua_tonumber(L, -1);
  1077. }
  1078. lua_pop(L, 1);
  1079. lua_pushstring(L, "opaque_data");
  1080. lua_gettable(L, -2);
  1081. if (!!lua_toboolean(L, -1)) {
  1082. flags |= LUA_REDIS_TEXTDATA;
  1083. }
  1084. lua_pop(L, 1);
  1085. if (cmd) {
  1086. lua_pushstring(L, "args");
  1087. lua_gettable(L, -2);
  1088. lua_redis_parse_args(L, -1, cmd, &args, &arglens, &nargs);
  1089. lua_pop(L, 1);
  1090. }
  1091. lua_pop(L, 1);
  1092. if (addr && cmd) {
  1093. ret = TRUE;
  1094. }
  1095. }
  1096. if (ret) {
  1097. double_to_tv(timeout, &tv);
  1098. if (rspamd_inet_address_get_af(addr->addr) == AF_UNIX) {
  1099. ctx = redisConnectUnixWithTimeout(
  1100. rspamd_inet_address_to_string(addr->addr), tv);
  1101. }
  1102. else {
  1103. ctx = redisConnectWithTimeout(
  1104. rspamd_inet_address_to_string(addr->addr),
  1105. rspamd_inet_address_get_port(addr->addr), tv);
  1106. }
  1107. if (ip) {
  1108. rspamd_inet_address_free(ip);
  1109. }
  1110. if (ctx == NULL || ctx->err) {
  1111. redisFree(ctx);
  1112. lua_redis_free_args(args, arglens, nargs);
  1113. lua_pushboolean(L, FALSE);
  1114. return 1;
  1115. }
  1116. r = redisCommandArgv(ctx,
  1117. nargs,
  1118. (const gchar **) args,
  1119. arglens);
  1120. if (r != NULL) {
  1121. if (r->type != REDIS_REPLY_ERROR) {
  1122. lua_pushboolean(L, TRUE);
  1123. lua_redis_push_reply(L, r, flags & LUA_REDIS_TEXTDATA);
  1124. }
  1125. else {
  1126. lua_pushboolean(L, FALSE);
  1127. lua_pushstring(L, r->str);
  1128. }
  1129. freeReplyObject(r);
  1130. redisFree(ctx);
  1131. lua_redis_free_args(args, arglens, nargs);
  1132. return 2;
  1133. }
  1134. else {
  1135. msg_info("call to redis failed: %s", ctx->errstr);
  1136. redisFree(ctx);
  1137. lua_redis_free_args(args, arglens, nargs);
  1138. lua_pushboolean(L, FALSE);
  1139. }
  1140. }
  1141. else {
  1142. if (ip) {
  1143. rspamd_inet_address_free(ip);
  1144. }
  1145. msg_err("bad arguments for redis request");
  1146. lua_redis_free_args(args, arglens, nargs);
  1147. lua_pushboolean(L, FALSE);
  1148. }
  1149. return 1;
  1150. }
  1151. /***
  1152. * @function rspamd_redis.connect({params})
  1153. * Make request to redis server, params is a table of key=value arguments in any order
  1154. * @param {task} task worker task object
  1155. * @param {ip|string} host server address
  1156. * @param {number} timeout timeout in seconds for request (1.0 by default)
  1157. * @return {boolean,redis} new connection object or nil if connection failed
  1158. */
  1159. static int
  1160. lua_redis_connect(lua_State *L)
  1161. {
  1162. LUA_TRACE_POINT;
  1163. struct lua_redis_userdata *ud;
  1164. struct lua_redis_ctx *ctx, **pctx;
  1165. gdouble timeout = REDIS_DEFAULT_TIMEOUT;
  1166. ctx = rspamd_lua_redis_prepare_connection(L, NULL, TRUE);
  1167. if (ctx) {
  1168. ud = &ctx->async;
  1169. lua_pushstring(L, "timeout");
  1170. lua_gettable(L, 1);
  1171. if (lua_type(L, -1) == LUA_TNUMBER) {
  1172. timeout = lua_tonumber(L, -1);
  1173. }
  1174. lua_pop(L, 1);
  1175. ud->timeout = timeout;
  1176. }
  1177. else {
  1178. lua_pushboolean(L, FALSE);
  1179. lua_pushnil(L);
  1180. return 2;
  1181. }
  1182. lua_pushboolean(L, TRUE);
  1183. pctx = lua_newuserdata(L, sizeof(ctx));
  1184. *pctx = ctx;
  1185. rspamd_lua_setclass(L, rspamd_redis_classname, -1);
  1186. return 2;
  1187. }
  1188. /***
  1189. * @function rspamd_redis.connect_sync({params})
  1190. * Make blocking request to redis server, params is a table of key=value arguments in any order
  1191. * @param {ip|string} host server address
  1192. * @param {number} timeout timeout in seconds for request (1.0 by default)
  1193. * @return {redis} redis object if a request has been successful
  1194. */
  1195. static int
  1196. lua_redis_connect_sync(lua_State *L)
  1197. {
  1198. LUA_TRACE_POINT;
  1199. gdouble timeout = REDIS_DEFAULT_TIMEOUT;
  1200. struct lua_redis_ctx *ctx, **pctx;
  1201. ctx = rspamd_lua_redis_prepare_connection(L, NULL, FALSE);
  1202. if (ctx) {
  1203. if (lua_istable(L, 1)) {
  1204. lua_pushstring(L, "timeout");
  1205. lua_gettable(L, 1);
  1206. if (lua_type(L, -1) == LUA_TNUMBER) {
  1207. timeout = lua_tonumber(L, -1);
  1208. }
  1209. lua_pop(L, 1);
  1210. }
  1211. ctx->async.timeout = timeout;
  1212. lua_pushboolean(L, TRUE);
  1213. pctx = lua_newuserdata(L, sizeof(ctx));
  1214. *pctx = ctx;
  1215. rspamd_lua_setclass(L, rspamd_redis_classname, -1);
  1216. }
  1217. else {
  1218. lua_pushboolean(L, FALSE);
  1219. lua_pushstring(L, "bad arguments for redis request");
  1220. return 2;
  1221. }
  1222. return 2;
  1223. }
  1224. /***
  1225. * @method rspamd_redis:add_cmd(cmd, {args})
  1226. * Append new cmd to redis pipeline
  1227. * @param {string} cmd command to be sent to redis
  1228. * @param {table} args array of strings used as redis arguments
  1229. * @return {boolean} `true` if a request has been successful
  1230. */
  1231. static int
  1232. lua_redis_add_cmd(lua_State *L)
  1233. {
  1234. LUA_TRACE_POINT;
  1235. struct lua_redis_ctx *ctx = lua_check_redis(L, 1);
  1236. struct lua_redis_request_specific_userdata *sp_ud;
  1237. struct lua_redis_userdata *ud;
  1238. const gchar *cmd = NULL;
  1239. gint args_pos = 2;
  1240. gint cbref = -1, ret;
  1241. if (ctx) {
  1242. if (ctx->flags & LUA_REDIS_TERMINATED) {
  1243. lua_pushboolean(L, FALSE);
  1244. lua_pushstring(L, "Connection is terminated");
  1245. return 2;
  1246. }
  1247. /* Async version */
  1248. if (lua_type(L, 2) == LUA_TSTRING) {
  1249. /* No callback version */
  1250. cmd = lua_tostring(L, 2);
  1251. args_pos = 3;
  1252. }
  1253. else if (lua_type(L, 2) == LUA_TFUNCTION) {
  1254. lua_pushvalue(L, 2);
  1255. cbref = luaL_ref(L, LUA_REGISTRYINDEX);
  1256. cmd = lua_tostring(L, 3);
  1257. args_pos = 4;
  1258. }
  1259. else {
  1260. return luaL_error(L, "invalid arguments");
  1261. }
  1262. sp_ud = g_malloc0(sizeof(*sp_ud));
  1263. if (IS_ASYNC(ctx)) {
  1264. sp_ud->c = &ctx->async;
  1265. ud = &ctx->async;
  1266. sp_ud->cbref = cbref;
  1267. }
  1268. else {
  1269. sp_ud->c = &ctx->async;
  1270. ud = &ctx->async;
  1271. }
  1272. sp_ud->ctx = ctx;
  1273. lua_redis_parse_args(L, args_pos, cmd, &sp_ud->args,
  1274. &sp_ud->arglens, &sp_ud->nargs);
  1275. LL_PREPEND(sp_ud->c->specific, sp_ud);
  1276. if (ud->s && rspamd_session_blocked(ud->s)) {
  1277. lua_pushboolean(L, 0);
  1278. lua_pushstring(L, "session is terminating");
  1279. return 2;
  1280. }
  1281. if (IS_ASYNC(ctx)) {
  1282. ret = redisAsyncCommandArgv(sp_ud->c->ctx,
  1283. lua_redis_callback,
  1284. sp_ud,
  1285. sp_ud->nargs,
  1286. (const gchar **) sp_ud->args,
  1287. sp_ud->arglens);
  1288. }
  1289. else {
  1290. ret = redisAsyncCommandArgv(sp_ud->c->ctx,
  1291. lua_redis_callback_sync,
  1292. sp_ud,
  1293. sp_ud->nargs,
  1294. (const gchar **) sp_ud->args,
  1295. sp_ud->arglens);
  1296. }
  1297. if (ret == REDIS_OK) {
  1298. if (ud->s) {
  1299. rspamd_session_add_event(ud->s,
  1300. lua_redis_fin,
  1301. sp_ud,
  1302. M);
  1303. if (ud->item) {
  1304. rspamd_symcache_item_async_inc(ud->task, ud->item, M);
  1305. }
  1306. }
  1307. sp_ud->timeout_ev.data = sp_ud;
  1308. if (IS_ASYNC(ctx)) {
  1309. ev_timer_init(&sp_ud->timeout_ev, lua_redis_timeout,
  1310. sp_ud->c->timeout, 0.0);
  1311. }
  1312. else {
  1313. ev_timer_init(&sp_ud->timeout_ev, lua_redis_timeout_sync,
  1314. sp_ud->c->timeout, 0.0);
  1315. }
  1316. ev_timer_start(ud->event_loop, &sp_ud->timeout_ev);
  1317. REDIS_RETAIN(ctx);
  1318. ctx->cmds_pending++;
  1319. }
  1320. else {
  1321. msg_info("call to redis failed: %s",
  1322. sp_ud->c->ctx->errstr);
  1323. lua_pushboolean(L, 0);
  1324. lua_pushstring(L, sp_ud->c->ctx->errstr);
  1325. return 2;
  1326. }
  1327. }
  1328. lua_pushboolean(L, true);
  1329. return 1;
  1330. }
  1331. /***
  1332. * @method rspamd_redis:exec()
  1333. * Executes pending commands (suitable for blocking IO only for now)
  1334. * @return {boolean}, {table}, ...: pairs in format [bool, result] for each request pending
  1335. */
  1336. static int
  1337. lua_redis_exec(lua_State *L)
  1338. {
  1339. LUA_TRACE_POINT;
  1340. struct lua_redis_ctx *ctx = lua_check_redis(L, 1);
  1341. if (ctx == NULL) {
  1342. lua_error(L);
  1343. return 1;
  1344. }
  1345. if (IS_ASYNC(ctx)) {
  1346. lua_pushstring(L, "Async redis pipelining is not implemented");
  1347. lua_error(L);
  1348. return 0;
  1349. }
  1350. else {
  1351. if (ctx->cmds_pending == 0 && g_queue_get_length(ctx->replies) == 0) {
  1352. lua_pushstring(L, "No pending commands to execute");
  1353. lua_error(L);
  1354. }
  1355. if (ctx->cmds_pending == 0 && g_queue_get_length(ctx->replies) > 0) {
  1356. gint results = lua_redis_push_results(ctx, L);
  1357. return results;
  1358. }
  1359. else {
  1360. ctx->thread = lua_thread_pool_get_running_entry(ctx->async.cfg->lua_thread_pool);
  1361. return lua_thread_yield(ctx->thread, 0);
  1362. }
  1363. }
  1364. }
  1365. static gint
  1366. lua_load_redis(lua_State *L)
  1367. {
  1368. lua_newtable(L);
  1369. luaL_register(L, NULL, redislib_f);
  1370. return 1;
  1371. }
  1372. static gint
  1373. lua_redis_null_idx(lua_State *L)
  1374. {
  1375. lua_pushnil(L);
  1376. return 1;
  1377. }
  1378. static void
  1379. lua_redis_null_mt(lua_State *L)
  1380. {
  1381. luaL_newmetatable(L, "redis{null}");
  1382. lua_pushcfunction(L, lua_redis_null_idx);
  1383. lua_setfield(L, -2, "__index");
  1384. lua_pushcfunction(L, lua_redis_null_idx);
  1385. lua_setfield(L, -2, "__tostring");
  1386. lua_pop(L, 1);
  1387. }
  1388. /**
  1389. * Open redis library
  1390. * @param L lua stack
  1391. * @return
  1392. */
  1393. void luaopen_redis(lua_State *L)
  1394. {
  1395. rspamd_lua_new_class(L, rspamd_redis_classname, redislib_m);
  1396. lua_pop(L, 1);
  1397. rspamd_lua_add_preload(L, "rspamd_redis", lua_load_redis);
  1398. /* Set null element */
  1399. lua_redis_null_mt(L);
  1400. redis_null = lua_newuserdata(L, 0);
  1401. luaL_getmetatable(L, "redis{null}");
  1402. lua_setmetatable(L, -2);
  1403. lua_setfield(L, LUA_REGISTRYINDEX, "redis.null");
  1404. }