doveadm-dsync.c revision cd611ca82b8b8a61736d21a8a0de58cfd53bc9b6
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "lib.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "lib-signals.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "array.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "execv-const.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "str.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "var-expand.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "settings-parser.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "master-service.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-storage-service.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-user.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-namespace.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "doveadm-settings.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "doveadm-mail.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "dsync-brain.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "dsync-worker.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "dsync-proxy-server.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "doveadm-dsync.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <stdio.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <stdlib.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <unistd.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <ctype.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#define DSYNC_LOCK_FILENAME ".dovecot-sync.lock"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistruct dsync_cmd_context {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct doveadm_mail_cmd_context ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum dsync_brain_flags brain_flags;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *mailbox, *namespace_prefix;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *local_location;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int fd_in, fd_out, fd_err;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct io *io_err;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int lock_timeout;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int lock:1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int default_replica_location:1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int reverse_workers:1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int remote:1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi};
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic bool legacy_dsync = FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void remote_error_input(struct dsync_cmd_context *ctx)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi char buf[1024];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ssize_t ret;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ret = read(ctx->fd_err, buf, sizeof(buf)-1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret == -1) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi io_remove(&ctx->io_err);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret > 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi buf[ret-1] = '\0';
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("remote: %s", buf);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomirun_cmd(struct dsync_cmd_context *ctx, const char *const *args)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int fd_in[2], fd_out[2], fd_err[2];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (pipe(fd_in) < 0 || pipe(fd_out) < 0 || pipe(fd_err) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("pipe() failed: %m");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi switch (fork()) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi case -1:
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("fork() failed: %m");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi case 0:
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* child, which will execute the proxy server. stdin/stdout
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi goes to pipes which we'll pass to proxy client. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (dup2(fd_in[0], STDIN_FILENO) < 0 ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dup2(fd_out[1], STDOUT_FILENO) < 0 ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dup2(fd_err[1], STDERR_FILENO) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("dup2() failed: %m");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_in[0]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_in[1]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_out[0]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_out[1]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_err[0]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_err[1]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi execvp_const(args[0], args);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi default:
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* parent */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi break;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_in[0]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_out[1]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd_err[1]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->fd_in = fd_out[0];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->fd_out = fd_in[1];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->fd_err = fd_err[0];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->io_err = io_add(ctx->fd_err, IO_READ, remote_error_input, ctx);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimirror_get_remote_cmd_line(const char *const *argv,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *const **cmd_args_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ARRAY_TYPE(const_string) cmd_args;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int i;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_assert(argv[0] != NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi t_array_init(&cmd_args, 16);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (i = 0; argv[i] != NULL; i++) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p = argv[i];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_append(&cmd_args, &p, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (legacy_dsync) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we're executing dsync */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p = "server";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we're executing doveadm */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p = "dsync-server";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_append(&cmd_args, &p, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)array_append_space(&cmd_args);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *cmd_args_r = array_idx(&cmd_args, 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *const *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiget_ssh_cmd_args(struct dsync_cmd_context *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *host, const char *login, const char *mail_user)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi static struct var_expand_table static_tab[] = {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'u', NULL, "user" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { '\0', NULL, "login" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { '\0', NULL, "host" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { '\0', NULL, "lock_timeout" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { '\0', NULL, "namespace" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { '\0', NULL, NULL }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi };
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct var_expand_table *tab;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ARRAY_TYPE(const_string) cmd_args;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi string_t *str, *str2;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *value, *const *args;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab = t_malloc(sizeof(static_tab));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(tab, static_tab, sizeof(static_tab));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[0].value = mail_user;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[1].value = login;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[2].value = host;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[3].value = dec2str(ctx->lock_timeout);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[4].value = ctx->namespace_prefix;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi t_array_init(&cmd_args, 8);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str = t_str_new(128);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str2 = t_str_new(128);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi args = t_strsplit(doveadm_settings->dsync_remote_cmd, " ");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (; *args != NULL; args++) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strchr(*args, '%') == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi value = *args;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* some automation: if parameter's all %variables
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi expand to empty, but the %variable isn't the only
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi text in the parameter, skip it. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str_truncate(str, 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str_truncate(str2, 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi var_expand(str, *args, tab);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi var_expand(str2, *args, static_tab);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(str_c(str), str_c(str2)) == 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str_len(str) > 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi continue;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi value = t_strdup(str_c(str));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek array_append(&cmd_args, &value, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)array_append_space(&cmd_args);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return array_idx(&cmd_args, 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic bool mirror_get_remote_cmd(struct dsync_cmd_context *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *user,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *const **cmd_args_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *p, *host, *const *argv = ctx->ctx.args;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (argv[1] != NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* more than one parameter, so it contains a full command
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (e.g. ssh host dsync) */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mirror_get_remote_cmd_line(argv, cmd_args_r);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* if it begins with /[a-z0-9]+:/, it's a mail location
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (e.g. mdbox:~/mail) */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (p = argv[0]; *p != '\0'; p++) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!i_isalnum(*p)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*p == ':')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi break;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strchr(argv[0], ' ') != NULL || strchr(argv[0], '/') != NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* a) the whole command is in one string. this is mainly for
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi backwards compatibility.
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi b) script/path */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mirror_get_remote_cmd_line(t_strsplit(argv[0], " "),
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi cmd_args_r);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* [user@]host */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi host = strchr(argv[0], '@');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (host != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi user = t_strdup_until(argv[0], host++);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi host = argv[0];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we'll assume virtual users, so in user@host it really means not to
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi give ssh a username, but to give dsync -u user parameter. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *cmd_args_r = get_ssh_cmd_args(ctx, host, "", user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic struct dsync_worker *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomicmd_dsync_run_local(struct dsync_cmd_context *ctx, struct mail_user *user)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_user *user2;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct dsync_worker *worker2;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct setting_parser_context *set_parser;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *set_line, *path1, *path2;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_assert(ctx->local_location != NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->brain_flags |= DSYNC_BRAIN_FLAG_LOCAL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_set_failure_prefix(t_strdup_printf("dsync(%s): ", user->username));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* update mail_location and create another user for the
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi second location. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_parser = mail_storage_service_user_get_settings_parser(ctx->ctx.cur_service_user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_line = t_strconcat("mail_location=", ctx->local_location, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (settings_parse_line(set_parser, set_line) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_unreached();
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mail_storage_service_next(ctx->ctx.storage_service,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->ctx.cur_service_user, &user2) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("User init failed");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi user2->admin = TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mail_namespaces_get_root_sep(user->namespaces) !=
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_namespaces_get_root_sep(user2->namespaces)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Mail locations must use the same "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "virtual mailbox hierarchy separator "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "(specify separator for the default namespace)");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path1 = mailbox_list_get_path(user->namespaces->list, NULL,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi MAILBOX_LIST_PATH_TYPE_MAILBOX);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path2 = mailbox_list_get_path(user2->namespaces->list, NULL,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi MAILBOX_LIST_PATH_TYPE_MAILBOX);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (path1 != NULL && path2 != NULL &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi strcmp(path1, path2) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Both source and destination mail_location "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "points to same directory: %s", path1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi worker2 = dsync_worker_init_local(user2, ctx->namespace_prefix,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *ctx->ctx.set->dsync_alt_char);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_user_unref(&user2);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return worker2;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic struct dsync_worker *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomicmd_dsync_run_remote(struct dsync_cmd_context *ctx, struct mail_user *user)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_set_failure_prefix(t_strdup_printf("dsync-local(%s): ",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi user->username));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return dsync_worker_init_proxy_client(ctx->fd_in, ctx->fd_out);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *const *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiparse_ssh_location(struct dsync_cmd_context *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *location, const char *username)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *host, *login;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi host = strchr(location, '@');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (host != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi login = t_strdup_until(location, host++);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi host = location;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi login = "";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return get_ssh_cmd_args(ctx, host, login, username);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int dsync_lock(struct mail_user *user, unsigned int lock_timeout,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **path_r, struct file_lock **lock_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *home, *path;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int ret, fd;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if ((ret = mail_user_get_home(user, &home)) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("Couldn't look up user's home dir");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("User has no home directory");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi path = t_strconcat(home, "/"DSYNC_LOCK_FILENAME, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi fd = creat(path, 0600);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (fd == -1) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("Couldn't create lock %s: %m", path);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (file_wait_lock(fd, path, F_WRLCK, FILE_LOCK_METHOD_FCNTL,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi lock_timeout, lock_r) <= 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("Couldn't lock %s: %m", path);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)close(fd);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *path_r = path;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return fd;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomicmd_dsync_start(struct dsync_cmd_context *ctx, struct dsync_worker *worker1,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct dsync_worker *worker2)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct dsync_brain *brain;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create and run the brain */
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek brain = dsync_brain_init(worker1, worker2, ctx->mailbox,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->brain_flags);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!ctx->remote)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dsync_brain_sync_all(brain);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dsync_brain_sync(brain);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!dsync_brain_has_failed(brain))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi io_loop_run(current_ioloop);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* deinit */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (dsync_brain_has_unexpected_changes(brain)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_warning("Mailbox changes caused a desync. "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "You may want to run dsync again.");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->ctx.exit_code = 2;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (dsync_brain_deinit(&brain) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->ctx.exit_code = EX_TEMPFAIL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomicmd_dsync_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
struct dsync_worker *worker1, *worker2, *workertmp;
const char *lock_path;
struct file_lock *lock;
int lock_fd, ret = 0;
user->admin = TRUE;
/* create workers */
worker1 = dsync_worker_init_local(user, ctx->namespace_prefix,
*_ctx->set->dsync_alt_char);
if (!ctx->remote)
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;
}
if (!ctx->lock)
ret = cmd_dsync_start(ctx, worker1, worker2);
else {
lock_fd = dsync_lock(user, ctx->lock_timeout, &lock_path, &lock);
if (lock_fd == -1) {
_ctx->exit_code = EX_TEMPFAIL;
ret = -1;
} else {
ret = cmd_dsync_start(ctx, worker1, worker2);
file_lock_free(&lock);
if (close(lock_fd) < 0)
i_error("close(%s) failed: %m", lock_path);
}
}
dsync_worker_deinit(&worker1);
dsync_worker_deinit(&worker2);
if (ctx->io_err != NULL)
io_remove(&ctx->io_err);
if (ctx->fd_err != -1) {
(void)close(ctx->fd_err);
ctx->fd_err = -1;
}
return ret;
}
static int cmd_dsync_prerun(struct doveadm_mail_cmd_context *_ctx,
struct mail_storage_service_user *service_user,
const char **error_r)
{
struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
const char *const *remote_cmd_args = NULL;
const struct mail_user_settings *user_set;
const char *username = "";
user_set = mail_storage_service_user_get_set(service_user)[0];
ctx->fd_in = STDIN_FILENO;
ctx->fd_out = STDOUT_FILENO;
ctx->fd_err = -1;
ctx->remote = FALSE;
if (ctx->default_replica_location) {
ctx->local_location =
mail_user_set_plugin_getenv(user_set, "mail_replica");
if (ctx->local_location == NULL ||
*ctx->local_location == '\0') {
*error_r = "User has no mail_replica in userdb";
_ctx->exit_code = DOVEADM_EX_NOTFOUND;
return -1;
}
} else {
/* if we're executing remotely, give -u parameter if we also
did a userdb lookup. */
if ((_ctx->service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0)
username = _ctx->cur_username;
if (!mirror_get_remote_cmd(ctx, username, &remote_cmd_args)) {
/* it's a mail_location */
if (_ctx->args[1] != NULL)
doveadm_mail_help_name(_ctx->cmd->name);
ctx->local_location = _ctx->args[0];
}
}
if (remote_cmd_args == NULL && ctx->local_location != NULL &&
strncmp(ctx->local_location, "remote:", 7) == 0) {
/* this is a remote (ssh) command */
remote_cmd_args = parse_ssh_location(ctx, ctx->local_location+7,
_ctx->cur_username);
}
if (remote_cmd_args != NULL) {
/* do this before mail_storage_service_next() in case it
drops process privileges */
run_cmd(ctx, remote_cmd_args);
ctx->remote = TRUE;
}
return 0;
}
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;
if (ctx->default_replica_location) {
if (args[0] != NULL)
i_error("Don't give mail location with -d parameter");
} else {
if (args[0] == NULL)
doveadm_mail_help_name(_ctx->cmd->name);
}
lib_signals_ignore(SIGHUP, TRUE);
if (doveadm_debug || doveadm_verbose)
ctx->brain_flags |= DSYNC_BRAIN_FLAG_VERBOSE;
}
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 'd':
ctx->default_replica_location = TRUE;
break;
case 'E':
/* dsync wrapper detection flag */
legacy_dsync = TRUE;
break;
case 'f':
ctx->brain_flags |= DSYNC_BRAIN_FLAG_FULL_SYNC;
break;
case 'l':
ctx->lock = TRUE;
if (str_to_uint(optarg, &ctx->lock_timeout) < 0)
i_error("Invalid -l parameter: %s", optarg);
break;
case 'm':
ctx->mailbox = optarg;
break;
case 'n':
ctx->namespace_prefix = 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 = "+dEfl:m:n:R";
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.prerun = cmd_dsync_prerun;
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 int
cmd_dsync_server_run(struct doveadm_mail_cmd_context *_ctx,
struct mail_user *user)
{
struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
struct dsync_proxy_server *server;
struct dsync_worker *worker;
struct file_lock *lock;
const char *lock_path;
int lock_fd, ret = 0;
user->admin = TRUE;
i_set_failure_prefix(t_strdup_printf("dsync-remote(%s): ",
user->username));
worker = dsync_worker_init_local(user, ctx->namespace_prefix,
*_ctx->set->dsync_alt_char);
server = dsync_proxy_server_init(STDIN_FILENO, STDOUT_FILENO, worker);
if (!ctx->lock)
io_loop_run(current_ioloop);
else {
lock_fd = dsync_lock(user, ctx->lock_timeout, &lock_path, &lock);
if (lock_fd == -1) {
_ctx->exit_code = EX_TEMPFAIL;
ret = -1;
} else {
io_loop_run(current_ioloop);
file_lock_free(&lock);
if (close(lock_fd) < 0)
i_error("close(%s) failed: %m", lock_path);
}
}
dsync_proxy_server_deinit(&server);
dsync_worker_deinit(&worker);
return ret;
}
static bool
cmd_mailbox_dsync_server_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c)
{
struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
switch (c) {
case 'E':
/* dsync wrapper detection flag */
legacy_dsync = TRUE;
break;
case 'l':
ctx->lock = TRUE;
if (str_to_uint(optarg, &ctx->lock_timeout) < 0)
i_error("Invalid -l parameter: %s", optarg);
break;
case 'n':
ctx->namespace_prefix = optarg;
break;
default:
return FALSE;
}
return TRUE;
}
static struct doveadm_mail_cmd_context *cmd_dsync_server_alloc(void)
{
struct dsync_cmd_context *ctx;
ctx = doveadm_mail_cmd_alloc(struct dsync_cmd_context);
ctx->ctx.getopt_args = "El:n:";
ctx->ctx.v.parse_arg = cmd_mailbox_dsync_server_parse_arg;
ctx->ctx.v.run = cmd_dsync_server_run;
return &ctx->ctx;
}
struct doveadm_mail_cmd cmd_dsync_mirror = {
cmd_dsync_alloc, "sync",
"[-dfR] [-l <secs>] [-m <mailbox>] [-n <namespace>] <dest>"
};
struct doveadm_mail_cmd cmd_dsync_backup = {
cmd_dsync_backup_alloc, "backup",
"[-dfR] [-l <secs>] [-m <mailbox>] [-n <namespace>] <dest>"
};
struct doveadm_mail_cmd cmd_dsync_server = {
cmd_dsync_server_alloc, "dsync-server", &doveadm_mail_cmd_hide
};
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, *username = NULL;
char *p, *dup, new_flags[6];
int max_argc, src, dest, i, j;
bool flag_f = FALSE, flag_R = FALSE, flag_m, flag_u, 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 + 7;
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; flag_u = 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;
case 'u':
flag_u = 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_u) {
if (src+1 == argc)
i_fatal("-u missing parameter");
username = 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);
}
/* mirror|backup|server */
if (src == argc)
i_fatal("Missing mirror or backup parameter");
if (strcmp(argv[src], "sync") == 0 ||
strcmp(argv[src], "dsync-server") == 0) {
/* we're re-executing dsync due to doveconf.
"backup" re-exec detection is later. */
return;
}
if (strcmp(argv[src], "mirror") == 0)
new_argv[dest] = "sync";
else if (strcmp(argv[src], "backup") == 0)
new_argv[dest] = "backup";
else if (strcmp(argv[src], "server") == 0)
new_argv[dest] = "dsync-server";
else
i_fatal("Invalid parameter: %s", argv[src]);
src++; dest++;
if (src < argc && strncmp(argv[src], "-E", 2) == 0) {
/* we're re-executing dsync due to doveconf */
return;
}
/* dsync flags */
new_flags[0] = '-';
new_flags[1] = 'E'; i = 2;
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;
}
if (username != NULL) {
new_argv[dest++] = "-u";
new_argv[dest++] = username;
}
/* rest of the parameters */
for (; src < argc; src++)
new_argv[dest++] = argv[src];
i_assert(dest < max_argc);
new_argv[dest] = NULL;
legacy_dsync = TRUE;
*_argc = dest;
*_argv = new_argv;
optind = 1;
}