view src/doveadm/doveadm.c @ 11321:5f350b5ff6d9 HEAD

Added initial implementation of a director process (for NFS users). There are still some unimplemented features and bugs. Also changing mail server list doesn't yet make sure that other directors won't assign the same user to a different server at the same time.
author Timo Sirainen <tss@iki.fi>
date Wed, 19 May 2010 09:56:49 +0200
parents 9f1fb4978a4a
children 07c9d1115029
line wrap: on
line source

/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "str.h"
#include "module-dir.h"
#include "master-service.h"
#include "master-service-settings.h"
#include "doveadm-mail.h"
#include "doveadm-settings.h"
#include "doveadm.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

bool doveadm_verbose = FALSE, doveadm_debug = FALSE;

static struct module *modules = NULL;
static ARRAY_DEFINE(doveadm_cmds, struct doveadm_cmd);

void doveadm_register_cmd(const struct doveadm_cmd *cmd)
{
	array_append(&doveadm_cmds, cmd, 1);
}

void usage(void)
{
	const struct doveadm_cmd *cmd;

	fprintf(stderr, "usage: doveadm [-Dv] <command> [<args>]\n");

	array_foreach(&doveadm_cmds, cmd) {
		fprintf(stderr, USAGE_CMDNAME_FMT" %s\n",
			cmd->name, cmd->short_usage);
	}
	doveadm_mail_usage();
	exit(1);
}

void help(const struct doveadm_cmd *cmd)
{
	fprintf(stderr, "doveadm %s %s\n", cmd->name, cmd->short_usage);
	if (cmd->long_usage != NULL)
		fprintf(stderr, "%s", cmd->long_usage);
	exit(0);
}

const char *unixdate2str(time_t timestamp)
{
	static char buf[64];
	struct tm *tm;

	tm = localtime(&timestamp);
	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
	return buf;
}

static void cmd_help(int argc, char *argv[])
{
	const struct doveadm_cmd *cmd;
	string_t *name;
	int i;

	if (argv[1] == NULL)
		usage();

	name = t_str_new(100);
	for (i = 1; i < argc; i++) {
		str_append(name, argv[i]);

		array_foreach(&doveadm_cmds, cmd) {
			if (strcmp(cmd->name, str_c(name)) == 0)
				help(cmd);
		}
		doveadm_mail_try_help_name(str_c(name));

		str_append_c(name, ' ');
	}
	usage();
}

static struct doveadm_cmd doveadm_cmd_help = {
	cmd_help, "help", "<cmd>", NULL
};

static bool
doveadm_try_run_multi_word(const struct doveadm_cmd *cmd,
			   const char *cmdname, int argc, char *argv[])
{
	unsigned int len;

	if (argc < 2)
		return FALSE;

	len = strlen(argv[1]);
	if (strncmp(cmdname, argv[1], len) != 0)
		return FALSE;

	if (cmdname[len] == ' ') {
		/* more args */
		return doveadm_try_run_multi_word(cmd, cmdname + len + 1,
						  argc - 1, argv + 1);
	}
	if (cmdname[len] != '\0')
		return FALSE;

	/* match */
	cmd->cmd(argc - 1, argv + 1);
	return TRUE;
}

static bool doveadm_try_run(const char *cmd_name, int argc, char *argv[])
{
	const struct doveadm_cmd *cmd;
	unsigned int cmd_name_len;

	i_assert(argc > 0);

	cmd_name_len = strlen(cmd_name);
	array_foreach(&doveadm_cmds, cmd) {
		if (strcmp(cmd->name, cmd_name) == 0) {
			cmd->cmd(argc, argv);
			return TRUE;
		}

		/* see if it matches a multi-word command */
		if (strncmp(cmd->name, cmd_name, cmd_name_len) == 0 &&
		    cmd->name[cmd_name_len] == ' ') {
			const char *subcmd = cmd->name + cmd_name_len + 1;

			if (doveadm_try_run_multi_word(cmd, subcmd,
						       argc, argv))
				return TRUE;
		}
	}

	return FALSE;
}

static void doveadm_load_modules(void)
{
	struct module_dir_load_settings mod_set;

	/* some doveadm plugins have dependencies to mail plugins. we can load
	   only those whose dependencies have been loaded earlier, the rest are
	   ignored. */
	memset(&mod_set, 0, sizeof(mod_set));
	mod_set.version = master_service_get_version_string(master_service);
	mod_set.require_init_funcs = TRUE;
	mod_set.debug = doveadm_debug;
	mod_set.ignore_dlopen_errors = !doveadm_debug;

	modules = module_dir_load_missing(modules, DOVEADM_MODULEDIR,
					  NULL, &mod_set);
	module_dir_init(modules);
}


static struct doveadm_cmd *doveadm_commands[] = {
	&doveadm_cmd_help,
	&doveadm_cmd_auth,
	&doveadm_cmd_user,
	&doveadm_cmd_dump,
	&doveadm_cmd_pw,
	&doveadm_cmd_who,
	&doveadm_cmd_penalty,
	&doveadm_cmd_kick,
	&doveadm_cmd_mailbox_convert
};

int main(int argc, char *argv[])
{
	const struct setting_parser_info *set_roots[] = {
		&doveadm_setting_parser_info,
		NULL
	};
	enum master_service_flags service_flags =
		MASTER_SERVICE_FLAG_STANDALONE |
		MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN;
	const char *cmd_name, *error;
	unsigned int i;
	int c;

	/* "+" is GNU extension to stop at the first non-option.
	   others just accept -+ option. */
	master_service = master_service_init("doveadm", service_flags,
					     &argc, &argv, "+Dv");
	while ((c = master_getopt(master_service)) > 0) {
		switch (c) {
		case 'D':
			doveadm_debug = TRUE;
			doveadm_verbose = TRUE;
			break;
		case 'v':
			doveadm_verbose = TRUE;
			break;
		default:
			return FATAL_DEFAULT;
		}
	}

	if (master_service_settings_read_simple(master_service, set_roots,
						&error) < 0)
		i_fatal("Error reading configuration: %s", error);
	doveadm_settings = master_service_settings_get_others(master_service)[0];

	i_array_init(&doveadm_cmds, 32);
	for (i = 0; i < N_ELEMENTS(doveadm_commands); i++)
		doveadm_register_cmd(doveadm_commands[i]);
	doveadm_register_director_commands();
	doveadm_mail_init();
	doveadm_load_modules();

	if (optind == argc)
		usage();

	cmd_name = argv[optind];
	argc -= optind;
	argv += optind;
	optind = 1;

	master_service_init_finish(master_service);
	if (!doveadm_debug) {
		/* disable debugging unless -D is given */
		i_set_debug_file("/dev/null");
	}

	if (!doveadm_try_run(cmd_name, argc, argv) &&
	    !doveadm_mail_try_run(cmd_name, argc, argv))
		usage();

	doveadm_mail_deinit();
	module_dir_unload(&modules);
	array_free(&doveadm_cmds);
	master_service_deinit(&master_service);
	return 0;
}