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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  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 "libserver/http/http_connection.h"
  19. #include "libserver/http/http_private.h"
  20. #include "libserver/http/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. #ifdef WITH_LUA_REPL
  27. #include "replxx.h"
  28. #endif
  29. #include "worker_util.h"
  30. #ifdef WITH_LUAJIT
  31. #include <luajit.h>
  32. #endif
  33. static gchar **paths = NULL;
  34. static gchar **scripts = NULL;
  35. static gchar **lua_args = NULL;
  36. static gchar *histfile = NULL;
  37. static guint max_history = 2000;
  38. static gchar *serve = NULL;
  39. static gchar *exec_line = NULL;
  40. static gint batch = -1;
  41. extern struct rspamd_async_session *rspamadm_session;
  42. static const char *default_history_file = ".rspamd_repl.hist";
  43. #ifdef WITH_LUA_REPL
  44. static Replxx *rx_instance = NULL;
  45. #endif
  46. #ifdef WITH_LUAJIT
  47. #define MAIN_PROMPT LUAJIT_VERSION "> "
  48. #else
  49. #define MAIN_PROMPT LUA_VERSION "> "
  50. #endif
  51. #define MULTILINE_PROMPT "... "
  52. static void rspamadm_lua (gint argc, gchar **argv,
  53. const struct rspamadm_command *cmd);
  54. static const char *rspamadm_lua_help (gboolean full_help,
  55. const struct rspamadm_command *cmd);
  56. struct rspamadm_command lua_command = {
  57. .name = "lua",
  58. .flags = 0,
  59. .help = rspamadm_lua_help,
  60. .run = rspamadm_lua,
  61. .lua_subrs = NULL,
  62. };
  63. /*
  64. * Dot commands
  65. */
  66. typedef void (*rspamadm_lua_dot_handler)(lua_State *L, gint argc, gchar **argv);
  67. struct rspamadm_lua_dot_command {
  68. const gchar *name;
  69. const gchar *description;
  70. rspamadm_lua_dot_handler handler;
  71. };
  72. static void rspamadm_lua_help_handler (lua_State *L, gint argc, gchar **argv);
  73. static void rspamadm_lua_load_handler (lua_State *L, gint argc, gchar **argv);
  74. static void rspamadm_lua_exec_handler (lua_State *L, gint argc, gchar **argv);
  75. static void rspamadm_lua_message_handler (lua_State *L, gint argc, gchar **argv);
  76. static void lua_thread_error_cb (struct thread_entry *thread, int ret, const char *msg);
  77. static void lua_thread_finish_cb (struct thread_entry *thread, int ret);
  78. static struct rspamadm_lua_dot_command cmds[] = {
  79. {
  80. .name = "help",
  81. .description = "shows help for commands",
  82. .handler = rspamadm_lua_help_handler
  83. },
  84. {
  85. .name = "load",
  86. .description = "load lua file",
  87. .handler = rspamadm_lua_load_handler
  88. },
  89. {
  90. .name = "exec",
  91. .description = "exec lua file",
  92. .handler = rspamadm_lua_exec_handler
  93. },
  94. {
  95. .name = "message",
  96. .description = "scans message using specified callback: .message <callback_name> <file>...",
  97. .handler = rspamadm_lua_message_handler
  98. },
  99. };
  100. static GHashTable *cmds_hash = NULL;
  101. static GOptionEntry entries[] = {
  102. {"script", 's', 0, G_OPTION_ARG_STRING_ARRAY, &scripts,
  103. "Load specified scripts", NULL},
  104. {"path", 'P', 0, G_OPTION_ARG_STRING_ARRAY, &paths,
  105. "Add specified paths to lua paths", NULL},
  106. {"history-file", 'H', 0, G_OPTION_ARG_FILENAME, &histfile,
  107. "Load history from the specified file", NULL},
  108. {"max-history", 'm', 0, G_OPTION_ARG_INT, &max_history,
  109. "Store this number of history entries", NULL},
  110. {"serve", 'S', 0, G_OPTION_ARG_STRING, &serve,
  111. "Serve http lua server", NULL},
  112. {"batch", 'b', 0, G_OPTION_ARG_NONE, &batch,
  113. "Batch execution mode", NULL},
  114. {"exec", 'e', 0, G_OPTION_ARG_STRING, &exec_line,
  115. "Execute specified script", NULL},
  116. {"args", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &lua_args,
  117. "Arguments to pass to Lua", NULL},
  118. {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
  119. };
  120. static const char *
  121. rspamadm_lua_help (gboolean full_help, const struct rspamadm_command *cmd)
  122. {
  123. const char *help_str;
  124. if (full_help) {
  125. help_str = "Run lua read/execute/print loop\n\n"
  126. "Usage: rspamadm lua [-P paths] [-s scripts]\n"
  127. "Where options are:\n\n"
  128. "-P: add additional lua paths (may be repeated)\n"
  129. "-p: split input to lines and feed each line to the script\n"
  130. "-s: load scripts on start from specified files (may be repeated)\n"
  131. "-S: listen on a specified address as HTTP server\n"
  132. "-a: pass argument to lua (may be repeated)\n"
  133. "-e: execute script specified in command line"
  134. "--help: shows available options and commands";
  135. }
  136. else {
  137. help_str = "Run LUA interpreter";
  138. }
  139. return help_str;
  140. }
  141. static void
  142. rspamadm_lua_add_path (lua_State *L, const gchar *path)
  143. {
  144. const gchar *old_path;
  145. gsize len;
  146. GString *new_path;
  147. lua_getglobal (L, "package");
  148. lua_getfield (L, -1, "path");
  149. old_path = luaL_checklstring (L, -1, &len);
  150. new_path = g_string_sized_new (len + strlen (path) + sizeof("/?.lua"));
  151. if (strstr (path, "?.lua") == NULL) {
  152. rspamd_printf_gstring (new_path, "%s/?.lua;%s", path, old_path);
  153. }
  154. else {
  155. rspamd_printf_gstring (new_path, "%s;%s", path, old_path);
  156. }
  157. lua_pushlstring (L, new_path->str, new_path->len);
  158. lua_setfield (L, -2, "path");
  159. lua_settop (L, 0);
  160. g_string_free (new_path, TRUE);
  161. }
  162. static void
  163. lua_thread_finish_cb (struct thread_entry *thread, int ret)
  164. {
  165. struct lua_call_data *cd = thread->cd;
  166. cd->ret = ret;
  167. }
  168. static void
  169. lua_thread_error_cb (struct thread_entry *thread, int ret, const char *msg)
  170. {
  171. struct lua_call_data *cd = thread->cd;
  172. rspamd_fprintf (stderr, "call failed: %s\n", msg);
  173. cd->ret = ret;
  174. }
  175. static void
  176. lua_thread_str_error_cb (struct thread_entry *thread, int ret, const char *msg)
  177. {
  178. struct lua_call_data *cd = thread->cd;
  179. const char *what = cd->ud;
  180. rspamd_fprintf (stderr, "call to %s failed: %s\n", what, msg);
  181. cd->ret = ret;
  182. }
  183. static gboolean
  184. rspamadm_lua_load_script (lua_State *L, const gchar *path)
  185. {
  186. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  187. L = thread->lua_state;
  188. if (luaL_loadfile (L, path) != 0) {
  189. rspamd_fprintf (stderr, "cannot load script %s: %s\n",
  190. path, lua_tostring (L, -1));
  191. lua_settop (L, 0);
  192. return FALSE;
  193. }
  194. if (lua_repl_thread_call (thread, 0, (void *)path, lua_thread_str_error_cb) != 0) {
  195. return FALSE;
  196. }
  197. lua_settop (L, 0);
  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. top = lua_gettop (L);
  229. if (lua_repl_thread_call (thread, 0, NULL, NULL) == 0) {
  230. /* Print output */
  231. for (i = top; i <= lua_gettop (L); i++) {
  232. if (lua_isfunction (L, i)) {
  233. lua_pushvalue (L, i);
  234. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  235. rspamd_printf ("local function: %d\n", cbref);
  236. } else {
  237. memset (&tr, 0, sizeof (tr));
  238. lua_logger_out_type (L, i, outbuf, sizeof (outbuf) - 1, &tr,
  239. LUA_ESCAPE_UNPRINTABLE);
  240. rspamd_printf ("%s\n", outbuf);
  241. }
  242. }
  243. }
  244. }
  245. static void
  246. wait_session_events (void)
  247. {
  248. /* XXX: it's probably worth to add timeout here - not to wait forever */
  249. while (rspamd_session_events_pending (rspamadm_session) > 0) {
  250. ev_loop (rspamd_main->event_loop, EVRUN_ONCE);
  251. }
  252. }
  253. gint
  254. lua_repl_thread_call (struct thread_entry *thread, gint narg, gpointer ud, lua_thread_error_t error_func)
  255. {
  256. int ret;
  257. struct lua_call_data *cd = g_new0 (struct lua_call_data, 1);
  258. cd->top = lua_gettop (thread->lua_state);
  259. cd->ud = ud;
  260. thread->finish_callback = lua_thread_finish_cb;
  261. if (error_func) {
  262. thread->error_callback = error_func;
  263. }
  264. else {
  265. thread->error_callback = lua_thread_error_cb;
  266. }
  267. thread->cd = cd;
  268. lua_thread_call (thread, narg);
  269. wait_session_events ();
  270. ret = cd->ret;
  271. g_free (cd);
  272. return ret;
  273. }
  274. static void
  275. rspamadm_lua_help_handler (lua_State *L, gint argc, gchar **argv)
  276. {
  277. guint i;
  278. struct rspamadm_lua_dot_command *cmd;
  279. if (argv[1] == NULL) {
  280. /* Print all commands */
  281. for (i = 0; i < G_N_ELEMENTS (cmds); i ++) {
  282. rspamd_printf ("%s: %s\n", cmds[i].name, cmds[i].description);
  283. }
  284. rspamd_printf ("{{: start multiline input\n");
  285. rspamd_printf ("}}: end multiline input\n");
  286. }
  287. else {
  288. for (i = 1; argv[i] != NULL; i ++) {
  289. cmd = g_hash_table_lookup (cmds_hash, argv[i]);
  290. if (cmd) {
  291. rspamd_printf ("%s: %s\n", cmds->name, cmds->description);
  292. }
  293. else {
  294. rspamd_printf ("%s: no such command\n", argv[i]);
  295. }
  296. }
  297. }
  298. }
  299. static void
  300. rspamadm_lua_load_handler (lua_State *L, gint argc, gchar **argv)
  301. {
  302. guint i;
  303. gboolean ret;
  304. for (i = 1; argv[i] != NULL; i ++) {
  305. ret = rspamadm_lua_load_script (L, argv[i]);
  306. rspamd_printf ("%s: %sloaded\n", argv[i], ret ? "" : "NOT ");
  307. }
  308. }
  309. static void
  310. rspamadm_lua_exec_handler (lua_State *L, gint argc, gchar **argv)
  311. {
  312. gint i;
  313. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  314. L = thread->lua_state;
  315. for (i = 1; argv[i] != NULL; i ++) {
  316. if (luaL_loadfile (L, argv[i]) != 0) {
  317. rspamd_fprintf (stderr, "cannot load script %s: %s\n",
  318. argv[i], lua_tostring (L, -1));
  319. lua_settop (L, 0);
  320. return;
  321. }
  322. lua_repl_thread_call (thread, 0, argv[i], lua_thread_str_error_cb);
  323. }
  324. }
  325. static void
  326. rspamadm_lua_message_handler (lua_State *L, gint argc, gchar **argv)
  327. {
  328. gulong cbref;
  329. gint old_top, func_idx, i, j;
  330. struct rspamd_task *task, **ptask;
  331. gpointer map;
  332. gsize len;
  333. gchar outbuf[8192];
  334. struct lua_logger_trace tr;
  335. if (argv[1] == NULL) {
  336. rspamd_printf ("no callback is specified\n");
  337. return;
  338. }
  339. for (i = 2; argv[i] != NULL; i ++) {
  340. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  341. L = thread->lua_state;
  342. if (rspamd_strtoul (argv[1], strlen (argv[1]), &cbref)) {
  343. lua_rawgeti (L, LUA_REGISTRYINDEX, cbref);
  344. }
  345. else {
  346. lua_getglobal (L, argv[1]);
  347. }
  348. if (lua_type (L, -1) != LUA_TFUNCTION) {
  349. rspamd_printf ("bad callback type: %s\n", lua_typename (L, lua_type (L, -1)));
  350. lua_thread_pool_return (rspamd_main->cfg->lua_thread_pool, thread);
  351. return;
  352. }
  353. /* Save index to reuse */
  354. func_idx = lua_gettop (L);
  355. map = rspamd_file_xmap (argv[i], PROT_READ, &len, TRUE);
  356. if (map == NULL) {
  357. rspamd_printf ("cannot open %s: %s\n", argv[i], strerror (errno));
  358. }
  359. else {
  360. task = rspamd_task_new (NULL, rspamd_main->cfg, NULL, NULL, NULL, FALSE);
  361. if (!rspamd_task_load_message (task, NULL, map, len)) {
  362. rspamd_printf ("cannot load %s\n", argv[i]);
  363. rspamd_task_free (task);
  364. munmap (map, len);
  365. continue;
  366. }
  367. if (!rspamd_message_parse (task)) {
  368. rspamd_printf ("cannot parse %s: %e\n", argv[i], task->err);
  369. rspamd_task_free (task);
  370. munmap (map, len);
  371. continue;
  372. }
  373. rspamd_message_process (task);
  374. old_top = lua_gettop (L);
  375. lua_pushvalue (L, func_idx);
  376. ptask = lua_newuserdata (L, sizeof (*ptask));
  377. *ptask = task;
  378. rspamd_lua_setclass (L, "rspamd{task}", -1);
  379. if (lua_repl_thread_call (thread, 1, argv[i], lua_thread_str_error_cb) == 0) {
  380. rspamd_printf ("lua callback for %s returned:\n", argv[i]);
  381. for (j = old_top + 1; j <= lua_gettop (L); j ++) {
  382. memset (&tr, 0, sizeof (tr));
  383. lua_logger_out_type (L, j, outbuf, sizeof (outbuf), &tr,
  384. LUA_ESCAPE_UNPRINTABLE);
  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. #ifdef WITH_LUA_REPL
  418. static gint lex_ref_idx = -1;
  419. static void
  420. lua_syntax_highlighter (const char *str, ReplxxColor *colours, int size, void *ud)
  421. {
  422. lua_State *L = (lua_State *)ud;
  423. if (lex_ref_idx == -1) {
  424. if (!rspamd_lua_require_function (L, "lua_lexer", "lex_to_table")) {
  425. fprintf (stderr, "cannot require lua_lexer!\n");
  426. exit (EXIT_FAILURE);
  427. }
  428. lex_ref_idx = luaL_ref (L, LUA_REGISTRYINDEX);
  429. }
  430. lua_rawgeti (L, LUA_REGISTRYINDEX, lex_ref_idx);
  431. lua_pushstring (L, str);
  432. if (lua_pcall (L, 1, 1, 0) != 0) {
  433. fprintf (stderr, "cannot lex a string!\n");
  434. }
  435. else {
  436. /* Process what we have after lexing */
  437. gsize nelts = rspamd_lua_table_size (L, -1);
  438. for (gsize i = 0; i < nelts; i ++) {
  439. /*
  440. * Indexes in the table:
  441. * 1 - type of element (string)
  442. * 2 - text (string)
  443. * 3 - line num (int), always 1...
  444. * 4 - column num (must be less than size)
  445. */
  446. const gchar *what;
  447. gsize column, tlen, cur_top, elt_pos;
  448. ReplxxColor elt_color = REPLXX_COLOR_DEFAULT;
  449. cur_top = lua_gettop (L);
  450. lua_rawgeti (L, -1, i + 1);
  451. elt_pos = lua_gettop (L);
  452. lua_rawgeti (L, elt_pos, 1);
  453. what = lua_tostring (L, -1);
  454. lua_rawgeti (L, elt_pos, 2);
  455. lua_tolstring (L, -1, &tlen);
  456. lua_rawgeti (L, elt_pos, 4);
  457. column = lua_tointeger (L, -1);
  458. g_assert (column > 0);
  459. column --; /* Start from 0 */
  460. if (column + tlen > size) {
  461. /* Likely utf8 case, too complicated to match */
  462. lua_settop (L, cur_top);
  463. continue;
  464. }
  465. /* Check what and adjust color */
  466. if (strcmp (what, "identifier") == 0) {
  467. elt_color = REPLXX_COLOR_NORMAL;
  468. }
  469. else if (strcmp (what, "number") == 0) {
  470. elt_color = REPLXX_COLOR_BLUE;
  471. }
  472. else if (strcmp (what, "string") == 0) {
  473. elt_color = REPLXX_COLOR_GREEN;
  474. }
  475. else if (strcmp (what, "keyword") == 0) {
  476. elt_color = REPLXX_COLOR_WHITE;
  477. }
  478. else if (strcmp (what, "constant") == 0) {
  479. elt_color = REPLXX_COLOR_WHITE;
  480. }
  481. else if (strcmp (what, "operator") == 0) {
  482. elt_color = REPLXX_COLOR_CYAN;
  483. }
  484. else if (strcmp (what, "comment") == 0) {
  485. elt_color = REPLXX_COLOR_BRIGHTGREEN;
  486. }
  487. else if (strcmp (what, "error") == 0) {
  488. elt_color = REPLXX_COLOR_ERROR;
  489. }
  490. for (gsize j = column; j < column + tlen; j ++) {
  491. colours[j] = elt_color;
  492. }
  493. /* Restore stack */
  494. lua_settop (L, cur_top);
  495. }
  496. }
  497. lua_settop (L, 0);
  498. }
  499. #endif
  500. static void
  501. rspamadm_lua_run_repl (lua_State *L, bool is_batch)
  502. {
  503. gchar *input;
  504. #ifdef WITH_LUA_REPL
  505. gboolean is_multiline = FALSE;
  506. GString *tb = NULL;
  507. gsize i;
  508. #endif
  509. for (;;) {
  510. #ifndef WITH_LUA_REPL
  511. size_t linecap = 0;
  512. ssize_t linelen;
  513. fprintf (stdout, "%s ", MAIN_PROMPT);
  514. linelen = getline (&input, &linecap, stdin);
  515. if (linelen > 0) {
  516. if (input[linelen - 1] == '\n') {
  517. linelen --;
  518. }
  519. rspamadm_exec_input (L, input);
  520. }
  521. else {
  522. break;
  523. }
  524. lua_settop (L, 0);
  525. #else
  526. if (!is_batch) {
  527. replxx_set_highlighter_callback (rx_instance, lua_syntax_highlighter,
  528. L);
  529. }
  530. if (!is_multiline) {
  531. input = (gchar *)replxx_input (rx_instance, MAIN_PROMPT);
  532. if (input == NULL) {
  533. return;
  534. }
  535. if (input[0] == '.') {
  536. if (rspamadm_lua_try_dot_command (L, input)) {
  537. if (!is_batch) {
  538. replxx_history_add (rx_instance, input);
  539. }
  540. continue;
  541. }
  542. }
  543. if (strcmp (input, "{{") == 0) {
  544. is_multiline = TRUE;
  545. tb = g_string_sized_new (8192);
  546. continue;
  547. }
  548. rspamadm_exec_input (L, input);
  549. if (!is_batch) {
  550. replxx_history_add (rx_instance, input);
  551. }
  552. lua_settop (L, 0);
  553. }
  554. else {
  555. input = (gchar *)replxx_input (rx_instance, MULTILINE_PROMPT);
  556. if (input == NULL) {
  557. g_string_free (tb, TRUE);
  558. return;
  559. }
  560. if (strcmp (input, "}}") == 0) {
  561. is_multiline = FALSE;
  562. rspamadm_exec_input (L, tb->str);
  563. /* Replace \n with ' ' for sanity */
  564. for (i = 0; i < tb->len; i ++) {
  565. if (tb->str[i] == '\n') {
  566. tb->str[i] = ' ';
  567. }
  568. }
  569. if (!is_batch) {
  570. replxx_history_add (rx_instance, tb->str);
  571. }
  572. g_string_free (tb, TRUE);
  573. }
  574. else {
  575. g_string_append (tb, input);
  576. g_string_append (tb, " \n");
  577. }
  578. }
  579. #endif
  580. }
  581. }
  582. struct rspamadm_lua_repl_context {
  583. struct rspamd_http_connection_router *rt;
  584. lua_State *L;
  585. };
  586. struct rspamadm_lua_repl_session {
  587. struct rspamd_http_connection_router *rt;
  588. rspamd_inet_addr_t *addr;
  589. struct rspamadm_lua_repl_context *ctx;
  590. gint sock;
  591. };
  592. static void
  593. rspamadm_lua_accept_cb (EV_P_ ev_io *w, int revents)
  594. {
  595. struct rspamadm_lua_repl_context *ctx =
  596. (struct rspamadm_lua_repl_context *)w->data;
  597. rspamd_inet_addr_t *addr;
  598. struct rspamadm_lua_repl_session *session;
  599. gint nfd;
  600. if ((nfd =
  601. rspamd_accept_from_socket (w->fd, &addr, NULL, NULL)) == -1) {
  602. rspamd_fprintf (stderr, "accept failed: %s", strerror (errno));
  603. return;
  604. }
  605. /* Check for EAGAIN */
  606. if (nfd == 0) {
  607. return;
  608. }
  609. session = g_malloc0 (sizeof (*session));
  610. session->rt = ctx->rt;
  611. session->ctx = ctx;
  612. session->addr = addr;
  613. session->sock = nfd;
  614. rspamd_http_router_handle_socket (ctx->rt, nfd, session);
  615. }
  616. static void
  617. rspamadm_lua_error_handler (struct rspamd_http_connection_entry *conn_ent,
  618. GError *err)
  619. {
  620. rspamd_fprintf (stderr, "http error occurred: %s\n", err->message);
  621. }
  622. static void
  623. rspamadm_lua_finish_handler (struct rspamd_http_connection_entry *conn_ent)
  624. {
  625. struct rspamadm_lua_repl_session *session = conn_ent->ud;
  626. g_free (session);
  627. }
  628. static void
  629. lua_thread_http_error_cb (struct thread_entry *thread, int ret, const char *msg)
  630. {
  631. struct lua_call_data *cd = thread->cd;
  632. struct rspamd_http_connection_entry *conn_ent = cd->ud;
  633. rspamd_controller_send_error (conn_ent, 500, "call failed: %s\n", msg);
  634. cd->ret = ret;
  635. }
  636. /*
  637. * Exec command handler:
  638. * request: /exec
  639. * body: lua script
  640. * reply: json {"status": "ok", "reply": {<lua json object>}}
  641. */
  642. static int
  643. rspamadm_lua_handle_exec (struct rspamd_http_connection_entry *conn_ent,
  644. struct rspamd_http_message *msg)
  645. {
  646. GString *tb;
  647. gint err_idx, i;
  648. lua_State *L;
  649. ucl_object_t *obj, *elt;
  650. const gchar *body;
  651. gsize body_len;
  652. struct thread_entry *thread = lua_thread_pool_get_for_config (rspamd_main->cfg);
  653. L = thread->lua_state;
  654. body = rspamd_http_message_get_body (msg, &body_len);
  655. if (body == NULL) {
  656. rspamd_controller_send_error (conn_ent, 400, "Empty lua script");
  657. return 0;
  658. }
  659. lua_pushcfunction (L, &rspamd_lua_traceback);
  660. err_idx = lua_gettop (L);
  661. /* First try return + input */
  662. tb = g_string_sized_new (body_len + sizeof ("return "));
  663. rspamd_printf_gstring (tb, "return %*s", (gint)body_len, body);
  664. if (luaL_loadstring (L, tb->str) != 0) {
  665. /* Reset stack */
  666. lua_settop (L, 0);
  667. lua_pushcfunction (L, &rspamd_lua_traceback);
  668. err_idx = lua_gettop (L);
  669. /* Try with no return */
  670. if (luaL_loadbuffer (L, body, body_len, "http input") != 0) {
  671. rspamd_controller_send_error (conn_ent, 400, "Invalid lua script");
  672. return 0;
  673. }
  674. }
  675. g_string_free (tb, TRUE);
  676. if (lua_repl_thread_call (thread, 0, conn_ent, lua_thread_http_error_cb) != 0) {
  677. return 0;
  678. }
  679. obj = ucl_object_typed_new (UCL_ARRAY);
  680. for (i = err_idx + 1; i <= lua_gettop (L); i ++) {
  681. if (lua_isfunction (L, i)) {
  682. /* XXX: think about API */
  683. }
  684. else {
  685. elt = ucl_object_lua_import (L, i);
  686. if (elt) {
  687. ucl_array_append (obj, elt);
  688. }
  689. }
  690. }
  691. rspamd_controller_send_ucl (conn_ent, obj);
  692. ucl_object_unref (obj);
  693. lua_settop (L, 0);
  694. return 0;
  695. }
  696. static void
  697. rspamadm_lua (gint argc, gchar **argv, const struct rspamadm_command *cmd)
  698. {
  699. GOptionContext *context;
  700. GError *error = NULL;
  701. gchar **elt;
  702. guint i;
  703. lua_State *L = rspamd_main->cfg->lua_state;
  704. context = g_option_context_new ("lua - run lua interpreter");
  705. g_option_context_set_summary (context,
  706. "Summary:\n Rspamd administration utility version "
  707. RVERSION
  708. "\n Release id: "
  709. RID);
  710. g_option_context_add_main_entries (context, entries, NULL);
  711. if (!g_option_context_parse (context, &argc, &argv, &error)) {
  712. fprintf (stderr, "option parsing failed: %s\n", error->message);
  713. g_error_free (error);
  714. g_option_context_free (context);
  715. exit (1);
  716. }
  717. g_option_context_free (context);
  718. if (batch == -1) {
  719. if (isatty (STDIN_FILENO)) {
  720. batch = 0;
  721. }
  722. else {
  723. batch = 1;
  724. }
  725. }
  726. if (paths) {
  727. for (elt = paths; *elt != NULL; elt ++) {
  728. rspamadm_lua_add_path (L, *elt);
  729. }
  730. }
  731. if (lua_args) {
  732. i = 1;
  733. lua_newtable (L);
  734. for (elt = lua_args; *elt != NULL; elt ++) {
  735. lua_pushinteger (L, i);
  736. lua_pushstring (L, *elt);
  737. lua_settable (L, -3);
  738. i++;
  739. }
  740. lua_setglobal (L, "arg");
  741. }
  742. if (scripts) {
  743. for (elt = scripts; *elt != NULL; elt ++) {
  744. if (!rspamadm_lua_load_script (L, *elt)) {
  745. exit (EXIT_FAILURE);
  746. }
  747. }
  748. }
  749. if (exec_line) {
  750. rspamadm_exec_input (L, exec_line);
  751. }
  752. if (serve) {
  753. /* HTTP Server mode */
  754. GPtrArray *addrs = NULL;
  755. gchar *name = NULL;
  756. struct ev_loop *ev_base;
  757. struct rspamd_http_connection_router *http;
  758. gint fd;
  759. struct rspamadm_lua_repl_context *ctx;
  760. if (rspamd_parse_host_port_priority (serve, &addrs, NULL, &name,
  761. 10000, NULL) == RSPAMD_PARSE_ADDR_FAIL) {
  762. fprintf (stderr, "cannot listen on %s", serve);
  763. exit (EXIT_FAILURE);
  764. }
  765. ev_base = rspamd_main->event_loop;
  766. ctx = g_malloc0 (sizeof (*ctx));
  767. http = rspamd_http_router_new (rspamadm_lua_error_handler,
  768. rspamadm_lua_finish_handler,
  769. 0.0,
  770. NULL,
  771. rspamd_main->http_ctx);
  772. ctx->L = L;
  773. ctx->rt = http;
  774. rspamd_http_router_add_path (http,
  775. "/exec",
  776. rspamadm_lua_handle_exec);
  777. for (i = 0; i < addrs->len; i ++) {
  778. rspamd_inet_addr_t *addr = g_ptr_array_index (addrs, i);
  779. fd = rspamd_inet_address_listen (addr, SOCK_STREAM, TRUE);
  780. if (fd != -1) {
  781. static ev_io ev;
  782. ev.data = ctx;
  783. ev_io_init (&ev, rspamadm_lua_accept_cb, fd, EV_READ);
  784. ev_io_start (ev_base, &ev);
  785. rspamd_printf ("listen on %s\n",
  786. rspamd_inet_address_to_string_pretty (addr));
  787. }
  788. }
  789. ev_loop (ev_base, 0);
  790. exit (EXIT_SUCCESS);
  791. }
  792. if (histfile == NULL) {
  793. const gchar *homedir;
  794. GString *hist_path;
  795. homedir = getenv ("HOME");
  796. if (homedir) {
  797. hist_path = g_string_sized_new (strlen (homedir) +
  798. strlen (default_history_file) + 1);
  799. rspamd_printf_gstring (hist_path, "%s/%s", homedir,
  800. default_history_file);
  801. }
  802. else {
  803. hist_path = g_string_sized_new (strlen (default_history_file) + 2);
  804. rspamd_printf_gstring (hist_path, "./%s", default_history_file);
  805. }
  806. histfile = hist_path->str;
  807. g_string_free (hist_path, FALSE);
  808. }
  809. if (argc > 1) {
  810. for (i = 1; i < argc; i ++) {
  811. if (!rspamadm_lua_load_script (L, argv[i])) {
  812. exit (EXIT_FAILURE);
  813. }
  814. }
  815. exit (EXIT_SUCCESS);
  816. }
  817. /* Init dot commands */
  818. cmds_hash = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal);
  819. for (i = 0; i < G_N_ELEMENTS (cmds); i ++) {
  820. g_hash_table_insert (cmds_hash, (gpointer)cmds[i].name, &cmds[i]);
  821. }
  822. #ifdef WITH_LUA_REPL
  823. rx_instance = replxx_init ();
  824. #endif
  825. if (!batch) {
  826. #ifdef WITH_LUA_REPL
  827. replxx_set_max_history_size (rx_instance, max_history);
  828. replxx_history_load (rx_instance, histfile);
  829. #endif
  830. rspamadm_lua_run_repl (L, false);
  831. #ifdef WITH_LUA_REPL
  832. replxx_history_save (rx_instance, histfile);
  833. #endif
  834. } else {
  835. rspamadm_lua_run_repl (L, true);
  836. }
  837. #ifdef WITH_LUA_REPL
  838. replxx_end (rx_instance);
  839. #endif
  840. }