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_repl.c 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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 "rspamadm.h"
  18. #include "libutil/http_connection.h"
  19. #include "libutil/http_private.h"
  20. #include "libutil/http_router.h"
  21. #include "printf.h"
  22. #include "lua/lua_common.h"
  23. #include "lua/lua_thread_pool.h"
  24. #include "message.h"
  25. #include "unix-std.h"
  26. #include "linenoise.h"
  27. #include "worker_util.h"
  28. #ifdef WITH_LUAJIT
  29. #include <luajit.h>
  30. #endif
  31. static gchar **paths = NULL;
  32. static gchar **scripts = NULL;
  33. static gchar **lua_args = NULL;
  34. static gchar *histfile = NULL;
  35. static guint max_history = 2000;
  36. static gchar *serve = NULL;
  37. static gchar *exec_line = NULL;
  38. static gint batch = -1;
  39. static gboolean per_line = FALSE;
  40. extern struct rspamd_async_session *rspamadm_session;
  41. static const char *default_history_file = ".rspamd_repl.hist";
  42. #ifdef WITH_LUAJIT
  43. #define MAIN_PROMPT LUAJIT_VERSION "> "
  44. #else
  45. #define MAIN_PROMPT LUA_VERSION "> "
  46. #endif
  47. #define MULTILINE_PROMPT "... "
  48. static void rspamadm_lua (gint argc, gchar **argv,
  49. const struct rspamadm_command *cmd);
  50. static const char *rspamadm_lua_help (gboolean full_help,
  51. const struct rspamadm_command *cmd);
  52. struct rspamadm_command lua_command = {
  53. .name = "lua",
  54. .flags = 0,
  55. .help = rspamadm_lua_help,
  56. .run = rspamadm_lua,
  57. .lua_subrs = NULL,
  58. };
  59. /*
  60. * Dot commands
  61. */
  62. typedef void (*rspamadm_lua_dot_handler)(lua_State *L, gint argc, gchar **argv);
  63. struct rspamadm_lua_dot_command {
  64. const gchar *name;
  65. const gchar *description;
  66. rspamadm_lua_dot_handler handler;
  67. };
  68. static void rspamadm_lua_help_handler (lua_State *L, gint argc, gchar **argv);
  69. static void rspamadm_lua_load_handler (lua_State *L, gint argc, gchar **argv);
  70. static void rspamadm_lua_exec_handler (lua_State *L, gint argc, gchar **argv);
  71. static void rspamadm_lua_message_handler (lua_State *L, gint argc, gchar **argv);
  72. static void lua_thread_error_cb (struct thread_entry *thread, int ret, const char *msg);
  73. static void lua_thread_finish_cb (struct thread_entry *thread, int ret);
  74. static struct rspamadm_lua_dot_command cmds[] = {
  75. {
  76. .name = "help",
  77. .description = "shows help for commands",
  78. .handler = rspamadm_lua_help_handler
  79. },
  80. {
  81. .name = "load",
  82. .description = "load lua file",
  83. .handler = rspamadm_lua_load_handler
  84. },
  85. {
  86. .name = "exec",
  87. .description = "exec lua file",
  88. .handler = rspamadm_lua_exec_handler
  89. },
  90. {
  91. .name = "message",
  92. .description = "scans message using specified callback: .message <callback_name> <file>...",
  93. .handler = rspamadm_lua_message_handler
  94. },
  95. };
  96. static GHashTable *cmds_hash = NULL;
  97. static GOptionEntry entries[] = {
  98. {"script", 's', 0, G_OPTION_ARG_STRING_ARRAY, &scripts,
  99. "Load specified scripts", NULL},
  100. {"path", 'P', 0, G_OPTION_ARG_STRING_ARRAY, &paths,
  101. "Add specified paths to lua paths", NULL},
  102. {"history-file", 'H', 0, G_OPTION_ARG_FILENAME, &histfile,
  103. "Load history from the specified file", NULL},
  104. {"max-history", 'm', 0, G_OPTION_ARG_INT, &max_history,
  105. "Store this number of history entries", NULL},
  106. {"serve", 'S', 0, G_OPTION_ARG_STRING, &serve,
  107. "Serve http lua server", NULL},
  108. {"batch", 'b', 0, G_OPTION_ARG_NONE, &batch,
  109. "Batch execution mode", NULL},
  110. {"per-line", 'p', 0, G_OPTION_ARG_NONE, &per_line,
  111. "Pass each line of input to the specified lua script", NULL},
  112. {"exec", 'e', 0, G_OPTION_ARG_STRING, &exec_line,
  113. "Execute specified script", NULL},
  114. {"args", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &lua_args,
  115. "Arguments to pass to Lua", NULL},
  116. {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
  117. };
  118. static const char *
  119. rspamadm_lua_help (gboolean full_help, const struct rspamadm_command *cmd)
  120. {
  121. const char *help_str;
  122. if (full_help) {
  123. help_str = "Run lua read/execute/print loop\n\n"
  124. "Usage: rspamadm lua [-P paths] [-s scripts]\n"
  125. "Where options are:\n\n"
  126. "-P: add additional lua paths (may be repeated)\n"
  127. "-p: split input to lines and feed each line to the script\n"
  128. "-s: load scripts on start from specified files (may be repeated)\n"
  129. "-S: listen on a specified address as HTTP server\n"
  130. "-a: pass argument to lua (may be repeated)\n"
  131. "-e: execute script specified in command line"
  132. "--help: shows available options and commands";
  133. }
  134. else {
  135. help_str = "Run LUA interpreter";
  136. }
  137. return help_str;
  138. }
  139. static void
  140. rspamadm_lua_add_path (lua_State *L, const gchar *path)
  141. {
  142. const gchar *old_path;
  143. gsize len;
  144. GString *new_path;
  145. lua_getglobal (L, "package");
  146. lua_getfield (L, -1, "path");
  147. old_path = luaL_checklstring (L, -1, &len);
  148. new_path = g_string_sized_new (len + strlen (path) + sizeof("/?.lua"));
  149. if (strstr (path, "?.lua") == NULL) {
  150. rspamd_printf_gstring (new_path, "%s/?.lua;%s", path, old_path);
  151. }
  152. else {
  153. rspamd_printf_gstring (new_path, "%s;%s", path, old_path);
  154. }
  155. lua_pushlstring (L, new_path->str, new_path->len);
  156. lua_setfield (L, -2, "path");
  157. lua_settop (L, 0);
  158. g_string_free (new_path, TRUE);
  159. }
  160. static void
  161. lua_thread_finish_cb (struct thread_entry *thread, int ret)
  162. {
  163. struct lua_call_data *cd = thread->cd;
  164. cd->ret = ret;
  165. }
  166. static void
  167. lua_thread_error_cb (struct thread_entry *thread, int ret, const char *msg)
  168. {
  169. struct lua_call_data *cd = thread->cd;
  170. rspamd_fprintf (stderr, "call failed: %s\n", msg);
  171. cd->ret = ret;
  172. }
  173. static void
  174. lua_thread_str_error_cb (struct thread_entry *thread, int ret, const char *msg)
  175. {
  176. struct lua_call_data *cd = thread->cd;
  177. const char *what = cd->ud;
  178. rspamd_fprintf (stderr, "call to %s failed: %s\n", what, msg);
  179. cd->ret = ret;
  180. }
  181. static gboolean
  182. rspamadm_lua_load_script (lua_State *L, const gchar *path)
  183. {
  184. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  185. L = thread->lua_state;
  186. if (luaL_loadfile (L, path) != 0) {
  187. rspamd_fprintf (stderr, "cannot load script %s: %s\n",
  188. path, lua_tostring (L, -1));
  189. lua_settop (L, 0);
  190. return FALSE;
  191. }
  192. if (!per_line) {
  193. if (lua_repl_thread_call (thread, 0, (void *)path, lua_thread_str_error_cb) != 0) {
  194. return FALSE;
  195. }
  196. lua_settop (L, 0);
  197. }
  198. return TRUE;
  199. }
  200. static void
  201. rspamadm_exec_input (lua_State *L, const gchar *input)
  202. {
  203. GString *tb;
  204. gint i, cbref;
  205. int top = 0;
  206. gchar outbuf[8192];
  207. struct lua_logger_trace tr;
  208. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  209. L = thread->lua_state;
  210. /* First try return + input */
  211. tb = g_string_sized_new (strlen (input) + sizeof ("return "));
  212. rspamd_printf_gstring (tb, "return %s", input);
  213. int r = luaL_loadstring (L, tb->str);
  214. if (r != 0) {
  215. /* Reset stack */
  216. lua_settop (L, 0);
  217. /* Try with no return */
  218. if (luaL_loadstring (L, input) != 0) {
  219. rspamd_fprintf (stderr, "cannot load string %s\n",
  220. input);
  221. g_string_free (tb, TRUE);
  222. lua_settop (L, 0);
  223. lua_thread_pool_return (rspamd_main->cfg->lua_thread_pool, thread);
  224. return;
  225. }
  226. }
  227. g_string_free (tb, TRUE);
  228. if (!per_line) {
  229. top = lua_gettop (L);
  230. if (lua_repl_thread_call (thread, 0, NULL, NULL) == 0) {
  231. /* Print output */
  232. for (i = top; i <= lua_gettop (L); i++) {
  233. if (lua_isfunction (L, i)) {
  234. lua_pushvalue (L, i);
  235. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  236. rspamd_printf ("local function: %d\n", cbref);
  237. } else {
  238. memset (&tr, 0, sizeof (tr));
  239. lua_logger_out_type (L, i, outbuf, sizeof (outbuf), &tr);
  240. rspamd_printf ("%s\n", outbuf);
  241. }
  242. }
  243. }
  244. }
  245. }
  246. static void
  247. wait_session_events (void)
  248. {
  249. /* XXX: it's probably worth to add timeout here - not to wait forever */
  250. while (rspamd_session_events_pending (rspamadm_session) > 0) {
  251. event_base_loop (rspamd_main->ev_base, EVLOOP_ONCE);
  252. }
  253. }
  254. gint
  255. lua_repl_thread_call (struct thread_entry *thread, gint narg, gpointer ud, lua_thread_error_t error_func)
  256. {
  257. int ret;
  258. struct lua_call_data *cd = g_new0 (struct lua_call_data, 1);
  259. cd->top = lua_gettop (thread->lua_state);
  260. cd->ud = ud;
  261. thread->finish_callback = lua_thread_finish_cb;
  262. if (error_func) {
  263. thread->error_callback = error_func;
  264. }
  265. else {
  266. thread->error_callback = lua_thread_error_cb;
  267. }
  268. thread->cd = cd;
  269. lua_thread_call (thread, narg);
  270. wait_session_events ();
  271. ret = cd->ret;
  272. g_free (cd);
  273. return ret;
  274. }
  275. static void
  276. rspamadm_lua_help_handler (lua_State *L, gint argc, gchar **argv)
  277. {
  278. guint i;
  279. struct rspamadm_lua_dot_command *cmd;
  280. if (argv[1] == NULL) {
  281. /* Print all commands */
  282. for (i = 0; i < G_N_ELEMENTS (cmds); i ++) {
  283. rspamd_printf ("%s: %s\n", cmds[i].name, cmds[i].description);
  284. }
  285. rspamd_printf ("{{: start multiline input\n");
  286. rspamd_printf ("}}: end multiline input\n");
  287. }
  288. else {
  289. for (i = 1; argv[i] != NULL; i ++) {
  290. cmd = g_hash_table_lookup (cmds_hash, argv[i]);
  291. if (cmd) {
  292. rspamd_printf ("%s: %s\n", cmds->name, cmds->description);
  293. }
  294. else {
  295. rspamd_printf ("%s: no such command\n", argv[i]);
  296. }
  297. }
  298. }
  299. }
  300. static void
  301. rspamadm_lua_load_handler (lua_State *L, gint argc, gchar **argv)
  302. {
  303. guint i;
  304. gboolean ret;
  305. for (i = 1; argv[i] != NULL; i ++) {
  306. ret = rspamadm_lua_load_script (L, argv[i]);
  307. rspamd_printf ("%s: %sloaded\n", argv[i], ret ? "" : "NOT ");
  308. }
  309. }
  310. static void
  311. rspamadm_lua_exec_handler (lua_State *L, gint argc, gchar **argv)
  312. {
  313. gint i;
  314. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  315. L = thread->lua_state;
  316. for (i = 1; argv[i] != NULL; i ++) {
  317. if (luaL_loadfile (L, argv[i]) != 0) {
  318. rspamd_fprintf (stderr, "cannot load script %s: %s\n",
  319. argv[i], lua_tostring (L, -1));
  320. lua_settop (L, 0);
  321. return;
  322. }
  323. lua_repl_thread_call (thread, 0, argv[i], lua_thread_str_error_cb);
  324. }
  325. }
  326. static void
  327. rspamadm_lua_message_handler (lua_State *L, gint argc, gchar **argv)
  328. {
  329. gulong cbref;
  330. gint old_top, func_idx, i, j;
  331. struct rspamd_task *task, **ptask;
  332. gpointer map;
  333. gsize len;
  334. gchar outbuf[8192];
  335. struct lua_logger_trace tr;
  336. if (argv[1] == NULL) {
  337. rspamd_printf ("no callback is specified\n");
  338. return;
  339. }
  340. for (i = 2; argv[i] != NULL; i ++) {
  341. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  342. L = thread->lua_state;
  343. if (rspamd_strtoul (argv[1], strlen (argv[1]), &cbref)) {
  344. lua_rawgeti (L, LUA_REGISTRYINDEX, cbref);
  345. }
  346. else {
  347. lua_getglobal (L, argv[1]);
  348. }
  349. if (lua_type (L, -1) != LUA_TFUNCTION) {
  350. rspamd_printf ("bad callback type: %s\n", lua_typename (L, lua_type (L, -1)));
  351. lua_thread_pool_return (rspamd_main->cfg->lua_thread_pool, thread);
  352. return;
  353. }
  354. /* Save index to reuse */
  355. func_idx = lua_gettop (L);
  356. map = rspamd_file_xmap (argv[i], PROT_READ, &len, TRUE);
  357. if (map == NULL) {
  358. rspamd_printf ("cannot open %s: %s\n", argv[i], strerror (errno));
  359. }
  360. else {
  361. task = rspamd_task_new (NULL, rspamd_main->cfg, NULL, NULL, NULL);
  362. if (!rspamd_task_load_message (task, NULL, map, len)) {
  363. rspamd_printf ("cannot load %s\n", argv[i]);
  364. rspamd_task_free (task);
  365. munmap (map, len);
  366. continue;
  367. }
  368. if (!rspamd_message_parse (task)) {
  369. rspamd_printf ("cannot parse %s: %e\n", argv[i], task->err);
  370. rspamd_task_free (task);
  371. munmap (map, len);
  372. continue;
  373. }
  374. rspamd_message_process (task);
  375. old_top = lua_gettop (L);
  376. lua_pushvalue (L, func_idx);
  377. ptask = lua_newuserdata (L, sizeof (*ptask));
  378. *ptask = task;
  379. rspamd_lua_setclass (L, "rspamd{task}", -1);
  380. if (lua_repl_thread_call (thread, 1, argv[i], lua_thread_str_error_cb) == 0) {
  381. rspamd_printf ("lua callback for %s returned:\n", argv[i]);
  382. for (j = old_top + 1; j <= lua_gettop (L); j ++) {
  383. memset (&tr, 0, sizeof (tr));
  384. lua_logger_out_type (L, j, outbuf, sizeof (outbuf), &tr);
  385. rspamd_printf ("%s\n", outbuf);
  386. }
  387. }
  388. rspamd_task_free (task);
  389. munmap (map, len);
  390. /* Pop all but the original function */
  391. lua_settop (L, func_idx);
  392. }
  393. }
  394. lua_settop (L, 0);
  395. }
  396. static gboolean
  397. rspamadm_lua_try_dot_command (lua_State *L, const gchar *input)
  398. {
  399. struct rspamadm_lua_dot_command *cmd;
  400. gchar **argv;
  401. argv = g_strsplit_set (input + 1, " ", -1);
  402. if (argv == NULL || argv[0] == NULL) {
  403. if (argv) {
  404. g_strfreev (argv);
  405. }
  406. return FALSE;
  407. }
  408. cmd = g_hash_table_lookup (cmds_hash, argv[0]);
  409. if (cmd) {
  410. cmd->handler (L, g_strv_length (argv), argv);
  411. g_strfreev (argv);
  412. return TRUE;
  413. }
  414. g_strfreev (argv);
  415. return FALSE;
  416. }
  417. static void
  418. rspamadm_lua_run_repl (lua_State *L)
  419. {
  420. gchar *input;
  421. gboolean is_multiline = FALSE;
  422. GString *tb;
  423. guint i;
  424. for (;;) {
  425. if (!is_multiline) {
  426. input = linenoise (MAIN_PROMPT);
  427. if (input == NULL) {
  428. return;
  429. }
  430. if (input[0] == '.') {
  431. if (rspamadm_lua_try_dot_command (L, input)) {
  432. linenoiseHistoryAdd (input);
  433. linenoiseFree (input);
  434. continue;
  435. }
  436. }
  437. if (strcmp (input, "{{") == 0) {
  438. is_multiline = TRUE;
  439. linenoiseFree (input);
  440. tb = g_string_sized_new (8192);
  441. continue;
  442. }
  443. rspamadm_exec_input (L, input);
  444. linenoiseHistoryAdd (input);
  445. linenoiseFree (input);
  446. lua_settop (L, 0);
  447. }
  448. else {
  449. input = linenoise (MULTILINE_PROMPT);
  450. if (input == NULL) {
  451. g_string_free (tb, TRUE);
  452. return;
  453. }
  454. if (strcmp (input, "}}") == 0) {
  455. is_multiline = FALSE;
  456. linenoiseFree (input);
  457. rspamadm_exec_input (L, tb->str);
  458. /* Replace \n with ' ' for sanity */
  459. for (i = 0; i < tb->len; i ++) {
  460. if (tb->str[i] == '\n') {
  461. tb->str[i] = ' ';
  462. }
  463. }
  464. linenoiseHistoryAdd (tb->str);
  465. g_string_free (tb, TRUE);
  466. }
  467. else {
  468. g_string_append (tb, input);
  469. g_string_append (tb, " \n");
  470. linenoiseFree (input);
  471. }
  472. }
  473. }
  474. }
  475. struct rspamadm_lua_repl_context {
  476. struct rspamd_http_connection_router *rt;
  477. lua_State *L;
  478. };
  479. struct rspamadm_lua_repl_session {
  480. struct rspamd_http_connection_router *rt;
  481. rspamd_inet_addr_t *addr;
  482. struct rspamadm_lua_repl_context *ctx;
  483. gint sock;
  484. };
  485. static void
  486. rspamadm_lua_accept_cb (gint fd, short what, void *arg)
  487. {
  488. struct rspamadm_lua_repl_context *ctx = arg;
  489. rspamd_inet_addr_t *addr;
  490. struct rspamadm_lua_repl_session *session;
  491. gint nfd;
  492. if ((nfd =
  493. rspamd_accept_from_socket (fd, &addr, NULL)) == -1) {
  494. rspamd_fprintf (stderr, "accept failed: %s", strerror (errno));
  495. return;
  496. }
  497. /* Check for EAGAIN */
  498. if (nfd == 0) {
  499. return;
  500. }
  501. session = g_malloc0 (sizeof (*session));
  502. session->rt = ctx->rt;
  503. session->ctx = ctx;
  504. session->addr = addr;
  505. session->sock = nfd;
  506. rspamd_http_router_handle_socket (ctx->rt, nfd, session);
  507. }
  508. static void
  509. rspamadm_lua_error_handler (struct rspamd_http_connection_entry *conn_ent,
  510. GError *err)
  511. {
  512. struct rspamadm_lua_repl_session *session = conn_ent->ud;
  513. rspamd_fprintf (stderr, "http error occurred: %s\n", err->message);
  514. }
  515. static void
  516. rspamadm_lua_finish_handler (struct rspamd_http_connection_entry *conn_ent)
  517. {
  518. struct rspamadm_lua_repl_session *session = conn_ent->ud;
  519. g_free (session);
  520. }
  521. static void
  522. lua_thread_http_error_cb (struct thread_entry *thread, int ret, const char *msg)
  523. {
  524. struct lua_call_data *cd = thread->cd;
  525. struct rspamd_http_connection_entry *conn_ent = cd->ud;
  526. rspamd_controller_send_error (conn_ent, 500, "call failed: %s\n", msg);
  527. cd->ret = ret;
  528. }
  529. /*
  530. * Exec command handler:
  531. * request: /exec
  532. * body: lua script
  533. * reply: json {"status": "ok", "reply": {<lua json object>}}
  534. */
  535. static int
  536. rspamadm_lua_handle_exec (struct rspamd_http_connection_entry *conn_ent,
  537. struct rspamd_http_message *msg)
  538. {
  539. GString *tb;
  540. gint err_idx, i;
  541. lua_State *L;
  542. struct rspamadm_lua_repl_context *ctx;
  543. struct rspamadm_lua_repl_session *session = conn_ent->ud;
  544. ucl_object_t *obj, *elt;
  545. const gchar *body;
  546. gsize body_len;
  547. ctx = session->ctx;
  548. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  549. L = thread->lua_state;
  550. body = rspamd_http_message_get_body (msg, &body_len);
  551. if (body == NULL) {
  552. rspamd_controller_send_error (conn_ent, 400, "Empty lua script");
  553. return 0;
  554. }
  555. lua_pushcfunction (L, &rspamd_lua_traceback);
  556. err_idx = lua_gettop (L);
  557. /* First try return + input */
  558. tb = g_string_sized_new (body_len + sizeof ("return "));
  559. rspamd_printf_gstring (tb, "return %*s", (gint)body_len, body);
  560. if (luaL_loadstring (L, tb->str) != 0) {
  561. /* Reset stack */
  562. lua_settop (L, 0);
  563. lua_pushcfunction (L, &rspamd_lua_traceback);
  564. err_idx = lua_gettop (L);
  565. /* Try with no return */
  566. if (luaL_loadbuffer (L, body, body_len, "http input") != 0) {
  567. rspamd_controller_send_error (conn_ent, 400, "Invalid lua script");
  568. return 0;
  569. }
  570. }
  571. g_string_free (tb, TRUE);
  572. if (lua_repl_thread_call (thread, 0, conn_ent, lua_thread_http_error_cb) != 0) {
  573. return 0;
  574. }
  575. obj = ucl_object_typed_new (UCL_ARRAY);
  576. for (i = err_idx + 1; i <= lua_gettop (L); i ++) {
  577. if (lua_isfunction (L, i)) {
  578. /* XXX: think about API */
  579. }
  580. else {
  581. elt = ucl_object_lua_import (L, i);
  582. if (elt) {
  583. ucl_array_append (obj, elt);
  584. }
  585. }
  586. }
  587. rspamd_controller_send_ucl (conn_ent, obj);
  588. ucl_object_unref (obj);
  589. lua_settop (L, 0);
  590. return 0;
  591. }
  592. static void
  593. rspamadm_lua (gint argc, gchar **argv, const struct rspamadm_command *cmd)
  594. {
  595. GOptionContext *context;
  596. GError *error = NULL;
  597. gchar **elt;
  598. guint i;
  599. lua_State *L = rspamd_main->cfg->lua_state;
  600. context = g_option_context_new ("lua - run lua interpreter");
  601. g_option_context_set_summary (context,
  602. "Summary:\n Rspamd administration utility version "
  603. RVERSION
  604. "\n Release id: "
  605. RID);
  606. g_option_context_add_main_entries (context, entries, NULL);
  607. if (!g_option_context_parse (context, &argc, &argv, &error)) {
  608. fprintf (stderr, "option parsing failed: %s\n", error->message);
  609. g_error_free (error);
  610. exit (1);
  611. }
  612. if (batch == -1) {
  613. if (isatty (STDIN_FILENO)) {
  614. batch = 0;
  615. }
  616. else {
  617. batch = 1;
  618. }
  619. }
  620. if (paths) {
  621. for (elt = paths; *elt != NULL; elt ++) {
  622. rspamadm_lua_add_path (L, *elt);
  623. }
  624. }
  625. if (lua_args) {
  626. i = 1;
  627. lua_newtable (L);
  628. for (elt = lua_args; *elt != NULL; elt ++) {
  629. lua_pushinteger (L, i);
  630. lua_pushstring (L, *elt);
  631. lua_settable (L, -3);
  632. i++;
  633. }
  634. lua_setglobal (L, "arg");
  635. }
  636. if (scripts) {
  637. for (elt = scripts; *elt != NULL; elt ++) {
  638. if (!rspamadm_lua_load_script (L, *elt)) {
  639. exit (EXIT_FAILURE);
  640. }
  641. }
  642. }
  643. if (exec_line) {
  644. rspamadm_exec_input (L, exec_line);
  645. }
  646. if (serve) {
  647. /* HTTP Server mode */
  648. GPtrArray *addrs = NULL;
  649. gchar *name = NULL;
  650. struct event_base *ev_base;
  651. struct rspamd_http_connection_router *http;
  652. gint fd;
  653. struct rspamadm_lua_repl_context *ctx;
  654. if (!rspamd_parse_host_port_priority (serve, &addrs, NULL, &name,
  655. 10000, NULL)) {
  656. fprintf (stderr, "cannot listen on %s", serve);
  657. exit (EXIT_FAILURE);
  658. }
  659. ev_base = rspamd_main->ev_base;
  660. ctx = g_malloc0 (sizeof (*ctx));
  661. http = rspamd_http_router_new (rspamadm_lua_error_handler,
  662. rspamadm_lua_finish_handler,
  663. NULL, ev_base,
  664. NULL, NULL);
  665. ctx->L = L;
  666. ctx->rt = http;
  667. rspamd_http_router_add_path (http,
  668. "/exec",
  669. rspamadm_lua_handle_exec);
  670. for (i = 0; i < addrs->len; i ++) {
  671. rspamd_inet_addr_t *addr = g_ptr_array_index (addrs, i);
  672. fd = rspamd_inet_address_listen (addr, SOCK_STREAM, TRUE);
  673. if (fd != -1) {
  674. struct event *ev;
  675. ev = g_malloc0 (sizeof (*ev));
  676. event_set (ev, fd, EV_READ|EV_PERSIST, rspamadm_lua_accept_cb,
  677. ctx);
  678. event_base_set (ev_base, ev);
  679. event_add (ev, NULL);
  680. rspamd_printf ("listen on %s\n",
  681. rspamd_inet_address_to_string_pretty (addr));
  682. }
  683. }
  684. event_base_loop (ev_base, 0);
  685. exit (EXIT_SUCCESS);
  686. }
  687. if (histfile == NULL) {
  688. const gchar *homedir;
  689. GString *hist_path;
  690. homedir = getenv ("HOME");
  691. if (homedir) {
  692. hist_path = g_string_sized_new (strlen (homedir) +
  693. strlen (default_history_file) + 1);
  694. rspamd_printf_gstring (hist_path, "%s/%s", homedir,
  695. default_history_file);
  696. }
  697. else {
  698. hist_path = g_string_sized_new (strlen (default_history_file) + 2);
  699. rspamd_printf_gstring (hist_path, "./%s", default_history_file);
  700. }
  701. histfile = hist_path->str;
  702. g_string_free (hist_path, FALSE);
  703. }
  704. if (argc > 1) {
  705. for (i = 1; i < argc; i ++) {
  706. if (!rspamadm_lua_load_script (L, argv[i])) {
  707. exit (EXIT_FAILURE);
  708. }
  709. }
  710. exit (EXIT_SUCCESS);
  711. }
  712. /* Init dot commands */
  713. cmds_hash = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal);
  714. for (i = 0; i < G_N_ELEMENTS (cmds); i ++) {
  715. g_hash_table_insert (cmds_hash, (gpointer)cmds[i].name, &cmds[i]);
  716. }
  717. if (per_line) {
  718. GIOChannel *in;
  719. GString *buf;
  720. gsize end_pos;
  721. GIOStatus ret;
  722. gint old_top;
  723. GError *err = NULL;
  724. in = g_io_channel_unix_new (STDIN_FILENO);
  725. buf = g_string_sized_new (BUFSIZ);
  726. again:
  727. while ((ret = g_io_channel_read_line_string (in, buf, &end_pos, &err)) ==
  728. G_IO_STATUS_NORMAL) {
  729. old_top = lua_gettop (L);
  730. lua_pushvalue (L, -1);
  731. lua_pushlstring (L, buf->str, MIN (buf->len, end_pos));
  732. lua_setglobal (L, "input");
  733. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  734. L = thread->lua_state;
  735. lua_repl_thread_call (thread, 0, NULL, NULL);
  736. lua_settop (L, old_top);
  737. }
  738. if (ret == G_IO_STATUS_AGAIN) {
  739. goto again;
  740. }
  741. g_string_free (buf, TRUE);
  742. g_io_channel_shutdown (in, FALSE, NULL);
  743. if (ret == G_IO_STATUS_EOF) {
  744. if (err) {
  745. g_error_free (err);
  746. }
  747. }
  748. else {
  749. rspamd_fprintf (stderr, "IO error: %e\n", err);
  750. if (err) {
  751. g_error_free (err);
  752. }
  753. exit (-errno);
  754. }
  755. }
  756. else {
  757. if (!batch) {
  758. linenoiseHistorySetMaxLen (max_history);
  759. linenoiseHistoryLoad (histfile);
  760. rspamadm_lua_run_repl (L);
  761. linenoiseHistorySave (histfile);
  762. } else {
  763. rspamadm_lua_run_repl (L);
  764. }
  765. }
  766. }