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 36KB

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