<commit_before>/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "hash.h"
#include "hostpid.h"
#include "mail-namespace.h"
#include "dsync-mailbox-tree.h"
#include "dsync-ibc.h"
#include "dsync-brain-private.h"

#include <sys/stat.h>

static const char *dsync_state_names[DSYNC_STATE_DONE+1] = {
	"recv_handshake",
	"send_last_common",
	"recv_last_common",
	"send_mailbox_tree",
	"send_mailbox_tree_deletes",
	"recv_mailbox_tree",
	"recv_mailbox_tree_deletes",
	"master_send_mailbox",
	"slave_recv_mailbox",
	"sync_mails",
	"done"
};

static void dsync_brain_run_io(void *context)
{
	struct dsync_brain *brain = context;
	bool changed, try_pending;

	if (dsync_ibc_has_failed(brain->ibc)) {
		io_loop_stop(current_ioloop);
		brain->failed = TRUE;
		return;
	}

	try_pending = TRUE;
	do {
		if (!dsync_brain_run(brain, &changed)) {
			io_loop_stop(current_ioloop);
			break;
		}
		if (changed)
			try_pending = TRUE;
		else if (try_pending) {
			if (dsync_ibc_has_pending_data(brain->ibc))
				changed = TRUE;
			try_pending = FALSE;
		}
	} while (changed);
}

static struct dsync_brain *
dsync_brain_common_init(struct mail_user *user, struct dsync_ibc *ibc)
{
	struct dsync_brain *brain;
	pool_t pool;

	pool = pool_alloconly_create("dsync brain", 10240);
	brain = p_new(pool, struct dsync_brain, 1);
	brain->pool = pool;
	brain->user = user;
	brain->ibc = ibc;
	brain->sync_type = DSYNC_BRAIN_SYNC_TYPE_UNKNOWN;
	brain->lock_fd = -1;
	hash_table_create(&brain->mailbox_states, pool, 0,
			  guid_128_hash, guid_128_cmp);
	p_array_init(&brain->remote_mailbox_states, pool, 64);
	return brain;
}

static void
dsync_brain_set_flags(struct dsync_brain *brain, enum dsync_brain_flags flags)
{
	brain->mail_requests =
		(flags & DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS) != 0;
	brain->backup_send = (flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0;
	brain->backup_recv = (flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0;
	brain->debug = (flags & DSYNC_BRAIN_FLAG_DEBUG) != 0;
	brain->sync_all_namespaces =
		(flags & DSYNC_BRAIN_FLAG_SYNC_ALL_NAMESPACES) != 0;
}

struct dsync_brain *
dsync_brain_master_init(struct mail_user *user, struct dsync_ibc *ibc,
			struct mail_namespace *sync_ns, const char *sync_box,
			enum dsync_brain_sync_type sync_type,
			enum dsync_brain_flags flags, unsigned int lock_timeout,
			const char *state)
{
	struct dsync_ibc_settings ibc_set;
	struct dsync_brain *brain;
	const char *error;

	i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_UNKNOWN);
	i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE || *state != '\0');

	brain = dsync_brain_common_init(user, ibc);
	brain->sync_type = sync_type;
	if (sync_ns != NULL)
		brain->sync_ns = sync_ns;
	brain->sync_box = p_strdup(brain->pool, sync_box);
	brain->lock_timeout = lock_timeout;
	brain->master_brain = TRUE;
	dsync_brain_set_flags(brain, flags);

	if (sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE &&
	    dsync_mailbox_states_import(brain->mailbox_states,
					brain->pool, state, &error) < 0) {
		hash_table_clear(brain->mailbox_states, FALSE);
		i_error("Saved sync state is invalid, "
			"falling back to full sync: %s", error);
		brain->sync_type = sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL;
	}
	dsync_brain_mailbox_trees_init(brain);

	memset(&ibc_set, 0, sizeof(ibc_set));
	ibc_set.hostname = my_hostdomain();
	ibc_set.sync_ns_prefix = sync_ns == NULL ? NULL : sync_ns->prefix;
	ibc_set.sync_box = sync_box;
	ibc_set.sync_type = sync_type;
	ibc_set.lock_timeout = lock_timeout;
	/* reverse the backup direction for the slave */
	ibc_set.brain_flags = flags & ~(DSYNC_BRAIN_FLAG_BACKUP_SEND |
					DSYNC_BRAIN_FLAG_BACKUP_RECV);
	if ((flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0)
		ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV;
	else if ((flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0)
		ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND;
	dsync_ibc_send_handshake(ibc, &ibc_set);

	dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
	brain->state = DSYNC_STATE_MASTER_RECV_HANDSHAKE;
	return brain;
}

struct dsync_brain *
dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc)
{
	struct dsync_ibc_settings ibc_set;
	struct dsync_brain *brain;

	brain = dsync_brain_common_init(user, ibc);
	brain->state = DSYNC_STATE_SLAVE_RECV_HANDSHAKE;

	memset(&ibc_set, 0, sizeof(ibc_set));
	ibc_set.hostname = my_hostdomain();
	dsync_ibc_send_handshake(ibc, &ibc_set);

	dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
	return brain;
}

int dsync_brain_deinit(struct dsync_brain **_brain)
{
	struct dsync_brain *brain = *_brain;
	int ret;

	*_brain = NULL;

	if (dsync_ibc_has_failed(brain->ibc) ||
	    brain->state != DSYNC_STATE_DONE)
		brain->failed = TRUE;
	dsync_ibc_close_mail_streams(brain->ibc);

	if (brain->box != NULL)
		dsync_brain_sync_mailbox_deinit(brain);
	if (brain->local_tree_iter != NULL)
		dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
	if (brain->mailbox_states_iter != NULL)
		hash_table_iterate_deinit(&brain->mailbox_states_iter);
	hash_table_destroy(&brain->mailbox_states);

	if (brain->lock_fd != -1) {
		/* unlink the lock file before it gets unlocked */
		if (unlink(brain->lock_path) < 0)
			i_error("unlink(%s) failed: %m", brain->lock_path);
		file_lock_free(&brain->lock);
		i_close_fd(&brain->lock_fd);
	}

	ret = brain->failed ? -1 : 0;
	pool_unref(&brain->pool);
	return ret;
}

static int
dsync_brain_lock(struct dsync_brain *brain, const char *remote_hostname)
{
	struct stat st1, st2;
	const char *home;
	int ret;

	if ((ret = strcmp(remote_hostname, my_hostname)) < 0) {
		/* locking done by remote */
		return 0;
	}
	if (ret == 0 && !brain->master_brain) {
		/* running dsync within the same server.
		   locking done by master brain. */
		return 0;
	}

	if ((ret = mail_user_get_home(brain->user, &home)) < 0) {
		i_error("Couldn't look up user's home dir");
		return -1;
	}
	if (ret == 0) {
		i_error("User has no home directory");
		return -1;
	}
<commit_msg>dsync: Fix to checking which side should do the locking.<commit_after>/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "hash.h"
#include "hostpid.h"
#include "mail-namespace.h"
#include "dsync-mailbox-tree.h"
#include "dsync-ibc.h"
#include "dsync-brain-private.h"

#include <sys/stat.h>

static const char *dsync_state_names[DSYNC_STATE_DONE+1] = {
	"recv_handshake",
	"send_last_common",
	"recv_last_common",
	"send_mailbox_tree",
	"send_mailbox_tree_deletes",
	"recv_mailbox_tree",
	"recv_mailbox_tree_deletes",
	"master_send_mailbox",
	"slave_recv_mailbox",
	"sync_mails",
	"done"
};

static void dsync_brain_run_io(void *context)
{
	struct dsync_brain *brain = context;
	bool changed, try_pending;

	if (dsync_ibc_has_failed(brain->ibc)) {
		io_loop_stop(current_ioloop);
		brain->failed = TRUE;
		return;
	}

	try_pending = TRUE;
	do {
		if (!dsync_brain_run(brain, &changed)) {
			io_loop_stop(current_ioloop);
			break;
		}
		if (changed)
			try_pending = TRUE;
		else if (try_pending) {
			if (dsync_ibc_has_pending_data(brain->ibc))
				changed = TRUE;
			try_pending = FALSE;
		}
	} while (changed);
}

static struct dsync_brain *
dsync_brain_common_init(struct mail_user *user, struct dsync_ibc *ibc)
{
	struct dsync_brain *brain;
	pool_t pool;

	pool = pool_alloconly_create("dsync brain", 10240);
	brain = p_new(pool, struct dsync_brain, 1);
	brain->pool = pool;
	brain->user = user;
	brain->ibc = ibc;
	brain->sync_type = DSYNC_BRAIN_SYNC_TYPE_UNKNOWN;
	brain->lock_fd = -1;
	hash_table_create(&brain->mailbox_states, pool, 0,
			  guid_128_hash, guid_128_cmp);
	p_array_init(&brain->remote_mailbox_states, pool, 64);
	return brain;
}

static void
dsync_brain_set_flags(struct dsync_brain *brain, enum dsync_brain_flags flags)
{
	brain->mail_requests =
		(flags & DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS) != 0;
	brain->backup_send = (flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0;
	brain->backup_recv = (flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0;
	brain->debug = (flags & DSYNC_BRAIN_FLAG_DEBUG) != 0;
	brain->sync_all_namespaces =
		(flags & DSYNC_BRAIN_FLAG_SYNC_ALL_NAMESPACES) != 0;
}

struct dsync_brain *
dsync_brain_master_init(struct mail_user *user, struct dsync_ibc *ibc,
			struct mail_namespace *sync_ns, const char *sync_box,
			enum dsync_brain_sync_type sync_type,
			enum dsync_brain_flags flags, unsigned int lock_timeout,
			const char *state)
{
	struct dsync_ibc_settings ibc_set;
	struct dsync_brain *brain;
	const char *error;

	i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_UNKNOWN);
	i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE || *state != '\0');

	brain = dsync_brain_common_init(user, ibc);
	brain->sync_type = sync_type;
	if (sync_ns != NULL)
		brain->sync_ns = sync_ns;
	brain->sync_box = p_strdup(brain->pool, sync_box);
	brain->lock_timeout = lock_timeout;
	brain->master_brain = TRUE;
	dsync_brain_set_flags(brain, flags);

	if (sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE &&
	    dsync_mailbox_states_import(brain->mailbox_states,
					brain->pool, state, &error) < 0) {
		hash_table_clear(brain->mailbox_states, FALSE);
		i_error("Saved sync state is invalid, "
			"falling back to full sync: %s", error);
		brain->sync_type = sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL;
	}
	dsync_brain_mailbox_trees_init(brain);

	memset(&ibc_set, 0, sizeof(ibc_set));
	ibc_set.hostname = my_hostdomain();
	ibc_set.sync_ns_prefix = sync_ns == NULL ? NULL : sync_ns->prefix;
	ibc_set.sync_box = sync_box;
	ibc_set.sync_type = sync_type;
	ibc_set.lock_timeout = lock_timeout;
	/* reverse the backup direction for the slave */
	ibc_set.brain_flags = flags & ~(DSYNC_BRAIN_FLAG_BACKUP_SEND |
					DSYNC_BRAIN_FLAG_BACKUP_RECV);
	if ((flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0)
		ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV;
	else if ((flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0)
		ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND;
	dsync_ibc_send_handshake(ibc, &ibc_set);

	dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
	brain->state = DSYNC_STATE_MASTER_RECV_HANDSHAKE;
	return brain;
}

struct dsync_brain *
dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc)
{
	struct dsync_ibc_settings ibc_set;
	struct dsync_brain *brain;

	brain = dsync_brain_common_init(user, ibc);
	brain->state = DSYNC_STATE_SLAVE_RECV_HANDSHAKE;

	memset(&ibc_set, 0, sizeof(ibc_set));
	ibc_set.hostname = my_hostdomain();
	dsync_ibc_send_handshake(ibc, &ibc_set);

	dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
	return brain;
}

int dsync_brain_deinit(struct dsync_brain **_brain)
{
	struct dsync_brain *brain = *_brain;
	int ret;

	*_brain = NULL;

	if (dsync_ibc_has_failed(brain->ibc) ||
	    brain->state != DSYNC_STATE_DONE)
		brain->failed = TRUE;
	dsync_ibc_close_mail_streams(brain->ibc);

	if (brain->box != NULL)
		dsync_brain_sync_mailbox_deinit(brain);
	if (brain->local_tree_iter != NULL)
		dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
	if (brain->mailbox_states_iter != NULL)
		hash_table_iterate_deinit(&brain->mailbox_states_iter);
	hash_table_destroy(&brain->mailbox_states);

	if (brain->lock_fd != -1) {
		/* unlink the lock file before it gets unlocked */
		if (unlink(brain->lock_path) < 0)
			i_error("unlink(%s) failed: %m", brain->lock_path);
		file_lock_free(&brain->lock);
		i_close_fd(&brain->lock_fd);
	}

	ret = brain->failed ? -1 : 0;
	pool_unref(&brain->pool);
	return ret;
}

static int
dsync_brain_lock(struct dsync_brain *brain, const char *remote_hostname)
{
	struct stat st1, st2;
	const char *home;
	int ret;

	if ((ret = strcmp(remote_hostname, my_hostdomain())) < 0) {
		/* locking done by remote */
		return 0;
	}
	if (ret == 0 && !brain->master_brain) {
		/* running dsync within the same server.
		   locking done by master brain. */
		return 0;
	}

	if ((ret = mail_user_get_home(brain->user, &home)) < 0) {
		i_error("Couldn't look up user's home dir");
		return -1;
	}
	if (ret == 0) {
		i_error("User has no home directory");
		return -1;
	}

{'fraction_non_alphanumeric': 0.12228238234069137, 'fraction_numerical': 0.007663473552686381, 'mean_word_length': 5.423756019261638, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 99, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2739950', 'n_tokens_mistral': 5244, 'n_tokens_neox': 4845, 'n_words': 905}