mbox-lock.c revision 7c27b0ab7213121ea43994499c04059413f6d0f2
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen/* 0.1 .. 0.2msec */
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen/* lock methods to use in wanted order */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl"
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen/* lock timeout */
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen/* assume stale dotlock if mbox file hasn't changed for n seconds */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen int (*func)(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainenstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen { MBOX_LOCK_DOTLOCK, "dotlock", mbox_lock_dotlock },
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl },
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock },
1ff487015234b23c32cf8bb4c9f8c02922535b8eTimo Sirainen { MBOX_LOCK_LOCKF, "lockf", mbox_lock_lockf },
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
675b2b1c4587a79478062b05725da61afa5d8045Timo Sirainenstatic int lock_timeout, dotlock_change_timeout;
c2f2a6cdf7dd55a4aabe72495cabb4deaa59cffcTimo Sirainenstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
675b2b1c4587a79478062b05725da61afa5d8045Timo Sirainenstatic int mbox_unlock_files(struct mbox_lock_context *ctx);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic void mbox_read_lock_methods(const char *str, const char *env,
675b2b1c4587a79478062b05725da61afa5d8045Timo Sirainen const char *const *lock;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen for (type = 0; lock_data[type].name != NULL; type++) {
c2f2a6cdf7dd55a4aabe72495cabb4deaa59cffcTimo Sirainen if (strcasecmp(*lock, lock_data[type].name) == 0) {
f16712057c1b82c6d2a3a4267c4521d357cd4b4cTimo Sirainen for (i = 0; i < dest; i++) {
cc3da53f3a49304a251bfae88f814505326ac210Timo Sirainen i_fatal("%s: Duplicated value %s", env, *lock);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* @UNSAFE */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen const char *str;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (str == NULL) str = DEFAULT_READ_LOCK_METHODS;
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks);
cc3da53f3a49304a251bfae88f814505326ac210Timo Sirainen if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS;
cc3da53f3a49304a251bfae88f814505326ac210Timo Sirainen mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* check that read/write list orders match. write_locks must contain
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen at least read_locks and possibly more. */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (read_locks[r] != (enum mbox_lock_type)-1) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen i_fatal("mbox read/write lock list settings are invalid. "
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen "Lock ordering must be the same with both, "
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen "and write locks must contain all read locks "
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen "(and possibly more)");
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen lock_timeout = str == NULL ? DEFAULT_LOCK_TIMEOUT : atoi(str);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (ctx->checked_file || lock_type == F_UNLCK)
064ccad9cea2a75a0d6caa2c679b6cd22be58174Timo Sirainenstatic int dotlock_callback(unsigned int secs_left, int stale, void *context)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* get next index we wish to try locking. it's the one after
064ccad9cea2a75a0d6caa2c679b6cd22be58174Timo Sirainen dotlocking. */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (lock_types[i] != (enum mbox_lock_type)-1 &&
f16712057c1b82c6d2a3a4267c4521d357cd4b4cTimo Sirainen if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) {
f16712057c1b82c6d2a3a4267c4521d357cd4b4cTimo Sirainen /* we couldn't get fd lock -
f16712057c1b82c6d2a3a4267c4521d357cd4b4cTimo Sirainen it's really locked */
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen index_storage_lock_notify(&ctx->mbox->ibox, stale ?
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mbox_set_syscall_error(mbox, "file_dotlock_delete()");
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen ret = file_dotlock_create(&set, mbox->path, 0, &mbox->mbox_dotlock);
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mbox_set_syscall_error(mbox, "file_lock_dotlock()");
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mail_storage_set_error(STORAGE(mbox->storage),
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen "Timeout while waiting for lock");
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen while (flock(ctx->mbox->mbox_fd, lock_type | LOCK_NB) < 0) {
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainenstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
f3aff3c6bf32d5bb0a61993b76a3fbe5ad798c09Timo Sirainen if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen while (lockf(ctx->mbox->mbox_fd, lock_type, 0) < 0) {
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainenstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen /* usually we're waiting here, but if we came from
d96f86fb881c5b106649e8994ead1052acf24030Timo Sirainen mbox_lock_dotlock(), we just want to try locking */
043c8a96a035379bcba04f487d58457beefdfcaaTimo Sirainen while (fcntl(ctx->mbox->mbox_fd, wait_type, &fl) < 0) {
alarm(0);
if (next_alarm == 0)
alarm(0);
if (ret <= 0)
return ret;
for (i = 0; i < MBOX_LOCK_COUNT; i++)
if (ret <= 0) {
if (!drop_locks)
if (ret == 0) {
return ret;
if (drop_locks) {
unsigned int *lock_id_r)
int ret;
if (ret <= 0)
return ret;
int ret = 0;
return ret;
for (i = 0; i < MBOX_LOCK_COUNT; i++)