bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "lib.h"
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen#include "istream.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "file-lock.h"
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen#include "file-dotlock.h"
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen#include "time-util.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen#include <time.h>
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen#include <sys/stat.h>
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#ifdef HAVE_FLOCK
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen# include <sys/file.h>
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#endif
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenstruct file_lock {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen int fd;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen char *path;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen struct dotlock *dotlock;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen struct timeval locked_time;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen int lock_type;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen enum file_lock_method lock_method;
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen bool unlink_on_free;
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen bool close_on_free;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen};
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainenstatic struct timeval lock_wait_start;
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainenstatic uint64_t file_lock_wait_usecs = 0;
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainenstatic long long file_lock_slow_warning_usecs = -1;
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock);
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainenbool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen{
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen if (strcasecmp(name, "fcntl") == 0)
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen *method_r = FILE_LOCK_METHOD_FCNTL;
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen else if (strcasecmp(name, "flock") == 0)
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen *method_r = FILE_LOCK_METHOD_FLOCK;
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen else if (strcasecmp(name, "dotlock") == 0)
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen *method_r = FILE_LOCK_METHOD_DOTLOCK;
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen else
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen return FALSE;
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen return TRUE;
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen}
6e77746e501c2b45850b1c530836058ed75e09eeTimo Sirainen
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainenconst char *file_lock_method_to_str(enum file_lock_method method)
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen{
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen switch (method) {
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen case FILE_LOCK_METHOD_FCNTL:
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen return "fcntl";
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen case FILE_LOCK_METHOD_FLOCK:
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen return "flock";
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen return "dotlock";
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen }
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen i_unreached();
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen}
8c8f7ac580b661aee3d8b8dd37df4a9b41c77000Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenint file_try_lock(int fd, const char *path, int lock_type,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen enum file_lock_method lock_method,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct file_lock **lock_r)
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen{
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen}
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
0dc7891233a973829f00371b27810f849b987c66Timo Sirainenint file_try_lock_error(int fd, const char *path, int lock_type,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen enum file_lock_method lock_method,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen struct file_lock **lock_r, const char **error_r)
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen{
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen return file_wait_lock_error(fd, path, lock_type, lock_method, 0,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen lock_r, error_r);
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen}
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainenstatic const char *
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainenfile_lock_find_fcntl(int lock_fd, int lock_type)
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen{
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen struct flock fl;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&fl);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fl.l_type = lock_type;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fl.l_whence = SEEK_SET;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fl.l_start = 0;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fl.l_len = 0;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (fcntl(lock_fd, F_GETLK, &fl) < 0 ||
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0)
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return "";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)",
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fl.l_type == F_RDLCK ? "READ" : "WRITE", (long)fl.l_pid);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen}
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainenstatic const char *
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainenfile_lock_find_proc_locks(int lock_fd ATTR_UNUSED)
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen{
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen /* do anything except Linux support this? don't bother trying it for
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen OSes we don't know about. */
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen#ifdef __linux__
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen static bool have_proc_locks = TRUE;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen struct stat st;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen char node_buf[MAX_INT_STRLEN*3 + 2 + 1];
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen struct istream *input;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen const char *line, *lock_type = "";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen pid_t pid = 0;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen int fd;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (!have_proc_locks)
d9a7e950a9cd21f2b4a90ec7759fca9e8fcc7995Timo Sirainen return NULL;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (fstat(lock_fd, &st) < 0)
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return "";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu",
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen major(st.st_dev), minor(st.st_dev),
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen (unsigned long long)st.st_ino);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen fd = open("/proc/locks", O_RDONLY);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (fd == -1) {
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen have_proc_locks = FALSE;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return "";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen }
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen input = i_stream_create_fd_autoclose(&fd, 512);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN {
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen const char *const *args = t_strsplit_spaces(line, " ");
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen /* number: FLOCK/POSIX ADVISORY READ/WRITE pid
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen major:minor:inode region-start region-end */
14cac26dcb71108abfdc95ea524e74be1f95774cPhil Carmody if (str_array_length(args) < 8) {
14cac26dcb71108abfdc95ea524e74be1f95774cPhil Carmody ; /* don't continue from within a T_BEGIN {...} T_END */
14cac26dcb71108abfdc95ea524e74be1f95774cPhil Carmody } else if (strcmp(args[5], node_buf) == 0) {
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen lock_type = strcmp(args[3], "READ") == 0 ?
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen "READ" : "WRITE";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (str_to_pid(args[4], &pid) < 0)
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen pid = 0;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen }
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen } T_END;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen i_stream_destroy(&input);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (pid == 0) {
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen /* not found */
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return "";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen }
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (pid == getpid())
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return " (BUG: lock is held by our own process)";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen#else
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return "";
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen#endif
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen}
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
1107c86ff3fa4f29796c2e76134b78d0b4a0db50Timo Sirainenconst char *file_lock_find(int lock_fd, enum file_lock_method lock_method,
1107c86ff3fa4f29796c2e76134b78d0b4a0db50Timo Sirainen int lock_type)
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen{
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen const char *ret;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (lock_method == FILE_LOCK_METHOD_FCNTL) {
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen ret = file_lock_find_fcntl(lock_fd, lock_type);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen if (ret[0] != '\0')
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return ret;
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen }
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen return file_lock_find_proc_locks(lock_fd);
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen}
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainenstatic bool err_is_lock_timeout(time_t started, unsigned int timeout_secs)
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen{
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen /* if EINTR took at least timeout_secs-1 number of seconds,
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen assume it was the alarm. otherwise log EINTR failure.
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen (We most likely don't want to retry EINTR since a signal
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen means somebody wants us to stop blocking). */
a8b37b688ceaa3ed3d40b3ccbdba5bb75cfb64b0Timo Sirainen return errno == EINTR &&
a8b37b688ceaa3ed3d40b3ccbdba5bb75cfb64b0Timo Sirainen (unsigned long)(time(NULL) - started + 1) >= timeout_secs;
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen}
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenstatic int file_lock_do(int fd, const char *path, int lock_type,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen enum file_lock_method lock_method,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen unsigned int timeout_secs, const char **error_r)
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen{
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen const char *lock_type_str;
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen time_t started = time(NULL);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen int ret;
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen i_assert(fd != -1);
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen if (timeout_secs != 0) {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen alarm(timeout_secs);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen file_lock_wait_start();
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen }
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen lock_type_str = lock_type == F_UNLCK ? "unlock" :
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen (lock_type == F_RDLCK ? "read-lock" : "write-lock");
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen switch (lock_method) {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen case FILE_LOCK_METHOD_FCNTL: {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#ifndef HAVE_FCNTL
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf(
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "Can't lock file %s: fcntl() locks not supported", path);
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen return -1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#else
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct flock fl;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen fl.l_type = lock_type;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen fl.l_whence = SEEK_SET;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen fl.l_start = 0;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen fl.l_len = 0;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen if (timeout_secs != 0) {
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen alarm(0);
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_wait_end(path);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen }
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (ret == 0)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen break;
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (timeout_secs == 0 &&
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen (errno == EACCES || errno == EAGAIN)) {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen /* locked by another process */
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf(
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "fcntl(%s, %s, F_SETLK) locking failed: %m "
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "(File is already locked)", path, lock_type_str);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return 0;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen errno = EAGAIN;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf(
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "fcntl(%s, %s, F_SETLKW) locking failed: "
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen "Timed out after %u seconds%s",
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen path, lock_type_str, timeout_secs,
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen file_lock_find(fd, lock_method, lock_type));
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen return 0;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen }
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m",
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_SETLKW");
36af6ff6b0249d0fb98b2c16c2ab8755e9394214Timo Sirainen if (errno == EDEADLK)
3d2fd3816b79caf8048582f1e951019187ee9d9dTimo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return -1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#endif
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen }
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen case FILE_LOCK_METHOD_FLOCK: {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#ifndef HAVE_FLOCK
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf(
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "Can't lock file %s: flock() not supported", path);
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen return -1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#else
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen int operation = timeout_secs != 0 ? 0 : LOCK_NB;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen switch (lock_type) {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen case F_RDLCK:
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen operation |= LOCK_SH;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen break;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen case F_WRLCK:
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen operation |= LOCK_EX;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen break;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen case F_UNLCK:
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen operation |= LOCK_UN;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen break;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen }
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen ret = flock(fd, operation);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen if (timeout_secs != 0) {
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen alarm(0);
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_wait_end(path);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen }
c2bb1764c359ce85a7f7f789ead11dd613ff9769Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (ret == 0)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen break;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen if (timeout_secs == 0 && errno == EWOULDBLOCK) {
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen /* locked by another process */
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf(
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "flock(%s, %s) failed: %m "
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen "(File is already locked)", path, lock_type_str);
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen return 0;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen }
4eb418849d5c6bf77b2721e4e6aef2e97deaa197Timo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
301b1524a822cec7b79856e8c599112c754bc4c0Timo Sirainen errno = EAGAIN;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: "
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen "Timed out after %u seconds%s",
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen path, lock_type_str, timeout_secs,
b5ff746939712c6a9bef71405fa786d5471cf177Timo Sirainen file_lock_find(fd, lock_method, lock_type));
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return 0;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen }
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: %m",
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen path, lock_type_str);
36af6ff6b0249d0fb98b2c16c2ab8755e9394214Timo Sirainen if (errno == EDEADLK)
3d2fd3816b79caf8048582f1e951019187ee9d9dTimo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return -1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#endif
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen }
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen /* we shouldn't get here */
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen i_unreached();
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen }
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen return 1;
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen}
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenint file_wait_lock(int fd, const char *path, int lock_type,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen enum file_lock_method lock_method,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen unsigned int timeout_secs,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct file_lock **lock_r)
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen{
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen const char *error;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen int ret;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen ret = file_wait_lock_error(fd, path, lock_type, lock_method,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen timeout_secs, lock_r, &error);
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen if (ret < 0)
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen i_error("%s", error);
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen return ret;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen}
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen
0dc7891233a973829f00371b27810f849b987c66Timo Sirainenint file_wait_lock_error(int fd, const char *path, int lock_type,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen enum file_lock_method lock_method,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen unsigned int timeout_secs,
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen struct file_lock **lock_r, const char **error_r)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen{
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct file_lock *lock;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen int ret;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_r);
3d2fd3816b79caf8048582f1e951019187ee9d9dTimo Sirainen if (ret <= 0)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return ret;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen lock = i_new(struct file_lock, 1);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen lock->fd = fd;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen lock->path = i_strdup(path);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen lock->lock_type = lock_type;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen lock->lock_method = lock_method;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (gettimeofday(&lock->locked_time, NULL) < 0)
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen i_fatal("gettimeofday() failed: %m");
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen *lock_r = lock;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return 1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen}
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenint file_lock_try_update(struct file_lock *lock, int lock_type)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen{
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen const char *error;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen int ret;
0dc7891233a973829f00371b27810f849b987c66Timo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen ret = file_lock_do(lock->fd, lock->path, lock_type,
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen lock->lock_method, 0, &error);
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (ret <= 0)
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen return ret;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen file_lock_log_warning_if_slow(lock);
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen lock->lock_type = lock_type;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen return 1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen}
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainenvoid file_lock_set_unlink_on_free(struct file_lock *lock, bool set)
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen{
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen lock->unlink_on_free = set;
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen}
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainenvoid file_lock_set_close_on_free(struct file_lock *lock, bool set)
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen{
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen lock->close_on_free = set;
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen}
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainenstruct file_lock *file_lock_from_dotlock(struct dotlock **dotlock)
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen{
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen struct file_lock *lock;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen lock = i_new(struct file_lock, 1);
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen lock->fd = -1;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen lock->path = i_strdup(file_dotlock_get_lock_path(*dotlock));
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen lock->lock_type = F_WRLCK;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen lock->lock_method = FILE_LOCK_METHOD_DOTLOCK;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen if (gettimeofday(&lock->locked_time, NULL) < 0)
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen i_fatal("gettimeofday() failed: %m");
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen lock->dotlock = *dotlock;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen *dotlock = NULL;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen return lock;
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen}
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainenstatic void file_unlock_real(struct file_lock *lock)
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen{
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen const char *error;
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen if (file_lock_do(lock->fd, lock->path, F_UNLCK,
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen lock->lock_method, 0, &error) == 0) {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen /* this shouldn't happen */
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen i_error("file_unlock(%s) failed: %m", lock->path);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen }
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen}
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenvoid file_unlock(struct file_lock **_lock)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen{
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct file_lock *lock = *_lock;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen *_lock = NULL;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen /* unlocking is unnecessary when the file is unlinked. or alternatively
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen the unlink() must be done before unlocking, because otherwise it
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen could be deleting the new lock. */
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen i_assert(!lock->unlink_on_free);
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen if (lock->dotlock == NULL)
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen file_unlock_real(lock);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen file_lock_free(&lock);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen}
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainenstatic void file_try_unlink_locked(struct file_lock *lock)
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen{
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen struct file_lock *temp_lock = NULL;
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen struct stat st1, st2;
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen const char *error;
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen int ret;
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen file_unlock_real(lock);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen ret = file_try_lock_error(lock->fd, lock->path, F_WRLCK,
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen lock->lock_method, &temp_lock, &error);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen if (ret < 0) {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen i_error("file_lock_free(): Unexpectedly failed to retry locking %s: %s",
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen lock->path, error);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen } else if (ret == 0) {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen /* already locked by someone else */
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen } else if (fstat(lock->fd, &st1) < 0) {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen /* not expected to happen */
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen i_error("file_lock_free(): fstat(%s) failed: %m", lock->path);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen } else if (stat(lock->path, &st2) < 0) {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen if (errno != ENOENT)
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen i_error("file_lock_free(): stat(%s) failed: %m", lock->path);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen } else if (st1.st_ino != st2.st_ino ||
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen !CMP_DEV_T(st1.st_dev, st2.st_dev)) {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen /* lock file was recreated already - don't delete it */
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen } else {
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen /* nobody was waiting on the lock - unlink it */
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen i_unlink(lock->path);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen }
5109f00ce0cf7c89be9ab20a488a93a59212ff2eJosef 'Jeff' Sipek file_lock_free(&temp_lock);
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen}
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenvoid file_lock_free(struct file_lock **_lock)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen{
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct file_lock *lock = *_lock;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
67654ca6848cdb83ef96be7a6584695f8a01535dTimo Sirainen if (lock == NULL)
67654ca6848cdb83ef96be7a6584695f8a01535dTimo Sirainen return;
67654ca6848cdb83ef96be7a6584695f8a01535dTimo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen *_lock = NULL;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen if (lock->dotlock != NULL)
f27a51cd9a66685b63357de25e733c37eb03b07cTimo Sirainen file_dotlock_delete(&lock->dotlock);
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen if (lock->unlink_on_free)
3581ece16eb740a5003c4c11dd3c7d02839a24a4Timo Sirainen file_try_unlink_locked(lock);
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen if (lock->close_on_free)
65f9a90ef5ed4c86fb9e44f22e472509126ae9f5Timo Sirainen i_close_fd(&lock->fd);
68332e3a49dea15013aa8f4daa16b5e07eb3d543Timo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen file_lock_log_warning_if_slow(lock);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen i_free(lock->path);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen i_free(lock);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen}
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
f1243d7759a3dddc1c3eaf7eda8a153c8ef0112bTimo Sirainenconst char *file_lock_get_path(struct file_lock *lock)
f1243d7759a3dddc1c3eaf7eda8a153c8ef0112bTimo Sirainen{
f1243d7759a3dddc1c3eaf7eda8a153c8ef0112bTimo Sirainen return lock->path;
f1243d7759a3dddc1c3eaf7eda8a153c8ef0112bTimo Sirainen}
f1243d7759a3dddc1c3eaf7eda8a153c8ef0112bTimo Sirainen
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainenvoid file_lock_set_path(struct file_lock *lock, const char *path)
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen{
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen if (path != lock->path) {
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen i_free(lock->path);
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen lock->path = i_strdup(path);
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen }
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen}
d8d93ecd89efa7c84a3a21264a550449619f715bTimo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainenvoid file_lock_wait_start(void)
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen{
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen i_assert(lock_wait_start.tv_sec == 0);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen if (gettimeofday(&lock_wait_start, NULL) < 0)
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen i_fatal("gettimeofday() failed: %m");
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen}
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainenstatic void file_lock_wait_init_warning(void)
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen{
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen const char *value;
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen i_assert(file_lock_slow_warning_usecs == -1);
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen value = getenv("FILE_LOCK_SLOW_WARNING_MSECS");
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen if (value == NULL)
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_slow_warning_usecs = LLONG_MAX;
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen else if (str_to_llong(value, &file_lock_slow_warning_usecs) == 0 &&
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_slow_warning_usecs > 0) {
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_slow_warning_usecs *= 1000;
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen } else {
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen i_error("FILE_LOCK_SLOW_WARNING_MSECS: "
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen "Invalid value '%s' - ignoring", value);
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_slow_warning_usecs = LLONG_MAX;
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen }
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen}
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock)
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen{
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (file_lock_slow_warning_usecs < 0)
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen file_lock_wait_init_warning();
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (file_lock_slow_warning_usecs == LLONG_MAX) {
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen /* slowness checking is disabled */
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen return;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen }
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (lock->lock_type != F_WRLCK) {
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen /* some shared locks can legitimately be kept for a long time.
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen don't warn about them. */
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen return;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen }
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen struct timeval now;
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (gettimeofday(&now, NULL) < 0)
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen i_fatal("gettimeofday() failed: %m");
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen int diff = timeval_diff_msecs(&now, &lock->locked_time);
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen if (diff > file_lock_slow_warning_usecs/1000) {
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen i_warning("Lock %s kept for %d.%03d secs", lock->path,
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen diff / 1000, diff % 1000);
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen }
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen}
1ea0aa8e14e843f2776746776a429b0a1aae299dTimo Sirainen
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainenvoid file_lock_wait_end(const char *lock_name)
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen{
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen struct timeval now;
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen i_assert(lock_wait_start.tv_sec != 0);
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen if (gettimeofday(&now, NULL) < 0)
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen i_fatal("gettimeofday() failed: %m");
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen long long diff = timeval_diff_usecs(&now, &lock_wait_start);
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen if (diff > file_lock_slow_warning_usecs) {
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen if (file_lock_slow_warning_usecs < 0)
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_wait_init_warning();
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen if (diff > file_lock_slow_warning_usecs) {
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen int diff_msecs = (diff + 999) / 1000;
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen i_warning("Locking %s took %d.%03d secs", lock_name,
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen diff_msecs / 1000, diff_msecs % 1000);
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen }
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen }
f83fd83f9c6708d198748e714aa947cad9362c02Timo Sirainen file_lock_wait_usecs += diff;
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen lock_wait_start.tv_sec = 0;
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen}
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainenuint64_t file_lock_wait_get_total_usecs(void)
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen{
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen return file_lock_wait_usecs;
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen}