~ubuntu-branches/ubuntu/wily/dovecot/wily

« back to all changes in this revision

Viewing changes to src/doveadm/dsync/dsync-brain.c

  • Committer: Package Import Robot
  • Author(s): Jaldhar H. Vyas
  • Date: 2013-09-09 00:57:32 UTC
  • mfrom: (1.13.11)
  • mto: (4.8.5 experimental) (1.16.1)
  • mto: This revision was merged to the branch mainline in revision 97.
  • Revision ID: package-import@ubuntu.com-20130909005732-dn1eell8srqbhh0e
Tags: upstream-2.2.5
ImportĀ upstreamĀ versionĀ 2.2.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
 
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
2
2
 
3
3
#include "lib.h"
4
4
#include "array.h"
5
5
#include "hash.h"
6
 
#include "dsync-worker.h"
 
6
#include "hostpid.h"
 
7
#include "str.h"
 
8
#include "process-title.h"
 
9
#include "settings-parser.h"
 
10
#include "master-service.h"
 
11
#include "master-service-settings.h"
 
12
#include "mail-namespace.h"
 
13
#include "dsync-mailbox-tree.h"
 
14
#include "dsync-ibc.h"
7
15
#include "dsync-brain-private.h"
8
 
 
9
 
#include <unistd.h>
10
 
 
11
 
#define DSYNC_WRONG_DIRECTION_ERROR_MSG \
12
 
        "dsync backup: " \
13
 
        "Looks like you're trying to run backup in wrong direction. " \
14
 
        "Source is empty and destination is not."
15
 
 
16
 
static void
17
 
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **list);
18
 
static void
19
 
dsync_brain_subs_list_deinit(struct dsync_brain_subs_list **list);
20
 
 
21
 
struct dsync_brain *
22
 
dsync_brain_init(struct dsync_worker *src_worker,
23
 
                 struct dsync_worker *dest_worker,
24
 
                 const char *mailbox, enum dsync_brain_flags flags)
25
 
{
26
 
        struct dsync_brain *brain;
27
 
 
28
 
        brain = i_new(struct dsync_brain, 1);
29
 
        brain->src_worker = src_worker;
30
 
        brain->dest_worker = dest_worker;
31
 
        brain->mailbox = i_strdup(mailbox);
32
 
        brain->flags = flags;
33
 
        brain->verbose = (flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0;
34
 
        brain->backup = (flags & DSYNC_BRAIN_FLAG_BACKUP) != 0;
35
 
        brain->stdout_tty = isatty(STDOUT_FILENO) > 0;
36
 
 
37
 
        if ((flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0) {
38
 
                dsync_worker_set_verbose(src_worker);
39
 
                dsync_worker_set_verbose(dest_worker);
40
 
        }
41
 
        return brain;
42
 
}
43
 
 
44
 
void dsync_brain_fail(struct dsync_brain *brain)
45
 
{
46
 
        brain->failed = TRUE;
47
 
        io_loop_stop(current_ioloop);
 
16
#include "dsync-mailbox-import.h"
 
17
#include "dsync-mailbox-export.h"
 
18
 
 
19
#include <sys/stat.h>
 
20
 
 
21
static const char *dsync_state_names[] = {
 
22
        "master_recv_handshake",
 
23
        "slave_recv_handshake",
 
24
        "master_send_last_common",
 
25
        "slave_recv_last_common",
 
26
        "send_mailbox_tree",
 
27
        "send_mailbox_tree_deletes",
 
28
        "recv_mailbox_tree",
 
29
        "recv_mailbox_tree_deletes",
 
30
        "master_send_mailbox",
 
31
        "slave_recv_mailbox",
 
32
        "sync_mails",
 
33
        "done"
 
34
};
 
35
 
 
36
static const char *dsync_brain_get_proctitle(struct dsync_brain *brain)
 
37
{
 
38
        string_t *str = t_str_new(128);
 
39
        const char *import_title, *export_title;
 
40
 
 
41
        str_append_c(str, '[');
 
42
        str_append(str, brain->user->username);
 
43
        if (brain->box == NULL) {
 
44
                str_append_c(str, ' ');
 
45
                str_append(str, dsync_state_names[brain->state]);
 
46
        } else {
 
47
                str_append_c(str, ' ');
 
48
                str_append(str, mailbox_get_vname(brain->box));
 
49
                import_title = brain->box_importer == NULL ? "" :
 
50
                        dsync_mailbox_import_get_proctitle(brain->box_importer);
 
51
                export_title = brain->box_exporter == NULL ? "" :
 
52
                        dsync_mailbox_export_get_proctitle(brain->box_exporter);
 
53
                if (import_title[0] == '\0' && export_title[0] == '\0') {
 
54
                        str_printfa(str, " send:%s recv:%s",
 
55
                                    dsync_box_state_names[brain->box_send_state],
 
56
                                    dsync_box_state_names[brain->box_recv_state]);
 
57
                } else {
 
58
                        if (import_title[0] != '\0') {
 
59
                                str_append(str, " import:");
 
60
                                str_append(str, import_title);
 
61
                        }
 
62
                        if (export_title[0] != '\0') {
 
63
                                str_append(str, " export:");
 
64
                                str_append(str, export_title);
 
65
                        }
 
66
                }
 
67
        }
 
68
        str_append_c(str, ']');
 
69
        return str_c(str);
 
70
}
 
71
 
 
72
static void dsync_brain_run_io(void *context)
 
73
{
 
74
        struct dsync_brain *brain = context;
 
75
        bool changed, try_pending;
 
76
 
 
77
        if (dsync_ibc_has_failed(brain->ibc)) {
 
78
                io_loop_stop(current_ioloop);
 
79
                brain->failed = TRUE;
 
80
                return;
 
81
        }
 
82
 
 
83
        try_pending = TRUE;
 
84
        do {
 
85
                if (!dsync_brain_run(brain, &changed)) {
 
86
                        io_loop_stop(current_ioloop);
 
87
                        break;
 
88
                }
 
89
                if (changed)
 
90
                        try_pending = TRUE;
 
91
                else if (try_pending) {
 
92
                        if (dsync_ibc_has_pending_data(brain->ibc))
 
93
                                changed = TRUE;
 
94
                        try_pending = FALSE;
 
95
                }
 
96
        } while (changed);
 
97
}
 
98
 
 
99
static struct dsync_brain *
 
100
dsync_brain_common_init(struct mail_user *user, struct dsync_ibc *ibc)
 
101
{
 
102
        struct dsync_brain *brain;
 
103
        const struct master_service_settings *service_set;
 
104
        pool_t pool;
 
105
 
 
106
        service_set = master_service_settings_get(master_service);
 
107
 
 
108
        pool = pool_alloconly_create("dsync brain", 10240);
 
109
        brain = p_new(pool, struct dsync_brain, 1);
 
110
        brain->pool = pool;
 
111
        brain->user = user;
 
112
        brain->ibc = ibc;
 
113
        brain->sync_type = DSYNC_BRAIN_SYNC_TYPE_UNKNOWN;
 
114
        brain->lock_fd = -1;
 
115
        brain->verbose_proctitle = service_set->verbose_proctitle;
 
116
        hash_table_create(&brain->mailbox_states, pool, 0,
 
117
                          guid_128_hash, guid_128_cmp);
 
118
        p_array_init(&brain->remote_mailbox_states, pool, 64);
 
119
        return brain;
 
120
}
 
121
 
 
122
static void
 
123
dsync_brain_set_flags(struct dsync_brain *brain, enum dsync_brain_flags flags)
 
124
{
 
125
        brain->mail_requests =
 
126
                (flags & DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS) != 0;
 
127
        brain->backup_send = (flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0;
 
128
        brain->backup_recv = (flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0;
 
129
        brain->debug = (flags & DSYNC_BRAIN_FLAG_DEBUG) != 0;
 
130
        brain->sync_visible_namespaces =
 
131
                (flags & DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES) != 0;
 
132
        brain->no_mail_sync = (flags & DSYNC_BRAIN_FLAG_NO_MAIL_SYNC) != 0;
 
133
        brain->no_backup_overwrite =
 
134
                (flags & DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE) != 0;
 
135
}
 
136
 
 
137
struct dsync_brain *
 
138
dsync_brain_master_init(struct mail_user *user, struct dsync_ibc *ibc,
 
139
                        enum dsync_brain_sync_type sync_type,
 
140
                        enum dsync_brain_flags flags,
 
141
                        const struct dsync_brain_settings *set)
 
142
{
 
143
        struct dsync_ibc_settings ibc_set;
 
144
        struct dsync_brain *brain;
 
145
        const char *error;
 
146
 
 
147
        i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_UNKNOWN);
 
148
        i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE ||
 
149
                 (set->state != NULL && *set->state != '\0'));
 
150
        i_assert(N_ELEMENTS(dsync_state_names) == DSYNC_STATE_DONE+1);
 
151
 
 
152
        brain = dsync_brain_common_init(user, ibc);
 
153
        brain->sync_type = sync_type;
 
154
        if (set->sync_ns != NULL)
 
155
                brain->sync_ns = set->sync_ns;
 
156
        brain->sync_box = p_strdup(brain->pool, set->sync_box);
 
157
        brain->exclude_mailboxes = set->exclude_mailboxes == NULL ? NULL :
 
158
                p_strarray_dup(brain->pool, set->exclude_mailboxes);
 
159
        memcpy(brain->sync_box_guid, set->sync_box_guid,
 
160
               sizeof(brain->sync_box_guid));
 
161
        brain->lock_timeout = set->lock_timeout_secs;
 
162
        brain->master_brain = TRUE;
 
163
        dsync_brain_set_flags(brain, flags);
 
164
 
 
165
        if (sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE &&
 
166
            dsync_mailbox_states_import(brain->mailbox_states,
 
167
                                        brain->pool, set->state, &error) < 0) {
 
168
                hash_table_clear(brain->mailbox_states, FALSE);
 
169
                i_error("Saved sync state is invalid, "
 
170
                        "falling back to full sync: %s", error);
 
171
                brain->sync_type = sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL;
 
172
        }
 
173
        dsync_brain_mailbox_trees_init(brain);
 
174
 
 
175
        memset(&ibc_set, 0, sizeof(ibc_set));
 
176
        ibc_set.hostname = my_hostdomain();
 
177
        ibc_set.sync_ns_prefix = set->sync_ns == NULL ? NULL :
 
178
                set->sync_ns->prefix;
 
179
        ibc_set.sync_box = set->sync_box;
 
180
        ibc_set.exclude_mailboxes = set->exclude_mailboxes;
 
181
        memcpy(ibc_set.sync_box_guid, set->sync_box_guid,
 
182
               sizeof(ibc_set.sync_box_guid));
 
183
        ibc_set.sync_type = sync_type;
 
184
        ibc_set.lock_timeout = set->lock_timeout_secs;
 
185
        /* reverse the backup direction for the slave */
 
186
        ibc_set.brain_flags = flags & ~(DSYNC_BRAIN_FLAG_BACKUP_SEND |
 
187
                                        DSYNC_BRAIN_FLAG_BACKUP_RECV);
 
188
        if ((flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0)
 
189
                ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV;
 
190
        else if ((flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0)
 
191
                ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND;
 
192
        dsync_ibc_send_handshake(ibc, &ibc_set);
 
193
 
 
194
        dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
 
195
        brain->state = DSYNC_STATE_MASTER_RECV_HANDSHAKE;
 
196
        return brain;
 
197
}
 
198
 
 
199
struct dsync_brain *
 
200
dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc,
 
201
                       bool local)
 
202
{
 
203
        struct dsync_ibc_settings ibc_set;
 
204
        struct dsync_brain *brain;
 
205
 
 
206
        brain = dsync_brain_common_init(user, ibc);
 
207
        brain->state = DSYNC_STATE_SLAVE_RECV_HANDSHAKE;
 
208
 
 
209
        if (local) {
 
210
                /* both master and slave are running within the same process,
 
211
                   update the proctitle only for master. */
 
212
                brain->verbose_proctitle = FALSE;
 
213
        }
 
214
 
 
215
        memset(&ibc_set, 0, sizeof(ibc_set));
 
216
        ibc_set.hostname = my_hostdomain();
 
217
        dsync_ibc_send_handshake(ibc, &ibc_set);
 
218
 
 
219
        dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
 
220
        return brain;
48
221
}
49
222
 
50
223
int dsync_brain_deinit(struct dsync_brain **_brain)
51
224
{
52
225
        struct dsync_brain *brain = *_brain;
53
 
        int ret = brain->failed ? -1 : 0;
54
 
 
55
 
        if (brain->state != DSYNC_STATE_SYNC_END)
56
 
                ret = -1;
57
 
        if (brain->to != NULL)
58
 
                timeout_remove(&brain->to);
59
 
 
60
 
        if (ret < 0) {
61
 
                /* make sure we unreference save input streams before workers
62
 
                   are deinitialized, so they can destroy the streams */
63
 
                dsync_worker_msg_save_cancel(brain->src_worker);
64
 
                dsync_worker_msg_save_cancel(brain->dest_worker);
65
 
        }
66
 
 
67
 
        if (brain->mailbox_sync != NULL)
68
 
                dsync_brain_msg_sync_deinit(&brain->mailbox_sync);
69
 
 
70
 
        if (brain->src_mailbox_list != NULL)
71
 
                dsync_brain_mailbox_list_deinit(&brain->src_mailbox_list);
72
 
        if (brain->dest_mailbox_list != NULL)
73
 
                dsync_brain_mailbox_list_deinit(&brain->dest_mailbox_list);
74
 
 
75
 
        if (brain->src_subs_list != NULL)
76
 
                dsync_brain_subs_list_deinit(&brain->src_subs_list);
77
 
        if (brain->dest_subs_list != NULL)
78
 
                dsync_brain_subs_list_deinit(&brain->dest_subs_list);
79
 
 
80
 
        if (dsync_worker_has_failed(brain->src_worker) ||
81
 
            dsync_worker_has_failed(brain->dest_worker))
82
 
                ret = -1;
 
226
        int ret;
83
227
 
84
228
        *_brain = NULL;
85
 
        i_free(brain->mailbox);
86
 
        i_free(brain);
 
229
 
 
230
        if (dsync_ibc_has_timed_out(brain->ibc)) {
 
231
                i_error("Timeout during state=%s%s",
 
232
                        dsync_state_names[brain->state],
 
233
                        brain->state != DSYNC_STATE_SYNC_MAILS ? "" :
 
234
                        t_strdup_printf(" (send=%s recv=%s)",
 
235
                                dsync_box_state_names[brain->box_send_state],
 
236
                                dsync_box_state_names[brain->box_recv_state]));
 
237
        }
 
238
        if (dsync_ibc_has_failed(brain->ibc) ||
 
239
            brain->state != DSYNC_STATE_DONE)
 
240
                brain->failed = TRUE;
 
241
        dsync_ibc_close_mail_streams(brain->ibc);
 
242
 
 
243
        if (brain->box != NULL)
 
244
                dsync_brain_sync_mailbox_deinit(brain);
 
245
        if (brain->local_tree_iter != NULL)
 
246
                dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
 
247
        if (brain->local_mailbox_tree != NULL)
 
248
                dsync_mailbox_tree_deinit(&brain->local_mailbox_tree);
 
249
        if (brain->remote_mailbox_tree != NULL)
 
250
                dsync_mailbox_tree_deinit(&brain->remote_mailbox_tree);
 
251
        if (brain->mailbox_states_iter != NULL)
 
252
                hash_table_iterate_deinit(&brain->mailbox_states_iter);
 
253
        hash_table_destroy(&brain->mailbox_states);
 
254
 
 
255
        if (brain->lock_fd != -1) {
 
256
                /* unlink the lock file before it gets unlocked */
 
257
                if (unlink(brain->lock_path) < 0)
 
258
                        i_error("unlink(%s) failed: %m", brain->lock_path);
 
259
                file_lock_free(&brain->lock);
 
260
                i_close_fd(&brain->lock_fd);
 
261
        }
 
262
 
 
263
        ret = brain->failed ? -1 : 0;
 
264
        pool_unref(&brain->pool);
87
265
        return ret;
88
266
}
89
267
 
90
 
static void dsync_brain_mailbox_list_finished(struct dsync_brain *brain)
91
 
{
92
 
        if (brain->src_mailbox_list->iter != NULL ||
93
 
            brain->dest_mailbox_list->iter != NULL)
94
 
                return;
95
 
 
96
 
        /* both lists are finished */
97
 
        brain->state++;
98
 
        dsync_brain_sync(brain);
99
 
}
100
 
 
101
 
static void dsync_worker_mailbox_input(void *context)
102
 
{
103
 
        struct dsync_brain_mailbox_list *list = context;
104
 
        struct dsync_mailbox dsync_box, *dup_box;
105
 
        int ret;
106
 
 
107
 
        while ((ret = dsync_worker_mailbox_iter_next(list->iter,
108
 
                                                     &dsync_box)) > 0) {
109
 
                if (list->brain->mailbox != NULL &&
110
 
                    strcmp(list->brain->mailbox, dsync_box.name) != 0)
111
 
                        continue;
112
 
 
113
 
                dup_box = dsync_mailbox_dup(list->pool, &dsync_box);
114
 
                if (!dsync_mailbox_is_noselect(dup_box))
115
 
                        array_append(&list->mailboxes, &dup_box, 1);
116
 
                else
117
 
                        array_append(&list->dirs, &dup_box, 1);
118
 
        }
119
 
        if (ret < 0) {
120
 
                /* finished listing mailboxes */
121
 
                if (dsync_worker_mailbox_iter_deinit(&list->iter) < 0)
122
 
                        dsync_brain_fail(list->brain);
123
 
                array_sort(&list->mailboxes, dsync_mailbox_p_guid_cmp);
124
 
                array_sort(&list->dirs, dsync_mailbox_p_name_sha1_cmp);
125
 
                dsync_brain_mailbox_list_finished(list->brain);
126
 
        }
127
 
}
128
 
 
129
 
static struct dsync_brain_mailbox_list *
130
 
dsync_brain_mailbox_list_init(struct dsync_brain *brain,
131
 
                              struct dsync_worker *worker)
132
 
{
133
 
        struct dsync_brain_mailbox_list *list;
134
 
        pool_t pool;
135
 
 
136
 
        pool = pool_alloconly_create("dsync brain mailbox list", 10240);
137
 
        list = p_new(pool, struct dsync_brain_mailbox_list, 1);
138
 
        list->pool = pool;
139
 
        list->brain = brain;
140
 
        list->worker = worker;
141
 
        list->iter = dsync_worker_mailbox_iter_init(worker);
142
 
        p_array_init(&list->mailboxes, pool, 128);
143
 
        p_array_init(&list->dirs, pool, 32);
144
 
        dsync_worker_set_input_callback(worker, dsync_worker_mailbox_input,
145
 
                                        list);
146
 
        return list;
147
 
}
148
 
 
149
 
static void
150
 
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **_list)
151
 
{
152
 
        struct dsync_brain_mailbox_list *list = *_list;
153
 
 
154
 
        *_list = NULL;
155
 
 
156
 
        if (list->iter != NULL)
157
 
                (void)dsync_worker_mailbox_iter_deinit(&list->iter);
158
 
        pool_unref(&list->pool);
159
 
}
160
 
 
161
 
static void dsync_brain_subs_list_finished(struct dsync_brain *brain)
162
 
{
163
 
        if (brain->src_subs_list->iter != NULL ||
164
 
            brain->dest_subs_list->iter != NULL)
165
 
                return;
166
 
 
167
 
        /* both lists are finished */
168
 
        brain->state++;
169
 
        dsync_brain_sync(brain);
170
 
}
171
 
 
172
 
static int
173
 
dsync_worker_subscription_cmp(const struct dsync_worker_subscription *s1,
174
 
                              const struct dsync_worker_subscription *s2)
175
 
{
176
 
        return strcmp(s1->vname, s2->vname);
177
 
}
178
 
 
179
 
static int
180
 
dsync_worker_unsubscription_cmp(const struct dsync_worker_unsubscription *u1,
181
 
                                const struct dsync_worker_unsubscription *u2)
182
 
{
183
 
        int ret;
184
 
 
185
 
        ret = strcmp(u1->ns_prefix, u2->ns_prefix);
186
 
        return ret != 0 ? ret :
187
 
                dsync_guid_cmp(&u1->name_sha1, &u2->name_sha1);
188
 
}
189
 
 
190
 
static void dsync_worker_subs_input(void *context)
191
 
{
192
 
        struct dsync_brain_subs_list *list = context;
193
 
        struct dsync_worker_subscription subs;
194
 
        struct dsync_worker_unsubscription unsubs;
195
 
        int ret;
196
 
 
197
 
        memset(&subs, 0, sizeof(subs));
198
 
        while ((ret = dsync_worker_subs_iter_next(list->iter, &subs)) > 0) {
199
 
                subs.vname = p_strdup(list->pool, subs.vname);
200
 
                subs.storage_name = p_strdup(list->pool, subs.storage_name);
201
 
                subs.ns_prefix = p_strdup(list->pool, subs.ns_prefix);
202
 
                array_append(&list->subscriptions, &subs, 1);
203
 
        }
204
 
        if (ret == 0)
205
 
                return;
206
 
 
207
 
        memset(&unsubs, 0, sizeof(unsubs));
208
 
        while ((ret = dsync_worker_subs_iter_next_un(list->iter,
209
 
                                                     &unsubs)) > 0) {
210
 
                unsubs.ns_prefix = p_strdup(list->pool, unsubs.ns_prefix);
211
 
                array_append(&list->unsubscriptions, &unsubs, 1);
212
 
        }
213
 
 
214
 
        if (ret < 0) {
215
 
                /* finished listing subscriptions */
216
 
                if (dsync_worker_subs_iter_deinit(&list->iter) < 0)
217
 
                        dsync_brain_fail(list->brain);
218
 
                array_sort(&list->subscriptions,
219
 
                           dsync_worker_subscription_cmp);
220
 
                array_sort(&list->unsubscriptions,
221
 
                           dsync_worker_unsubscription_cmp);
222
 
                dsync_brain_subs_list_finished(list->brain);
223
 
        }
224
 
}
225
 
 
226
 
static struct dsync_brain_subs_list *
227
 
dsync_brain_subs_list_init(struct dsync_brain *brain,
228
 
                              struct dsync_worker *worker)
229
 
{
230
 
        struct dsync_brain_subs_list *list;
231
 
        pool_t pool;
232
 
 
233
 
        pool = pool_alloconly_create(MEMPOOL_GROWING"dsync brain subs list",
234
 
                                     1024*4);
235
 
        list = p_new(pool, struct dsync_brain_subs_list, 1);
236
 
        list->pool = pool;
237
 
        list->brain = brain;
238
 
        list->worker = worker;
239
 
        list->iter = dsync_worker_subs_iter_init(worker);
240
 
        p_array_init(&list->subscriptions, pool, 128);
241
 
        p_array_init(&list->unsubscriptions, pool, 64);
242
 
        dsync_worker_set_input_callback(worker, dsync_worker_subs_input, list);
243
 
        return list;
244
 
}
245
 
 
246
 
static void
247
 
dsync_brain_subs_list_deinit(struct dsync_brain_subs_list **_list)
248
 
{
249
 
        struct dsync_brain_subs_list *list = *_list;
250
 
 
251
 
        *_list = NULL;
252
 
 
253
 
        if (list->iter != NULL)
254
 
                (void)dsync_worker_subs_iter_deinit(&list->iter);
255
 
        pool_unref(&list->pool);
256
 
}
257
 
 
258
 
enum dsync_brain_mailbox_action {
259
 
        DSYNC_BRAIN_MAILBOX_ACTION_NONE,
260
 
        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
261
 
        DSYNC_BRAIN_MAILBOX_ACTION_DELETE
262
 
};
263
 
 
264
 
static void
265
 
dsync_brain_mailbox_action(struct dsync_brain *brain,
266
 
                           enum dsync_brain_mailbox_action action,
267
 
                           struct dsync_worker *action_worker,
268
 
                           struct dsync_mailbox *action_box)
269
 
{
270
 
        struct dsync_mailbox new_box;
271
 
 
272
 
        if (brain->backup && action_worker == brain->src_worker) {
273
 
                /* backup mode: switch actions */
274
 
                action_worker = brain->dest_worker;
275
 
                switch (action) {
276
 
                case DSYNC_BRAIN_MAILBOX_ACTION_NONE:
277
 
                        break;
278
 
                case DSYNC_BRAIN_MAILBOX_ACTION_CREATE:
279
 
                        action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
280
 
                        break;
281
 
                case DSYNC_BRAIN_MAILBOX_ACTION_DELETE:
282
 
                        action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
283
 
                        break;
284
 
                }
285
 
        }
286
 
 
287
 
        switch (action) {
288
 
        case DSYNC_BRAIN_MAILBOX_ACTION_NONE:
289
 
                break;
290
 
        case DSYNC_BRAIN_MAILBOX_ACTION_CREATE:
291
 
                new_box = *action_box;
292
 
                new_box.uid_next = action_box->uid_validity == 0 ? 0 : 1;
293
 
                new_box.first_recent_uid = 0;
294
 
                new_box.highest_modseq = 0;
295
 
                dsync_worker_create_mailbox(action_worker, &new_box);
296
 
                break;
297
 
        case DSYNC_BRAIN_MAILBOX_ACTION_DELETE:
298
 
                if (!dsync_mailbox_is_noselect(action_box))
299
 
                        dsync_worker_delete_mailbox(action_worker, action_box);
300
 
                else
301
 
                        dsync_worker_delete_dir(action_worker, action_box);
302
 
                break;
303
 
        }
304
 
}
305
 
 
306
 
static bool
307
 
dsync_mailbox_list_is_empty(const ARRAY_TYPE(dsync_mailbox) *boxes_arr)
308
 
{
309
 
        struct dsync_mailbox *const *boxes;
310
 
        unsigned int count;
311
 
 
312
 
        boxes = array_get(boxes_arr, &count);
313
 
        if (count == 0)
314
 
                return TRUE;
315
 
        if (count == 1 && strcasecmp(boxes[0]->name, "INBOX") == 0 &&
316
 
            boxes[0]->message_count == 0 && boxes[0]->uid_next == 1)
317
 
                return TRUE;
318
 
        return FALSE;
319
 
}
320
 
 
321
 
static void dsync_brain_sync_mailboxes(struct dsync_brain *brain)
322
 
{
323
 
        struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
324
 
        struct dsync_mailbox *action_box = NULL;
325
 
        struct dsync_worker *action_worker = NULL;
326
 
        unsigned int src, dest, src_count, dest_count;
327
 
        enum dsync_brain_mailbox_action action;
328
 
        bool src_deleted, dest_deleted;
329
 
        int ret;
330
 
 
331
 
        if (brain->backup &&
332
 
            dsync_mailbox_list_is_empty(&brain->src_mailbox_list->mailboxes) &&
333
 
            !dsync_mailbox_list_is_empty(&brain->dest_mailbox_list->mailboxes)) {
334
 
                i_fatal(DSYNC_WRONG_DIRECTION_ERROR_MSG);
335
 
        }
336
 
 
337
 
        /* create/delete missing mailboxes. the mailboxes are sorted by
338
 
           GUID, so we can do this quickly. */
339
 
        src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
340
 
        dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
341
 
        for (src = dest = 0; src < src_count && dest < dest_count; ) {
342
 
                action = DSYNC_BRAIN_MAILBOX_ACTION_NONE;
343
 
                src_deleted = (src_boxes[src]->flags &
344
 
                               DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
345
 
                dest_deleted = (dest_boxes[dest]->flags &
346
 
                                DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
347
 
                ret = dsync_mailbox_guid_cmp(src_boxes[src],
348
 
                                             dest_boxes[dest]);
349
 
                if (ret < 0) {
350
 
                        /* exists only in source */
351
 
                        if (!src_deleted) {
352
 
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
353
 
                                action_worker = brain->dest_worker;
354
 
                                action_box = src_boxes[src];
355
 
                        }
356
 
                        src++;
357
 
                } else if (ret > 0) {
358
 
                        /* exists only in dest */
359
 
                        if (!dest_deleted) {
360
 
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
361
 
                                action_worker = brain->src_worker;
362
 
                                action_box = dest_boxes[dest];
363
 
                        }
364
 
                        dest++;
365
 
                } else if (src_deleted) {
366
 
                        /* delete from dest too */
367
 
                        if (!dest_deleted) {
368
 
                                action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
369
 
                                action_worker = brain->dest_worker;
370
 
                                action_box = dest_boxes[dest];
371
 
                        }
372
 
                        src++; dest++;
373
 
                } else if (dest_deleted) {
374
 
                        /* delete from src too */
375
 
                        action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
376
 
                        action_worker = brain->src_worker;
377
 
                        action_box = src_boxes[src];
378
 
                        src++; dest++;
379
 
                } else {
380
 
                        src++; dest++;
381
 
                }
382
 
                dsync_brain_mailbox_action(brain, action,
383
 
                                           action_worker, action_box);
384
 
        }
385
 
        for (; src < src_count; src++) {
386
 
                if ((src_boxes[src]->flags &
387
 
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
388
 
                        continue;
389
 
 
390
 
                dsync_brain_mailbox_action(brain,
391
 
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
392
 
                        brain->dest_worker, src_boxes[src]);
393
 
        }
394
 
        for (; dest < dest_count; dest++) {
395
 
                if ((dest_boxes[dest]->flags &
396
 
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
397
 
                        continue;
398
 
 
399
 
                dsync_brain_mailbox_action(brain,
400
 
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
401
 
                        brain->src_worker, dest_boxes[dest]);
402
 
        }
403
 
}
404
 
 
405
 
static void dsync_brain_sync_dirs(struct dsync_brain *brain)
406
 
{
407
 
        struct dsync_mailbox *const *src_boxes, *const *dest_boxes, *action_box;
408
 
        unsigned int src, dest, src_count, dest_count;
409
 
        enum dsync_brain_mailbox_action action;
410
 
        struct dsync_worker *action_worker = NULL;
411
 
        bool src_deleted, dest_deleted;
412
 
        int ret;
413
 
 
414
 
        /* create/delete missing directories. */
415
 
        src_boxes = array_get(&brain->src_mailbox_list->dirs, &src_count);
416
 
        dest_boxes = array_get(&brain->dest_mailbox_list->dirs, &dest_count);
417
 
        for (src = dest = 0; src < src_count && dest < dest_count; ) {
418
 
                action = DSYNC_BRAIN_MAILBOX_ACTION_NONE;
419
 
                action_box = NULL;
420
 
 
421
 
                src_deleted = (src_boxes[src]->flags &
422
 
                               DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
423
 
                dest_deleted = (dest_boxes[dest]->flags &
424
 
                                DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
425
 
                ret = memcmp(src_boxes[src]->name_sha1.guid,
426
 
                             dest_boxes[dest]->name_sha1.guid,
427
 
                             sizeof(src_boxes[src]->name_sha1.guid));
428
 
                if (ret < 0) {
429
 
                        /* exists only in source */
430
 
                        if (!src_deleted) {
431
 
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
432
 
                                action_worker = brain->dest_worker;
433
 
                                action_box = src_boxes[src];
434
 
                        }
435
 
                        src++;
436
 
                } else if (ret > 0) {
437
 
                        /* exists only in dest */
438
 
                        if (!dest_deleted) {
439
 
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
440
 
                                action_worker = brain->src_worker;
441
 
                                action_box = dest_boxes[dest];
442
 
                        }
443
 
                        dest++;
444
 
                } else if (src_deleted) {
445
 
                        /* delete from dest too */
446
 
                        if (!dest_deleted) {
447
 
                                action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
448
 
                                action_worker = brain->dest_worker;
449
 
                                action_box = dest_boxes[dest];
450
 
                        }
451
 
                        src++; dest++;
452
 
                } else if (dest_deleted) {
453
 
                        /* delete from src too */
454
 
                        action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
455
 
                        action_worker = brain->src_worker;
456
 
                        action_box = src_boxes[src];
457
 
                        src++; dest++;
458
 
                } else {
459
 
                        src++; dest++;
460
 
                }
461
 
                i_assert(action_box == NULL ||
462
 
                         dsync_mailbox_is_noselect(action_box));
463
 
                dsync_brain_mailbox_action(brain, action,
464
 
                                           action_worker, action_box);
465
 
        }
466
 
        for (; src < src_count; src++) {
467
 
                if ((src_boxes[src]->flags &
468
 
                     DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
469
 
                        continue;
470
 
 
471
 
                dsync_brain_mailbox_action(brain,
472
 
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
473
 
                        brain->dest_worker, src_boxes[src]);
474
 
        }
475
 
        for (; dest < dest_count; dest++) {
476
 
                if ((dest_boxes[dest]->flags &
477
 
                     DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
478
 
                        continue;
479
 
 
480
 
                dsync_brain_mailbox_action(brain,
481
 
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
482
 
                        brain->src_worker, dest_boxes[dest]);
483
 
        }
484
 
}
485
 
 
486
 
static bool
487
 
dsync_brain_is_unsubscribed(struct dsync_brain_subs_list *list,
488
 
                            const struct dsync_worker_subscription *subs,
489
 
                            time_t *last_change_r)
490
 
{
491
 
        const struct dsync_worker_unsubscription *unsubs;
492
 
        struct dsync_worker_unsubscription lookup;
493
 
 
494
 
        lookup.ns_prefix = subs->ns_prefix;
495
 
        dsync_str_sha_to_guid(subs->storage_name, &lookup.name_sha1);
496
 
        unsubs = array_bsearch(&list->unsubscriptions, &lookup,
497
 
                               dsync_worker_unsubscription_cmp);
498
 
        if (unsubs == NULL) {
499
 
                *last_change_r = 0;
500
 
                return FALSE;
501
 
        } else if (unsubs->last_change <= subs->last_change) {
502
 
                *last_change_r = subs->last_change;
503
 
                return FALSE;
504
 
        } else {
505
 
                *last_change_r = unsubs->last_change;
506
 
                return TRUE;
507
 
        }
508
 
}
509
 
 
510
 
static void dsync_brain_sync_subscriptions(struct dsync_brain *brain)
511
 
{
512
 
        const struct dsync_worker_subscription *src_subs, *dest_subs;
513
 
        const struct dsync_worker_subscription *action_subs;
514
 
        struct dsync_worker *action_worker;
515
 
        unsigned int src, dest, src_count, dest_count;
516
 
        time_t last_change;
517
 
        bool subscribe;
518
 
        int ret;
519
 
 
520
 
        /* subscriptions are sorted by name. */
521
 
        src_subs = array_get(&brain->src_subs_list->subscriptions, &src_count);
522
 
        dest_subs = array_get(&brain->dest_subs_list->subscriptions, &dest_count);
523
 
        for (src = dest = 0;; ) {
524
 
                if (src == src_count) {
525
 
                        if (dest == dest_count)
526
 
                                break;
527
 
                        ret = 1;
528
 
                } else if (dest == dest_count) {
529
 
                        ret = -1;
530
 
                } else {
531
 
                        ret = strcmp(src_subs[src].vname,
532
 
                                     dest_subs[dest].vname);
533
 
                        if (ret == 0) {
534
 
                                src++; dest++;
535
 
                                continue;
536
 
                        }
537
 
                }
538
 
 
539
 
                if (ret < 0) {
540
 
                        /* subscribed only in source */
541
 
                        action_subs = &src_subs[src];
542
 
                        if (dsync_brain_is_unsubscribed(brain->dest_subs_list,
543
 
                                                        &src_subs[src],
544
 
                                                        &last_change)) {
545
 
                                action_worker = brain->src_worker;
546
 
                                subscribe = FALSE;
547
 
                        } else {
548
 
                                action_worker = brain->dest_worker;
549
 
                                subscribe = TRUE;
550
 
                        }
551
 
                        src++;
552
 
                } else {
553
 
                        /* subscribed only in dest */
554
 
                        action_subs = &dest_subs[dest];
555
 
                        if (dsync_brain_is_unsubscribed(brain->src_subs_list,
556
 
                                                        &dest_subs[dest],
557
 
                                                        &last_change)) {
558
 
                                action_worker = brain->dest_worker;
559
 
                                subscribe = FALSE;
560
 
                        } else {
561
 
                                action_worker = brain->src_worker;
562
 
                                subscribe = TRUE;
563
 
                        }
564
 
                        dest++;
565
 
                }
566
 
 
567
 
                if (brain->backup && action_worker == brain->src_worker) {
568
 
                        /* backup mode: switch action */
569
 
                        action_worker = brain->dest_worker;
570
 
                        subscribe = !subscribe;
571
 
                        last_change = ioloop_time;
572
 
                }
573
 
                dsync_worker_set_subscribed(action_worker, action_subs->vname,
574
 
                                            last_change, subscribe);
575
 
        }
576
 
}
577
 
 
578
 
static bool dsync_mailbox_has_changed_msgs(struct dsync_brain *brain,
579
 
                                           const struct dsync_mailbox *box1,
580
 
                                           const struct dsync_mailbox *box2)
581
 
{
582
 
        const char *name = *box1->name != '\0' ? box1->name : box2->name;
583
 
 
584
 
        if (box1->uid_validity != box2->uid_validity) {
585
 
                if (brain->verbose) {
586
 
                        i_info("%s: uidvalidity changed: %u != %u", name,
587
 
                               box1->uid_validity, box2->uid_validity);
588
 
                }
589
 
                return TRUE;
590
 
        }
591
 
        if (box1->uid_next != box2->uid_next) {
592
 
                if (brain->verbose) {
593
 
                        i_info("%s: uidnext changed: %u != %u", name,
594
 
                               box1->uid_next, box2->uid_next);
595
 
                }
596
 
                return TRUE;
597
 
        }
598
 
        if (box1->highest_modseq != box2->highest_modseq) {
599
 
                if (brain->verbose) {
600
 
                        i_info("%s: highest_modseq changed: %llu != %llu", name,
601
 
                               (unsigned long long)box1->highest_modseq,
602
 
                               (unsigned long long)box2->highest_modseq);
603
 
                }
604
 
                return TRUE;
605
 
        }
606
 
        if (box1->message_count != box2->message_count) {
607
 
                if (brain->verbose) {
608
 
                        i_info("%s: message_count changed: %u != %u", name,
609
 
                               box1->message_count, box2->message_count);
610
 
                }
611
 
                return TRUE;
612
 
        }
613
 
        return FALSE;
614
 
}
615
 
 
616
 
static bool dsync_mailbox_has_changes(struct dsync_brain *brain,
617
 
                                      const struct dsync_mailbox *box1,
618
 
                                      const struct dsync_mailbox *box2)
619
 
{
620
 
        if (strcmp(box1->name, box2->name) != 0)
621
 
                return TRUE;
622
 
        return dsync_mailbox_has_changed_msgs(brain, box1, box2);
623
 
}
624
 
 
625
 
static void
626
 
dsync_brain_get_changed_mailboxes(struct dsync_brain *brain,
627
 
                                  ARRAY_TYPE(dsync_brain_mailbox) *brain_boxes,
628
 
                                  bool full_sync)
629
 
{
630
 
        struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
631
 
        struct dsync_brain_mailbox *brain_box;
632
 
        unsigned int src, dest, src_count, dest_count;
633
 
        bool src_deleted, dest_deleted;
634
 
        int ret;
635
 
 
636
 
        src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
637
 
        dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
638
 
 
639
 
        for (src = dest = 0; src < src_count && dest < dest_count; ) {
640
 
                src_deleted = (src_boxes[src]->flags &
641
 
                               DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
642
 
                dest_deleted = (dest_boxes[dest]->flags &
643
 
                                DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
644
 
 
645
 
                ret = dsync_mailbox_guid_cmp(src_boxes[src], dest_boxes[dest]);
646
 
                if (ret == 0) {
647
 
                        if ((full_sync ||
648
 
                             dsync_mailbox_has_changes(brain, src_boxes[src],
649
 
                                                       dest_boxes[dest])) &&
650
 
                            !src_deleted && !dest_deleted) {
651
 
                                brain_box = array_append_space(brain_boxes);
652
 
                                brain_box->box = *src_boxes[src];
653
 
 
654
 
                                brain_box->box.highest_modseq =
655
 
                                        I_MAX(src_boxes[src]->highest_modseq,
656
 
                                              dest_boxes[dest]->highest_modseq);
657
 
                                brain_box->box.uid_next =
658
 
                                        I_MAX(src_boxes[src]->uid_next,
659
 
                                              dest_boxes[dest]->uid_next);
660
 
                                brain_box->src = src_boxes[src];
661
 
                                brain_box->dest = dest_boxes[dest];
662
 
                        }
663
 
                        src++; dest++;
664
 
                } else if (ret < 0) {
665
 
                        /* exists only in source */
666
 
                        if (!src_deleted) {
667
 
                                brain_box = array_append_space(brain_boxes);
668
 
                                brain_box->box = *src_boxes[src];
669
 
                                brain_box->src = src_boxes[src];
670
 
                                if (brain->verbose) {
671
 
                                        i_info("%s: only in source (guid=%s)",
672
 
                                               brain_box->box.name,
673
 
                                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
674
 
                                }
675
 
                        }
676
 
                        src++;
677
 
                } else {
678
 
                        /* exists only in dest */
679
 
                        if (!dest_deleted) {
680
 
                                brain_box = array_append_space(brain_boxes);
681
 
                                brain_box->box = *dest_boxes[dest];
682
 
                                brain_box->dest = dest_boxes[dest];
683
 
                                if (brain->verbose) {
684
 
                                        i_info("%s: only in dest (guid=%s)",
685
 
                                               brain_box->box.name,
686
 
                                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
687
 
                                }
688
 
                        }
689
 
                        dest++;
690
 
                }
691
 
        }
692
 
        for (; src < src_count; src++) {
693
 
                if ((src_boxes[src]->flags &
694
 
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
695
 
                        continue;
696
 
 
697
 
                brain_box = array_append_space(brain_boxes);
698
 
                brain_box->box = *src_boxes[src];
699
 
                brain_box->src = src_boxes[src];
700
 
                if (brain->verbose) {
701
 
                        i_info("%s: only in source (guid=%s)",
702
 
                               brain_box->box.name,
703
 
                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
704
 
                }
705
 
        }
706
 
        for (; dest < dest_count; dest++) {
707
 
                if ((dest_boxes[dest]->flags &
708
 
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
709
 
                        continue;
710
 
 
711
 
                brain_box = array_append_space(brain_boxes);
712
 
                brain_box->box = *dest_boxes[dest];
713
 
                brain_box->dest = dest_boxes[dest];
714
 
                if (brain->verbose) {
715
 
                        i_info("%s: only in dest (guid=%s)",
716
 
                               brain_box->box.name,
717
 
                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
718
 
                }
719
 
        }
720
 
}
721
 
 
722
 
static bool dsync_brain_sync_msgs(struct dsync_brain *brain)
723
 
{
724
 
        ARRAY_TYPE(dsync_brain_mailbox) mailboxes;
725
 
        pool_t pool;
 
268
static int
 
269
dsync_brain_lock(struct dsync_brain *brain, const char *remote_hostname)
 
270
{
 
271
        struct stat st1, st2;
 
272
        const char *home;
 
273
        int ret;
 
274
 
 
275
        if ((ret = strcmp(remote_hostname, my_hostdomain())) < 0) {
 
276
                /* locking done by remote */
 
277
                return 0;
 
278
        }
 
279
        if (ret == 0 && !brain->master_brain) {
 
280
                /* running dsync within the same server.
 
281
                   locking done by master brain. */
 
282
                return 0;
 
283
        }
 
284
 
 
285
        if ((ret = mail_user_get_home(brain->user, &home)) < 0) {
 
286
                i_error("Couldn't look up user's home dir");
 
287
                return -1;
 
288
        }
 
289
        if (ret == 0) {
 
290
                i_error("User has no home directory");
 
291
                return -1;
 
292
        }
 
293
 
 
294
        brain->lock_path = p_strconcat(brain->pool, home,
 
295
                                       "/"DSYNC_LOCK_FILENAME, NULL);
 
296
        for (;;) {
 
297
                brain->lock_fd = creat(brain->lock_path, 0600);
 
298
                if (brain->lock_fd == -1) {
 
299
                        i_error("Couldn't create lock %s: %m",
 
300
                                brain->lock_path);
 
301
                        return -1;
 
302
                }
 
303
 
 
304
                if (file_wait_lock(brain->lock_fd, brain->lock_path, F_WRLCK,
 
305
                                   FILE_LOCK_METHOD_FCNTL, brain->lock_timeout,
 
306
                                   &brain->lock) <= 0) {
 
307
                        if (errno == EAGAIN) {
 
308
                                i_error("Couldn't lock %s: Timed out after %u seconds",
 
309
                                        brain->lock_path, brain->lock_timeout);
 
310
                        } else {
 
311
                                i_error("Couldn't lock %s: %m", brain->lock_path);
 
312
                        }
 
313
                        break;
 
314
                }
 
315
                if (fstat(brain->lock_fd, &st1) < 0) {
 
316
                        if (errno != ESTALE) {
 
317
                                i_error("fstat(%s) failed: %m", brain->lock_path);
 
318
                                break;
 
319
                        }
 
320
                } else if (stat(brain->lock_path, &st2) < 0) {
 
321
                        if (errno != ENOENT) {
 
322
                                i_error("stat(%s) failed: %m", brain->lock_path);
 
323
                                break;
 
324
                        }
 
325
                } else if (st1.st_ino == st2.st_ino) {
 
326
                        /* success */
 
327
                        return 0;
 
328
                }
 
329
                /* file was recreated, try again */
 
330
                i_close_fd(&brain->lock_fd);
 
331
        }
 
332
        i_close_fd(&brain->lock_fd);
 
333
        return -1;
 
334
}
 
335
 
 
336
static bool dsync_brain_master_recv_handshake(struct dsync_brain *brain)
 
337
{
 
338
        const struct dsync_ibc_settings *ibc_set;
 
339
 
 
340
        i_assert(brain->master_brain);
 
341
 
 
342
        if (dsync_ibc_recv_handshake(brain->ibc, &ibc_set) == 0)
 
343
                return FALSE;
 
344
 
 
345
        if (brain->lock_timeout > 0) {
 
346
                if (dsync_brain_lock(brain, ibc_set->hostname) < 0) {
 
347
                        brain->failed = TRUE;
 
348
                        return FALSE;
 
349
                }
 
350
        }
 
351
 
 
352
        brain->state = brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE ?
 
353
                DSYNC_STATE_MASTER_SEND_LAST_COMMON :
 
354
                DSYNC_STATE_SEND_MAILBOX_TREE;
 
355
        return TRUE;
 
356
}
 
357
 
 
358
static bool dsync_brain_slave_recv_handshake(struct dsync_brain *brain)
 
359
{
 
360
        const struct dsync_ibc_settings *ibc_set;
 
361
 
 
362
        i_assert(!brain->master_brain);
 
363
 
 
364
        if (dsync_ibc_recv_handshake(brain->ibc, &ibc_set) == 0)
 
365
                return FALSE;
 
366
 
 
367
        if (ibc_set->lock_timeout > 0) {
 
368
                brain->lock_timeout = ibc_set->lock_timeout;
 
369
                if (dsync_brain_lock(brain, ibc_set->hostname) < 0) {
 
370
                        brain->failed = TRUE;
 
371
                        return FALSE;
 
372
                }
 
373
        }
 
374
 
 
375
        if (ibc_set->sync_ns_prefix != NULL) {
 
376
                brain->sync_ns = mail_namespace_find(brain->user->namespaces,
 
377
                                                     ibc_set->sync_ns_prefix);
 
378
        }
 
379
        brain->sync_box = p_strdup(brain->pool, ibc_set->sync_box);
 
380
        brain->exclude_mailboxes = ibc_set->exclude_mailboxes == NULL ? NULL :
 
381
                p_strarray_dup(brain->pool, ibc_set->exclude_mailboxes);
 
382
        memcpy(brain->sync_box_guid, ibc_set->sync_box_guid,
 
383
               sizeof(brain->sync_box_guid));
 
384
        i_assert(brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_UNKNOWN);
 
385
        brain->sync_type = ibc_set->sync_type;
 
386
        dsync_brain_set_flags(brain, ibc_set->brain_flags);
 
387
 
 
388
        dsync_brain_mailbox_trees_init(brain);
 
389
 
 
390
        if (brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE)
 
391
                brain->state = DSYNC_STATE_SLAVE_RECV_LAST_COMMON;
 
392
        else
 
393
                brain->state = DSYNC_STATE_SEND_MAILBOX_TREE;
 
394
        return TRUE;
 
395
}
 
396
 
 
397
static void dsync_brain_master_send_last_common(struct dsync_brain *brain)
 
398
{
 
399
        struct dsync_mailbox_state *state;
 
400
        uint8_t *guid;
 
401
        enum dsync_ibc_send_ret ret = DSYNC_IBC_SEND_RET_OK;
 
402
 
 
403
        i_assert(brain->master_brain);
 
404
 
 
405
        if (brain->mailbox_states_iter == NULL) {
 
406
                brain->mailbox_states_iter =
 
407
                        hash_table_iterate_init(brain->mailbox_states);
 
408
        }
 
409
 
 
410
        for (;;) {
 
411
                if (ret == DSYNC_IBC_SEND_RET_FULL)
 
412
                        return;
 
413
                if (!hash_table_iterate(brain->mailbox_states_iter,
 
414
                                        brain->mailbox_states, &guid, &state))
 
415
                        break;
 
416
                ret = dsync_ibc_send_mailbox_state(brain->ibc, state);
 
417
        }
 
418
        hash_table_iterate_deinit(&brain->mailbox_states_iter);
 
419
 
 
420
        dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX_STATE);
 
421
        brain->state = DSYNC_STATE_SEND_MAILBOX_TREE;
 
422
}
 
423
 
 
424
static void dsync_mailbox_state_add(struct dsync_brain *brain,
 
425
                                    const struct dsync_mailbox_state *state)
 
426
{
 
427
        struct dsync_mailbox_state *dupstate;
 
428
        uint8_t *guid_p;
 
429
 
 
430
        dupstate = p_new(brain->pool, struct dsync_mailbox_state, 1);
 
431
        *dupstate = *state;
 
432
        guid_p = dupstate->mailbox_guid;
 
433
        hash_table_insert(brain->mailbox_states, guid_p, dupstate);
 
434
}
 
435
 
 
436
static bool dsync_brain_slave_recv_last_common(struct dsync_brain *brain)
 
437
{
 
438
        struct dsync_mailbox_state state;
 
439
        enum dsync_ibc_recv_ret ret;
 
440
        bool changed = FALSE;
 
441
 
 
442
        i_assert(!brain->master_brain);
 
443
 
 
444
        while ((ret = dsync_ibc_recv_mailbox_state(brain->ibc, &state)) > 0) {
 
445
                dsync_mailbox_state_add(brain, &state);
 
446
                changed = TRUE;
 
447
        }
 
448
        if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
 
449
                brain->state = DSYNC_STATE_SEND_MAILBOX_TREE;
 
450
                changed = TRUE;
 
451
        }
 
452
        return changed;
 
453
}
 
454
 
 
455
static bool dsync_brain_run_real(struct dsync_brain *brain, bool *changed_r)
 
456
{
 
457
        enum dsync_state orig_state = brain->state;
 
458
        enum dsync_box_state orig_box_recv_state = brain->box_recv_state;
 
459
        enum dsync_box_state orig_box_send_state = brain->box_send_state;
 
460
        bool changed = FALSE, ret = TRUE;
 
461
 
 
462
        if (brain->failed)
 
463
                return FALSE;
 
464
 
 
465
        if (brain->debug) {
 
466
                i_debug("brain %c: in state=%s", brain->master_brain ? 'M' : 'S',
 
467
                        dsync_state_names[brain->state]);
 
468
        }
 
469
        switch (brain->state) {
 
470
        case DSYNC_STATE_MASTER_RECV_HANDSHAKE:
 
471
                changed = dsync_brain_master_recv_handshake(brain);
 
472
                break;
 
473
        case DSYNC_STATE_SLAVE_RECV_HANDSHAKE:
 
474
                changed = dsync_brain_slave_recv_handshake(brain);
 
475
                break;
 
476
        case DSYNC_STATE_MASTER_SEND_LAST_COMMON:
 
477
                dsync_brain_master_send_last_common(brain);
 
478
                changed = TRUE;
 
479
                break;
 
480
        case DSYNC_STATE_SLAVE_RECV_LAST_COMMON:
 
481
                changed = dsync_brain_slave_recv_last_common(brain);
 
482
                break;
 
483
        case DSYNC_STATE_SEND_MAILBOX_TREE:
 
484
                dsync_brain_send_mailbox_tree(brain);
 
485
                changed = TRUE;
 
486
                break;
 
487
        case DSYNC_STATE_RECV_MAILBOX_TREE:
 
488
                changed = dsync_brain_recv_mailbox_tree(brain);
 
489
                break;
 
490
        case DSYNC_STATE_SEND_MAILBOX_TREE_DELETES:
 
491
                dsync_brain_send_mailbox_tree_deletes(brain);
 
492
                changed = TRUE;
 
493
                break;
 
494
        case DSYNC_STATE_RECV_MAILBOX_TREE_DELETES:
 
495
                changed = dsync_brain_recv_mailbox_tree_deletes(brain);
 
496
                break;
 
497
        case DSYNC_STATE_MASTER_SEND_MAILBOX:
 
498
                dsync_brain_master_send_mailbox(brain);
 
499
                changed = TRUE;
 
500
                break;
 
501
        case DSYNC_STATE_SLAVE_RECV_MAILBOX:
 
502
                changed = dsync_brain_slave_recv_mailbox(brain);
 
503
                break;
 
504
        case DSYNC_STATE_SYNC_MAILS:
 
505
                changed = dsync_brain_sync_mails(brain);
 
506
                break;
 
507
        case DSYNC_STATE_DONE:
 
508
                changed = TRUE;
 
509
                ret = FALSE;
 
510
                break;
 
511
        }
 
512
        if (brain->debug) {
 
513
                i_debug("brain %c: out state=%s changed=%d",
 
514
                        brain->master_brain ? 'M' : 'S',
 
515
                        dsync_state_names[brain->state], changed);
 
516
        }
 
517
        if (brain->verbose_proctitle) {
 
518
                if (orig_state != brain->state ||
 
519
                    orig_box_recv_state != brain->box_recv_state ||
 
520
                    orig_box_send_state != brain->box_send_state ||
 
521
                    ++brain->proctitle_update_counter % 100 == 0)
 
522
                        process_title_set(dsync_brain_get_proctitle(brain));
 
523
        }
 
524
        *changed_r = changed;
 
525
        return brain->failed ? FALSE : ret;
 
526
}
 
527
 
 
528
bool dsync_brain_run(struct dsync_brain *brain, bool *changed_r)
 
529
{
726
530
        bool ret;
727
531
 
728
 
        pool = pool_alloconly_create(MEMPOOL_GROWING"dsync changed mailboxes",
729
 
                                     10240);
730
 
        p_array_init(&mailboxes, pool, 128);
731
 
        dsync_brain_get_changed_mailboxes(brain, &mailboxes,
732
 
                (brain->flags & DSYNC_BRAIN_FLAG_FULL_SYNC) != 0);
733
 
        if (array_count(&mailboxes) > 0) {
734
 
                brain->mailbox_sync =
735
 
                        dsync_brain_msg_sync_init(brain, &mailboxes);
736
 
                dsync_brain_msg_sync_more(brain->mailbox_sync);
737
 
                ret = TRUE;
738
 
        } else {
739
 
                ret = FALSE;
 
532
        *changed_r = FALSE;
 
533
 
 
534
        if (dsync_ibc_has_failed(brain->ibc)) {
 
535
                brain->failed = TRUE;
 
536
                return FALSE;
740
537
        }
741
 
        pool_unref(&pool);
 
538
 
 
539
        T_BEGIN {
 
540
                ret = dsync_brain_run_real(brain, changed_r);
 
541
        } T_END;
742
542
        return ret;
743
543
}
744
544
 
745
 
static void
746
 
dsync_brain_sync_rename_mailbox(struct dsync_brain *brain,
747
 
                                const struct dsync_brain_mailbox *mailbox)
748
 
{
749
 
        if (mailbox->src->last_change > mailbox->dest->last_change ||
750
 
            brain->backup) {
751
 
                dsync_worker_rename_mailbox(brain->dest_worker,
752
 
                                            &mailbox->box.mailbox_guid,
753
 
                                            mailbox->src);
754
 
        } else {
755
 
                dsync_worker_rename_mailbox(brain->src_worker,
756
 
                                            &mailbox->box.mailbox_guid,
757
 
                                            mailbox->dest);
758
 
        }
759
 
}
760
 
 
761
 
static void
762
 
dsync_brain_sync_update_mailboxes(struct dsync_brain *brain)
763
 
{
764
 
        const struct dsync_brain_mailbox *mailbox;
765
 
        bool failed_changes = dsync_brain_has_unexpected_changes(brain) ||
766
 
                dsync_worker_has_failed(brain->src_worker) ||
767
 
                dsync_worker_has_failed(brain->dest_worker);
768
 
 
769
 
        if (brain->mailbox_sync == NULL) {
770
 
                /* no mailboxes changed */
771
 
                return;
772
 
        }
773
 
 
774
 
        array_foreach(&brain->mailbox_sync->mailboxes, mailbox) {
775
 
                /* don't update mailboxes if any changes had failed.
776
 
                   for example if some messages couldn't be saved, we don't
777
 
                   want to increase the next_uid to jump over them */
778
 
                if (!brain->backup && !failed_changes) {
779
 
                        dsync_worker_update_mailbox(brain->src_worker,
780
 
                                                    &mailbox->box);
781
 
                }
782
 
                if (!failed_changes) {
783
 
                        dsync_worker_update_mailbox(brain->dest_worker,
784
 
                                                    &mailbox->box);
785
 
                }
786
 
 
787
 
                if (mailbox->src != NULL && mailbox->dest != NULL &&
788
 
                    strcmp(mailbox->src->name, mailbox->dest->name) != 0)
789
 
                        dsync_brain_sync_rename_mailbox(brain, mailbox);
790
 
        }
791
 
}
792
 
 
793
 
static void dsync_brain_worker_finished(bool success, void *context)
794
 
{
795
 
        struct dsync_brain *brain = context;
796
 
 
797
 
        switch (brain->state) {
798
 
        case DSYNC_STATE_SYNC_MSGS_FLUSH:
799
 
        case DSYNC_STATE_SYNC_MSGS_FLUSH2:
800
 
        case DSYNC_STATE_SYNC_FLUSH:
801
 
        case DSYNC_STATE_SYNC_FLUSH2:
802
 
                break;
803
 
        default:
804
 
                i_panic("dsync brain state=%d", brain->state);
805
 
        }
806
 
 
807
 
        if (!success)
808
 
                dsync_brain_fail(brain);
809
 
 
810
 
        brain->state++;
811
 
        if (brain->to == NULL && (brain->flags & DSYNC_BRAIN_FLAG_LOCAL) == 0)
812
 
                brain->to = timeout_add(0, dsync_brain_sync, brain);
813
 
}
814
 
 
815
 
void dsync_brain_sync(struct dsync_brain *brain)
816
 
{
817
 
        if (dsync_worker_has_failed(brain->src_worker) ||
818
 
            dsync_worker_has_failed(brain->dest_worker)) {
819
 
                /* we can't safely continue, especially with backup */
820
 
                return;
821
 
        }
822
 
 
823
 
        if (brain->to != NULL)
824
 
                timeout_remove(&brain->to);
825
 
        switch (brain->state) {
826
 
        case DSYNC_STATE_GET_MAILBOXES:
827
 
                i_assert(brain->src_mailbox_list == NULL);
828
 
                brain->src_mailbox_list =
829
 
                        dsync_brain_mailbox_list_init(brain, brain->src_worker);
830
 
                brain->dest_mailbox_list =
831
 
                        dsync_brain_mailbox_list_init(brain, brain->dest_worker);
832
 
                dsync_worker_mailbox_input(brain->src_mailbox_list);
833
 
                dsync_worker_mailbox_input(brain->dest_mailbox_list);
834
 
                break;
835
 
        case DSYNC_STATE_GET_SUBSCRIPTIONS:
836
 
                i_assert(brain->src_subs_list == NULL);
837
 
                brain->src_subs_list =
838
 
                        dsync_brain_subs_list_init(brain, brain->src_worker);
839
 
                brain->dest_subs_list =
840
 
                        dsync_brain_subs_list_init(brain, brain->dest_worker);
841
 
                dsync_worker_subs_input(brain->src_subs_list);
842
 
                dsync_worker_subs_input(brain->dest_subs_list);
843
 
                break;
844
 
        case DSYNC_STATE_SYNC_MAILBOXES:
845
 
                dsync_worker_set_input_callback(brain->src_worker, NULL, NULL);
846
 
                dsync_worker_set_input_callback(brain->dest_worker, NULL, NULL);
847
 
 
848
 
                dsync_brain_sync_mailboxes(brain);
849
 
                dsync_brain_sync_dirs(brain);
850
 
                brain->state++;
851
 
                /* fall through */
852
 
        case DSYNC_STATE_SYNC_SUBSCRIPTIONS:
853
 
                dsync_brain_sync_subscriptions(brain);
854
 
                brain->state++;
855
 
                /* fall through */
856
 
        case DSYNC_STATE_SYNC_MSGS:
857
 
                if (dsync_brain_sync_msgs(brain))
858
 
                        break;
859
 
                brain->state++;
860
 
                /* no mailboxes changed */
861
 
        case DSYNC_STATE_SYNC_MSGS_FLUSH:
862
 
                /* wait until all saves are done, so we don't try to close
863
 
                   the mailbox too early */
864
 
                dsync_worker_finish(brain->src_worker,
865
 
                                    dsync_brain_worker_finished, brain);
866
 
                dsync_worker_finish(brain->dest_worker,
867
 
                                    dsync_brain_worker_finished, brain);
868
 
                break;
869
 
        case DSYNC_STATE_SYNC_MSGS_FLUSH2:
870
 
                break;
871
 
        case DSYNC_STATE_SYNC_UPDATE_MAILBOXES:
872
 
                dsync_brain_sync_update_mailboxes(brain);
873
 
                brain->state++;
874
 
                /* fall through */
875
 
        case DSYNC_STATE_SYNC_FLUSH:
876
 
                dsync_worker_finish(brain->src_worker,
877
 
                                    dsync_brain_worker_finished, brain);
878
 
                dsync_worker_finish(brain->dest_worker,
879
 
                                    dsync_brain_worker_finished, brain);
880
 
                break;
881
 
        case DSYNC_STATE_SYNC_FLUSH2:
882
 
                break;
883
 
        case DSYNC_STATE_SYNC_END:
884
 
                io_loop_stop(current_ioloop);
885
 
                break;
886
 
        default:
887
 
                i_unreached();
888
 
        }
889
 
}
890
 
 
891
 
void dsync_brain_sync_all(struct dsync_brain *brain)
892
 
{
893
 
        enum dsync_state old_state;
894
 
 
895
 
        while (brain->state != DSYNC_STATE_SYNC_END) {
896
 
                old_state = brain->state;
897
 
                dsync_brain_sync(brain);
898
 
 
899
 
                if (dsync_brain_has_failed(brain))
900
 
                        break;
901
 
 
902
 
                i_assert(brain->state != old_state);
903
 
        }
 
545
void dsync_brain_get_state(struct dsync_brain *brain, string_t *output)
 
546
{
 
547
        struct hash_iterate_context *iter;
 
548
        struct dsync_mailbox_node *node;
 
549
        const struct dsync_mailbox_state *new_state;
 
550
        struct dsync_mailbox_state *state;
 
551
        const uint8_t *guid_p;
 
552
        uint8_t *guid;
 
553
 
 
554
        /* update mailbox states */
 
555
        array_foreach(&brain->remote_mailbox_states, new_state) {
 
556
                guid_p = new_state->mailbox_guid;
 
557
                state = hash_table_lookup(brain->mailbox_states, guid_p);
 
558
                if (state != NULL)
 
559
                        *state = *new_state;
 
560
                else
 
561
                        dsync_mailbox_state_add(brain, new_state);
 
562
        }
 
563
 
 
564
        /* remove nonexistent mailboxes */
 
565
        iter = hash_table_iterate_init(brain->mailbox_states);
 
566
        while (hash_table_iterate(iter, brain->mailbox_states, &guid, &state)) {
 
567
                node = dsync_mailbox_tree_lookup_guid(brain->local_mailbox_tree,
 
568
                                                      guid);
 
569
                if (node == NULL ||
 
570
                    node->existence != DSYNC_MAILBOX_NODE_EXISTS)
 
571
                        hash_table_remove(brain->mailbox_states, guid);
 
572
        }
 
573
        hash_table_iterate_deinit(&iter);
 
574
 
 
575
        dsync_mailbox_states_export(brain->mailbox_states, output);
 
576
}
 
577
 
 
578
enum dsync_brain_sync_type dsync_brain_get_sync_type(struct dsync_brain *brain)
 
579
{
 
580
        return brain->sync_type;
 
581
}
 
582
 
 
583
bool dsync_brain_has_failed(struct dsync_brain *brain)
 
584
{
 
585
        return brain->failed;
904
586
}
905
587
 
906
588
bool dsync_brain_has_unexpected_changes(struct dsync_brain *brain)
907
589
{
908
 
        return brain->unexpected_changes ||
909
 
                dsync_worker_has_unexpected_changes(brain->src_worker) ||
910
 
                dsync_worker_has_unexpected_changes(brain->dest_worker);
 
590
        return brain->changes_during_sync;
911
591
}
912
592
 
913
 
bool dsync_brain_has_failed(struct dsync_brain *brain)
 
593
bool dsync_brain_want_namespace(struct dsync_brain *brain,
 
594
                                struct mail_namespace *ns)
914
595
{
915
 
        return brain->failed ||
916
 
                dsync_worker_has_failed(brain->src_worker) ||
917
 
                dsync_worker_has_failed(brain->dest_worker);
 
596
        if (brain->sync_ns != NULL)
 
597
                return brain->sync_ns == ns;
 
598
        if (ns->alias_for != NULL) {
 
599
                /* always skip aliases */
 
600
                return FALSE;
 
601
        }
 
602
        if (brain->sync_visible_namespaces) {
 
603
                if ((ns->flags & NAMESPACE_FLAG_HIDDEN) == 0)
 
604
                        return TRUE;
 
605
                if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
 
606
                                  NAMESPACE_FLAG_LIST_CHILDREN)) != 0)
 
607
                        return TRUE;
 
608
                return FALSE;
 
609
        } else {
 
610
                return strcmp(ns->unexpanded_set->location,
 
611
                              SETTING_STRVAR_UNEXPANDED) == 0;
 
612
        }
918
613
}