diff options
Diffstat (limited to '')
-rw-r--r-- | src/rspamadm/confighelp.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/rspamadm/confighelp.c b/src/rspamadm/confighelp.c new file mode 100644 index 0000000..2ad07c0 --- /dev/null +++ b/src/rspamadm/confighelp.c @@ -0,0 +1,301 @@ +/* + * Copyright 2023 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <ucl.h> +#include "config.h" +#include "rspamadm.h" +#include "cfg_file.h" +#include "cfg_rcl.h" +#include "rspamd.h" +#include "lua/lua_common.h" + +static gboolean json = FALSE; +static gboolean compact = FALSE; +static gboolean keyword = FALSE; +static const gchar *plugins_path = RSPAMD_PLUGINSDIR; +extern struct rspamd_main *rspamd_main; +/* Defined in modules.c */ +extern module_t *modules[]; +extern worker_t *workers[]; + +static void rspamadm_confighelp(gint argc, gchar **argv, + const struct rspamadm_command *cmd); + +static const char *rspamadm_confighelp_help(gboolean full_help, + const struct rspamadm_command *cmd); + +struct rspamadm_command confighelp_command = { + .name = "confighelp", + .flags = 0, + .help = rspamadm_confighelp_help, + .run = rspamadm_confighelp, + .lua_subrs = NULL, +}; + +static GOptionEntry entries[] = { + {"json", 'j', 0, G_OPTION_ARG_NONE, &json, + "Output json", NULL}, + {"compact", 'c', 0, G_OPTION_ARG_NONE, &compact, + "Output compacted", NULL}, + {"keyword", 'k', 0, G_OPTION_ARG_NONE, &keyword, + "Search by keyword", NULL}, + {"plugins", 'P', 0, G_OPTION_ARG_STRING, &plugins_path, + "Use the following plugin path (" RSPAMD_PLUGINSDIR ")", NULL}, + {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}}; + +static const char * +rspamadm_confighelp_help(gboolean full_help, const struct rspamadm_command *cmd) +{ + const char *help_str; + + if (full_help) { + help_str = "Shows help for the specified configuration options\n\n" + "Usage: rspamadm confighelp [option[, option...]]\n" + "Where options are:\n\n" + "-c: output compacted JSON\n" + "-j: output pretty formatted JSON\n" + "-k: search by keyword in doc string\n" + "-P: use specific Lua plugins path\n" + "--no-color: disable coloured output\n" + "--short: show only option names\n" + "--no-examples: do not show examples (implied by --short)\n" + "--help: shows available options and commands"; + } + else { + help_str = "Shows help for configuration options"; + } + + return help_str; +} + +static void +rspamadm_confighelp_show(struct rspamd_config *cfg, gint argc, gchar **argv, + const char *key, const ucl_object_t *obj) +{ + rspamd_fstring_t *out; + + rspamd_lua_set_path(cfg->lua_state, NULL, ucl_vars); + out = rspamd_fstring_new(); + + if (json) { + rspamd_ucl_emit_fstring(obj, UCL_EMIT_JSON, &out); + } + else if (compact) { + rspamd_ucl_emit_fstring(obj, UCL_EMIT_JSON_COMPACT, &out); + } + else { + /* TODO: add lua helper for output */ + if (key) { + rspamd_fprintf(stdout, "Showing help for %s%s:\n", + keyword ? "keyword " : "", key); + } + else { + rspamd_fprintf(stdout, "Showing help for all options:\n"); + } + + rspamadm_execute_lua_ucl_subr(argc, + argv, + obj, + "confighelp", + TRUE); + + rspamd_fstring_free(out); + return; + } + + rspamd_fprintf(stdout, "%V", out); + rspamd_fprintf(stdout, "\n"); + + rspamd_fstring_free(out); +} + +static void +rspamadm_confighelp_search_word_step(const ucl_object_t *obj, + ucl_object_t *res, + const gchar *str, + gsize len, + GString *path) +{ + ucl_object_iter_t it = NULL; + const ucl_object_t *cur, *elt; + const gchar *dot_pos; + + while ((cur = ucl_object_iterate(obj, &it, true)) != NULL) { + if (cur->keylen > 0) { + rspamd_printf_gstring(path, ".%*s", (int) cur->keylen, cur->key); + + if (rspamd_substring_search_caseless(cur->key, + cur->keylen, + str, + len) != -1) { + ucl_object_insert_key(res, ucl_object_ref(cur), + path->str, path->len, true); + goto fin; + } + } + + if (ucl_object_type(cur) == UCL_OBJECT) { + elt = ucl_object_lookup(cur, "data"); + + if (elt != NULL && ucl_object_type(elt) == UCL_STRING) { + if (rspamd_substring_search_caseless(elt->value.sv, + elt->len, + str, + len) != -1) { + ucl_object_insert_key(res, ucl_object_ref(cur), + path->str, path->len, true); + goto fin; + } + } + + rspamadm_confighelp_search_word_step(cur, res, str, len, path); + } + + fin: + /* Remove the last component of the path */ + dot_pos = strrchr(path->str, '.'); + + if (dot_pos) { + g_string_erase(path, dot_pos - path->str, + path->len - (dot_pos - path->str)); + } + } +} + +static ucl_object_t * +rspamadm_confighelp_search_word(const ucl_object_t *obj, const gchar *str) +{ + gsize len = strlen(str); + GString *path = g_string_new(""); + ucl_object_t *res; + + + res = ucl_object_typed_new(UCL_OBJECT); + + rspamadm_confighelp_search_word_step(obj, res, str, len, path); + + return res; +} + +__attribute__((noreturn)) static void +rspamadm_confighelp(gint argc, gchar **argv, const struct rspamadm_command *cmd) +{ + struct rspamd_config *cfg; + ucl_object_t *doc_obj; + const ucl_object_t *elt; + GOptionContext *context; + GError *error = NULL; + module_t *mod, **pmod; + worker_t **pworker; + struct module_ctx *mod_ctx; + gint i, ret = 0, processed_args = 0; + + context = g_option_context_new( + "confighelp - displays help for the configuration options"); + g_option_context_set_summary(context, + "Summary:\n Rspamd administration utility version " RVERSION + "\n Release id: " RID); + g_option_context_add_main_entries(context, entries, NULL); + g_option_context_set_ignore_unknown_options(context, TRUE); + + if (!g_option_context_parse(context, &argc, &argv, &error)) { + rspamd_fprintf(stderr, "option parsing failed: %s\n", error->message); + g_error_free(error); + g_option_context_free(context); + exit(EXIT_FAILURE); + } + + g_option_context_free(context); + pworker = &workers[0]; + while (*pworker) { + /* Init string quarks */ + (void) g_quark_from_static_string((*pworker)->name); + pworker++; + } + + cfg = rspamd_config_new(RSPAMD_CONFIG_INIT_SKIP_LUA); + cfg->lua_state = rspamd_main->cfg->lua_state; + cfg->compiled_modules = modules; + cfg->compiled_workers = workers; + + rspamd_rcl_config_init(cfg, NULL); + lua_pushboolean(cfg->lua_state, true); + lua_setglobal(cfg->lua_state, "confighelp"); + rspamd_rcl_add_lua_plugins_path(cfg->rcl_top_section, cfg, plugins_path, FALSE, NULL); + + /* Init modules to get documentation strings */ + i = 0; + for (pmod = cfg->compiled_modules; pmod != NULL && *pmod != NULL; pmod++) { + mod = *pmod; + mod_ctx = g_malloc0(sizeof(struct module_ctx)); + + if (mod->module_init_func(cfg, &mod_ctx) == 0) { + g_ptr_array_add(cfg->c_modules, mod_ctx); + mod_ctx->mod = mod; + mod->ctx_offset = i++; + mod_ctx->mod = mod; + } + } + /* Also init all workers */ + for (pworker = cfg->compiled_workers; *pworker != NULL; pworker++) { + (*pworker)->worker_init_func(cfg); + } + + /* Init lua modules */ + rspamd_lua_set_path(cfg->lua_state, cfg->cfg_ucl_obj, ucl_vars); + rspamd_init_lua_filters(cfg, true, false); + + if (argc > 1) { + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + + if (keyword) { + doc_obj = rspamadm_confighelp_search_word(cfg->doc_strings, + argv[i]); + } + else { + doc_obj = ucl_object_typed_new(UCL_OBJECT); + elt = ucl_object_lookup_path(cfg->doc_strings, argv[i]); + + if (elt) { + ucl_object_insert_key(doc_obj, ucl_object_ref(elt), + argv[i], 0, false); + } + } + + if (doc_obj != NULL) { + rspamadm_confighelp_show(cfg, argc, argv, argv[i], doc_obj); + ucl_object_unref(doc_obj); + } + else { + rspamd_fprintf(stderr, + "Cannot find help for %s\n", + argv[i]); + ret = EXIT_FAILURE; + } + processed_args++; + } + } + } + + if (processed_args == 0) { + /* Show all documentation strings */ + rspamadm_confighelp_show(cfg, argc, argv, NULL, cfg->doc_strings); + } + + rspamd_config_free(cfg); + + exit(ret); +} |