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.

configdump.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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 "cfg_file.h"
  19. #include "cfg_rcl.h"
  20. #include "utlist.h"
  21. #include "rspamd.h"
  22. #include "lua/lua_common.h"
  23. #include "utlist.h"
  24. static gboolean json = FALSE;
  25. static gboolean compact = FALSE;
  26. static gboolean show_help = FALSE;
  27. static gboolean show_comments = FALSE;
  28. static gboolean modules_state = FALSE;
  29. static gboolean symbol_groups_only = FALSE;
  30. static gboolean symbol_full_details = FALSE;
  31. static gboolean skip_template = FALSE;
  32. static gchar *config = NULL;
  33. extern struct rspamd_main *rspamd_main;
  34. /* Defined in modules.c */
  35. extern module_t *modules[];
  36. extern worker_t *workers[];
  37. static void rspamadm_configdump (gint argc, gchar **argv, const struct rspamadm_command *);
  38. static const char *rspamadm_configdump_help (gboolean full_help, const struct rspamadm_command *);
  39. struct rspamadm_command configdump_command = {
  40. .name = "configdump",
  41. .flags = 0,
  42. .help = rspamadm_configdump_help,
  43. .run = rspamadm_configdump,
  44. .lua_subrs = NULL,
  45. };
  46. static GOptionEntry entries[] = {
  47. {"json", 'j', 0, G_OPTION_ARG_NONE, &json,
  48. "Json output (pretty formatted)", NULL},
  49. {"compact", 'C', 0, G_OPTION_ARG_NONE, &compact,
  50. "Compacted json output", NULL},
  51. {"config", 'c', 0, G_OPTION_ARG_STRING, &config,
  52. "Config file to test", NULL},
  53. {"show-help", 'h', 0, G_OPTION_ARG_NONE, &show_help,
  54. "Show help as comments for each option", NULL },
  55. {"show-comments", 's', 0, G_OPTION_ARG_NONE, &show_comments,
  56. "Show saved comments from the configuration file", NULL },
  57. {"modules-state", 'm', 0, G_OPTION_ARG_NONE, &modules_state,
  58. "Show modules state only", NULL},
  59. {"groups", 'g', 0, G_OPTION_ARG_NONE, &symbol_groups_only,
  60. "Show symbols groups only", NULL},
  61. {"symbol-details", 'd', 0, G_OPTION_ARG_NONE, &symbol_full_details,
  62. "Show full symbol details only", NULL},
  63. {"skip-template", 'T', 0, G_OPTION_ARG_NONE, &skip_template,
  64. "Do not apply Jinja templates", NULL},
  65. {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
  66. };
  67. static const char *
  68. rspamadm_configdump_help (gboolean full_help, const struct rspamadm_command *cmd)
  69. {
  70. const char *help_str;
  71. if (full_help) {
  72. help_str = "Perform configuration file dump\n\n"
  73. "Usage: rspamadm configdump [-c <config_name> [-j --compact -m] [<path1> [<path2> ...]]]\n"
  74. "Where options are:\n\n"
  75. "-j: output plain json\n"
  76. "--compact: output compacted json\n"
  77. "-c: config file to test\n"
  78. "-m: show state of modules only\n"
  79. "-h: show help for dumped options\n"
  80. "--help: shows available options and commands";
  81. }
  82. else {
  83. help_str = "Perform configuration file dump";
  84. }
  85. return help_str;
  86. }
  87. static void
  88. config_logger (rspamd_mempool_t *pool, gpointer ud)
  89. {
  90. }
  91. static void
  92. rspamadm_add_doc_elt (const ucl_object_t *obj, const ucl_object_t *doc_obj,
  93. ucl_object_t *comment_obj)
  94. {
  95. rspamd_fstring_t *comment = rspamd_fstring_new ();
  96. const ucl_object_t *elt;
  97. ucl_object_t *nobj, *cur_comment;
  98. if (ucl_object_lookup_len (comment_obj, (const char *)&obj,
  99. sizeof (void *))) {
  100. rspamd_fstring_free (comment);
  101. /* Do not rewrite the existing comment */
  102. return;
  103. }
  104. if (doc_obj != NULL) {
  105. /* Create doc comment */
  106. nobj = ucl_object_fromstring_common ("/*", 0, 0);
  107. }
  108. else {
  109. rspamd_fstring_free (comment);
  110. return;
  111. }
  112. /* We create comments as a list of parts */
  113. elt = ucl_object_lookup (doc_obj, "data");
  114. if (elt) {
  115. rspamd_printf_fstring (&comment, " * %s", ucl_object_tostring (elt));
  116. cur_comment = ucl_object_fromstring_common (comment->str, comment->len, 0);
  117. rspamd_fstring_erase (comment, 0, comment->len);
  118. DL_APPEND (nobj, cur_comment);
  119. }
  120. elt = ucl_object_lookup (doc_obj, "type");
  121. if (elt) {
  122. rspamd_printf_fstring (&comment, " * Type: %s", ucl_object_tostring (elt));
  123. cur_comment = ucl_object_fromstring_common (comment->str, comment->len, 0);
  124. rspamd_fstring_erase (comment, 0, comment->len);
  125. DL_APPEND (nobj, cur_comment);
  126. }
  127. elt = ucl_object_lookup (doc_obj, "required");
  128. if (elt) {
  129. rspamd_printf_fstring (&comment, " * Required: %s",
  130. ucl_object_toboolean (elt) ? "true" : "false");
  131. cur_comment = ucl_object_fromstring_common (comment->str, comment->len, 0);
  132. rspamd_fstring_erase (comment, 0, comment->len);
  133. DL_APPEND (nobj, cur_comment);
  134. }
  135. cur_comment = ucl_object_fromstring (" */");
  136. DL_APPEND (nobj, cur_comment);
  137. rspamd_fstring_free (comment);
  138. ucl_object_insert_key (comment_obj, ucl_object_ref (nobj),
  139. (const char *)&obj,
  140. sizeof (void *), true);
  141. ucl_object_unref (nobj);
  142. }
  143. static void
  144. rspamadm_gen_comments (const ucl_object_t *obj, const ucl_object_t *doc_obj,
  145. ucl_object_t *comments)
  146. {
  147. const ucl_object_t *cur_obj, *cur_doc, *cur_elt;
  148. ucl_object_iter_t it = NULL;
  149. if (obj == NULL || doc_obj == NULL) {
  150. return;
  151. }
  152. if (obj->keylen > 0) {
  153. rspamadm_add_doc_elt (obj, doc_obj, comments);
  154. }
  155. if (ucl_object_type (obj) == UCL_OBJECT) {
  156. while ((cur_obj = ucl_object_iterate (obj, &it, true))) {
  157. cur_doc = ucl_object_lookup_len (doc_obj, cur_obj->key,
  158. cur_obj->keylen);
  159. if (cur_doc != NULL) {
  160. LL_FOREACH (cur_obj, cur_elt) {
  161. if (ucl_object_lookup_len (comments, (const char *)&cur_elt,
  162. sizeof (void *)) == NULL) {
  163. rspamadm_gen_comments (cur_elt, cur_doc, comments);
  164. }
  165. }
  166. }
  167. }
  168. }
  169. }
  170. static void
  171. rspamadm_dump_section_obj (struct rspamd_config *cfg,
  172. const ucl_object_t *obj, const ucl_object_t *doc_obj)
  173. {
  174. rspamd_fstring_t *output;
  175. ucl_object_t *comments = NULL;
  176. output = rspamd_fstring_new ();
  177. if (show_help) {
  178. if (show_comments) {
  179. comments = cfg->config_comments;
  180. }
  181. else {
  182. comments = ucl_object_typed_new (UCL_OBJECT);
  183. }
  184. rspamadm_gen_comments (obj, doc_obj, comments);
  185. }
  186. else if (show_comments) {
  187. comments = cfg->config_comments;
  188. }
  189. if (json) {
  190. rspamd_ucl_emit_fstring_comments (obj, UCL_EMIT_JSON, &output, comments);
  191. }
  192. else if (compact) {
  193. rspamd_ucl_emit_fstring_comments (obj, UCL_EMIT_JSON_COMPACT, &output,
  194. comments);
  195. }
  196. else {
  197. rspamd_ucl_emit_fstring_comments (obj, UCL_EMIT_CONFIG, &output,
  198. comments);
  199. }
  200. rspamd_printf ("%V", output);
  201. rspamd_fstring_free (output);
  202. if (comments != NULL) {
  203. ucl_object_unref (comments);
  204. }
  205. }
  206. __attribute__((noreturn))
  207. static void
  208. rspamadm_configdump (gint argc, gchar **argv, const struct rspamadm_command *cmd)
  209. {
  210. GOptionContext *context;
  211. GError *error = NULL;
  212. const gchar *confdir;
  213. const ucl_object_t *obj = NULL, *cur, *doc_obj;
  214. struct rspamd_config *cfg = rspamd_main->cfg;
  215. gboolean ret = TRUE;
  216. worker_t **pworker;
  217. gint i;
  218. context = g_option_context_new (
  219. "configdump - dumps Rspamd configuration");
  220. g_option_context_set_summary (context,
  221. "Summary:\n Rspamd administration utility version "
  222. RVERSION
  223. "\n Release id: "
  224. RID);
  225. g_option_context_add_main_entries (context, entries, NULL);
  226. if (!g_option_context_parse (context, &argc, &argv, &error)) {
  227. fprintf (stderr, "option parsing failed: %s\n", error->message);
  228. g_error_free (error);
  229. g_option_context_free (context);
  230. exit (EXIT_FAILURE);
  231. }
  232. g_option_context_free (context);
  233. if (config == NULL) {
  234. if ((confdir = g_hash_table_lookup (ucl_vars, "CONFDIR")) == NULL) {
  235. confdir = RSPAMD_CONFDIR;
  236. }
  237. config = g_strdup_printf ("%s%c%s", confdir, G_DIR_SEPARATOR,
  238. "rspamd.conf");
  239. }
  240. pworker = &workers[0];
  241. while (*pworker) {
  242. /* Init string quarks */
  243. (void) g_quark_from_static_string ((*pworker)->name);
  244. pworker++;
  245. }
  246. cfg->compiled_modules = modules;
  247. cfg->compiled_workers = workers;
  248. cfg->cfg_name = config;
  249. if (!rspamd_config_read (cfg, cfg->cfg_name, config_logger, rspamd_main,
  250. ucl_vars, skip_template, lua_env)) {
  251. ret = FALSE;
  252. }
  253. else {
  254. /* Do post-load actions */
  255. rspamd_lua_post_load_config (cfg);
  256. (void)rspamd_init_filters (rspamd_main->cfg, false, false);
  257. rspamd_config_post_load (cfg, RSPAMD_CONFIG_INIT_SYMCACHE);
  258. }
  259. if (ret) {
  260. if (modules_state) {
  261. rspamadm_execute_lua_ucl_subr (argc,
  262. argv,
  263. cfg->rcl_obj,
  264. "plugins_stats",
  265. FALSE);
  266. exit (EXIT_SUCCESS);
  267. }
  268. if (symbol_full_details) {
  269. /*
  270. * Create object from symbols groups and output it using the
  271. * specified format
  272. */
  273. ucl_object_t *out = ucl_object_typed_new (UCL_OBJECT);
  274. GHashTableIter it;
  275. gpointer sk, sv;
  276. g_hash_table_iter_init (&it, cfg->symbols);
  277. ucl_object_t *sym_ucl = ucl_object_typed_new (UCL_OBJECT);
  278. const ucl_object_t *all_symbols_ucl = ucl_object_lookup(cfg->rcl_obj, "symbols");
  279. while (g_hash_table_iter_next (&it, &sk, &sv)) {
  280. const gchar *sym_name = (const gchar *)sk;
  281. struct rspamd_symbol *s = (struct rspamd_symbol *)sv;
  282. ucl_object_t *this_sym_ucl = ucl_object_typed_new (UCL_OBJECT);
  283. ucl_object_insert_key (this_sym_ucl,
  284. ucl_object_fromdouble (s->score),
  285. "score", strlen ("score"),
  286. false);
  287. ucl_object_insert_key (this_sym_ucl,
  288. ucl_object_fromstring (s->description),
  289. "description", strlen ("description"), false);
  290. rspamd_symcache_get_symbol_details(cfg->cache, sym_name, this_sym_ucl);
  291. ucl_object_insert_key (this_sym_ucl,
  292. ucl_object_frombool (!!(s->flags & RSPAMD_SYMBOL_FLAG_DISABLED)),
  293. "disabled", strlen ("disabled"),
  294. false);
  295. if (s->nshots == 1) {
  296. ucl_object_insert_key (this_sym_ucl,
  297. ucl_object_frombool (true),
  298. "one_shot", strlen ("one_shot"),
  299. false);
  300. }
  301. else {
  302. ucl_object_insert_key (this_sym_ucl,
  303. ucl_object_frombool (false),
  304. "one_shot", strlen ("one_shot"),
  305. false);
  306. }
  307. if (s->gr != NULL) {
  308. struct rspamd_symbols_group *gr = s->gr;
  309. const char *gr_name = gr->name;
  310. if (strcmp(gr_name, "ungrouped") != 0) {
  311. ucl_object_insert_key (this_sym_ucl,
  312. ucl_object_fromstring (gr_name),
  313. "group", strlen("group"),
  314. false);
  315. }
  316. if (s->groups) {
  317. ucl_object_t *add_groups = ucl_object_typed_new (UCL_ARRAY);
  318. guint j;
  319. struct rspamd_symbols_group *add_gr;
  320. bool has_extra_groups = false;
  321. PTR_ARRAY_FOREACH (s->groups, j, add_gr) {
  322. if (add_gr->name && strcmp (add_gr->name, gr_name) != 0) {
  323. ucl_array_append (add_groups,
  324. ucl_object_fromstring (add_gr->name));
  325. has_extra_groups = true;
  326. }
  327. }
  328. if (has_extra_groups == true) {
  329. ucl_object_insert_key (this_sym_ucl,
  330. add_groups,
  331. "groups", strlen ("groups"),
  332. false);
  333. }
  334. }
  335. }
  336. const ucl_object_t *loaded_symbol_ucl = ucl_object_lookup(all_symbols_ucl, sym_name);
  337. if (loaded_symbol_ucl) {
  338. ucl_object_iter_t it = NULL;
  339. while ((cur = ucl_iterate_object (loaded_symbol_ucl, &it, true)) != NULL)
  340. {
  341. const char *key = ucl_object_key(cur);
  342. /* If this key isn't something we have direct in the symbol item, grab the key/value */
  343. if ((strcmp(key, "score") != 0 ) &&
  344. (strcmp(key, "description") != 0) &&
  345. (strcmp(key, "disabled") != 0) &&
  346. (strcmp(key, "condition") != 0) &&
  347. (strcmp(key, "one_shot") != 0) &&
  348. (strcmp(key, "any_shot") != 0) &&
  349. (strcmp(key, "nshots") != 0) &&
  350. (strcmp(key, "one_param") != 0) &&
  351. (strcmp(key, "priority") != 0))
  352. {
  353. ucl_object_insert_key(this_sym_ucl, (ucl_object_t *)cur, key, strlen(key), false);
  354. }
  355. }
  356. }
  357. ucl_object_insert_key (sym_ucl, this_sym_ucl, sym_name,
  358. strlen (sym_name), true);
  359. }
  360. ucl_object_insert_key (out, sym_ucl, "symbols",
  361. strlen ("symbols"), true);
  362. rspamadm_dump_section_obj (cfg, out, NULL);
  363. exit (EXIT_SUCCESS);
  364. }
  365. if (symbol_groups_only) {
  366. /*
  367. * Create object from symbols groups and output it using the
  368. * specified format
  369. */
  370. ucl_object_t *out = ucl_object_typed_new (UCL_OBJECT);
  371. GHashTableIter it;
  372. gpointer k, v;
  373. g_hash_table_iter_init (&it, cfg->groups);
  374. while (g_hash_table_iter_next (&it, &k, &v)) {
  375. const gchar *gr_name = (const gchar *)k;
  376. struct rspamd_symbols_group *gr = (struct rspamd_symbols_group *)v;
  377. ucl_object_t *gr_ucl = ucl_object_typed_new (UCL_OBJECT);
  378. ucl_object_insert_key (gr_ucl,
  379. ucl_object_frombool (!!(gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC)),
  380. "public", strlen ("public"), false);
  381. ucl_object_insert_key (gr_ucl,
  382. ucl_object_frombool (!!(gr->flags & RSPAMD_SYMBOL_GROUP_DISABLED)),
  383. "disabled", strlen ("disabled"), false);
  384. ucl_object_insert_key (gr_ucl,
  385. ucl_object_frombool (!!(gr->flags & RSPAMD_SYMBOL_GROUP_ONE_SHOT)),
  386. "one_shot", strlen ("one_shot"), false);
  387. ucl_object_insert_key (gr_ucl,
  388. ucl_object_fromdouble (gr->max_score),
  389. "max_score", strlen ("max_score"), false);
  390. ucl_object_insert_key (gr_ucl,
  391. ucl_object_fromstring (gr->description),
  392. "description", strlen ("description"), false);
  393. if (gr->symbols) {
  394. GHashTableIter sit;
  395. gpointer sk, sv;
  396. g_hash_table_iter_init (&sit, gr->symbols);
  397. ucl_object_t *sym_ucl = ucl_object_typed_new (UCL_OBJECT);
  398. while (g_hash_table_iter_next (&sit, &sk, &sv)) {
  399. const gchar *sym_name = (const gchar *) sk;
  400. struct rspamd_symbol *s = (struct rspamd_symbol *)sv;
  401. ucl_object_t *spec_sym = ucl_object_typed_new (UCL_OBJECT);
  402. ucl_object_insert_key (spec_sym,
  403. ucl_object_fromdouble (s->score),
  404. "score", strlen ("score"),
  405. false);
  406. ucl_object_insert_key (spec_sym,
  407. ucl_object_fromstring (s->description),
  408. "description", strlen ("description"), false);
  409. ucl_object_insert_key (spec_sym,
  410. ucl_object_frombool (!!(s->flags & RSPAMD_SYMBOL_FLAG_DISABLED)),
  411. "disabled", strlen ("disabled"),
  412. false);
  413. if (s->nshots == 1) {
  414. ucl_object_insert_key (spec_sym,
  415. ucl_object_frombool (true),
  416. "one_shot", strlen ("one_shot"),
  417. false);
  418. }
  419. else {
  420. ucl_object_insert_key (spec_sym,
  421. ucl_object_frombool (false),
  422. "one_shot", strlen ("one_shot"),
  423. false);
  424. }
  425. ucl_object_t *add_groups = ucl_object_typed_new (UCL_ARRAY);
  426. guint j;
  427. struct rspamd_symbols_group *add_gr;
  428. PTR_ARRAY_FOREACH (s->groups, j, add_gr) {
  429. if (add_gr->name && strcmp (add_gr->name, gr_name) != 0) {
  430. ucl_array_append (add_groups,
  431. ucl_object_fromstring (add_gr->name));
  432. }
  433. }
  434. ucl_object_insert_key (spec_sym,
  435. add_groups,
  436. "extra_groups", strlen ("extra_groups"),
  437. false);
  438. ucl_object_insert_key (sym_ucl, spec_sym, sym_name,
  439. strlen (sym_name), true);
  440. }
  441. ucl_object_insert_key (gr_ucl, sym_ucl, "symbols",
  442. strlen ("symbols"), false);
  443. }
  444. ucl_object_insert_key (out, gr_ucl, gr_name, strlen (gr_name),
  445. true);
  446. }
  447. rspamadm_dump_section_obj (cfg, out, NULL);
  448. exit (EXIT_SUCCESS);
  449. }
  450. /* Output configuration */
  451. if (argc == 1) {
  452. rspamadm_dump_section_obj (cfg, cfg->rcl_obj, cfg->doc_strings);
  453. }
  454. else {
  455. for (i = 1; i < argc; i ++) {
  456. obj = ucl_object_lookup_path (cfg->rcl_obj, argv[i]);
  457. doc_obj = ucl_object_lookup_path (cfg->doc_strings, argv[i]);
  458. if (!obj) {
  459. rspamd_printf ("Section %s NOT FOUND\n", argv[i]);
  460. }
  461. else {
  462. LL_FOREACH (obj, cur) {
  463. if (!json && !compact) {
  464. rspamd_printf ("*** Section %s ***\n", argv[i]);
  465. }
  466. rspamadm_dump_section_obj (cfg, cur, doc_obj);
  467. if (!json && !compact) {
  468. rspamd_printf ("\n*** End of section %s ***\n", argv[i]);
  469. }
  470. else {
  471. rspamd_printf ("\n");
  472. }
  473. }
  474. }
  475. }
  476. }
  477. }
  478. exit (ret ? EXIT_SUCCESS : EXIT_FAILURE);
  479. }