doveadm-dsync.c revision d229d26d263a57a77eec8fe7cba24fbfd9509966
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2009-2013 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib-signals.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "execv-const.h"
e8bdf1be00aec45d0c6dd72ad9c8be02a3dfc778Timo Sirainen#include "fd-set-nonblock.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream.h"
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen#include "ostream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "iostream-ssl.h"
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen#include "iostream-rawlog.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "write-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "strescape.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "var-expand.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "settings-parser.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "master-service.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "master-service-ssl-settings.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "mail-storage-service.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "mail-user.h"
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen#include "mail-namespace.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "mailbox-list-private.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "doveadm-settings.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "doveadm-mail.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "doveadm-print.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "doveadm-server.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "client-connection.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "server-connection.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "dsync-brain.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "dsync-ibc.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "doveadm-dsync.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#include <stdio.h>
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#include <stdlib.h>
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#include <unistd.h>
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#include <ctype.h>
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#include <sys/wait.h>
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen#define DSYNC_COMMON_GETOPT_ARGS "+dEfg:l:m:n:Nr:Rs:Ux:"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define DSYNC_REMOTE_CMD_EXIT_WAIT_SECS 30
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* The broken_char is mainly set to get a proper error message when trying to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen convert a mailbox with a name that can't be used properly translated between
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen vname/storage_name and would otherwise be mixed up with a normal "mailbox
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen doesn't exist" error message. This could be any control character, since
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen none of them are allowed to be created in regular mailbox names. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define DSYNC_LIST_BROKEN_CHAR '\003'
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenenum dsync_run_type {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DSYNC_RUN_TYPE_LOCAL,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DSYNC_RUN_TYPE_STREAM,
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen DSYNC_RUN_TYPE_CMD
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen};
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainenstruct dsync_cmd_context {
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen struct doveadm_mail_cmd_context ctx;
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen enum dsync_brain_sync_type sync_type;
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen const char *mailbox, *namespace_prefix;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen guid_128_t mailbox_guid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *state_input, *rawlog_path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY_TYPE(const_string) exclude_mailboxes;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen const char *remote_name;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen const char *local_location;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen pid_t remote_pid;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen int fd_in, fd_out, fd_err;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen struct io *io_err;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen struct istream *input, *err_stream;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen struct ostream *output;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen struct ssl_iostream_context *ssl_ctx;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen struct ssl_iostream *ssl_iostream;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen enum dsync_run_type run_type;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen struct server_connection *tcp_conn;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen const char *error;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen unsigned int lock_timeout;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int lock:1;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen unsigned int sync_visible_namespaces:1;
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen unsigned int default_replica_location:1;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen unsigned int backup:1;
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen unsigned int reverse_backup:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int remote_user_prefix:1;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen unsigned int no_mail_sync:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int local_location_from_arg:1;
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen unsigned int replicator_notify:1;
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen};
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainenstatic bool legacy_dsync = FALSE;
fc40a9a002458e372ff4b9f6f4e15239520c0bcdTimo Sirainen
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainenstatic void remote_error_input(struct dsync_cmd_context *ctx)
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen{
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen const unsigned char *data;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen size_t size;
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen const char *line;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen switch (i_stream_read(ctx->err_stream)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case -2:
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen data = i_stream_get_data(ctx->err_stream, &size);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen fprintf(stderr, "%.*s", (int)size, data);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_skip(ctx->err_stream, size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case -1:
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen if (ctx->io_err != NULL)
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen io_remove(&ctx->io_err);
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen break;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen default:
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen while ((line = i_stream_next_line(ctx->err_stream)) != NULL)
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen fprintf(stderr, "%s\n", line);
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen break;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen }
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen}
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainenstatic void
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainenrun_cmd(struct dsync_cmd_context *ctx, const char *const *args)
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen{
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen int fd_in[2], fd_out[2], fd_err[2];
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen if (pipe(fd_in) < 0 || pipe(fd_out) < 0 || pipe(fd_err) < 0)
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen i_fatal("pipe() failed: %m");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->remote_pid = fork();
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen switch (ctx->remote_pid) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case -1:
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_fatal("fork() failed: %m");
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen case 0:
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen /* child, which will execute the proxy server. stdin/stdout
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen goes to pipes which we'll pass to proxy client. */
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen if (dup2(fd_in[0], STDIN_FILENO) < 0 ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dup2(fd_out[1], STDOUT_FILENO) < 0 ||
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen dup2(fd_err[1], STDERR_FILENO) < 0)
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen i_fatal("dup2() failed: %m");
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen i_close_fd(&fd_in[0]);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_close_fd(&fd_in[1]);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_close_fd(&fd_out[0]);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_close_fd(&fd_out[1]);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_close_fd(&fd_err[0]);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_close_fd(&fd_err[1]);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen execvp_const(args[0], args);
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainen default:
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen /* parent */
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen break;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen }
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainen i_close_fd(&fd_in[0]);
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainen i_close_fd(&fd_out[1]);
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen i_close_fd(&fd_err[1]);
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen ctx->fd_in = fd_out[0];
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen ctx->fd_out = fd_in[1];
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen ctx->fd_err = fd_err[0];
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen if (ctx->remote_user_prefix) {
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen const char *prefix =
1eb17e61d3d38372674aa0c55caedb0185a985f5Timo Sirainen t_strdup_printf("%s\n", ctx->ctx.cur_username);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (write_full(ctx->fd_out, prefix, strlen(prefix)) < 0)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_fatal("write(remote out) failed: %m");
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen fd_set_nonblock(ctx->fd_err, TRUE);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ctx->err_stream = i_stream_create_fd(ctx->fd_err, IO_BLOCK_SIZE, FALSE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_set_return_partial_line(ctx->err_stream, TRUE);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen}
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainenstatic void
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenmirror_get_remote_cmd_line(const char *const *argv,
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen const char *const **cmd_args_r)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen{
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen ARRAY_TYPE(const_string) cmd_args;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen unsigned int i;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen const char *p;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_assert(argv[0] != NULL);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen t_array_init(&cmd_args, 16);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen for (i = 0; argv[i] != NULL; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen p = argv[i];
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen array_append(&cmd_args, &p, 1);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (legacy_dsync) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* we're executing dsync */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen p = "server";
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen } else {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* we're executing doveadm */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen p = "dsync-server";
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen array_append(&cmd_args, &p, 1);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen array_append_zero(&cmd_args);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen *cmd_args_r = array_idx(&cmd_args, 0);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen}
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainenstatic const char *const *
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainenget_ssh_cmd_args(const char *host, const char *login, const char *mail_user)
51078c3413b7ed4811bc725acbb1289723361ba9Timo Sirainen{
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen static struct var_expand_table static_tab[] = {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen { 'u', NULL, "user" },
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen { '\0', NULL, "login" },
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen { '\0', NULL, "host" },
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen { '\0', NULL, NULL }
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen };
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen struct var_expand_table *tab;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen ARRAY_TYPE(const_string) cmd_args;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen string_t *str, *str2;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen const char *value, *const *args;
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen tab = t_malloc(sizeof(static_tab));
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen memcpy(tab, static_tab, sizeof(static_tab));
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen tab[0].value = mail_user;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen tab[1].value = login;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen tab[2].value = host;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen t_array_init(&cmd_args, 8);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen str = t_str_new(128);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen str2 = t_str_new(128);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen args = t_strsplit(doveadm_settings->dsync_remote_cmd, " ");
d22301419109ed4a38351715e6760011421dadecTimo Sirainen for (; *args != NULL; args++) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (strchr(*args, '%') == NULL)
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen value = *args;
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen else {
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen /* some automation: if parameter's all %variables
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen expand to empty, but the %variable isn't the only
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen text in the parameter, skip it. */
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen str_truncate(str, 0);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen str_truncate(str2, 0);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen var_expand(str, *args, tab);
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen var_expand(str2, *args, static_tab);
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (strcmp(str_c(str), str_c(str2)) == 0 &&
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen str_len(str) > 0)
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen continue;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen value = t_strdup(str_c(str));
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen array_append(&cmd_args, &value, 1);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen }
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen array_append_zero(&cmd_args);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen return array_idx(&cmd_args, 0);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen}
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenstatic bool mirror_get_remote_cmd(struct dsync_cmd_context *ctx,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen const char *user,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen const char *const **cmd_args_r)
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen{
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen const char *p, *host, *const *argv = ctx->ctx.args;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (argv[1] != NULL) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* more than one parameter, so it contains a full command
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen (e.g. ssh host dsync) */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen mirror_get_remote_cmd_line(argv, cmd_args_r);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen return TRUE;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen }
8b58939517a381db55670089c0984da39fc0f099Timo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* if it begins with /[a-z0-9]+:/, it's a mail location
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen (e.g. mdbox:~/mail) */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen for (p = argv[0]; *p != '\0'; p++) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (!i_isalnum(*p)) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (*p == ':')
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen return FALSE;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen break;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen }
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen }
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (strchr(argv[0], ' ') != NULL || strchr(argv[0], '/') != NULL) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* a) the whole command is in one string. this is mainly for
d22301419109ed4a38351715e6760011421dadecTimo Sirainen backwards compatibility.
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen b) script/path */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen mirror_get_remote_cmd_line(t_strsplit(argv[0], " "),
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen cmd_args_r);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen return TRUE;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen }
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* [user@]host */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen host = strchr(argv[0], '@');
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (host != NULL)
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen user = t_strdup_until(argv[0], host++);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen else
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen host = argv[0];
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* we'll assume virtual users, so in user@host it really means not to
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen give ssh a username, but to give dsync -u user parameter. */
236bedf76e31651ea9fca63fbdc25be673819526Timo Sirainen *cmd_args_r = get_ssh_cmd_args(host, "", user);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return TRUE;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen}
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenstatic void doveadm_user_init_dsync(struct mail_user *user)
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen{
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen struct mail_namespace *ns;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen user->dsyncing = TRUE;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen for (ns = user->namespaces; ns != NULL; ns = ns->next)
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen ns->list->set.broken_char = DSYNC_LIST_BROKEN_CHAR;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen}
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenstatic bool paths_are_equal(struct mail_user *user1, struct mail_user *user2,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen enum mailbox_list_path_type type)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen const char *path1, *path2;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_assert(user1->namespaces != NULL);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen i_assert(user2->namespaces != NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainen return mailbox_list_get_root_path(user1->namespaces->list, type, &path1) &&
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen mailbox_list_get_root_path(user2->namespaces->list, type, &path2) &&
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen strcmp(path1, path2) == 0;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen}
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainenstatic int
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainencmd_dsync_run_local(struct dsync_cmd_context *ctx, struct mail_user *user,
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen struct dsync_brain *brain, struct dsync_ibc *ibc2,
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen bool *changes_during_sync_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dsync_brain *brain2;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen struct mail_user *user2;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen struct setting_parser_context *set_parser;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen const char *set_line, *location;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen bool brain1_running, brain2_running, changed1, changed2;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen int ret;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (ctx->local_location_from_arg)
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainen location = ctx->ctx.args[0];
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen else {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_assert(ctx->local_location != NULL);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen location = ctx->local_location;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen }
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen i_set_failure_prefix("dsync(%s): ", user->username);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* update mail_location and create another user for the
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen second location. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set_parser = mail_storage_service_user_get_settings_parser(ctx->ctx.cur_service_user);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen set_line = t_strconcat("mail_location=", location, NULL);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (settings_parse_line(set_parser, set_line) < 0)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_unreached();
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen ret = mail_storage_service_next(ctx->ctx.storage_service,
d22301419109ed4a38351715e6760011421dadecTimo Sirainen ctx->ctx.cur_service_user, &user2);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (ret < 0) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen ctx->ctx.exit_code = ret == -1 ? EX_TEMPFAIL : EX_CONFIG;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen return -1;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen doveadm_user_init_dsync(user2);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (mail_namespaces_get_root_sep(user->namespaces) !=
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen mail_namespaces_get_root_sep(user2->namespaces)) {
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen i_error("Mail locations must use the same "
55b6e3105184ad6a2f987346380966f556300055Timo Sirainen "virtual mailbox hierarchy separator "
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen "(specify separator for the default namespace)");
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen ctx->ctx.exit_code = EX_CONFIG;
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen mail_user_unref(&user2);
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen return -1;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen }
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen if (paths_are_equal(user, user2, MAILBOX_LIST_PATH_TYPE_MAILBOX) &&
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen paths_are_equal(user, user2, MAILBOX_LIST_PATH_TYPE_INDEX)) {
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen i_error("Both source and destination mail_location "
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen "points to same directory: %s",
2ff23d6fb7e2ff85aa23b7f4769aeac1d0316a1bTimo Sirainen mailbox_list_get_root_forced(user->namespaces->list,
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX));
d22301419109ed4a38351715e6760011421dadecTimo Sirainen ctx->ctx.exit_code = EX_CONFIG;
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen mail_user_unref(&user2);
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen return -1;
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen }
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen
2ff23d6fb7e2ff85aa23b7f4769aeac1d0316a1bTimo Sirainen brain2 = dsync_brain_slave_init(user2, ibc2);
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen brain1_running = brain2_running = TRUE;
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen changed1 = changed2 = TRUE;
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen while (brain1_running || brain2_running) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen if (dsync_brain_has_failed(brain) ||
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen dsync_brain_has_failed(brain2))
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen break;
18065635d4e79dd96eb3b3215718abd12f6a6808Timo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_assert(changed1 || changed2);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen brain1_running = dsync_brain_run(brain, &changed1);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen brain2_running = dsync_brain_run(brain2, &changed2);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen }
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen mail_user_unref(&user2);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen *changes_during_sync_r = dsync_brain_has_unexpected_changes(brain2);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (dsync_brain_deinit(&brain2) < 0) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen ctx->ctx.exit_code = EX_TEMPFAIL;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen return -1;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen return 0;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen}
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainenstatic void cmd_dsync_wait_remote(struct dsync_cmd_context *ctx,
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen int *status_r)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen{
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* wait for the remote command to finish to see any final errors.
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen don't wait very long though. */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen alarm(DSYNC_REMOTE_CMD_EXIT_WAIT_SECS);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (waitpid(ctx->remote_pid, status_r, 0) == -1) {
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (errno != EINTR) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_error("waitpid(%ld) failed: %m",
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen (long)ctx->remote_pid);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen } else {
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen i_error("Remote command process isn't dying, killing it");
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (kill(ctx->remote_pid, SIGKILL) < 0 && errno != ESRCH) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_error("kill(%ld, SIGKILL) failed: %m",
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen (long)ctx->remote_pid);
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen *status_r = -1;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen}
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainenstatic void cmd_dsync_log_remote_status(int status, bool remote_errors_logged)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen{
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (status == -1)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen ;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen else if (WIFSIGNALED(status))
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen i_error("Remote command died with signal %d", WTERMSIG(status));
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen else if (!WIFEXITED(status))
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_error("Remote command failed with status %d", status);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen else if (WEXITSTATUS(status) == EX_TEMPFAIL && remote_errors_logged) {
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen /* remote most likely already logged the error.
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen don't bother logging another line about it */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen } else if (WEXITSTATUS(status) != 0) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_error("Remote command returned error %d", WEXITSTATUS(status));
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen}
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenstatic void cmd_dsync_run_remote(struct mail_user *user)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen{
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_set_failure_prefix("dsync-local(%s): ", user->username);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen io_loop_run(current_ioloop);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen}
9fdd27b307347a06871ddab74b165122c878553cTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenstatic const char *const *
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenparse_ssh_location(const char *location, const char *username)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen{
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen const char *host, *login;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen host = strchr(location, '@');
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (host != NULL)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen login = t_strdup_until(location, host++);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen else {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen host = location;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen login = "";
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen }
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainen return get_ssh_cmd_args(host, login, username);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen}
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenstatic struct dsync_ibc *
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainencmd_dsync_icb_stream_init(struct dsync_cmd_context *ctx,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen const char *name, const char *temp_prefix)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen{
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (ctx->input == NULL) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen fd_set_nonblock(ctx->fd_in, TRUE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen fd_set_nonblock(ctx->fd_out, TRUE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen ctx->input = i_stream_create_fd(ctx->fd_in, (size_t)-1, FALSE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen ctx->output = o_stream_create_fd(ctx->fd_out, (size_t)-1, FALSE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen } else {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_stream_ref(ctx->input);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen o_stream_ref(ctx->output);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen }
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (ctx->rawlog_path != NULL) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen iostream_rawlog_create_path(ctx->rawlog_path,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen &ctx->input, &ctx->output);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen }
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen return dsync_ibc_init_stream(ctx->input, ctx->output,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen name, temp_prefix);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen}
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic void
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainendsync_replicator_notify(struct dsync_cmd_context *ctx,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen enum dsync_brain_sync_type sync_type,
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen const char *state_str)
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen#define REPLICATOR_HANDSHAKE "VERSION\treplicator-doveadm-client\t1\t0\n"
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen const char *path;
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen string_t *str;
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen int fd;
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen path = t_strdup_printf("%s/replicator-doveadm",
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen ctx->ctx.cur_mail_user->set->base_dir);
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen fd = net_connect_unix(path);
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen if (fd == -1) {
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen if (errno == ECONNREFUSED || errno == ENOENT) {
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen /* replicator not running on this server. ignore. */
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen return;
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen }
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen i_error("net_connect_unix(%s) failed: %m", path);
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen return;
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen }
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen str = t_str_new(128);
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen str_append(str, REPLICATOR_HANDSHAKE"NOTIFY\t");
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen str_append_tabescaped(str, ctx->ctx.cur_mail_user->username);
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen str_append_c(str, '\t');
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen if (sync_type == DSYNC_BRAIN_SYNC_TYPE_FULL)
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen str_append_c(str, 'f');
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen str_append_c(str, '\t');
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen str_append_tabescaped(str, state_str);
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen str_append_c(str, '\n');
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen if (write_full(fd, str_data(str), str_len(str)) < 0)
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen i_error("write(%s) failed: %m", path);
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen /* we only wanted to notify replicator. we don't care enough about the
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen answer to wait for it. */
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (close(fd) < 0)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_error("close(%s) failed: %m", path);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen}
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainenstatic int
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainencmd_dsync_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen{
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen struct dsync_cmd_context *ctx = (struct dsync_cmd_context *)_ctx;
8754bb7a1f24705ffa5434f9e10d57e0b3b88d6eTimo Sirainen struct dsync_ibc *ibc, *ibc2 = NULL;
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen struct dsync_brain *brain;
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen struct dsync_brain_settings set;
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen enum dsync_brain_flags brain_flags;
129cc29ae6d8ad64d6d2b72f18da18fa134d0f3eTimo Sirainen bool remote_errors_logged = FALSE;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen bool changes_during_sync = FALSE;
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen int status = 0, ret = 0;
e76c494ad6535d3de314cc0d3ac7a44b06e53c4bTimo Sirainen
e76c494ad6535d3de314cc0d3ac7a44b06e53c4bTimo Sirainen memset(&set, 0, sizeof(set));
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen set.sync_box = ctx->mailbox;
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen memcpy(set.sync_box_guid, ctx->mailbox_guid, sizeof(set.sync_box_guid));
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen set.lock_timeout_secs = ctx->lock_timeout;
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen set.state = ctx->state_input;
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen if (array_count(&ctx->exclude_mailboxes) > 0) {
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen /* array is NULL-terminated in init() */
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen set.exclude_mailboxes = array_idx(&ctx->exclude_mailboxes, 0);
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen }
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen doveadm_user_init_dsync(user);
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen
0d73d91d1a1809f173d433023ccf97e6ec5ba629Timo Sirainen if (ctx->namespace_prefix != NULL) {
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen set.sync_ns = mail_namespace_find(user->namespaces,
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen ctx->namespace_prefix);
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen }
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen if (ctx->run_type == DSYNC_RUN_TYPE_LOCAL)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen dsync_ibc_init_pipe(&ibc, &ibc2);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen else {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen string_t *temp_prefix = t_str_new(64);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mail_user_set_get_temp_prefix(temp_prefix, user->set);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ibc = cmd_dsync_icb_stream_init(ctx, ctx->remote_name,
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen str_c(temp_prefix));
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (ctx->fd_err != -1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->io_err = io_add(ctx->fd_err, IO_READ,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen remote_error_input, ctx);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen brain_flags = DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen if (ctx->sync_visible_namespaces)
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen brain_flags |= DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (ctx->reverse_backup)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else if (ctx->backup)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->no_mail_sync)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen brain_flags |= DSYNC_BRAIN_FLAG_NO_MAIL_SYNC;
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen if (doveadm_debug)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen brain_flags |= DSYNC_BRAIN_FLAG_DEBUG;
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen brain = dsync_brain_master_init(user, ibc, ctx->sync_type,
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen brain_flags, &set);
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen if (ctx->run_type == DSYNC_RUN_TYPE_LOCAL) {
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen if (cmd_dsync_run_local(ctx, user, brain, ibc2,
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen &changes_during_sync) < 0)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen ret = -1;
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen } else {
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen cmd_dsync_run_remote(user);
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen }
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen if (ctx->state_input != NULL) {
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen string_t *state_str = t_str_new(128);
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen dsync_brain_get_state(brain, state_str);
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen doveadm_print(str_c(state_str));
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (dsync_brain_has_unexpected_changes(brain) || changes_during_sync) {
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen i_warning("Mailbox changes caused a desync. "
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen "You may want to run dsync again.");
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen ctx->ctx.exit_code = 2;
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen }
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (dsync_brain_deinit(&brain) < 0) {
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen ctx->ctx.exit_code = EX_TEMPFAIL;
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen ret = -1;
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen }
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen dsync_ibc_deinit(&ibc);
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (ibc2 != NULL)
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen dsync_ibc_deinit(&ibc2);
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (ctx->ssl_iostream != NULL)
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen ssl_iostream_destroy(&ctx->ssl_iostream);
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen if (ctx->ssl_ctx != NULL)
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen ssl_iostream_context_deinit(&ctx->ssl_ctx);
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen if (ctx->fd_in != -1) {
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen if (ctx->fd_out != ctx->fd_in)
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen i_close_fd(&ctx->fd_out);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen i_close_fd(&ctx->fd_in);
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen }
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (ctx->run_type == DSYNC_RUN_TYPE_CMD)
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen cmd_dsync_wait_remote(ctx, &status);
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen /* print any final errors after the process has died. not closing
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen stdin/stdout before wait() may cause the process to hang, but stderr
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen shouldn't (at least with ssh) and we need stderr to be open to be
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen able to print the final errors */
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (ctx->err_stream != NULL) {
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen remote_error_input(ctx);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen remote_errors_logged = ctx->err_stream->v_offset > 0;
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen i_stream_destroy(&ctx->err_stream);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (ctx->run_type == DSYNC_RUN_TYPE_CMD)
a12399903f415a7e14c2816cffa2f7a09dcbb097Timo Sirainen cmd_dsync_log_remote_status(status, remote_errors_logged);
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen if (ctx->io_err != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen io_remove(&ctx->io_err);
439980f88f421039dea8335e92d3fa82b3f470a1Timo Sirainen if (ctx->fd_err != -1)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_close_fd(&ctx->fd_err);
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen ctx->input = NULL;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ctx->output = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen}
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainenstatic void dsync_connected_callback(int exit_code, void *context)
d67fde1a8ebc1d85704c5986d8f93aae97eccef3Timo Sirainen{
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen struct dsync_cmd_context *ctx = context;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
87460b08cb97b31cde640d4975a6aa2c1d0e7226Timo Sirainen ctx->ctx.exit_code = exit_code;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen switch (exit_code) {
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen case 0:
9af6cc9ebc9986c1275ebdfa29c39e152af1557eTimo Sirainen server_connection_extract(ctx->tcp_conn, &ctx->input,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen &ctx->output, &ctx->ssl_iostream);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen break;
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen case SERVER_EXIT_CODE_DISCONNECTED:
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen ctx->error = "Disconnected from remote";
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen break;
61e27995aadbc2f97927d3635f98d1d4ac4751e0Timo Sirainen case EX_NOUSER:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->error = "Unknown user in remote";
break;
default:
ctx->error = p_strdup_printf(ctx->ctx.pool,
"Failed to start dsync-server command: %u", exit_code);
break;
}
io_loop_stop(current_ioloop);
}
static int dsync_init_ssl_ctx(struct dsync_cmd_context *ctx,
const struct mail_storage_settings *mail_set,
const char **error_r)
{
struct ssl_iostream_settings ssl_set;
if (ctx->ssl_ctx != NULL)
return 0;
memset(&ssl_set, 0, sizeof(ssl_set));
ssl_set.ca_dir = mail_set->ssl_client_ca_dir;
ssl_set.ca_file = mail_set->ssl_client_ca_file;
ssl_set.verify_remote_cert = TRUE;
ssl_set.crypto_device = mail_set->ssl_crypto_device;
return ssl_iostream_context_init_client(&ssl_set, &ctx->ssl_ctx, error_r);
}
static int
dsync_connect_tcp(struct dsync_cmd_context *ctx,
const struct mail_storage_settings *mail_set,
const char *target, bool ssl, const char **error_r)
{
struct doveadm_server *server;
struct server_connection *conn;
struct ioloop *ioloop;
string_t *cmd;
const char *error;
server = p_new(ctx->ctx.pool, struct doveadm_server, 1);
server->name = p_strdup(ctx->ctx.pool, target);
if (ssl) {
if (dsync_init_ssl_ctx(ctx, mail_set, &error) < 0) {
*error_r = t_strdup_printf(
"Couldn't initialize SSL context: %s", error);
return -1;
}
server->ssl_ctx = ctx->ssl_ctx;
}
p_array_init(&server->connections, ctx->ctx.pool, 1);
p_array_init(&server->queue, ctx->ctx.pool, 1);
ioloop = io_loop_create();
if (server_connection_create(server, &conn) < 0) {
*error_r = "Couldn't create server connection";
return -1;
}
/* <flags> <username> <command> [<args>] */
cmd = t_str_new(256);
if (doveadm_debug)
str_append_c(cmd, 'D');
str_append_c(cmd, '\t');
str_append_tabescaped(cmd, ctx->ctx.cur_username);
str_append(cmd, "\tdsync-server\t-u");
str_append_tabescaped(cmd, ctx->ctx.cur_username);
if (ctx->replicator_notify)
str_append(cmd, "\t-U");
str_append_c(cmd, '\n');
ctx->tcp_conn = conn;
server_connection_cmd(conn, str_c(cmd),
dsync_connected_callback, ctx);
io_loop_run(ioloop);
ctx->tcp_conn = NULL;
if (array_count(&server->connections) > 0)
server_connection_destroy(&conn);
io_loop_destroy(&ioloop);
if (ctx->error != NULL) {
*error_r = ctx->error;
ctx->error = NULL;
return -1;
}
ctx->run_type = DSYNC_RUN_TYPE_STREAM;
return 0;
}
static int
parse_location(struct dsync_cmd_context *ctx,
const struct mail_storage_settings *mail_set,
const char *location,
const char *const **remote_cmd_args_r, const char **error_r)
{
if (strncmp(location, "tcp:", 4) == 0) {
/* TCP connection to remote dsync */
ctx->remote_name = location+4;
return dsync_connect_tcp(ctx, mail_set, ctx->remote_name,
FALSE, error_r);
}
if (strncmp(location, "tcps:", 5) == 0) {
/* TCP+SSL connection to remote dsync */
ctx->remote_name = location+5;
return dsync_connect_tcp(ctx, mail_set, ctx->remote_name,
TRUE, error_r);
}
if (strncmp(location, "remote:", 7) == 0) {
/* this is a remote (ssh) command */
ctx->remote_name = location+7;
} else if (strncmp(location, "remoteprefix:", 13) == 0) {
/* this is a remote (ssh) command with a "user\n"
prefix sent before dsync actually starts */
ctx->remote_name = location+13;
ctx->remote_user_prefix = TRUE;
} else {
/* local with e.g. maildir:path */
ctx->remote_name = NULL;
return 0;
}
*remote_cmd_args_r =
parse_ssh_location(ctx->remote_name, ctx->ctx.cur_username);
return 0;
}
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 struct mail_storage_settings *mail_set;
const char *username = "";
user_set = mail_storage_service_user_get_set(service_user)[0];
mail_set = mail_storage_service_user_get_mail_set(service_user);
ctx->fd_in = -1;
ctx->fd_out = -1;
ctx->fd_err = -1;
ctx->run_type = DSYNC_RUN_TYPE_LOCAL;
ctx->remote_name = "remote";
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];
ctx->local_location_from_arg = TRUE;
}
}
if (remote_cmd_args == NULL && ctx->local_location != NULL) {
if (parse_location(ctx, mail_set, ctx->local_location,
&remote_cmd_args, error_r) < 0)
return -1;
}
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->run_type = DSYNC_RUN_TYPE_CMD;
}
if (ctx->sync_visible_namespaces &&
ctx->run_type == DSYNC_RUN_TYPE_LOCAL)
i_fatal("-N parameter requires syncing with remote host");
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);
}
if (array_count(&ctx->exclude_mailboxes) > 0)
array_append_zero(&ctx->exclude_mailboxes);
lib_signals_ignore(SIGHUP, TRUE);
}
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;
const char *str;
switch (c) {
case 'd':
ctx->default_replica_location = TRUE;
break;
case 'E':
/* dsync wrapper detection flag */
legacy_dsync = TRUE;
break;
case 'f':
ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL;
break;
case 'g':
if (optarg[0] == '\0')
ctx->no_mail_sync = TRUE;
else if (guid_128_from_string(optarg, ctx->mailbox_guid) < 0 ||
guid_128_is_empty(ctx->mailbox_guid))
i_fatal("Invalid -g parameter: %s", optarg);
break;
case 'l':
ctx->lock = TRUE;
if (str_to_uint(optarg, &ctx->lock_timeout) < 0)
i_fatal("Invalid -l parameter: %s", optarg);
break;
case 'm':
if (optarg[0] == '\0')
ctx->no_mail_sync = TRUE;
else
ctx->mailbox = optarg;
break;
case 'x':
str = optarg;
array_append(&ctx->exclude_mailboxes, &str, 1);
break;
case 'n':
ctx->namespace_prefix = optarg;
break;
case 'N':
ctx->sync_visible_namespaces = TRUE;
break;
case 'r':
ctx->rawlog_path = optarg;
break;
case 'R':
if (!ctx->backup)
return FALSE;
ctx->reverse_backup = TRUE;
break;
case 's':
if (ctx->sync_type != DSYNC_BRAIN_SYNC_TYPE_FULL &&
*optarg != '\0')
ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_STATE;
ctx->state_input = optarg;
break;
case 'U':
ctx->replicator_notify = 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 = DSYNC_COMMON_GETOPT_ARGS;
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;
ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_CHANGED;
doveadm_print_init(DOVEADM_PRINT_TYPE_FLOW);
doveadm_print_header("state", "state",
DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
p_array_init(&ctx->exclude_mailboxes, ctx->ctx.pool, 4);
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->getopt_args = DSYNC_COMMON_GETOPT_ARGS"R";
ctx = (struct dsync_cmd_context *)_ctx;
ctx->backup = TRUE;
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_ibc *ibc;
struct dsync_brain *brain;
string_t *temp_prefix, *state_str = NULL;
enum dsync_brain_sync_type sync_type;
if (_ctx->conn != NULL) {
/* doveadm-server connection. start with a success reply.
after that follows the regular dsync protocol. */
ctx->fd_in = ctx->fd_out = -1;
ctx->input = _ctx->conn->input;
ctx->output = _ctx->conn->output;
o_stream_nsend(ctx->output, "\n+\n", 3);
}
doveadm_user_init_dsync(user);
i_set_failure_prefix("dsync-remote(%s): ", user->username);
temp_prefix = t_str_new(64);
mail_user_set_get_temp_prefix(temp_prefix, user->set);
ibc = cmd_dsync_icb_stream_init(ctx, "local", str_c(temp_prefix));
brain = dsync_brain_slave_init(user, ibc);
io_loop_run(current_ioloop);
if (ctx->replicator_notify) {
state_str = t_str_new(128);
dsync_brain_get_state(brain, state_str);
}
sync_type = dsync_brain_get_sync_type(brain);
if (dsync_brain_deinit(&brain) < 0)
_ctx->exit_code = EX_TEMPFAIL;
dsync_ibc_deinit(&ibc);
if (_ctx->conn != NULL) {
/* make sure nothing more is written by the generic doveadm
connection code */
o_stream_close(_ctx->conn->output);
}
if (ctx->replicator_notify && _ctx->exit_code == 0)
dsync_replicator_notify(ctx, sync_type, str_c(state_str));
return _ctx->exit_code == 0 ? 0 : -1;
}
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 'r':
ctx->rawlog_path = optarg;
break;
case 'U':
ctx->replicator_notify = TRUE;
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 = "Er:U";
ctx->ctx.v.parse_arg = cmd_mailbox_dsync_server_parse_arg;
ctx->ctx.v.run = cmd_dsync_server_run;
ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_CHANGED;
ctx->fd_in = STDIN_FILENO;
ctx->fd_out = STDOUT_FILENO;
return &ctx->ctx;
}
struct doveadm_mail_cmd cmd_dsync_mirror = {
cmd_dsync_alloc, "sync",
"[-dfR] [-l <secs>] [-m <mailbox>] [-n <namespace>] [-x <exclude>] [-s <state>] <dest>"
};
struct doveadm_mail_cmd cmd_dsync_backup = {
cmd_dsync_backup_alloc, "backup",
"[-dfR] [-l <secs>] [-m <mailbox>] [-n <namespace>] [-x <exclude>] [-s <state>] <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;
bool dsync_server = FALSE;
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 = t_new(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 = t_strdup_noconst(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";
dsync_server = TRUE;
} 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 (!dsync_server) {
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;
}