/*- * Copyright 2016 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 "rspamadm.h" #include "libutil/util.h" #include "libserver/logger.h" #include "lua/lua_common.h" #include "lua/lua_thread_pool.h" extern struct rspamadm_command pw_command; extern struct rspamadm_command configtest_command; extern struct rspamadm_command configdump_command; extern struct rspamadm_command control_command; extern struct rspamadm_command confighelp_command; extern struct rspamadm_command statconvert_command; extern struct rspamadm_command fuzzyconvert_command; extern struct rspamadm_command signtool_command; extern struct rspamadm_command lua_command; const struct rspamadm_command *commands[] = { &help_command, &pw_command, &configtest_command, &configdump_command, &control_command, &confighelp_command, &statconvert_command, &fuzzyconvert_command, &signtool_command, &lua_command, NULL}; const struct rspamadm_command * rspamadm_search_command(const char *name, GPtrArray *all_commands) { const struct rspamadm_command *ret = NULL, *cmd; const char *alias; unsigned int i, j; if (name == NULL) { name = "help"; } PTR_ARRAY_FOREACH(all_commands, i, cmd) { if (strcmp(name, cmd->name) == 0) { ret = cmd; break; } PTR_ARRAY_FOREACH(cmd->aliases, j, alias) { if (strcmp(name, alias) == 0) { ret = cmd; break; } } } return ret; } void rspamadm_fill_internal_commands(GPtrArray *dest) { unsigned int i; for (i = 0; i < G_N_ELEMENTS(commands); i++) { if (commands[i]) { g_ptr_array_add(dest, (gpointer) commands[i]); } } } static void lua_thread_str_error_cb(struct thread_entry *thread, int ret, const char *msg) { msg_err("call to rspamadm lua script failed (%d): %s", ret, msg); } static void rspamadm_lua_command_run(int argc, char **argv, const struct rspamadm_command *cmd) { struct thread_entry *thread = lua_thread_pool_get_for_config(rspamd_main->cfg); lua_State *L = thread->lua_state; int table_idx = GPOINTER_TO_INT(cmd->command_data); int i; /* Function */ lua_rawgeti(L, LUA_REGISTRYINDEX, table_idx); lua_pushstring(L, "handler"); lua_gettable(L, -2); /* Args */ lua_createtable(L, argc + 1, 0); for (i = 0; i < argc; i++) { lua_pushstring(L, argv[i]); lua_rawseti(L, -2, i); /* Starting from zero ! */ } if (lua_repl_thread_call(thread, 1, (void *) cmd, lua_thread_str_error_cb) != 0) { exit(EXIT_FAILURE); } lua_settop(L, 0); } static const char * rspamadm_lua_command_help(gboolean full_help, const struct rspamadm_command *cmd) { int table_idx = GPOINTER_TO_INT(cmd->command_data); if (full_help) { struct thread_entry *thread = lua_thread_pool_get_for_config(rspamd_main->cfg); lua_State *L = thread->lua_state; lua_rawgeti(L, LUA_REGISTRYINDEX, table_idx); /* Function */ lua_pushstring(L, "handler"); lua_gettable(L, -2); /* Args */ lua_createtable(L, 2, 0); lua_pushstring(L, cmd->name); lua_rawseti(L, -2, 0); /* Starting from zero ! */ lua_pushstring(L, "--help"); lua_rawseti(L, -2, 1); if (lua_repl_thread_call(thread, 1, (void *) cmd, lua_thread_str_error_cb) != 0) { exit(EXIT_FAILURE); } lua_settop(L, 0); } else { lua_State *L = rspamd_main->cfg->lua_state; lua_rawgeti(L, LUA_REGISTRYINDEX, table_idx); lua_pushstring(L, "description"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { printf(" %-18s %-60s\n", cmd->name, lua_tostring(L, -1)); } else { printf(" %-18s %-60s\n", cmd->name, "no description available"); } lua_settop(L, 0); } return NULL; /* Must be handled in rspamadm itself */ } void rspamadm_fill_lua_commands(lua_State *L, GPtrArray *dest) { int i; GPtrArray *lua_paths; GError *err = NULL; const char *lualibdir = RSPAMD_LUALIBDIR, *path; struct rspamadm_command *lua_cmd; char search_dir[PATH_MAX]; if (g_hash_table_lookup(ucl_vars, "LUALIBDIR")) { lualibdir = g_hash_table_lookup(ucl_vars, "LUALIBDIR"); } rspamd_snprintf(search_dir, sizeof(search_dir), "%s%crspamadm%c", lualibdir, G_DIR_SEPARATOR, G_DIR_SEPARATOR); if ((lua_paths = rspamd_glob_path(search_dir, "*.lua", FALSE, &err)) == NULL) { msg_err("cannot glob files in %s/*.lua: %e", search_dir, err); g_error_free(err); return; } PTR_ARRAY_FOREACH(lua_paths, i, path) { if (luaL_dofile(L, path) != 0) { msg_err("cannot execute lua script %s: %s", path, lua_tostring(L, -1)); lua_settop(L, 0); continue; } else { if (lua_type(L, -1) == LUA_TTABLE) { lua_pushstring(L, "handler"); lua_gettable(L, -2); } else { continue; /* Something goes wrong, huh */ } if (lua_type(L, -1) != LUA_TFUNCTION) { msg_err("rspamadm script %s does not have 'handler' field with type " "function", path); continue; } /* Pop handler */ lua_pop(L, 1); lua_cmd = g_malloc0(sizeof(*lua_cmd)); lua_pushstring(L, "name"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { lua_cmd->name = g_strdup(lua_tostring(L, -1)); } else { goffset ext_pos; char *name; name = g_path_get_basename(path); /* Remove .lua */ ext_pos = rspamd_substring_search(path, strlen(path), ".lua", 4); if (ext_pos != -1) { name[ext_pos] = '\0'; } lua_cmd->name = name; } lua_pop(L, 1); lua_pushstring(L, "aliases"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TTABLE) { lua_cmd->aliases = g_ptr_array_new_full( rspamd_lua_table_size(L, -1), g_free); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { if (lua_isstring(L, -1)) { g_ptr_array_add(lua_cmd->aliases, g_strdup(lua_tostring(L, -1))); } } } lua_pop(L, 1); lua_pushvalue(L, -1); /* Reference table itself */ lua_cmd->command_data = GINT_TO_POINTER(luaL_ref(L, LUA_REGISTRYINDEX)); lua_cmd->flags |= RSPAMADM_FLAG_LUA | RSPAMADM_FLAG_DYNAMIC; lua_cmd->run = rspamadm_lua_command_run; lua_cmd->help = rspamadm_lua_command_help; g_ptr_array_add(dest, lua_cmd); } lua_settop(L, 0); } g_ptr_array_free(lua_paths, TRUE); }