diff options
Diffstat (limited to 'src/doveadm/doveadm.c')
-rw-r--r-- | src/doveadm/doveadm.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/doveadm/doveadm.c b/src/doveadm/doveadm.c new file mode 100644 index 0000000..f175f2c --- /dev/null +++ b/src/doveadm/doveadm.c @@ -0,0 +1,384 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "sort.h" +#include "ostream.h" +#include "env-util.h" +#include "execv-const.h" +#include "dict.h" +#include "master-service-private.h" +#include "master-service-settings.h" +#include "settings-parser.h" +#include "doveadm-print-private.h" +#include "doveadm-dump.h" +#include "doveadm-mail.h" +#include "doveadm-settings.h" +#include "doveadm-dsync.h" +#include "doveadm.h" + +#include <unistd.h> + +const struct doveadm_print_vfuncs *doveadm_print_vfuncs_all[] = { + &doveadm_print_flow_vfuncs, + &doveadm_print_tab_vfuncs, + &doveadm_print_table_vfuncs, + &doveadm_print_pager_vfuncs, + &doveadm_print_json_vfuncs, + &doveadm_print_formatted_vfuncs, + NULL +}; + +int doveadm_exit_code = 0; + +static void failure_exit_callback(int *status) +{ + enum fatal_exit_status fatal_status = *status; + + switch (fatal_status) { + case FATAL_LOGWRITE: + case FATAL_LOGERROR: + case FATAL_LOGOPEN: + case FATAL_OUTOFMEM: + case FATAL_EXEC: + case FATAL_DEFAULT: + *status = EX_TEMPFAIL; + break; + } +} + +static void +doveadm_usage_compress_lines(FILE *out, const char *str, const char *prefix) +{ + const char *cmd, *args, *p, *short_name, *sub_name; + const char *prev_name = "", *prev_sub_name = ""; + const char **lines; + unsigned int i, count; + size_t prefix_len = strlen(prefix); + + /* split lines */ + lines = (void *)p_strsplit(pool_datastack_create(), str, "\n"); + for (count = 0; lines[count] != NULL; count++) ; + + /* sort lines */ + i_qsort(lines, count, sizeof(*lines), i_strcmp_p); + + /* print lines, compress subcommands into a single line */ + for (i = 0; i < count; i++) { + args = strchr(lines[i], '\t'); + if (args == NULL) { + cmd = lines[i]; + args = ""; + } else { + cmd = t_strdup_until(lines[i], args); + args++; + } + if (*prefix != '\0') { + if (strncmp(cmd, prefix, prefix_len) != 0 || + cmd[prefix_len] != ' ') + continue; + cmd += prefix_len + 1; + } + + p = strchr(cmd, ' '); + if (p == NULL) { + if (*prev_name != '\0') { + fprintf(out, "\n"); + prev_name = ""; + } + fprintf(out, USAGE_CMDNAME_FMT" %s\n", cmd, args); + } else { + short_name = t_strdup_until(cmd, p); + if (strcmp(prev_name, short_name) != 0) { + if (*prev_name != '\0') + fprintf(out, "\n"); + fprintf(out, USAGE_CMDNAME_FMT" %s", + short_name, t_strcut(p + 1, ' ')); + prev_name = short_name; + prev_sub_name = ""; + } else { + sub_name = t_strcut(p + 1, ' '); + if (strcmp(prev_sub_name, sub_name) != 0) { + fprintf(out, "|%s", sub_name); + prev_sub_name = sub_name; + } + } + } + } + if (*prev_name != '\0') + fprintf(out, "\n"); +} + +static void ATTR_NORETURN +usage_prefix(const char *prefix) +{ + const struct doveadm_cmd_ver2 *cmd2; + string_t *str = t_str_new(1024); + + fprintf(stderr, "usage: doveadm [-Dv] [-f <formatter>] "); + if (*prefix != '\0') + fprintf(stderr, "%s ", prefix); + fprintf(stderr, "<command> [<args>]\n"); + + array_foreach(&doveadm_cmds_ver2, cmd2) + str_printfa(str, "%s\t%s\n", cmd2->name, cmd2->usage); + + doveadm_usage_compress_lines(stderr, str_c(str), prefix); + + lib_exit(EX_USAGE); +} + +void usage(void) +{ + usage_prefix(""); +} + +void help_ver2(const struct doveadm_cmd_ver2 *cmd) +{ + fprintf(stderr, "doveadm %s %s\n", cmd->name, cmd->usage); + lib_exit(EX_USAGE); +} + +static void cmd_help(struct doveadm_cmd_context *cctx) +{ + const char *cmd, *man_argv[3]; + + if (!doveadm_cmd_param_str(cctx, "cmd", &cmd)) + usage_prefix(""); + + env_put("MANPATH", MANDIR); + man_argv[0] = "man"; + man_argv[1] = t_strconcat("doveadm-", cmd, NULL); + man_argv[2] = NULL; + execvp_const(man_argv[0], man_argv); +} + +static struct doveadm_cmd_ver2 doveadm_cmd_help = { + .name = "help", + .cmd = cmd_help, + .usage = "[<cmd>]", +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_PARAM('\0', "cmd", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +static void cmd_config(struct doveadm_cmd_context *cctx) +{ + const char *const *args, **argv; + + if (!doveadm_cmd_param_array(cctx, "args", &args)) + args = NULL; + + env_put(MASTER_CONFIG_FILE_ENV, + master_service_get_config_path(master_service)); + + unsigned int len = str_array_length(args); + argv = t_new(const char *, len + 2); + argv[0] = BINDIR"/doveconf"; + if (len > 0) { + i_assert(args != NULL); + memcpy(argv+1, args, len * sizeof(args[0])); + } + execv_const(argv[0], argv); +} + +static struct doveadm_cmd_ver2 doveadm_cmd_config = { + .name = "config", + .cmd = cmd_config, + .usage = "[doveconf parameters]", + .flags = CMD_FLAG_NO_OPTIONS, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_PARAM('\0', "args", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +static void cmd_exec(struct doveadm_cmd_context *cctx); +static struct doveadm_cmd_ver2 doveadm_cmd_exec = { + .name = "exec", + .cmd = cmd_exec, + .usage = "<binary> [binary parameters]", + .flags = CMD_FLAG_NO_OPTIONS, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_PARAM('\0', "binary", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAM('\0', "args", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +}; + +static void cmd_exec(struct doveadm_cmd_context *cctx) +{ + const char *path, *binary, *const *args, **argv; + + if (!doveadm_cmd_param_str(cctx, "binary", &binary)) + help_ver2(&doveadm_cmd_exec); + if (!doveadm_cmd_param_array(cctx, "args", &args)) + args = NULL; + + path = t_strdup_printf("%s/%s", doveadm_settings->libexec_dir, binary); + + unsigned int len = str_array_length(args); + argv = t_new(const char *, len + 2); + argv[0] = path; + if (len > 0) { + i_assert(args != NULL); + memcpy(argv+1, args, len * sizeof(args[0])); + } + execv_const(argv[0], argv); +} + +static bool doveadm_has_subcommands(const char *cmd_name) +{ + const struct doveadm_cmd_ver2 *cmd2; + size_t len = strlen(cmd_name); + + array_foreach(&doveadm_cmds_ver2, cmd2) { + if (strncmp(cmd2->name, cmd_name, len) == 0 && + cmd2->name[len] == ' ') + return TRUE; + } + return FALSE; +} + +static struct doveadm_cmd_ver2 *doveadm_cmdline_commands_ver2[] = { + &doveadm_cmd_config, + &doveadm_cmd_dump, + &doveadm_cmd_exec, + &doveadm_cmd_help, + &doveadm_cmd_oldstats_top_ver2, + &doveadm_cmd_pw, + &doveadm_cmd_zlibconnect, +}; + +int main(int argc, char *argv[]) +{ + enum master_service_flags service_flags = + MASTER_SERVICE_FLAG_STANDALONE | + MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN | + MASTER_SERVICE_FLAG_NO_SSL_INIT | + MASTER_SERVICE_FLAG_NO_INIT_DATASTACK_FRAME; + struct doveadm_cmd_context cctx; + const char *cmd_name; + unsigned int i; + bool quick_init = FALSE; + int c; + + i_zero(&cctx); + cctx.conn_type = DOVEADM_CONNECTION_TYPE_CLI; + + i_set_failure_exit_callback(failure_exit_callback); + doveadm_dsync_main(&argc, &argv); + + /* "+" is GNU extension to stop at the first non-option. + others just accept -+ option. */ + master_service = master_service_init("doveadm", service_flags, + &argc, &argv, "+Df:hv"); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'D': + doveadm_debug = TRUE; + doveadm_verbose = TRUE; + break; + case 'f': + doveadm_print_init(optarg); + break; + case 'h': + doveadm_print_hide_titles = TRUE; + break; + case 'v': + doveadm_verbose = TRUE; + break; + default: + return FATAL_DEFAULT; + } + } + cmd_name = argv[optind]; + + if (cmd_name != NULL && strcmp(cmd_name, "help") == 0 && + argv[optind+1] != NULL) { + /* "help cmd" doesn't need any configuration */ + quick_init = TRUE; + } + master_service_init_log(master_service); + + doveadm_settings_init(); + doveadm_cmds_init(); + for (i = 0; i < N_ELEMENTS(doveadm_cmdline_commands_ver2); i++) + doveadm_cmd_register_ver2(doveadm_cmdline_commands_ver2[i]); + doveadm_register_auth_commands(); + + if (cmd_name != NULL && (quick_init || + strcmp(cmd_name, "config") == 0 || + strcmp(cmd_name, "stop") == 0 || + strcmp(cmd_name, "reload") == 0)) { + /* special case commands: even if there is something wrong + with the config (e.g. mail_plugins), don't fail these + commands */ + if (strcmp(cmd_name, "help") != 0) + doveadm_read_settings(); + quick_init = TRUE; + } else { + quick_init = FALSE; + doveadm_print_ostream = o_stream_create_fd(STDOUT_FILENO, 0); + o_stream_set_no_error_handling(doveadm_print_ostream, TRUE); + o_stream_cork(doveadm_print_ostream); + doveadm_dump_init(); + doveadm_mail_init(); + dict_drivers_register_builtin(); + doveadm_load_modules(); + + /* read settings only after loading doveadm plugins, which + may modify what settings are read */ + doveadm_read_settings(); + if (doveadm_debug && getenv("LOG_STDERR_TIMESTAMP") == NULL) + i_set_failure_timestamp_format(master_service->set->log_timestamp); + master_service_init_stats_client(master_service, TRUE); + /* Load mail_plugins */ + doveadm_mail_init_finish(); + /* kludgy: Load the rest of the doveadm plugins after + mail_plugins have been loaded. */ + doveadm_load_modules(); + + if (cmd_name == NULL) { + /* show usage after registering all plugins */ + usage_prefix(""); + } + } + + argc -= optind; + argv += optind; + i_getopt_reset(); + + master_service_init_finish(master_service); + if (!doveadm_debug) { + /* disable debugging unless -D is given */ + i_set_debug_file("/dev/null"); + } + + /* this has to be done here because proctitle hack can break + the env pointer */ + cctx.username = getenv("USER"); + + if (!doveadm_cmd_try_run_ver2(cmd_name, argc, (const char**)argv, &cctx)) { + if (doveadm_has_subcommands(cmd_name)) + usage_prefix(cmd_name); + if (doveadm_has_unloaded_plugin(cmd_name)) { + i_fatal("Unknown command '%s', but plugin %s exists. " + "Try to set mail_plugins=%s", + cmd_name, cmd_name, cmd_name); + } + usage(); + } + + if (!quick_init) { + doveadm_mail_deinit(); + doveadm_dump_deinit(); + doveadm_unload_modules(); + dict_drivers_unregister_builtin(); + doveadm_print_deinit(); + o_stream_unref(&doveadm_print_ostream); + } + doveadm_cmds_deinit(); + doveadm_settings_deinit(); + master_service_deinit(&master_service); + return doveadm_exit_code; +} |