summaryrefslogtreecommitdiffstats
path: root/src/rspamadm/confighelp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rspamadm/confighelp.c301
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);
+}