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

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