file-lock.c revision f1243d7759a3dddc1c3eaf7eda8a153c8ef0112b
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainenstatic long long file_lock_slow_warning_usecs = -1;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenbool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainenconst char *file_lock_method_to_str(enum file_lock_method method)
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen return "fcntl";
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen return "flock";
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return "dotlock";
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenint file_try_lock(int fd, const char *path, int lock_type,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenint file_try_lock_error(int fd, const char *path, int lock_type,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct file_lock **lock_r, const char **error_r)
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen return file_wait_lock_error(fd, path, lock_type, lock_method, 0,
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainenstatic const char *
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenfile_lock_find_fcntl(int lock_fd, int lock_type)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)",
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen fl.l_type == F_RDLCK ? "READ" : "WRITE", (long)fl.l_pid);
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainenstatic const char *
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainenfile_lock_find_proc_locks(int lock_fd ATTR_UNUSED)
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen /* do anything except Linux support this? don't bother trying it for
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen OSes we don't know about. */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu",
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen input = i_stream_create_fd_autoclose(&fd, 512);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *const *args = t_strsplit_spaces(line, " ");
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* number: FLOCK/POSIX ADVISORY READ/WRITE pid
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen major:minor:inode region-start region-end */
63e207529879438e9f4412d97cdc34bdc82a3702Timo Sirainen ; /* don't continue from within a T_BEGIN {...} T_END */
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen /* not found */
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen return " (BUG: lock is held by our own process)";
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenconst char *file_lock_find(int lock_fd, enum file_lock_method lock_method,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const char *ret;
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen ret = file_lock_find_fcntl(lock_fd, lock_type);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainenstatic bool err_is_lock_timeout(time_t started, unsigned int timeout_secs)
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen /* if EINTR took at least timeout_secs-1 number of seconds,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen assume it was the alarm. otherwise log EINTR failure.
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen (We most likely don't want to retry EINTR since a signal
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen means somebody wants us to stop blocking). */
5214b67a7dabab87da74e04bb8b227f94b95bce4Timo Sirainen (unsigned long)(time(NULL) - started + 1) >= timeout_secs;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic int file_lock_do(int fd, const char *path, int lock_type,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen unsigned int timeout_secs, const char **error_r)
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen lock_type_str = lock_type == F_UNLCK ? "unlock" :
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk (lock_type == F_RDLCK ? "read-lock" : "write-lock");
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk "Can't lock file %s: fcntl() locks not supported", path);
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl);
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen /* locked by another process */
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen "fcntl(%s, %s, F_SETLK) locking failed: %m "
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen "(File is already locked)", path, lock_type_str);
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen "fcntl(%s, %s, F_SETLKW) locking failed: "
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen "Timed out after %u seconds%s",
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m",
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_SETLKW");
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen "Can't lock file %s: flock() not supported", path);
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen int operation = timeout_secs != 0 ? 0 : LOCK_NB;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (timeout_secs == 0 && errno == EWOULDBLOCK) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen /* locked by another process */
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen "flock(%s, %s) failed: %m "
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen "(File is already locked)", path, lock_type_str);
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: "
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen "Timed out after %u seconds%s",
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: %m",
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen /* we shouldn't get here */
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainenint file_wait_lock(int fd, const char *path, int lock_type,
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen ret = file_wait_lock_error(fd, path, lock_type, lock_method,
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainenint file_wait_lock_error(int fd, const char *path, int lock_type,
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen struct file_lock **lock_r, const char **error_r)
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_r);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (gettimeofday(&lock->locked_time, NULL) < 0)
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainenint file_lock_try_update(struct file_lock *lock, int lock_type)
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen ret = file_lock_do(lock->fd, lock->path, lock_type,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (file_lock_do(lock->fd, lock->path, F_UNLCK,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen /* this shouldn't happen */
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen i_error("file_unlock(%s) failed: %m", lock->path);
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainenconst char *file_lock_get_path(struct file_lock *lock)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen value = getenv("FILE_LOCK_SLOW_WARNING_MSECS");
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen else if (str_to_llong(value, &file_lock_slow_warning_usecs) == 0 &&
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock)
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen if (file_lock_slow_warning_usecs == LLONG_MAX) {
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen /* slowness checking is disabled */
08ed4ab71fd2a4e800d9025a736f0f46b771ea90Timo Sirainen /* some shared locks can legitimately be kept for a long time.
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen don't warn about them. */
08ed4ab71fd2a4e800d9025a736f0f46b771ea90Timo Sirainen int diff = timeval_diff_msecs(&now, &lock->locked_time);
08ed4ab71fd2a4e800d9025a736f0f46b771ea90Timo Sirainen if (diff > file_lock_slow_warning_usecs/1000) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen i_warning("Lock %s kept for %d.%03d secs", lock->path,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen long long diff = timeval_diff_usecs(&now, &lock_wait_start);
e4cb3bfcd42f1f2c9e676ece6f7f53803f5c6a16Timo Sirainen i_warning("Locking %s took %d.%03d secs", lock_name,