123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- /*
- * 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 "config.h"
- #include "rspamadm.h"
- #include "rspamd.h"
- #include "ottery.h"
- #include "lua/lua_common.h"
- #include "lua/lua_thread_pool.h"
- #include "lua_ucl.h"
- #include "unix-std.h"
- #include "contrib/libev/ev.h"
-
- #ifdef HAVE_LIBUTIL_H
- #include <libutil.h>
- #endif
-
- static gboolean verbose = FALSE;
- static gboolean list_commands = FALSE;
- static gboolean show_help = FALSE;
- static gboolean show_version = FALSE;
- GHashTable *ucl_vars = NULL;
- char **lua_env = NULL;
- struct rspamd_main *rspamd_main = NULL;
- struct rspamd_async_session *rspamadm_session = NULL;
- lua_State *L = NULL;
-
- /* Defined in modules.c */
- extern module_t *modules[];
- extern worker_t *workers[];
-
- static void rspamadm_help(int argc, char **argv, const struct rspamadm_command *);
- static const char *rspamadm_help_help(gboolean full_help, const struct rspamadm_command *);
-
- struct rspamadm_command help_command = {
- .name = "help",
- .flags = RSPAMADM_FLAG_NOHELP,
- .help = rspamadm_help_help,
- .run = rspamadm_help};
-
- static gboolean rspamadm_parse_ucl_var(const char *option_name,
- const char *value, gpointer data,
- GError **error);
-
-
- static GOptionEntry entries[] = {
- {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
- "Enable verbose logging", NULL},
- {"list-commands", 'l', 0, G_OPTION_ARG_NONE, &list_commands,
- "List available commands", NULL},
- {"var", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &rspamadm_parse_ucl_var,
- "Redefine/define environment variable", NULL},
- {"help", 'h', 0, G_OPTION_ARG_NONE, &show_help,
- "Show help", NULL},
- {"version", 'V', 0, G_OPTION_ARG_NONE, &show_version,
- "Show version", NULL},
- {"lua-env", '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &lua_env,
- "Load lua environment from the specified files", NULL},
- {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}};
-
- GQuark
- rspamadm_error(void)
- {
- return g_quark_from_static_string("rspamadm");
- }
-
- static void
- rspamadm_version(void)
- {
- rspamd_printf("Rspamadm %s\n", RVERSION);
- }
-
- static void
- rspamadm_usage(GOptionContext *context)
- {
- char *help_str;
-
- help_str = g_option_context_get_help(context, TRUE, NULL);
- rspamd_printf("%s", help_str);
- }
-
- static void
- rspamadm_commands(GPtrArray *all_commands)
- {
- const struct rspamadm_command *cmd;
- unsigned int i;
-
- rspamd_printf("Rspamadm %s\n", RVERSION);
- rspamd_printf("Usage: rspamadm [global_options] command [command_options]\n");
- rspamd_printf("\nAvailable commands:\n");
-
- PTR_ARRAY_FOREACH(all_commands, i, cmd)
- {
- if (!(cmd->flags & RSPAMADM_FLAG_NOHELP)) {
- if (cmd->flags & RSPAMADM_FLAG_LUA) {
- (void) cmd->help(FALSE, cmd);
- }
- else {
- printf(" %-18s %-60s\n", cmd->name, cmd->help(FALSE, cmd));
- }
- }
- }
- }
-
- static const char *
- rspamadm_help_help(gboolean full_help, const struct rspamadm_command *cmd)
- {
- const char *help_str;
-
- if (full_help) {
- help_str = "Shows help for a specified command\n"
- "Usage: rspamadm help <command>";
- }
- else {
- help_str = "Shows help for a specified command";
- }
-
- return help_str;
- }
-
- static void
- rspamadm_help(int argc, char **argv, const struct rspamadm_command *command)
- {
- const char *cmd_name;
- const struct rspamadm_command *cmd;
- GPtrArray *all_commands = (GPtrArray *) command->command_data;
-
- rspamd_printf("Rspamadm %s\n", RVERSION);
- rspamd_printf("Usage: rspamadm [global_options] command [command_options]\n\n");
-
- if (argc <= 1) {
- cmd_name = "help";
- }
- else {
- cmd_name = argv[1];
- rspamd_printf("Showing help for %s command\n\n", cmd_name);
- }
-
- cmd = rspamadm_search_command(cmd_name, all_commands);
-
- if (cmd == NULL) {
- fprintf(stderr, "Invalid command name: %s\n", cmd_name);
- exit(EXIT_FAILURE);
- }
-
- if (strcmp(cmd_name, "help") == 0) {
- unsigned int i;
- rspamd_printf("Available commands:\n");
-
- PTR_ARRAY_FOREACH(all_commands, i, cmd)
- {
- if (!(cmd->flags & RSPAMADM_FLAG_NOHELP)) {
- if (!(cmd->flags & RSPAMADM_FLAG_LUA)) {
- printf(" %-18s %-60s\n", cmd->name,
- cmd->help(FALSE, cmd));
- }
- else {
- /* Just call lua subr */
- (void) cmd->help(FALSE, cmd);
- }
- }
- }
- }
- else {
- if (!(cmd->flags & RSPAMADM_FLAG_LUA)) {
- rspamd_printf("%s\n", cmd->help(TRUE, cmd));
- }
- else {
- /* Just call lua subr */
- (void) cmd->help(TRUE, cmd);
- }
- }
- }
-
- static gboolean
- rspamadm_parse_ucl_var(const char *option_name,
- const char *value, gpointer data,
- GError **error)
- {
- char *k, *v, *t;
-
- t = strchr(value, '=');
-
- if (t != NULL) {
- k = g_strdup(value);
- t = k + (t - value);
- v = g_strdup(t + 1);
- *t = '\0';
-
- g_hash_table_insert(ucl_vars, k, v);
- }
- else {
- g_set_error(error, rspamadm_error(), EINVAL,
- "Bad variable format: %s", value);
- return FALSE;
- }
-
- return TRUE;
- }
-
- static void
- lua_thread_str_error_cb(struct thread_entry *thread, int ret, const char *msg)
- {
- struct lua_call_data *cd = thread->cd;
-
- msg_err("call to rspamadm lua script failed (%d): %s", ret, msg);
-
- cd->ret = ret;
- }
-
- gboolean
- rspamadm_execute_lua_ucl_subr(int argc, char **argv,
- const ucl_object_t *res,
- const char *script_name,
- gboolean rspamadm_subcommand)
- {
- struct thread_entry *thread = lua_thread_pool_get_for_config(rspamd_main->cfg);
-
- lua_State *L = thread->lua_state;
-
- int i;
- char str[PATH_MAX];
-
- g_assert(script_name != NULL);
- g_assert(res != NULL);
- g_assert(L != NULL);
-
- /* Init internal rspamadm routines */
-
- if (rspamadm_subcommand) {
- rspamd_snprintf(str, sizeof(str), "return require \"%s.%s\"", "rspamadm",
- script_name);
- }
- else {
- rspamd_snprintf(str, sizeof(str), "return require \"%s\"",
- script_name);
- }
-
- if (luaL_dostring(L, str) != 0) {
- msg_err("cannot execute lua script %s: %s",
- str, lua_tostring(L, -1));
- return FALSE;
- }
- else {
- if (lua_type(L, -1) == LUA_TTABLE) {
- lua_pushstring(L, "handler");
- lua_gettable(L, -2);
- }
-
- if (lua_type(L, -1) != LUA_TFUNCTION) {
- msg_err("lua script must return "
- "function and not %s",
- lua_typename(L, lua_type(L, -1)));
-
- return FALSE;
- }
- }
-
- /* Push function */
- lua_pushvalue(L, -1);
-
- /* Push argv */
- lua_newtable(L);
-
- for (i = 1; i < argc; i++) {
- lua_pushstring(L, argv[i]);
- lua_rawseti(L, -2, i);
- }
-
- /* Push results */
- ucl_object_push_lua(L, res, TRUE);
-
- if (lua_repl_thread_call(thread, 2, NULL, lua_thread_str_error_cb) != 0) {
-
- return FALSE;
- }
-
- /* error function */
- lua_settop(L, 0);
-
- return TRUE;
- }
-
- static int
- rspamdadm_commands_sort_func(gconstpointer a, gconstpointer b)
- {
- const struct rspamadm_command *cmda = *((struct rspamadm_command const **) a),
- *cmdb = *((struct rspamadm_command const **) b);
-
- return strcmp(cmda->name, cmdb->name);
- }
-
- static gboolean
- rspamadm_command_maybe_match_name(const char *cmd, const char *input)
- {
- gsize clen, inplen;
-
- clen = strlen(cmd);
- inplen = strlen(input);
-
- if (rspamd_strings_levenshtein_distance(cmd, clen,
- input, inplen, 1) == 1) {
- return TRUE;
- }
- else if ((clen > inplen &&
- rspamd_substring_search(cmd, clen, input, inplen) != -1) ||
- (inplen > clen &&
- rspamd_substring_search(input, inplen, cmd, clen) != -1)) {
- return TRUE;
- }
-
- return FALSE;
- }
-
-
- static void
- rspamadm_add_lua_globals(struct rspamd_dns_resolver *resolver)
- {
- struct rspamd_async_session **psession;
- struct ev_loop **pev_base;
- struct rspamd_dns_resolver **presolver;
-
- rspamadm_session = rspamd_session_create(rspamd_main->cfg->cfg_pool, NULL,
- NULL, (event_finalizer_t) NULL, NULL);
-
- psession = lua_newuserdata(L, sizeof(struct rspamd_async_session *));
- rspamd_lua_setclass(L, rspamd_session_classname, -1);
- *psession = rspamadm_session;
- lua_setglobal(L, "rspamadm_session");
-
- pev_base = lua_newuserdata(L, sizeof(struct ev_loop *));
- rspamd_lua_setclass(L, rspamd_ev_base_classname, -1);
- *pev_base = rspamd_main->event_loop;
- lua_setglobal(L, "rspamadm_ev_base");
-
- presolver = lua_newuserdata(L, sizeof(struct rspamd_dns_resolver *));
- rspamd_lua_setclass(L, rspamd_resolver_classname, -1);
- *presolver = resolver;
- lua_setglobal(L, "rspamadm_dns_resolver");
- }
-
- static void
- rspamadm_cmd_dtor(gpointer p)
- {
- struct rspamadm_command *cmd = (struct rspamadm_command *) p;
-
- if (cmd->flags & RSPAMADM_FLAG_DYNAMIC) {
- if (cmd->aliases) {
- g_ptr_array_free(cmd->aliases, TRUE);
- }
-
- g_free((gpointer) cmd->name);
- g_free(cmd);
- }
- }
-
- int main(int argc, char **argv, char **env)
- {
- GError *error = NULL;
- GOptionContext *context;
- GOptionGroup *og;
- struct rspamd_config *cfg;
- GQuark process_quark;
- char **nargv, **targv;
- const char *cmd_name;
- const struct rspamadm_command *cmd;
- struct rspamd_dns_resolver *resolver;
- GPtrArray *all_commands = g_ptr_array_new_full(32,
- rspamadm_cmd_dtor); /* Discovered during check */
- int i, nargc, targc;
- worker_t **pworker;
- gboolean lua_file = FALSE;
- int retcode = 0;
-
- ucl_vars = g_hash_table_new_full(rspamd_strcase_hash,
- rspamd_strcase_equal, g_free, g_free);
- process_quark = g_quark_from_static_string("rspamadm");
- cfg = rspamd_config_new(RSPAMD_CONFIG_INIT_DEFAULT | RSPAMD_CONFIG_INIT_WIPE_LUA_MEM);
- cfg->libs_ctx = rspamd_init_libs();
- rspamd_main = g_malloc0(sizeof(*rspamd_main));
- rspamd_main->cfg = cfg;
- rspamd_main->pid = getpid();
- rspamd_main->type = process_quark;
- rspamd_main->server_pool = rspamd_mempool_new(rspamd_mempool_suggest_size(),
- "rspamadm", 0);
-
- rspamadm_fill_internal_commands(all_commands);
- help_command.command_data = all_commands;
-
- /* Now read options and store everything till the first non-dash argument */
- nargv = g_malloc0(sizeof(char *) * (argc + 1));
- nargv[0] = g_strdup(argv[0]);
-
- for (i = 1, nargc = 1; i < argc; i++) {
- if (argv[i] && argv[i][0] == '-') {
- /* Copy to nargv */
- nargv[nargc] = g_strdup(argv[i]);
- nargc++;
- }
- else {
- break;
- }
- }
-
- context = g_option_context_new("command - rspamd administration utility");
- og = g_option_group_new("global", "global options", "global options",
- NULL, NULL);
- g_option_context_set_help_enabled(context, FALSE);
- g_option_group_add_entries(og, entries);
- g_option_context_set_summary(context,
- "Summary:\n Rspamd administration utility version " RVERSION
- "\n Release id: " RID);
- g_option_context_set_main_group(context, og);
-
- targv = nargv;
- targc = nargc;
-
- if (!g_option_context_parse(context, &targc, &targv, &error)) {
- fprintf(stderr, "option parsing failed: %s\n", error->message);
- g_error_free(error);
- g_option_context_free(context);
- exit(EXIT_FAILURE);
- }
-
-
- /* Setup logger */
- if (verbose) {
- rspamd_main->logger = rspamd_log_open_emergency(rspamd_main->server_pool,
- RSPAMD_LOG_FLAG_USEC | RSPAMD_LOG_FLAG_ENFORCED | RSPAMD_LOG_FLAG_RSPAMADM);
- rspamd_log_set_log_level(rspamd_main->logger, G_LOG_LEVEL_DEBUG);
- }
- else {
- rspamd_main->logger = rspamd_log_open_emergency(rspamd_main->server_pool,
- RSPAMD_LOG_FLAG_RSPAMADM);
- rspamd_log_set_log_level(rspamd_main->logger, G_LOG_LEVEL_MESSAGE);
- }
-
- rspamd_main->event_loop = ev_default_loop(rspamd_config_ev_backend_get(cfg));
-
- resolver = rspamd_dns_resolver_init(rspamd_main->logger,
- rspamd_main->event_loop,
- cfg);
- rspamd_main->http_ctx = rspamd_http_context_create(cfg, rspamd_main->event_loop,
- NULL);
-
- g_log_set_default_handler(rspamd_glib_log_function, rspamd_main->logger);
- g_set_printerr_handler(rspamd_glib_printerr_function);
- rspamd_config_post_load(cfg,
- RSPAMD_CONFIG_INIT_LIBS | RSPAMD_CONFIG_INIT_URL | RSPAMD_CONFIG_INIT_NO_TLD);
-
- pworker = &workers[0];
- while (*pworker) {
- /* Init string quarks */
- (void) g_quark_from_static_string((*pworker)->name);
- pworker++;
- }
-
- cfg->compiled_modules = modules;
- cfg->compiled_workers = workers;
-
- rspamd_setproctitle("rspamdadm");
-
- L = cfg->lua_state;
- rspamd_lua_set_path(L, NULL, ucl_vars);
-
- if (!rspamd_lua_set_env(L, ucl_vars, lua_env, &error)) {
- rspamd_fprintf(stderr, "Cannot load lua environment: %e", error);
- g_error_free(error);
-
- goto end;
- }
-
- rspamd_lua_set_globals(cfg, L);
- rspamadm_add_lua_globals(resolver);
- rspamd_redis_pool_config(cfg->redis_pool, cfg, rspamd_main->event_loop);
-
- /* Init rspamadm global */
- lua_newtable(L);
-
- PTR_ARRAY_FOREACH(all_commands, i, cmd)
- {
- if (cmd->lua_subrs != NULL) {
- cmd->lua_subrs(L);
- }
-
- cmd++;
- }
-
- lua_setglobal(L, "rspamadm");
-
- rspamadm_fill_lua_commands(L, all_commands);
- rspamd_lua_start_gc(cfg);
- g_ptr_array_sort(all_commands, rspamdadm_commands_sort_func);
-
- g_strfreev(nargv);
-
- if (show_version) {
- rspamadm_version();
- goto end;
- }
- if (show_help) {
- rspamadm_usage(context);
- goto end;
- }
- if (list_commands) {
- rspamadm_commands(all_commands);
- goto end;
- }
-
- cmd_name = argv[nargc];
-
- if (cmd_name == NULL) {
- cmd_name = "help";
- }
-
- gsize cmdlen = strlen(cmd_name);
-
- if (cmdlen > 4 && memcmp(cmd_name + (cmdlen - 4), ".lua", 4) == 0) {
- cmd_name = "lua";
- lua_file = TRUE;
- }
-
- cmd = rspamadm_search_command(cmd_name, all_commands);
-
- if (cmd == NULL) {
- rspamd_fprintf(stderr, "Invalid command name: %s\n", cmd_name);
-
- /* Try fuzz search */
- rspamd_fprintf(stderr, "Suggested commands:\n");
- PTR_ARRAY_FOREACH(all_commands, i, cmd)
- {
- unsigned int j;
- const char *alias;
-
- if (rspamadm_command_maybe_match_name(cmd->name, cmd_name)) {
- rspamd_fprintf(stderr, "%s\n", cmd->name);
- }
- else {
- PTR_ARRAY_FOREACH(cmd->aliases, j, alias)
- {
- if (rspamadm_command_maybe_match_name(alias, cmd_name)) {
- rspamd_fprintf(stderr, "%s\n", alias);
- }
- }
- }
- }
-
- retcode = EXIT_FAILURE;
- goto end;
- }
-
- if (nargc < argc) {
-
- if (lua_file) {
- nargv = g_malloc0(sizeof(char *) * (argc - nargc + 2));
- nargv[1] = g_strdup(argv[nargc]);
- i = 2;
- argc++;
- }
- else {
- nargv = g_malloc0(sizeof(char *) * (argc - nargc + 1));
- i = 1;
- }
-
- nargv[0] = g_strdup_printf("%s %s", argv[0], cmd_name);
-
- for (; i < argc - nargc; i++) {
- if (lua_file) {
- /*
- * We append prefix '--arg=' to each argument and shift argv index
- */
- gsize arglen = strlen(argv[i + nargc - 1]);
-
- arglen += sizeof("--args="); /* Including \0 */
- nargv[i] = g_malloc(arglen);
- rspamd_snprintf(nargv[i], arglen, "--args=%s", argv[i + nargc - 1]);
- }
- else {
- nargv[i] = g_strdup(argv[i + nargc]);
- }
- }
-
- targc = argc - nargc;
- targv = nargv;
- cmd->run(targc, targv, cmd);
- g_strfreev(nargv);
- }
- else {
- cmd->run(0, NULL, cmd);
- }
-
- ev_break(rspamd_main->event_loop, EVBREAK_ALL);
-
- end:
- rspamd_session_destroy(rspamadm_session);
- g_option_context_free(context);
- rspamd_dns_resolver_deinit(resolver);
- REF_RELEASE(rspamd_main->cfg);
- rspamd_http_context_free(rspamd_main->http_ctx);
- rspamd_log_close(rspamd_main->logger);
- rspamd_url_deinit();
- g_ptr_array_free(all_commands, TRUE);
- ev_loop_destroy(rspamd_main->event_loop);
- g_hash_table_unref(ucl_vars);
- rspamd_mempool_delete(rspamd_main->server_pool);
- g_free(rspamd_main);
-
- return retcode;
- }
|