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