file-dotlock.c revision 181aa01111e2de2dae413b4c1ccfcfc4e801ac40
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen/* 0.1 .. 0.2msec */
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainenstatic pid_t read_local_pid(const char *lock_path)
9398c0935613ba038cf2275ff66c43b25092cfd0Timo Sirainen /* read line */
9398c0935613ba038cf2275ff66c43b25092cfd0Timo Sirainen /* fix the string */
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen /* it should contain pid:host */
657afb33796f8216c568ad813627da89970760beTimo Sirainen /* host must be ours */
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainenstatic int check_lock(time_t now, struct lock_info *lock_info)
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen i_error("lstat(%s) failed: %m", lock_info->lock_path);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen if (lock_info->immediate_stale_timeout != 0 &&
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen now > st.st_mtime + (time_t)lock_info->immediate_stale_timeout &&
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen now > st.st_ctime + (time_t)lock_info->immediate_stale_timeout) {
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen /* old lock file */
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen /* no change checking */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* either our first check or someone else got the lock file.
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen check if it contains a pid whose existence we can verify */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen lock_info->pid = read_local_pid(lock_info->lock_path);
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* we've local PID. Check if it exists. */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (kill(lock_info->pid, 0) == 0 || errno != ESRCH)
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* doesn't exist - go ahead and delete */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen /* file doesn't exist. treat it as if
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen it hasn't changed */
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen i_error("stat(%s) failed: %m", lock_info->path);
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen } else if (lock_info->last_size != st.st_size ||
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if (now > lock_info->last_change + (time_t)lock_info->stale_timeout) {
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen /* no changes for a while, assume stale lock */
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainenstatic int create_temp_file(const char *prefix, const char **path_r)
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen fd = open(*path_r, O_RDWR | O_EXCL | O_CREAT, 0666);
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainenstatic int try_create_lock(struct lock_info *lock_info, const char *temp_prefix)
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen const char *str, *p;
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen /* we'll need our temp file first. */
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen temp_prefix = t_strconcat(".temp.", my_hostname, ".",
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen str = t_strdup_until(lock_info->lock_path, p+1);
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen temp_prefix = t_strconcat(str, temp_prefix, NULL);
42061dd44f742de25d0a52ed1ef766a56df421f1Timo Sirainen lock_info->fd = create_temp_file(temp_prefix, &str);
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen if (link(lock_info->temp_path, lock_info->lock_path) < 0) {
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen if (unlink(lock_info->temp_path) < 0 && errno != ENOENT) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->temp_path);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* non-fatal, continue */
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainenstatic int dotlock_create(const char *path, const char *temp_prefix,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen unsigned int timeout, unsigned int stale_timeout,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen int (*callback)(unsigned int secs_left, int stale,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen lock_info.immediate_stale_timeout = immediate_stale_timeout;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen ret = try_create_lock(&lock_info, temp_prefix);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* we don't want to override */
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainenint file_lock_dotlock(const char *path, const char *temp_prefix, int checkonly,
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen unsigned int timeout, unsigned int stale_timeout,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen int (*callback)(unsigned int secs_left, int stale,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen ret = dotlock_create(path, temp_prefix, checkonly, &fd,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen timeout, stale_timeout, immediate_stale_timeout,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen /* write our pid and host, if possible */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen str = t_strdup_printf("%s:%s", my_pid, my_hostname);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen /* failed, leave it empty then */
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen i_error("ftruncate(%s) failed: %m", lock_path);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* save the inode info after writing */
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainenstatic int dotlock_delete(const char *path, const struct dotlock *dotlock)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_warning("Our dotlock file %s was deleted", lock_path);
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen i_warning("Our dotlock file %s was overridden", lock_path);
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen i_warning("Our dotlock file %s was modified (%s vs %s), "
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen dec2str(dotlock->mtime), dec2str(st.st_mtime));
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen i_warning("Our dotlock file %s was deleted", lock_path);
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainenint file_unlock_dotlock(const char *path, const struct dotlock *dotlock)
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainenint file_dotlock_open(const char *path, const char *temp_prefix,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen unsigned int timeout, unsigned int stale_timeout,
31e7be5e1d41a77f08d26cef46aba1df24b3f1baTimo Sirainen int (*callback)(unsigned int secs_left, int stale,
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen ret = dotlock_create(path, temp_prefix, FALSE, &fd,
31e7be5e1d41a77f08d26cef46aba1df24b3f1baTimo Sirainen timeout, stale_timeout, immediate_stale_timeout,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainenint file_dotlock_replace(const char *path, int fd, int verify_owner)
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen i_warning("Our dotlock file %s was overridden",
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen i_error("rename(%s, %s) failed: %m", lock_path, path);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainenint file_dotlock_delete(const char *path, int fd)