file-dotlock.c revision ac67a027f8db99d0b65c656fe1e4f26261ae750f
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (C) 2003 Timo Sirainen */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include "lib.h"
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include "str.h"
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen#include "hex-binary.h"
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include "hostpid.h"
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include "randgen.h"
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include "write-full.h"
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include "file-dotlock.h"
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include <stdio.h>
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include <stdlib.h>
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include <signal.h>
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#include <time.h>
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen#include <sys/stat.h>
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen#define DEFAULT_LOCK_SUFFIX ".lock"
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen/* 0.1 .. 0.2msec */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainenstruct lock_info {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen const char *path, *lock_path, *temp_path;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen unsigned int stale_timeout;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen unsigned int immediate_stale_timeout;
ebfbf5d78dcf95e8b176429f4b5b0694eb4e17d5Timo Sirainen int fd;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
ebfbf5d78dcf95e8b176429f4b5b0694eb4e17d5Timo Sirainen dev_t dev;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen ino_t ino;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen off_t size;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen time_t ctime, mtime;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen off_t last_size;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen time_t last_ctime, last_mtime;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen time_t last_change;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen pid_t pid;
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen time_t last_pid_check;
1777c974563740daac427d3ef738903d8f6ad7d0Timo Sirainen};
1777c974563740daac427d3ef738903d8f6ad7d0Timo Sirainen
1777c974563740daac427d3ef738903d8f6ad7d0Timo Sirainenstatic pid_t read_local_pid(const char *lock_path)
ebfbf5d78dcf95e8b176429f4b5b0694eb4e17d5Timo Sirainen{
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen char buf[512], *host;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen int fd;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen ssize_t ret;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen
ebfbf5d78dcf95e8b176429f4b5b0694eb4e17d5Timo Sirainen fd = open(lock_path, O_RDONLY);
ebfbf5d78dcf95e8b176429f4b5b0694eb4e17d5Timo Sirainen if (fd == -1)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return -1; /* ignore the actual error */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen /* read line */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen ret = read(fd, buf, sizeof(buf)-1);
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen (void)close(fd);
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (ret <= 0)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return -1;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen /* fix the string */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (buf[ret-1] == '\n')
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen ret--;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen buf[ret] = '\0';
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen /* it should contain pid:host */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen host = strchr(buf, ':');
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (host == NULL)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return -1;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen *host++ = '\0';
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen /* host must be ours */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (strcmp(host, my_hostname) != 0)
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return -1;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (!is_numeric(buf, '\0'))
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return -1;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return (pid_t)strtoul(buf, NULL, 0);
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen}
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainenstatic int check_lock(time_t now, struct lock_info *lock_info)
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen{
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen struct stat st;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (lstat(lock_info->lock_path, &st) < 0) {
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen if (errno != ENOENT) {
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen i_error("lstat(%s) failed: %m", lock_info->lock_path);
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return -1;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return 1;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen }
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (lock_info->immediate_stale_timeout != 0 &&
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen now > st.st_mtime + (time_t)lock_info->immediate_stale_timeout &&
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen now > st.st_ctime + (time_t)lock_info->immediate_stale_timeout) {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen /* old lock file */
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen return -1;
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return 1;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (lock_info->stale_timeout == 0) {
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen /* no change checking */
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return 0;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen }
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (lock_info->ino != st.st_ino ||
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen !CMP_DEV_T(lock_info->dev, st.st_dev) ||
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->ctime != st.st_ctime ||
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen lock_info->mtime != st.st_mtime ||
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen lock_info->size != st.st_size) {
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen /* either our first check or someone else got the lock file.
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen check if it contains a pid whose existence we can verify */
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen lock_info->dev = st.st_dev;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->ino = st.st_ino;
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen lock_info->ctime = st.st_ctime;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->mtime = st.st_mtime;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->size = st.st_size;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->pid = read_local_pid(lock_info->lock_path);
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen lock_info->last_change = now;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen }
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (lock_info->pid != -1) {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen /* we've local PID. Check if it exists. */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen if (lock_info->last_pid_check == now)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return 0;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (kill(lock_info->pid, 0) == 0 || errno != ESRCH)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return 0;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen /* doesn't exist - go ahead and delete */
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen return -1;
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return 1;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen }
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen if (lock_info->last_change != now) {
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (stat(lock_info->path, &st) < 0) {
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen if (errno == ENOENT) {
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen /* file doesn't exist. treat it as if
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen it hasn't changed */
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen } else {
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen i_error("stat(%s) failed: %m", lock_info->path);
916221f976af0ed8b397f06f4f381c0ac0be3b86Timo Sirainen return -1;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen } else if (lock_info->last_size != st.st_size ||
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->last_ctime != st.st_ctime ||
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->last_mtime != st.st_mtime) {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->last_change = now;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen lock_info->last_size = st.st_size;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen lock_info->last_ctime = st.st_ctime;
916221f976af0ed8b397f06f4f381c0ac0be3b86Timo Sirainen lock_info->last_mtime = st.st_mtime;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen }
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (now > lock_info->last_change + (time_t)lock_info->stale_timeout) {
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen /* no changes for a while, assume stale lock */
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
916221f976af0ed8b397f06f4f381c0ac0be3b86Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen return -1;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen }
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return 1;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen }
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen return 0;
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen}
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainenstatic int create_temp_file(const char *prefix, const char **path_r)
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen{
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen string_t *path;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen size_t len;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen struct stat st;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen char randbuf[8];
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen int fd;
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen path = t_str_new(256);
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen str_append(path, prefix);
e62f6437a4ff01d692a5a61369fe4168d69191edTimo Sirainen len = str_len(path);
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen
1b0cfbf3cc77a670b92fff5c30f7b1eb17a63ab1Timo Sirainen for (;;) {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen do {
74ab5ea66c0c4b388f1c774ae6a47ab94f1b4f18Timo Sirainen random_fill(randbuf, sizeof(randbuf));
str_truncate(path, len);
str_append(path,
binary_to_hex(randbuf, sizeof(randbuf)));
*path_r = str_c(path);
} while (stat(*path_r, &st) == 0);
if (errno != ENOENT) {
i_error("stat(%s) failed: %m", *path_r);
return -1;
}
fd = open(*path_r, O_RDWR | O_EXCL | O_CREAT, 0666);
if (fd != -1)
return fd;
if (errno != EEXIST) {
i_error("open(%s) failed: %m", *path_r);
return -1;
}
}
}
static int try_create_lock(struct lock_info *lock_info, const char *temp_prefix)
{
const char *str, *p;
if (lock_info->temp_path == NULL) {
/* we'll need our temp file first. */
if (temp_prefix == NULL) {
temp_prefix = t_strconcat(".temp.", my_hostname, ".",
my_pid, ".", NULL);
}
p = *temp_prefix == '/' ? NULL :
strrchr(lock_info->lock_path, '/');
if (p != NULL) {
str = t_strdup_until(lock_info->lock_path, p+1);
temp_prefix = t_strconcat(str, temp_prefix, NULL);
}
lock_info->fd = create_temp_file(temp_prefix, &str);
if (lock_info->fd == -1)
return -1;
lock_info->temp_path = str;
}
if (link(lock_info->temp_path, lock_info->lock_path) < 0) {
if (errno == EEXIST)
return 0;
i_error("link(%s, %s) failed: %m",
lock_info->temp_path, lock_info->lock_path);
return -1;
}
if (unlink(lock_info->temp_path) < 0 && errno != ENOENT) {
i_error("unlink(%s) failed: %m", lock_info->temp_path);
/* non-fatal, continue */
}
lock_info->temp_path = NULL;
return 1;
}
static int
dotlock_create(const char *path, const char *temp_prefix,
const char *lock_suffix, int checkonly, int *fd,
unsigned int timeout, unsigned int stale_timeout,
unsigned int immediate_stale_timeout,
int (*callback)(unsigned int secs_left, int stale,
void *context),
void *context)
{
const char *lock_path;
struct lock_info lock_info;
unsigned int stale_notify_threshold;
unsigned int change_secs, wait_left;
time_t now, max_wait_time, last_notify;
int do_wait, ret;
now = time(NULL);
lock_path = t_strconcat(path, lock_suffix, NULL);
stale_notify_threshold = stale_timeout / 2;
max_wait_time = now + timeout;
memset(&lock_info, 0, sizeof(lock_info));
lock_info.path = path;
lock_info.lock_path = lock_path;
lock_info.stale_timeout = stale_timeout;
lock_info.immediate_stale_timeout = immediate_stale_timeout;
lock_info.last_change = now;
lock_info.fd = -1;
last_notify = 0; do_wait = FALSE;
do {
if (do_wait) {
usleep(LOCK_RANDOM_USLEEP_TIME);
do_wait = FALSE;
}
ret = check_lock(now, &lock_info);
if (ret < 0)
break;
if (ret == 1) {
if (checkonly)
break;
ret = try_create_lock(&lock_info, temp_prefix);
if (ret != 0)
break;
}
do_wait = TRUE;
if (last_notify != now && callback != NULL) {
last_notify = now;
change_secs = now - lock_info.last_change;
wait_left = max_wait_time - now;
t_push();
if (change_secs >= stale_notify_threshold &&
change_secs <= wait_left) {
if (!callback(stale_timeout - change_secs,
TRUE, context)) {
/* we don't want to override */
lock_info.last_change = now;
}
} else {
(void)callback(wait_left, FALSE, context);
}
t_pop();
}
now = time(NULL);
} while (now < max_wait_time);
if (ret <= 0 && lock_info.fd != -1) {
int old_errno = errno;
(void)close(lock_info.fd);
lock_info.fd = -1;
errno = old_errno;
}
*fd = lock_info.fd;
if (ret == 0)
errno = EAGAIN;
return ret;
}
int file_lock_dotlock(const char *path, const char *temp_prefix, int checkonly,
unsigned int timeout, unsigned int stale_timeout,
unsigned int immediate_stale_timeout,
int (*callback)(unsigned int secs_left, int stale,
void *context),
void *context, struct dotlock *dotlock_r)
{
const char *lock_path, *str;
struct stat st;
int fd, ret;
lock_path = t_strconcat(path, DEFAULT_LOCK_SUFFIX, NULL);
ret = dotlock_create(path, temp_prefix, DEFAULT_LOCK_SUFFIX,
checkonly, &fd, timeout, stale_timeout,
immediate_stale_timeout, callback, context);
if (ret <= 0 || checkonly)
return ret;
/* write our pid and host, if possible */
str = t_strdup_printf("%s:%s", my_pid, my_hostname);
if (write_full(fd, str, strlen(str)) < 0) {
/* failed, leave it empty then */
if (ftruncate(fd, 0) < 0) {
i_error("ftruncate(%s) failed: %m", lock_path);
(void)close(fd);
return -1;
}
}
/* save the inode info after writing */
if (fstat(fd, &st) < 0) {
i_error("fstat(%s) failed: %m", lock_path);
(void)close(fd);
return -1;
}
if (close(fd) < 0) {
i_error("fstat(%s) failed: %m", lock_path);
return -1;
}
dotlock_r->dev = st.st_dev;
dotlock_r->ino = st.st_ino;
dotlock_r->mtime = st.st_mtime;
return 1;
}
static int dotlock_delete(const char *path, const char *lock_suffix,
const struct dotlock *dotlock, int check_mtime)
{
const char *lock_path;
struct stat st;
lock_path = t_strconcat(path, lock_suffix, NULL);
if (lstat(lock_path, &st) < 0) {
if (errno == ENOENT) {
i_warning("Our dotlock file %s was deleted", lock_path);
return 0;
}
i_error("lstat(%s) failed: %m", lock_path);
return -1;
}
if (dotlock->ino != st.st_ino ||
!CMP_DEV_T(dotlock->dev, st.st_dev)) {
i_warning("Our dotlock file %s was overridden", lock_path);
errno = EEXIST;
return 0;
}
if (dotlock->mtime != st.st_mtime && check_mtime) {
i_warning("Our dotlock file %s was modified (%s vs %s), "
"assuming it wasn't overridden", lock_path,
dec2str(dotlock->mtime), dec2str(st.st_mtime));
}
if (unlink(lock_path) < 0) {
if (errno == ENOENT) {
i_warning("Our dotlock file %s was deleted", lock_path);
return 0;
}
i_error("unlink(%s) failed: %m", lock_path);
return -1;
}
return 1;
}
int file_unlock_dotlock(const char *path, const struct dotlock *dotlock)
{
return dotlock_delete(path, DEFAULT_LOCK_SUFFIX, dotlock, TRUE);
}
int file_dotlock_open(const char *path,
const char *temp_prefix, const char *lock_suffix,
unsigned int timeout, unsigned int stale_timeout,
unsigned int immediate_stale_timeout,
int (*callback)(unsigned int secs_left, int stale,
void *context),
void *context)
{
int ret, fd;
if (lock_suffix == NULL)
lock_suffix = DEFAULT_LOCK_SUFFIX;
ret = dotlock_create(path, temp_prefix, lock_suffix, FALSE, &fd,
timeout, stale_timeout, immediate_stale_timeout,
callback, context);
if (ret <= 0)
return -1;
return fd;
}
int file_dotlock_replace(const char *path, const char *lock_suffix,
int fd, int verify_owner)
{
struct stat st, st2;
const char *lock_path;
int old_errno;
if (lock_suffix == NULL)
lock_suffix = DEFAULT_LOCK_SUFFIX;
lock_path = t_strconcat(path, lock_suffix, NULL);
if (verify_owner) {
if (fstat(fd, &st) < 0) {
old_errno = errno;
i_error("fstat(%s) failed: %m", lock_path);
(void)close(fd);
errno = old_errno;
return -1;
}
}
if (close(fd) < 0) {
i_error("close(%s) failed: %m", lock_path);
return -1;
}
if (verify_owner) {
if (lstat(lock_path, &st2) < 0) {
i_error("lstat(%s) failed: %m", lock_path);
return -1;
}
if (st.st_ino != st2.st_ino ||
!CMP_DEV_T(st.st_dev, st2.st_dev)) {
i_warning("Our dotlock file %s was overridden",
lock_path);
errno = EEXIST;
return 0;
}
}
if (rename(lock_path, path) < 0) {
i_error("rename(%s, %s) failed: %m", lock_path, path);
return -1;
}
return 1;
}
int file_dotlock_delete(const char *path, const char *lock_suffix, int fd)
{
struct dotlock dotlock;
struct stat st;
int old_errno;
if (lock_suffix == NULL)
lock_suffix = DEFAULT_LOCK_SUFFIX;
if (fstat(fd, &st) < 0) {
old_errno = errno;
i_error("fstat(%s) failed: %m",
t_strconcat(path, lock_suffix, NULL));
(void)close(fd);
errno = old_errno;
return -1;
}
if (close(fd) < 0) {
i_error("close(%s) failed: %m",
t_strconcat(path, lock_suffix, NULL));
return -1;
}
dotlock.dev = st.st_dev;
dotlock.ino = st.st_ino;
dotlock.mtime = st.st_mtime;
return dotlock_delete(path, lock_suffix, &dotlock, FALSE);
}