dsync symlink is installed for backwards compatibility.
src/dns/dns-client
src/doveadm/doveadm
src/doveadm/doveadm-server
-src/dsync/dsync
src/imap-login/imap-login
src/imap/imap
src/indexer/indexer
src/auth/Makefile
src/config/Makefile
src/doveadm/Makefile
-src/dsync/Makefile
+src/doveadm/dsync/Makefile
src/lda/Makefile
src/log/Makefile
src/lmtp/Makefile
director \
util \
doveadm \
- dsync \
ssl-params \
stats \
plugins
doveadm_moduledir = $(moduledir)/doveadm
pkglibexecdir = $(libexecdir)/dovecot
+SUBDIRS = dsync
+
bin_PROGRAMS = doveadm
pkglibexec_PROGRAMS = doveadm-server
../lib-otp/libotp.a
libs = \
+ dsync/libdsync.a \
$(LIBDOVECOT_STORAGE) \
$(unused_objects)
doveadm-settings.h \
doveadm-util.h \
doveadm-who.h
+
+install-exec-local:
+ rm -f $(DESTDIR)$(bindir)/dsync
+ $(LN_S) doveadm $(DESTDIR)$(bindir)/dsync
const struct mail_storage_service_input *input,
int argc, char *argv[])
{
- enum mail_storage_service_flags service_flags =
- MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT |
- MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
struct doveadm_mail_cmd_context *ctx;
const struct doveadm_mail_cmd *cmd;
const char *getopt_args;
return FALSE;
}
- if (doveadm_debug)
- service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
-
ctx = doveadm_mail_cmd_init(cmd, set);
ctx->full_args = (const void *)(argv + 1);
+ ctx->service_flags |=
+ MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT |
+ MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
+ if (doveadm_debug)
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
+
getopt_args = t_strconcat("AS:u:", ctx->getopt_args, NULL);
while ((c = getopt(argc, argv, getopt_args)) > 0) {
switch (c) {
}
ctx->args = (const void *)argv;
- doveadm_mail_single_user(ctx, input, service_flags);
+ if (ctx->v.preinit != NULL)
+ ctx->v.preinit(ctx);
+
+ doveadm_mail_single_user(ctx, input);
doveadm_mail_server_flush();
ctx->v.deinit(ctx);
doveadm_print_flush();
#include "doveadm.h"
#include "doveadm-settings.h"
#include "doveadm-print.h"
+#include "dsync/doveadm-dsync.h"
#include "doveadm-mail.h"
#include <stdio.h>
const struct mail_storage_service_input *input,
const char **error_r)
{
- struct mail_storage_service_user *service_user;
const char *error;
int ret;
return ret;
ret = mail_storage_service_lookup(ctx->storage_service, input,
- &service_user, &error);
+ &ctx->cur_service_user, &error);
if (ret <= 0) {
if (ret < 0) {
*error_r = t_strdup_printf("User lookup failed: %s",
return ret;
}
- ret = mail_storage_service_next(ctx->storage_service, service_user,
+ ret = mail_storage_service_next(ctx->storage_service,
+ ctx->cur_service_user,
&ctx->cur_mail_user);
if (ret < 0) {
*error_r = "User init failed";
- mail_storage_service_user_free(&service_user);
+ mail_storage_service_user_free(&ctx->cur_service_user);
return ret;
}
ctx->v.run(ctx, ctx->cur_mail_user);
mail_user_unref(&ctx->cur_mail_user);
- mail_storage_service_user_free(&service_user);
+ mail_storage_service_user_free(&ctx->cur_service_user);
return 1;
}
void doveadm_mail_single_user(struct doveadm_mail_cmd_context *ctx,
- const struct mail_storage_service_input *input,
- enum mail_storage_service_flags service_flags)
+ const struct mail_storage_service_input *input)
{
const char *error;
int ret;
i_assert(input->username != NULL);
+ ctx->cur_username = input->username;
ctx->storage_service = mail_storage_service_init(master_service, NULL,
- service_flags);
+ ctx->service_flags);
ctx->v.init(ctx, ctx->args);
if (hook_doveadm_mail_init != NULL)
hook_doveadm_mail_init(ctx);
static void
doveadm_mail_all_users(struct doveadm_mail_cmd_context *ctx, char *argv[],
- const char *wildcard_user,
- enum mail_storage_service_flags service_flags)
+ const char *wildcard_user)
{
struct mail_storage_service_input input;
unsigned int user_idx, user_count, interval, n;
const char *user, *error;
int ret;
- service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
memset(&input, 0, sizeof(input));
input.service = "doveadm";
ctx->storage_service = mail_storage_service_init(master_service, NULL,
- service_flags);
+ ctx->service_flags);
lib_signals_set_handler(SIGINT, 0, sig_die, NULL);
lib_signals_set_handler(SIGTERM, 0, sig_die, NULL);
static void
doveadm_mail_cmd(const struct doveadm_mail_cmd *cmd, int argc, char *argv[])
{
- enum mail_storage_service_flags service_flags =
- MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
struct doveadm_mail_cmd_context *ctx;
- const char *getopt_args, *username, *wildcard_user;
+ const char *getopt_args, *wildcard_user;
int c;
- if (doveadm_debug)
- service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
-
ctx = doveadm_mail_cmd_init(cmd, doveadm_settings);
ctx->full_args = (const void *)(argv + 1);
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
+ if (doveadm_debug)
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
+
getopt_args = t_strconcat("AS:u:", ctx->getopt_args, NULL);
- username = getenv("USER");
+ ctx->cur_username = getenv("USER");
wildcard_user = NULL;
while ((c = getopt(argc, argv, getopt_args)) > 0) {
switch (c) {
doveadm_settings->doveadm_worker_count = 1;
break;
case 'u':
- service_flags |=
+ ctx->service_flags |=
MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
- username = optarg;
- if (strchr(username, '*') != NULL ||
- strchr(username, '?') != NULL)
- wildcard_user = username;
+ ctx->cur_username = optarg;
+ if (strchr(ctx->cur_username, '*') != NULL ||
+ strchr(ctx->cur_username, '?') != NULL) {
+ wildcard_user = ctx->cur_username;
+ ctx->cur_username = NULL;
+ }
break;
default:
if (ctx->v.parse_arg == NULL ||
cmd->name, argv[0]);
}
ctx->args = (const void *)argv;
+ if (ctx->v.preinit != NULL)
+ ctx->v.preinit(ctx);
ctx->iterate_single_user =
!ctx->iterate_all_users && wildcard_user == NULL;
if (ctx->iterate_single_user) {
struct mail_storage_service_input input;
- if (username == NULL)
+ if (ctx->cur_username == NULL)
i_fatal("USER environment is missing and -u option not used");
memset(&input, 0, sizeof(input));
input.service = "doveadm";
- input.username = username;
- doveadm_mail_single_user(ctx, &input, service_flags);
+ input.username = ctx->cur_username;
+ doveadm_mail_single_user(ctx, &input);
} else {
- service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP;
- doveadm_mail_all_users(ctx, argv, wildcard_user, service_flags);
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP;
+ doveadm_mail_all_users(ctx, argv, wildcard_user);
}
if (ctx->search_args != NULL)
mail_search_args_unref(&ctx->search_args);
/* service deinit unloads mail plugins, so do it late */
mail_storage_service_deinit(&ctx->storage_service);
+ if (ctx->exit_code != 0)
+ exit(ctx->exit_code);
if (ctx->failed)
exit(FATAL_DEFAULT);
pool_unref(&ctx->pool);
&cmd_mailbox_rename,
&cmd_mailbox_subscribe,
&cmd_mailbox_unsubscribe,
- &cmd_mailbox_status
+ &cmd_mailbox_status,
+ &cmd_dsync_backup,
+ &cmd_dsync_mirror,
+ &cmd_dsync_server
};
void doveadm_mail_init(void)
#include <stdio.h>
#include "doveadm-util.h"
#include "module-context.h"
+#include "mail-storage-service.h"
-enum mail_storage_service_flags;
struct mailbox;
struct mail_user;
-struct mail_storage_service_ctx;
-struct mail_storage_service_input;
-struct mail_storage_service_user;
struct doveadm_mail_cmd_context;
struct doveadm_mail_cmd_vfuncs {
bool (*parse_arg)(struct doveadm_mail_cmd_context *ctx,int c);
+ void (*preinit)(struct doveadm_mail_cmd_context *ctx);
void (*init)(struct doveadm_mail_cmd_context *ctx,
const char *const args[]);
int (*get_next_user)(struct doveadm_mail_cmd_context *ctx,
const char *getopt_args;
const struct doveadm_settings *set;
+ enum mail_storage_service_flags service_flags;
struct mail_storage_service_ctx *storage_service;
/* search args aren't set for all mail commands */
struct mail_search_args *search_args;
+ const char *cur_username;
+ struct mail_storage_service_user *cur_service_user;
struct mail_user *cur_mail_user;
struct doveadm_mail_cmd_vfuncs v;
ARRAY_DEFINE(module_contexts, union doveadm_mail_cmd_module_context *);
+ /* if non-zero, exit with this code */
+ int exit_code;
+
/* We're handling only a single user */
unsigned int iterate_single_user:1;
/* We're going through all users (not set for wildcard usernames) */
doveadm_mail_cmd_init(const struct doveadm_mail_cmd *cmd,
const struct doveadm_settings *set);
void doveadm_mail_single_user(struct doveadm_mail_cmd_context *ctx,
- const struct mail_storage_service_input *input,
- enum mail_storage_service_flags service_flags);
+ const struct mail_storage_service_input *input);
int doveadm_mail_server_user(struct doveadm_mail_cmd_context *ctx,
const struct mail_storage_service_input *input,
const char **error_r);
DEF(SET_UINT, doveadm_proxy_port),
DEF(SET_STR, doveadm_password),
DEF(SET_STR, doveadm_allowed_commands),
+ DEF(SET_STR, dsync_alt_char),
{ SET_STRLIST, "plugin", offsetof(struct doveadm_settings, plugin_envs), NULL },
.doveadm_proxy_port = 0,
.doveadm_password = "",
.doveadm_allowed_commands = "",
+ .dsync_alt_char = "_",
.plugin_envs = ARRAY_INIT
};
unsigned int doveadm_proxy_port;
const char *doveadm_password;
const char *doveadm_allowed_commands;
+ const char *dsync_alt_char;
ARRAY_DEFINE(plugin_envs, const char *);
};
#include "doveadm-dump.h"
#include "doveadm-mail.h"
#include "doveadm-settings.h"
+#include "dsync/doveadm-dsync.h"
#include "doveadm.h"
#include <stdlib.h>
bool quick_init = FALSE;
int c;
+ 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,
-bin_PROGRAMS = dsync
+noinst_LIBRARIES = libdsync.a
AM_CPPFLAGS = \
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-mail \
-I$(top_srcdir)/src/lib-imap \
-I$(top_srcdir)/src/lib-index \
- -I$(top_srcdir)/src/lib-storage
-
-if !BUILD_SHARED_LIBS
-unused_objects = \
- ../lib/mountpoint.o \
- ../lib-storage/mail-search-parser-imap.o
-endif
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/doveadm
libs = \
- $(LIBDOVECOT_STORAGE) \
- $(unused_objects)
+ $(LIBDOVECOT_STORAGE)
-dsync_LDADD = $(libs) $(LIBDOVECOT) $(MODULE_LIBS)
-dsync_DEPENDENCIES = $(libs) $(LIBDOVECOT_DEPS)
-dsync_SOURCES = \
- dsync.c \
+libdsync_a_SOURCES = \
+ doveadm-dsync.c \
dsync-brain.c \
dsync-brain-msgs.c \
dsync-brain-msgs-new.c \
noinst_PROGRAMS = $(test_programs)
test_libs = \
- ../lib-test/libtest.la \
- ../lib-mail/libmail.la \
- ../lib-imap/libimap.la \
- ../lib-charset/libcharset.la \
- ../lib/liblib.la
+ ../../lib-test/libtest.la \
+ ../../lib-mail/libmail.la \
+ ../../lib-imap/libimap.la \
+ ../../lib-charset/libcharset.la \
+ ../../lib/liblib.la
test_ldadd = \
$(test_libs) \
--- /dev/null
+/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "lib-signals.h"
+#include "array.h"
+#include "execv-const.h"
+#include "settings-parser.h"
+#include "master-service.h"
+#include "mail-storage-service.h"
+#include "mail-user.h"
+#include "mail-namespace.h"
+#include "doveadm-settings.h"
+#include "doveadm-mail.h"
+#include "dsync-brain.h"
+#include "dsync-worker.h"
+#include "dsync-proxy-server.h"
+#include "doveadm-dsync.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+struct dsync_cmd_context {
+ struct doveadm_mail_cmd_context ctx;
+ enum dsync_brain_flags brain_flags;
+ const char *mailbox;
+
+ const char *const *remote_cmd_args;
+ const char *local_location;
+
+ int fd_in, fd_out;
+
+ unsigned int reverse_workers:1;
+};
+
+static const char *ssh_cmd = "ssh";
+
+static void run_cmd(const char *const *args, int *fd_in_r, int *fd_out_r)
+{
+ int fd_in[2], fd_out[2];
+
+ if (pipe(fd_in) < 0 || pipe(fd_out) < 0)
+ i_fatal("pipe() failed: %m");
+
+ switch (fork()) {
+ case -1:
+ i_fatal("fork() failed: %m");
+ break;
+ case 0:
+ /* child, which will execute the proxy server. stdin/stdout
+ goes to pipes which we'll pass to proxy client. */
+ if (dup2(fd_in[0], STDIN_FILENO) < 0 ||
+ dup2(fd_out[1], STDOUT_FILENO) < 0)
+ i_fatal("dup2() failed: %m");
+
+ (void)close(fd_in[0]);
+ (void)close(fd_in[1]);
+ (void)close(fd_out[0]);
+ (void)close(fd_out[1]);
+
+ execvp_const(args[0], args);
+ break;
+ default:
+ /* parent */
+ (void)close(fd_in[0]);
+ (void)close(fd_out[1]);
+ *fd_in_r = fd_out[0];
+ *fd_out_r = fd_in[1];
+ break;
+ }
+}
+
+static void
+mirror_get_remote_cmd_line(const char *const *argv,
+ const char *const **cmd_args_r)
+{
+ ARRAY_TYPE(const_string) cmd_args;
+ unsigned int i;
+ const char *p;
+
+ t_array_init(&cmd_args, 16);
+ for (i = 0; argv[i] != NULL; i++) {
+ p = argv[i];
+ array_append(&cmd_args, &p, 1);
+ }
+
+ p = strchr(argv[0], '/');
+ if (p == NULL) p = argv[0];
+ if (strstr(p, "dsync") == NULL) {
+ /* we're executing doveadm (not dsync) */
+ p = "dsync"; array_append(&cmd_args, &p, 1);
+ }
+ p = "server"; array_append(&cmd_args, &p, 1);
+ (void)array_append_space(&cmd_args);
+ *cmd_args_r = array_idx(&cmd_args, 0);
+}
+
+static bool mirror_get_remote_cmd(const char *const *argv, const char *user,
+ const char *const **cmd_args_r)
+{
+ ARRAY_TYPE(const_string) cmd_args;
+ const char *p, *host;
+
+ if (argv[1] != NULL) {
+ /* more than one parameter, so it contains a full command
+ (e.g. ssh host dsync) */
+ mirror_get_remote_cmd_line(argv, cmd_args_r);
+ return TRUE;
+ }
+
+ /* if it begins with /[a-z0-9]+:/, it's a mail location
+ (e.g. mdbox:~/mail) */
+ for (p = argv[0]; *p != '\0'; p++) {
+ if (!i_isalnum(*p)) {
+ if (*p == ':')
+ return FALSE;
+ break;
+ }
+ }
+
+ if (strchr(argv[0], ' ') != NULL || strchr(argv[0], '/') != NULL) {
+ /* a) the whole command is in one string. this is mainly for
+ backwards compatibility.
+ b) script/path */
+ mirror_get_remote_cmd_line(t_strsplit(argv[0], " "),
+ cmd_args_r);
+ return TRUE;
+ }
+
+ /* [user@]host */
+ host = strchr(argv[0], '@');
+ if (host != NULL)
+ user = t_strdup_until(argv[0], host++);
+ else
+ host = argv[0];
+
+ /* we'll assume virtual users, so in user@host it really means not to
+ give ssh a username, but to give dsync -u user parameter. */
+ t_array_init(&cmd_args, 8);
+ array_append(&cmd_args, &ssh_cmd, 1);
+ array_append(&cmd_args, &host, 1);
+ p = "doveadm"; array_append(&cmd_args, &p, 1);
+ p = "dsync"; array_append(&cmd_args, &p, 1);
+ p = "server"; array_append(&cmd_args, &p, 1);
+ if (*user != '\0') {
+ p = "-u"; array_append(&cmd_args, &p, 1);
+ array_append(&cmd_args, &user, 1);
+ }
+ (void)array_append_space(&cmd_args);
+ *cmd_args_r = array_idx(&cmd_args, 0);
+ return TRUE;
+}
+
+static struct dsync_worker *
+cmd_dsync_run_local(struct dsync_cmd_context *ctx, struct mail_user *user)
+{
+ struct mail_user *user2;
+ struct dsync_worker *worker2;
+ struct setting_parser_context *set_parser;
+ const char *set_line, *path1, *path2;
+
+ i_assert(ctx->local_location != NULL);
+
+ ctx->brain_flags |= DSYNC_BRAIN_FLAG_LOCAL;
+ i_set_failure_prefix(t_strdup_printf("dsync(%s): ", user->username));
+
+ /* update mail_location and create another user for the
+ second location. */
+ set_parser = mail_storage_service_user_get_settings_parser(ctx->ctx.cur_service_user);
+ set_line = t_strconcat("mail_location=", ctx->local_location, NULL);
+ if (settings_parse_line(set_parser, set_line) < 0)
+ i_unreached();
+ if (mail_storage_service_next(ctx->ctx.storage_service,
+ ctx->ctx.cur_service_user, &user2) < 0)
+ i_fatal("User init failed");
+ user2->admin = TRUE;
+
+ if (mail_namespaces_get_root_sep(user->namespaces) !=
+ mail_namespaces_get_root_sep(user2->namespaces)) {
+ i_fatal("Mail locations must use the same "
+ "virtual mailbox hierarchy separator "
+ "(specify separator for the default namespace)");
+ }
+ path1 = mailbox_list_get_path(user->namespaces->list, NULL,
+ MAILBOX_LIST_PATH_TYPE_MAILBOX);
+ path2 = mailbox_list_get_path(user2->namespaces->list, NULL,
+ MAILBOX_LIST_PATH_TYPE_MAILBOX);
+ if (path1 != NULL && path2 != NULL &&
+ strcmp(path1, path2) == 0) {
+ i_fatal("Both source and destination mail_location "
+ "points to same directory: %s", path1);
+ }
+
+ worker2 = dsync_worker_init_local(user2, *ctx->ctx.set->dsync_alt_char);
+ mail_user_unref(&user2);
+ return worker2;
+}
+
+static struct dsync_worker *
+cmd_dsync_run_remote(struct dsync_cmd_context *ctx, struct mail_user *user)
+{
+ i_set_failure_prefix(t_strdup_printf("dsync-local(%s): ",
+ user->username));
+ return dsync_worker_init_proxy_client(ctx->fd_in, ctx->fd_out);
+}
+
+static void
+cmd_dsync_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
+{
+ struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
+ struct dsync_worker *worker1, *worker2, *workertmp;
+ struct dsync_brain *brain;
+
+ user->admin = TRUE;
+
+ /* create workers */
+ worker1 = dsync_worker_init_local(user, *_ctx->set->dsync_alt_char);
+ if (ctx->remote_cmd_args == NULL)
+ worker2 = cmd_dsync_run_local(ctx, user);
+ else
+ worker2 = cmd_dsync_run_remote(ctx, user);
+ if (ctx->reverse_workers) {
+ workertmp = worker1;
+ worker1 = worker2;
+ worker2 = workertmp;
+ }
+
+ /* create and run the brain */
+ brain = dsync_brain_init(worker1, worker2, ctx->mailbox,
+ ctx->brain_flags);
+ if (ctx->remote_cmd_args == NULL)
+ dsync_brain_sync_all(brain);
+ else {
+ dsync_brain_sync(brain);
+ if (!dsync_brain_has_failed(brain))
+ io_loop_run(current_ioloop);
+ }
+ /* deinit */
+ if (dsync_brain_has_unexpected_changes(brain)) {
+ i_warning("Mailbox changes caused a desync. "
+ "You may want to run dsync again.");
+ _ctx->exit_code = 2;
+ }
+ if (dsync_brain_deinit(&brain) < 0)
+ _ctx->exit_code = 1;
+
+ dsync_worker_deinit(&worker1);
+ dsync_worker_deinit(&worker2);
+}
+
+static void cmd_dsync_init(struct doveadm_mail_cmd_context *_ctx,
+ const char *const args[])
+{
+ struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
+ const char *username = "";
+
+ if (args[0] == NULL)
+ doveadm_mail_help_name("dsync");
+
+ lib_signals_ignore(SIGHUP, TRUE);
+
+ if (doveadm_debug || doveadm_verbose)
+ ctx->brain_flags |= DSYNC_BRAIN_FLAG_VERBOSE;
+
+ /* if we're executing remotely, give -u parameter if we also
+ did a userdb lookup. this works only when we're handling a
+ single user */
+ if ((_ctx->service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0 &&
+ _ctx->cur_username != NULL)
+ username = _ctx->cur_username;
+ if (!mirror_get_remote_cmd(args, username, &ctx->remote_cmd_args)) {
+ /* it's a mail_location */
+ if (args[1] != NULL)
+ doveadm_mail_help_name("dsync");
+ ctx->local_location = args[0];
+ }
+
+ if (ctx->remote_cmd_args != NULL) {
+ /* do this before mail_storage_service_next() in case it
+ drops process privileges */
+ run_cmd(ctx->remote_cmd_args, &ctx->fd_in, &ctx->fd_out);
+ } else {
+ ctx->fd_in = STDIN_FILENO;
+ ctx->fd_out = STDOUT_FILENO;
+ }
+}
+
+static void cmd_dsync_preinit(struct doveadm_mail_cmd_context *ctx)
+{
+ if ((ctx->service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0)
+ ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR;
+}
+
+static bool
+cmd_mailbox_dsync_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c)
+{
+ struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
+
+ switch (c) {
+ case 'f':
+ ctx->brain_flags |= DSYNC_BRAIN_FLAG_FULL_SYNC;
+ break;
+ case 'm':
+ ctx->mailbox = optarg;
+ break;
+ case 'R':
+ ctx->reverse_workers = TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static struct doveadm_mail_cmd_context *cmd_dsync_alloc(void)
+{
+ struct dsync_cmd_context *ctx;
+
+ ctx = doveadm_mail_cmd_alloc(struct dsync_cmd_context);
+ ctx->ctx.getopt_args = "fRm:";
+ ctx->ctx.v.parse_arg = cmd_mailbox_dsync_parse_arg;
+ ctx->ctx.v.preinit = cmd_dsync_preinit;
+ ctx->ctx.v.init = cmd_dsync_init;
+ ctx->ctx.v.run = cmd_dsync_run;
+ return &ctx->ctx;
+}
+
+static struct doveadm_mail_cmd_context *cmd_dsync_backup_alloc(void)
+{
+ struct doveadm_mail_cmd_context *_ctx;
+ struct dsync_cmd_context *ctx;
+
+ _ctx = cmd_dsync_alloc();
+ ctx = (struct dsync_cmd_context *)_ctx;
+ ctx->brain_flags |= DSYNC_BRAIN_FLAG_BACKUP;
+ return _ctx;
+}
+
+static void
+cmd_dsync_server_run(struct doveadm_mail_cmd_context *ctx,
+ struct mail_user *user)
+{
+ struct dsync_proxy_server *server;
+ struct dsync_worker *worker;
+
+ user->admin = TRUE;
+
+ i_set_failure_prefix(t_strdup_printf("dsync-remote(%s): ",
+ user->username));
+ worker = dsync_worker_init_local(user, *ctx->set->dsync_alt_char);
+ server = dsync_proxy_server_init(STDIN_FILENO, STDOUT_FILENO, worker);
+
+ io_loop_run(current_ioloop);
+
+ dsync_proxy_server_deinit(&server);
+ dsync_worker_deinit(&worker);
+}
+
+static struct doveadm_mail_cmd_context *cmd_dsync_server_alloc(void)
+{
+ struct doveadm_mail_cmd_context *ctx;
+
+ ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context);
+ ctx->v.run = cmd_dsync_server_run;
+ return ctx;
+}
+
+struct doveadm_mail_cmd cmd_dsync_mirror = {
+ cmd_dsync_alloc, "dsync mirror", "[-fR] [-m <mailbox>] <dest>"
+};
+struct doveadm_mail_cmd cmd_dsync_backup = {
+ cmd_dsync_backup_alloc, "dsync backup",
+ "[-fR] [-m <mailbox>] <dest>"
+};
+struct doveadm_mail_cmd cmd_dsync_server = {
+ cmd_dsync_server_alloc, "dsync server", NULL
+};
+
+void doveadm_dsync_main(int *_argc, char **_argv[])
+{
+ int argc = *_argc;
+ const char *getopt_str;
+ char **argv = *_argv;
+ char **new_argv, *mailbox = NULL, *alt_char = NULL;
+ char *p, *dup, new_flags[5];
+ int max_argc, src, dest, i, j;
+ bool flag_f = FALSE, flag_R = FALSE, flag_m, flag_C, has_arg;
+
+ p = strrchr(argv[0], '/');
+ if (p == NULL) p = argv[0];
+ if (strstr(p, "dsync") == NULL)
+ return;
+
+ /* @UNSAFE: this is called when the "doveadm" binary is called as
+ "dsync" (for backwards compatibility) */
+ max_argc = argc + 5;
+ new_argv = calloc(sizeof(char *), max_argc);
+ new_argv[0] = argv[0];
+ dest = 1;
+ getopt_str = master_service_getopt_string();
+
+ /* add global doveadm flags */
+ for (src = 1; src < argc; src++) {
+ if (argv[src][0] != '-')
+ break;
+
+ flag_m = FALSE; flag_C = FALSE; has_arg = FALSE;
+ dup = strdup(argv[src]);
+ for (i = j = 1; argv[src][i] != '\0'; i++) {
+ switch (argv[src][i]) {
+ case 'C':
+ flag_C = TRUE;
+ break;
+ case 'f':
+ flag_f = TRUE;
+ break;
+ case 'R':
+ flag_R = TRUE;
+ break;
+ case 'm':
+ flag_m = TRUE;
+ break;
+ default:
+ p = strchr(getopt_str, argv[src][i]);
+ if (p != NULL && p[1] == ':')
+ has_arg = TRUE;
+ dup[j++] = argv[src][i];
+ break;
+ }
+ }
+ if (j > 1) {
+ dup[j++] = '\0';
+ new_argv[dest++] = dup;
+ if (has_arg && src+1 < argc)
+ new_argv[dest++] = argv[++src];
+ }
+ if (flag_m) {
+ if (src+1 == argc)
+ i_fatal("-m missing parameter");
+ mailbox = argv[++src];
+ }
+ if (flag_C) {
+ if (src+1 == argc)
+ i_fatal("-C missing parameter");
+ alt_char = argv[++src];
+ }
+ }
+ if (alt_char != NULL) {
+ new_argv[dest++] = "-o";
+ new_argv[dest++] =
+ p_strconcat(pool_datastack_create(),
+ "dsync_alt_char=", alt_char, NULL);
+ }
+
+ new_argv[dest++] = "dsync";
+ if (src < argc) {
+ /* mirror|backup|server */
+ if (strcmp(argv[src], "dsync") == 0) {
+ /* looks like we executed doveconf, which
+ re-executed ourself with new parameters.
+ no need to change them anymore. */
+ return;
+ }
+ new_argv[dest++] = argv[src++];
+ }
+
+ /* dsync flags */
+ new_flags[0] = '-'; i = 1;
+ if (flag_f)
+ new_flags[i++] = 'f';
+ if (flag_R)
+ new_flags[i++] = 'R';
+ if (mailbox != NULL)
+ new_flags[i++] = 'm';
+ i_assert((unsigned int)i < sizeof(new_flags));
+ new_flags[i] = '\0';
+
+ if (i > 1) {
+ new_argv[dest++] = strdup(new_flags);
+ if (mailbox != NULL)
+ new_argv[dest++] = mailbox;
+ }
+
+ /* rest of the parameters */
+ for (; src < argc; src++)
+ new_argv[dest++] = argv[src];
+ i_assert(dest < max_argc);
+ new_argv[dest] = NULL;
+
+ *_argc = dest;
+ *_argv = new_argv;
+ optind = 1;
+}
--- /dev/null
+#ifndef DOVEADM_DSYNC_H
+#define DOVEADM_DSYNC_H
+
+extern struct doveadm_mail_cmd cmd_dsync_mirror;
+extern struct doveadm_mail_cmd cmd_dsync_backup;
+extern struct doveadm_mail_cmd cmd_dsync_server;
+
+void doveadm_dsync_main(int *_argc, char **_argv[]);
+
+#endif
#include "lib.h"
#include "array.h"
#include "hash.h"
-#include "master-service.h"
#include "dsync-worker.h"
#include "dsync-brain-private.h"
void dsync_brain_fail(struct dsync_brain *brain)
{
brain->failed = TRUE;
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
}
int dsync_brain_deinit(struct dsync_brain **_brain)
case DSYNC_STATE_SYNC_FLUSH2:
break;
case DSYNC_STATE_SYNC_END:
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
break;
default:
i_unreached();
#include "ostream.h"
#include "str.h"
#include "strescape.h"
-#include "master-service.h"
#include "imap-util.h"
#include "dsync-proxy.h"
#include "dsync-worker-private.h"
{
i_stream_close(worker->input);
dsync_worker_set_failure(&worker->worker);
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
}
static int
#include "istream-dot.h"
#include "ostream.h"
#include "imap-util.h"
-#include "master-service.h"
#include "dsync-worker.h"
#include "dsync-proxy.h"
#include "dsync-proxy-server.h"
#include "fd-set-nonblock.h"
#include "istream.h"
#include "ostream.h"
-#include "master-service.h"
#include "dsync-worker.h"
#include "dsync-proxy.h"
#include "dsync-proxy-server.h"
if (server->input->stream_errno != 0) {
errno = server->input->stream_errno;
i_error("read() from proxy client failed: %m");
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
return -1;
}
if (server->input->eof) {
if (!server->finished)
i_error("read() from proxy client failed: EOF");
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
return -1;
}
}
if (!server->handshake_received) {
if (strcmp(*line_r, DSYNC_PROXY_CLIENT_GREETING_LINE) != 0) {
i_error("Invalid client handshake: %s", *line_r);
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
return -1;
}
server->handshake_received = TRUE;
ret = -1;
if (ret < 0)
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
timeout_reset(server->to);
}
}
}
if (output->closed)
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
timeout_reset(server->to);
return ret;
}
static void dsync_proxy_server_timeout(void *context ATTR_UNUSED)
{
i_error("proxy server timed out");
- master_service_stop(master_service);
+ io_loop_stop(current_ioloop);
}
struct dsync_proxy_server *
i_array_init(&worker->msg_get_queue, 32);
p_array_init(&worker->subs_namespaces, pool, 8);
dsync_drop_extra_namespaces(worker);
+
+ mail_user_ref(worker->user);
return &worker->worker;
}
local_worker_msg_box_close(worker);
local_worker_mailbox_close(worker);
+ mail_user_unref(&worker->user);
+
hash_table_destroy(&worker->mailbox_hash);
if (worker->mailbox_changes_hash != NULL)
hash_table_destroy(&worker->mailbox_changes_hash);
#include "lib.h"
#include "array.h"
-#include "master-service.h"
#include "dsync-brain-private.h"
#include "test-dsync-worker.h"
#include "test-dsync-common.h"
struct master_service *master_service;
static struct test_dsync_worker *src_test_worker, *dest_test_worker;
-void master_service_stop(struct master_service *master_service ATTR_UNUSED)
-{
-}
-
struct dsync_brain_mailbox_sync *
dsync_brain_msg_sync_init(struct dsync_brain *brain,
const ARRAY_TYPE(dsync_brain_mailbox) *mailboxes)
#include "strescape.h"
#include "istream.h"
#include "ostream.h"
-#include "master-service.h"
#include "test-common.h"
#include "dsync-proxy-server.h"
#include "test-dsync-worker.h"
static struct dsync_proxy_server_command *cur_cmd;
static const char *cur_cmd_args[20];
-void master_service_stop(struct master_service *service ATTR_UNUSED) {}
-
static void out_clear(void)
{
o_stream_seek(server->output, 0);
+++ /dev/null
-/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "lib-signals.h"
-#include "array.h"
-#include "execv-const.h"
-#include "settings-parser.h"
-#include "master-service.h"
-#include "master-service-settings.h"
-#include "mail-storage-service.h"
-#include "mail-user.h"
-#include "mail-namespace.h"
-#include "dsync-brain.h"
-#include "dsync-worker.h"
-#include "dsync-proxy-server.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-
-static const char *ssh_cmd = "ssh";
-static struct dsync_brain *brain;
-static struct dsync_proxy_server *server;
-
-static void run_cmd(const char *const *args, int *fd_in_r, int *fd_out_r)
-{
- int fd_in[2], fd_out[2];
-
- if (pipe(fd_in) < 0 || pipe(fd_out) < 0)
- i_fatal("pipe() failed: %m");
-
- switch (fork()) {
- case -1:
- i_fatal("fork() failed: %m");
- break;
- case 0:
- /* child, which will execute the proxy server. stdin/stdout
- goes to pipes which we'll pass to proxy client. */
- if (dup2(fd_in[0], STDIN_FILENO) < 0 ||
- dup2(fd_out[1], STDOUT_FILENO) < 0)
- i_fatal("dup2() failed: %m");
-
- (void)close(fd_in[0]);
- (void)close(fd_in[1]);
- (void)close(fd_out[0]);
- (void)close(fd_out[1]);
-
- execvp_const(args[0], args);
- break;
- default:
- /* parent */
- (void)close(fd_in[0]);
- (void)close(fd_out[1]);
- *fd_in_r = fd_out[0];
- *fd_out_r = fd_in[1];
- break;
- }
-}
-
-static void
-mirror_get_remote_cmd_line(char **argv, const char *const **cmd_args_r)
-{
- ARRAY_TYPE(const_string) cmd_args;
- unsigned int i;
- const char *p;
-
- t_array_init(&cmd_args, 16);
- for (i = 0; argv[i] != NULL; i++) {
- p = argv[i];
- array_append(&cmd_args, &p, 1);
- }
-
- p = "server"; array_append(&cmd_args, &p, 1);
- (void)array_append_space(&cmd_args);
- *cmd_args_r = array_idx(&cmd_args, 0);
-}
-
-static bool mirror_get_remote_cmd(char **argv, const char *const **cmd_args_r)
-{
- ARRAY_TYPE(const_string) cmd_args;
- const char *p, *user, *host;
-
- if (argv[1] != NULL) {
- /* more than one parameter, so it contains a full command
- (e.g. ssh host dsync) */
- mirror_get_remote_cmd_line(argv, cmd_args_r);
- return TRUE;
- }
-
- /* if it begins with /[a-z0-9]+:/, it's a mail location
- (e.g. mdbox:~/mail) */
- for (p = argv[0]; *p != '\0'; p++) {
- if (!i_isalnum(*p)) {
- if (*p == ':')
- return FALSE;
- break;
- }
- }
-
- if (strchr(argv[0], ' ') != NULL || strchr(argv[0], '/') != NULL) {
- /* a) the whole command is in one string. this is mainly for
- backwards compatibility.
- b) script/path */
- argv = p_strsplit(pool_datastack_create(), argv[0], " ");
- mirror_get_remote_cmd_line(argv, cmd_args_r);
- return TRUE;
- }
-
- /* [user@]host */
- host = strchr(argv[0], '@');
- if (host != NULL)
- user = t_strdup_until(argv[0], host++);
- else {
- user = "";
- host = argv[0];
- }
-
- /* we'll assume virtual users, so in user@host it really means not to
- give ssh a username, but to give dsync -u user parameter. */
- t_array_init(&cmd_args, 8);
- array_append(&cmd_args, &ssh_cmd, 1);
- array_append(&cmd_args, &host, 1);
- p = "dsync"; array_append(&cmd_args, &p, 1);
- if (*user != '\0') {
- p = "-u"; array_append(&cmd_args, &p, 1);
- array_append(&cmd_args, &user, 1);
- }
- p = "server"; array_append(&cmd_args, &p, 1);
- (void)array_append_space(&cmd_args);
- *cmd_args_r = array_idx(&cmd_args, 0);
- return TRUE;
-}
-
-static void ATTR_NORETURN
-usage(void)
-{
- fprintf(stderr,
-"usage: dsync [-C <alt char>] [-m <mailbox>] [-u <user>] [-frRv]\n"
-" mirror <local mail_location> | [<user>@]<host> | <remote dsync command>\n"
-);
- exit(1);
-}
-
-static void
-dsync_connected(struct master_service_connection *conn ATTR_UNUSED)
-{
- i_fatal("Running as service not supported currently");
-}
-
-int main(int argc, char *argv[])
-{
- enum mail_storage_service_flags ssflags =
- MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR |
- MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
- enum dsync_brain_flags brain_flags = 0;
- 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, *mail_user2 = NULL;
- struct dsync_worker *worker1, *worker2, *workertmp;
- const char *error, *username, *cmd_name, *mailbox = NULL;
- const char *local_location = NULL, *const *remote_cmd_args = NULL;
- const char *path1, *path2;
- bool dsync_server = FALSE, unexpected_changes = FALSE;
- bool dsync_debug = FALSE, reverse_workers = FALSE;
- char alt_char = '_';
- int c, ret, fd_in = STDIN_FILENO, fd_out = STDOUT_FILENO;
-
- master_service = master_service_init("dsync",
- MASTER_SERVICE_FLAG_STANDALONE,
- &argc, &argv, "+C:Dfm:Ru:v");
-
- username = getenv("USER");
- while ((c = master_getopt(master_service)) > 0) {
- if (c == '-')
- break;
- switch (c) {
- case 'C':
- alt_char = optarg[0];
- break;
- case 'D':
- dsync_debug = TRUE;
- brain_flags |= DSYNC_BRAIN_FLAG_VERBOSE;
- ssflags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
- break;
- case 'm':
- mailbox = optarg;
- break;
- case 'R':
- reverse_workers = TRUE;
- break;
- case 'f':
- brain_flags |= DSYNC_BRAIN_FLAG_FULL_SYNC;
- break;
- case 'u':
- username = optarg;
- ssflags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
- ssflags &= ~MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR;
- break;
- case 'v':
- brain_flags |= DSYNC_BRAIN_FLAG_VERBOSE;
- break;
- default:
- usage();
- }
- }
- if (optind == argc)
- usage();
- if (username == NULL)
- i_fatal("USER environment not set and -u parameter not given");
- cmd_name = argv[optind++];
-
- if (strcmp(cmd_name, "mirror") == 0 ||
- strcmp(cmd_name, "convert") == 0 ||
- strcmp(cmd_name, "backup") == 0) {
- if (optind == argc)
- usage();
-
- if (strcmp(cmd_name, "backup") == 0)
- brain_flags |= DSYNC_BRAIN_FLAG_BACKUP;
- if (!mirror_get_remote_cmd(argv+optind, &remote_cmd_args)) {
- if (optind+1 != argc)
- usage();
- local_location = argv[optind];
- }
- optind++;
- } else if (strcmp(cmd_name, "server") == 0) {
- dsync_server = TRUE;
- } else {
- usage();
- }
- master_service_init_finish(master_service);
- lib_signals_ignore(SIGHUP, TRUE);
-
- if (!dsync_debug) {
- /* disable debugging unless -D is given */
- i_set_debug_file("/dev/null");
- }
-
- memset(&input, 0, sizeof(input));
- input.module = "mail";
- input.service = "dsync";
- input.username = username;
-
- storage_service = mail_storage_service_init(master_service, NULL,
- ssflags);
- if (mail_storage_service_lookup(storage_service, &input,
- &service_user, &error) <= 0)
- i_fatal("User lookup failed: %s", error);
-
- if (remote_cmd_args != NULL) {
- /* _service_lookup() may exec doveconf, so do our forking
- after that. but do it before _service_next() in case it
- drops process privileges */
- run_cmd(remote_cmd_args, &fd_in, &fd_out);
- }
-
- if (mail_storage_service_next(storage_service, service_user,
- &mail_user) < 0)
- i_fatal("User init failed");
- mail_user->admin = TRUE;
-
- /* create the first local worker */
- worker1 = dsync_worker_init_local(mail_user, alt_char);
- if (local_location != NULL) {
- /* update mail_location and create another user for the
- second location. */
- struct setting_parser_context *set_parser;
- const char *set_line =
- t_strconcat("mail_location=", local_location, NULL);
-
- set_parser = mail_storage_service_user_get_settings_parser(service_user);
- if (settings_parse_line(set_parser, set_line) < 0)
- i_unreached();
- if (mail_storage_service_next(storage_service, service_user,
- &mail_user2) < 0)
- i_fatal("User init failed");
- mail_user2->admin = TRUE;
-
- if (mail_namespaces_get_root_sep(mail_user->namespaces) !=
- mail_namespaces_get_root_sep(mail_user2->namespaces)) {
- i_fatal("Mail locations must use the same "
- "virtual mailbox hierarchy separator "
- "(specify separator for the default namespace)");
- }
- path1 = mailbox_list_get_path(mail_user->namespaces->list, NULL,
- MAILBOX_LIST_PATH_TYPE_MAILBOX);
- path2 = mailbox_list_get_path(mail_user2->namespaces->list, NULL,
- MAILBOX_LIST_PATH_TYPE_MAILBOX);
- if (path1 != NULL && path2 != NULL &&
- strcmp(path1, path2) == 0) {
- i_fatal("Both source and destination mail_location "
- "points to same directory: %s", path1);
- }
-
- worker2 = dsync_worker_init_local(mail_user2, alt_char);
- if (reverse_workers) {
- workertmp = worker1;
- worker1 = worker2;
- worker2 = workertmp;
- }
-
- i_set_failure_prefix(t_strdup_printf("dsync(%s): ", username));
- brain = dsync_brain_init(worker1, worker2, mailbox,
- brain_flags | DSYNC_BRAIN_FLAG_LOCAL);
- server = NULL;
- dsync_brain_sync_all(brain);
- } else if (dsync_server) {
- i_set_failure_prefix(t_strdup_printf("dsync-remote(%s): ",
- username));
- server = dsync_proxy_server_init(fd_in, fd_out, worker1);
- worker2 = NULL;
-
- master_service_run(master_service, dsync_connected);
- } else {
- i_assert(remote_cmd_args != NULL);
- i_set_failure_prefix(t_strdup_printf("dsync-local(%s): ",
- username));
-
- worker2 = dsync_worker_init_proxy_client(fd_in, fd_out);
- if (reverse_workers) {
- workertmp = worker1;
- worker1 = worker2;
- worker2 = workertmp;
- }
-
- brain = dsync_brain_init(worker1, worker2,
- mailbox, brain_flags);
- server = NULL;
- dsync_brain_sync(brain);
-
- if (!dsync_brain_has_failed(brain))
- master_service_run(master_service, dsync_connected);
- }
-
- if (brain == NULL)
- ret = 0;
- else {
- if (dsync_brain_has_unexpected_changes(brain))
- unexpected_changes = TRUE;
- ret = dsync_brain_deinit(&brain);
- }
- if (server != NULL)
- dsync_proxy_server_deinit(&server);
-
- dsync_worker_deinit(&worker1);
- if (worker2 != NULL)
- dsync_worker_deinit(&worker2);
-
- mail_user_unref(&mail_user);
- if (mail_user2 != NULL)
- mail_user_unref(&mail_user2);
- mail_storage_service_user_free(&service_user);
-
- if (unexpected_changes) {
- i_warning("Mailbox changes caused a desync. "
- "You may want to run dsync again.");
- }
-
- mail_storage_service_deinit(&storage_service);
- master_service_deinit(&master_service);
- return ret < 0 ? 1 : (unexpected_changes ? 2 : 0);
-}