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_worker.c 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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 "config.h"
  17. #include "util.h"
  18. #include "rspamd.h"
  19. #include "libserver/worker_util.h"
  20. #include "protocol.h"
  21. #include "upstream.h"
  22. #include "cfg_file.h"
  23. #include "url.h"
  24. #include "message.h"
  25. #include "map.h"
  26. #include "dns.h"
  27. #include "unix-std.h"
  28. #include "lua/lua_common.h"
  29. #ifdef WITH_GPERF_TOOLS
  30. # include <glib/gprintf.h>
  31. #endif
  32. /* 60 seconds for worker's IO */
  33. #define DEFAULT_WORKER_IO_TIMEOUT 60000
  34. gpointer init_lua_worker (struct rspamd_config *cfg);
  35. void start_lua_worker (struct rspamd_worker *worker);
  36. worker_t lua_worker = {
  37. "lua", /* Name */
  38. init_lua_worker, /* Init function */
  39. start_lua_worker, /* Start function */
  40. RSPAMD_WORKER_HAS_SOCKET | RSPAMD_WORKER_KILLABLE,
  41. RSPAMD_WORKER_SOCKET_TCP, /* TCP socket */
  42. RSPAMD_WORKER_VER /* Version info */
  43. };
  44. static const guint64 rspamd_lua_ctx_magic = 0x8055e2652aacf96eULL;
  45. /*
  46. * Worker's context
  47. */
  48. struct rspamd_lua_worker_ctx {
  49. guint64 magic;
  50. /* Events base */
  51. struct event_base *ev_base;
  52. /* DNS resolver */
  53. struct rspamd_dns_resolver *resolver;
  54. /* Config */
  55. struct rspamd_config *cfg;
  56. /* END OF COMMON PART */
  57. /* Other params */
  58. GHashTable *params;
  59. /* Lua script to load */
  60. gchar *file;
  61. /* Lua state */
  62. lua_State *L;
  63. /* Callback for accept */
  64. gint cbref_accept;
  65. /* Callback for finishing */
  66. gint cbref_fin;
  67. /* The rest options */
  68. ucl_object_t *opts;
  69. };
  70. /* Lua bindings */
  71. LUA_FUNCTION_DEF (worker, get_ev_base);
  72. LUA_FUNCTION_DEF (worker, register_accept_callback);
  73. LUA_FUNCTION_DEF (worker, register_exit_callback);
  74. LUA_FUNCTION_DEF (worker, get_option);
  75. LUA_FUNCTION_DEF (worker, get_resolver);
  76. LUA_FUNCTION_DEF (worker, get_cfg);
  77. static const struct luaL_reg lua_workerlib_m[] = {
  78. LUA_INTERFACE_DEF (worker, get_ev_base),
  79. LUA_INTERFACE_DEF (worker, register_accept_callback),
  80. LUA_INTERFACE_DEF (worker, register_exit_callback),
  81. LUA_INTERFACE_DEF (worker, get_option),
  82. LUA_INTERFACE_DEF (worker, get_resolver),
  83. LUA_INTERFACE_DEF (worker, get_cfg),
  84. {"__tostring", rspamd_lua_class_tostring},
  85. {NULL, NULL}
  86. };
  87. /* Basic functions of LUA API for worker object */
  88. static gint
  89. luaopen_lua_worker (lua_State * L)
  90. {
  91. rspamd_lua_new_class (L, "rspamd{lua_worker}", lua_workerlib_m);
  92. luaL_register (L, "rspamd_lua_worker", null_reg);
  93. lua_pop (L, 1); /* remove metatable from stack */
  94. return 1;
  95. }
  96. struct rspamd_lua_worker_ctx *
  97. lua_check_lua_worker (lua_State * L)
  98. {
  99. void *ud = luaL_checkudata (L, 1, "rspamd{lua_worker}");
  100. luaL_argcheck (L, ud != NULL, 1, "'lua_worker' expected");
  101. return ud ? *((struct rspamd_lua_worker_ctx **)ud) : NULL;
  102. }
  103. static int
  104. lua_worker_get_ev_base (lua_State *L)
  105. {
  106. struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L);
  107. struct event_base **pbase;
  108. if (ctx) {
  109. pbase = lua_newuserdata (L, sizeof (struct event_base *));
  110. rspamd_lua_setclass (L, "rspamd{ev_base}", -1);
  111. *pbase = ctx->ev_base;
  112. }
  113. else {
  114. lua_pushnil (L);
  115. }
  116. return 1;
  117. }
  118. static int
  119. lua_worker_register_accept_callback (lua_State *L)
  120. {
  121. struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L);
  122. if (ctx) {
  123. if (!lua_isfunction (L, 2)) {
  124. msg_err ("invalid callback passed");
  125. lua_pushnil (L);
  126. }
  127. else {
  128. lua_pushvalue (L, 2);
  129. ctx->cbref_accept = luaL_ref (L, LUA_REGISTRYINDEX);
  130. return 0;
  131. }
  132. }
  133. else {
  134. lua_pushnil (L);
  135. }
  136. return 1;
  137. }
  138. static int
  139. lua_worker_register_exit_callback (lua_State *L)
  140. {
  141. struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L);
  142. if (ctx) {
  143. if (!lua_isfunction (L, 2)) {
  144. msg_err ("invalid callback passed");
  145. lua_pushnil (L);
  146. }
  147. else {
  148. lua_pushvalue (L, 2);
  149. ctx->cbref_fin = luaL_ref (L, LUA_REGISTRYINDEX);
  150. return 0;
  151. }
  152. }
  153. else {
  154. lua_pushnil (L);
  155. }
  156. return 1;
  157. }
  158. /* XXX: This functions should be rewritten completely */
  159. static int
  160. lua_worker_get_option (lua_State *L)
  161. {
  162. struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L);
  163. const ucl_object_t *val;
  164. const gchar *name;
  165. if (ctx) {
  166. name = luaL_checkstring (L, 2);
  167. if (name == NULL) {
  168. msg_err ("no name specified");
  169. lua_pushnil (L);
  170. }
  171. else {
  172. val = ucl_object_lookup (ctx->opts, name);
  173. if (val == NULL) {
  174. lua_pushnil (L);
  175. }
  176. else {
  177. ucl_object_push_lua (L, val, TRUE);
  178. }
  179. }
  180. }
  181. else {
  182. lua_pushnil (L);
  183. }
  184. return 1;
  185. }
  186. static int
  187. lua_worker_get_resolver (lua_State *L)
  188. {
  189. struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L);
  190. struct rspamd_dns_resolver **presolver;
  191. if (ctx) {
  192. presolver = lua_newuserdata (L, sizeof (gpointer));
  193. rspamd_lua_setclass (L, "rspamd{resolver}", -1);
  194. *presolver = ctx->resolver;
  195. }
  196. else {
  197. lua_pushnil (L);
  198. }
  199. return 1;
  200. }
  201. static int
  202. lua_worker_get_cfg (lua_State *L)
  203. {
  204. struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L);
  205. struct rspamd_config **pcfg;
  206. if (ctx) {
  207. pcfg = lua_newuserdata (L, sizeof (gpointer));
  208. rspamd_lua_setclass (L, "rspamd{config}", -1);
  209. *pcfg = ctx->cfg;
  210. }
  211. else {
  212. lua_pushnil (L);
  213. }
  214. return 1;
  215. }
  216. /* End of lua API */
  217. /*
  218. * Accept new connection and construct task
  219. */
  220. static void
  221. lua_accept_socket (gint fd, short what, void *arg)
  222. {
  223. struct rspamd_worker *worker = (struct rspamd_worker *) arg;
  224. struct rspamd_lua_worker_ctx *ctx, **pctx;
  225. gint nfd;
  226. lua_State *L;
  227. rspamd_inet_addr_t *addr;
  228. ctx = worker->ctx;
  229. L = ctx->L;
  230. if ((nfd =
  231. rspamd_accept_from_socket (fd, &addr, worker->accept_events)) == -1) {
  232. msg_warn ("accept failed: %s", strerror (errno));
  233. return;
  234. }
  235. /* Check for EAGAIN */
  236. if (nfd == 0) {
  237. return;
  238. }
  239. msg_info ("accepted connection from %s port %d",
  240. rspamd_inet_address_to_string (addr),
  241. rspamd_inet_address_get_port (addr));
  242. /* Call finalizer function */
  243. lua_rawgeti (L, LUA_REGISTRYINDEX, ctx->cbref_accept);
  244. pctx = lua_newuserdata (L, sizeof (gpointer));
  245. rspamd_lua_setclass (L, "rspamd{lua_worker}", -1);
  246. *pctx = ctx;
  247. lua_pushinteger (L, nfd);
  248. rspamd_lua_ip_push (L, addr);
  249. lua_pushinteger (L, 0);
  250. if (lua_pcall (L, 4, 0, 0) != 0) {
  251. msg_info ("call to worker accept failed: %s", lua_tostring (L, -1));
  252. lua_pop (L, 1);
  253. }
  254. rspamd_inet_address_free (addr);
  255. close (nfd);
  256. }
  257. static gboolean
  258. rspamd_lua_worker_parser (ucl_object_t *obj, gpointer ud)
  259. {
  260. struct rspamd_lua_worker_ctx *ctx = ud;
  261. ctx->opts = obj;
  262. return TRUE;
  263. }
  264. gpointer
  265. init_lua_worker (struct rspamd_config *cfg)
  266. {
  267. struct rspamd_lua_worker_ctx *ctx;
  268. GQuark type;
  269. type = g_quark_try_string ("lua");
  270. ctx = rspamd_mempool_alloc (cfg->cfg_pool,
  271. sizeof (struct rspamd_lua_worker_ctx));
  272. ctx->magic = rspamd_lua_ctx_magic;
  273. ctx->params = g_hash_table_new_full (rspamd_str_hash,
  274. rspamd_str_equal,
  275. g_free,
  276. (GDestroyNotify)g_list_free);
  277. rspamd_rcl_register_worker_option (cfg,
  278. type,
  279. "file",
  280. rspamd_rcl_parse_struct_string,
  281. ctx,
  282. G_STRUCT_OFFSET (struct rspamd_lua_worker_ctx, file),
  283. 0,
  284. "Run the following lua script when accepting a connection");
  285. rspamd_rcl_register_worker_parser (cfg, type, rspamd_lua_worker_parser,
  286. ctx);
  287. return ctx;
  288. }
  289. /*
  290. * Start worker process
  291. */
  292. void
  293. start_lua_worker (struct rspamd_worker *worker)
  294. {
  295. struct rspamd_lua_worker_ctx *ctx = worker->ctx, **pctx;
  296. lua_State *L;
  297. #ifdef WITH_PROFILER
  298. extern void _start (void), etext (void);
  299. monstartup ((u_long) & _start, (u_long) & etext);
  300. #endif
  301. ctx->ev_base = rspamd_prepare_worker (worker,
  302. "lua_worker",
  303. lua_accept_socket);
  304. L = worker->srv->cfg->lua_state;
  305. ctx->L = L;
  306. ctx->cfg = worker->srv->cfg;
  307. ctx->resolver = dns_resolver_init (worker->srv->logger,
  308. ctx->ev_base,
  309. worker->srv->cfg);
  310. /* Open worker's lib */
  311. luaopen_lua_worker (L);
  312. if (ctx->file == NULL) {
  313. msg_err ("No lua script defined, so no reason to exist");
  314. exit (EXIT_SUCCESS);
  315. }
  316. if (access (ctx->file, R_OK) == -1) {
  317. msg_err ("Error reading lua script %s: %s", ctx->file,
  318. strerror (errno));
  319. exit (EXIT_SUCCESS);
  320. }
  321. pctx = lua_newuserdata (L, sizeof (gpointer));
  322. rspamd_lua_setclass (L, "rspamd{lua_worker}", -1);
  323. lua_setglobal (L, "rspamd_lua_worker");
  324. *pctx = ctx;
  325. if (luaL_dofile (L, ctx->file) != 0) {
  326. msg_err ("Error executing lua script %s: %s", ctx->file,
  327. lua_tostring (L, -1));
  328. exit (EXIT_SUCCESS);
  329. }
  330. if (ctx->cbref_accept == 0) {
  331. msg_err ("No accept function defined, so no reason to exist");
  332. exit (EXIT_SUCCESS);
  333. }
  334. rspamd_lua_run_postloads (ctx->cfg->lua_state, ctx->cfg, ctx->ev_base,
  335. worker);
  336. event_base_loop (ctx->ev_base, 0);
  337. rspamd_worker_block_signals ();
  338. luaL_unref (L, LUA_REGISTRYINDEX, ctx->cbref_accept);
  339. if (ctx->cbref_fin != 0) {
  340. /* Call finalizer function */
  341. lua_rawgeti (L, LUA_REGISTRYINDEX, ctx->cbref_fin);
  342. pctx = lua_newuserdata (L, sizeof (gpointer));
  343. rspamd_lua_setclass (L, "rspamd{lua_worker}", -1);
  344. *pctx = ctx;
  345. if (lua_pcall (L, 1, 0, 0) != 0) {
  346. msg_info ("call to worker finalizer failed: %s", lua_tostring (L,
  347. -1));
  348. lua_pop (L, 1);
  349. }
  350. /* Free resources */
  351. luaL_unref (L, LUA_REGISTRYINDEX, ctx->cbref_fin);
  352. }
  353. REF_RELEASE (ctx->cfg);
  354. rspamd_log_close (worker->srv->logger, TRUE);
  355. exit (EXIT_SUCCESS);
  356. }