view src/doveadm/doveadm-mail.c @ 10662:8b138b29dc01 HEAD

lib-storage: Split mailbox_close() and mailbox_free() functionality.
author Timo Sirainen <tss@iki.fi>
date Sun, 07 Feb 2010 17:50:08 +0200
parents 615eef3139c2
children b7c8221cea5b
line wrap: on
line source

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

#include "lib.h"
#include "array.h"
#include "lib-signals.h"
#include "ioloop.h"
#include "master-service.h"
#include "mail-user.h"
#include "mail-namespace.h"
#include "mail-storage.h"
#include "mail-storage-settings.h"
#include "mail-storage-service.h"
#include "doveadm.h"
#include "doveadm-mail.h"

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

ARRAY_TYPE(doveadm_mail_cmd) doveadm_mail_cmds;

static int killed_signo = 0;

static void cmd_purge(struct mail_user *user, const char *args[] ATTR_UNUSED)
{
	struct mail_namespace *ns;

	for (ns = user->namespaces; ns != NULL; ns = ns->next) {
		if (ns->type != NAMESPACE_PRIVATE || ns->alias_for != NULL)
			continue;

		if (mail_storage_purge(ns->storage) < 0) {
			i_error("Purging namespace '%s' failed: %s", ns->prefix,
				mail_storage_get_last_error(ns->storage, NULL));
		}
	}
}

static struct mailbox *
mailbox_find_and_open(struct mail_user *user, const char *mailbox)
{
	struct mail_namespace *ns;
	struct mailbox *box;
	const char *orig_mailbox = mailbox;

	ns = mail_namespace_find(user->namespaces, &mailbox);
	if (ns == NULL)
		i_fatal("Can't find namespace for mailbox %s", mailbox);

	box = mailbox_alloc(ns->list, mailbox, NULL, MAILBOX_FLAG_KEEP_RECENT |
			    MAILBOX_FLAG_IGNORE_ACLS);
	if (mailbox_open(box) < 0) {
		i_fatal("Opening mailbox %s failed: %s", orig_mailbox,
			mail_storage_get_last_error(mailbox_get_storage(box),
						    NULL));
	}
	return box;
}

static void cmd_force_resync(struct mail_user *user, const char *args[])
{
	const char *mailbox = args[0];
	struct mail_storage *storage;
	struct mailbox *box;

	if (mailbox == NULL)
		doveadm_mail_help_name("force-resync");

	box = mailbox_find_and_open(user, mailbox);
	storage = mailbox_get_storage(box);
	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FORCE_RESYNC |
			 MAILBOX_SYNC_FLAG_FIX_INCONSISTENT) < 0) {
		i_fatal("Forcing a resync on mailbox %s failed: %s", mailbox,
			mail_storage_get_last_error(storage, NULL));
	}
	mailbox_free(&box);
}

static void
doveadm_mail_single_user(doveadm_mail_command_t *cmd, const char *username,
			 enum mail_storage_service_flags service_flags,
			 const char *args[])
{
	struct mail_storage_service_ctx *storage_service;
	struct mail_storage_service_user *service_user;
	struct mail_storage_service_input input;
	struct mail_user *mail_user;
	const char *error;

	if (username == NULL)
		i_fatal("USER environment is missing and -u option not used");

	memset(&input, 0, sizeof(input));
	input.username = username;

	storage_service = mail_storage_service_init(master_service, NULL,
						    service_flags);
	if (mail_storage_service_lookup_next(storage_service, &input,
					     &service_user, &mail_user,
					     &error) <= 0)
		i_fatal("%s", error);
	cmd(mail_user, args);
	mail_user_unref(&mail_user);
	mail_storage_service_user_free(&service_user);
	mail_storage_service_deinit(&storage_service);
}

static int
doveadm_mail_next_user(doveadm_mail_command_t *cmd,
		       struct mail_storage_service_ctx *storage_service,
		       const struct mail_storage_service_input *input,
		       const char *args[])
{
	struct mail_storage_service_user *service_user;
	struct mail_user *mail_user;
	const char *error;
	int ret;

	i_set_failure_prefix(t_strdup_printf("doveadm(%s): ", input->username));
	ret = mail_storage_service_lookup(storage_service, input,
					  &service_user, &error);
	if (ret <= 0) {
		if (ret == 0) {
			i_info("User no longer exists, skipping");
			return 0;
		} else {
			i_error("User lookup failed: %s", error);
			return -1;
		}
	}
	if (mail_storage_service_next(storage_service, service_user,
				      &mail_user, &error) < 0) {
		i_error("User init failed: %s", error);
		mail_storage_service_user_free(&service_user);
		return -1;
	}
	cmd(mail_user, args);
	mail_storage_service_user_free(&service_user);
	mail_user_unref(&mail_user);
	return 0;
}

static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
{
	killed_signo = si->si_signo;
}

static void
doveadm_mail_all_users(doveadm_mail_command_t *cmd,
		       enum mail_storage_service_flags service_flags,
		       const char *args[])
{
	struct mail_storage_service_input input;
	struct mail_storage_service_ctx *storage_service;
	unsigned int user_idx, user_count, interval, n;
	const char *user;
	int ret;

	service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;

	memset(&input, 0, sizeof(input));
	input.service = "doveadm";

	storage_service = mail_storage_service_init(master_service, NULL,
						    service_flags);

        lib_signals_set_handler(SIGINT, FALSE, sig_die, NULL);
	lib_signals_set_handler(SIGTERM, FALSE, sig_die, NULL);

	user_count = mail_storage_service_all_init(storage_service);
	n = user_count / 10000;
	for (interval = 10; n > 0 && interval < 1000; interval *= 10)
		n /= 10;
	
	user_idx = 0;
	while ((ret = mail_storage_service_all_next(storage_service,
						    &user)) > 0) {
		input.username = user;
		T_BEGIN {
			ret = doveadm_mail_next_user(cmd, storage_service,
						     &input, args);
		} T_END;
		if (ret < 0)
			break;
		if (doveadm_verbose) {
			if (++user_idx % interval == 0) {
				printf("\r%d / %d", user_idx, user_count);
				fflush(stdout);
			}
		}
		if (killed_signo != 0) {
			i_warning("Killed with signal %d", killed_signo);
			ret = -1;
			break;
		}
	}
	if (doveadm_verbose)
		printf("\n");
	i_set_failure_prefix("doveadm: ");
	if (ret < 0)
		i_error("Failed to iterate through some users");
	mail_storage_service_deinit(&storage_service);
}

static void
doveadm_mail_cmd(const struct doveadm_mail_cmd *cmd, int argc, char *argv[])
{
	enum mail_storage_service_flags service_flags = 0;
	const char *username;
	bool all_users = FALSE;
	int c;

	if (doveadm_debug)
		service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;

	while ((c = getopt(argc, argv, "a")) > 0) {
		switch (c) {
		case 'a':
			all_users = TRUE;
			break;
		default:
			doveadm_mail_help(cmd);
		}
	}
	if (!all_users) {
		if (optind == argc)
			doveadm_mail_help(cmd);
		service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
		username = argv[optind++];
		doveadm_mail_single_user(cmd->cmd, username, service_flags,
					 (const char **)argv + optind);
	} else {
		service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP;
		doveadm_mail_all_users(cmd->cmd, service_flags,
				       (const char **)argv + optind);
	}
}

bool doveadm_mail_try_run(const char *cmd_name, int argc, char *argv[])
{
	const struct doveadm_mail_cmd *cmd;

	array_foreach(&doveadm_mail_cmds, cmd) {
		if (strcmp(cmd->name, cmd_name) == 0) {
			doveadm_mail_cmd(cmd, argc, argv);
			return TRUE;
		}
	}
	return FALSE;
}

void doveadm_mail_register_cmd(const struct doveadm_mail_cmd *cmd)
{
	/* for now we'll just assume that cmd will be permanently in memory */
	array_append(&doveadm_mail_cmds, cmd, 1);
}

void doveadm_mail_usage(void)
{
	const struct doveadm_mail_cmd *cmd;

	array_foreach(&doveadm_mail_cmds, cmd) {
		fprintf(stderr, USAGE_CMDNAME_FMT" <user>|-a", cmd->name);
		if (cmd->usage_args != NULL)
			fprintf(stderr, " %s", cmd->usage_args);
		fputc('\n', stderr);
	}
}

void doveadm_mail_help(const struct doveadm_mail_cmd *cmd)
{
	fprintf(stderr, "doveadm %s %s\n", cmd->name,
		cmd->usage_args == NULL ? "" : cmd->usage_args);
	exit(0);
}

void doveadm_mail_help_name(const char *cmd_name)
{
	const struct doveadm_mail_cmd *cmd;

	array_foreach(&doveadm_mail_cmds, cmd) {
		if (strcmp(cmd->name, cmd_name) == 0)
			doveadm_mail_help(cmd);
	}
}

static struct doveadm_mail_cmd mail_commands[] = {
	{ cmd_purge, "purge", NULL },
	{ cmd_force_resync, "force-resync", "<mailbox>" }
};

void doveadm_mail_init(void)
{
	unsigned int i;

	i_array_init(&doveadm_mail_cmds, 32);
	for (i = 0; i < N_ELEMENTS(mail_commands); i++)
		doveadm_mail_register_cmd(&mail_commands[i]);
}

void doveadm_mail_deinit(void)
{
	array_free(&doveadm_mail_cmds);
}