file-dotlock.c revision 4ea21f54bb97c0204539760f74eb08323ecde63a
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen/* 0.1 .. 0.2msec */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic pid_t read_local_pid(const char *lock_path)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* read line */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* fix the string */
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen /* it should contain pid:host */
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen /* host must be ours */
798cfe56c9871262770384da1239162b3800cce1Timo Sirainenstatic int check_lock(time_t now, struct lock_info *lock_info)
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen i_error("lstat(%s) failed: %m", lock_info->lock_path);
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen /* either our first check or someone else got the lock file.
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen check if it contains a pid whose existence we can verify */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen lock_info->pid = read_local_pid(lock_info->lock_path);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen /* we've local PID. Check if it exists. */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen if (kill(lock_info->pid, 0) == 0 || errno != ESRCH)
fc8d5f0ac909cca77840538e8beef98a8d40c21cTimo Sirainen /* doesn't exist - go ahead and delete */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
fc8d5f0ac909cca77840538e8beef98a8d40c21cTimo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
5d4855d7b4dcffb6975ed8e3c9c376dac74e5c8aTimo Sirainen /* see if the file we're locking is being modified */
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen /* file doesn't exist. treat it as if
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen it hasn't changed */
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen i_error("stat(%s) failed: %m", lock_info->path);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else if (lock_info->last_size != st.st_size ||
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen if (now > lock_info->last_change + (time_t)lock_info->stale_timeout) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* no changes for a while, assume stale lock */
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic int try_create_lock(const char *lock_path, struct dotlock *dotlock_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char *str;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen fd = open(lock_path, O_WRONLY | O_EXCL | O_CREAT, 0644);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* got it, save the inode info */
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen /* write our pid and host, if possible */
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen str = t_strdup_printf("%s:%s", my_pid, my_hostname);
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen /* failed, leave it empty then */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_error("ftruncate(%s) failed: %m", lock_path);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenint file_lock_dotlock(const char *path, int checkonly,
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen unsigned int timeout, unsigned int stale_timeout,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen int (*callback)(unsigned int secs_left, int stale,
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen /* There's two ways to do this:
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen a) Rely on O_EXCL. Historically this hasn't always worked with NFS.
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen b) Create temp file and link() it to the file we want.
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen We now use a). It's easier to do and it never leaves temporary files
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen lying around. Also Postfix relies on it too, so I guess it's safe
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen enough nowadays.
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* we don't want to override */
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainenint file_unlock_dotlock(const char *path, const struct dotlock *dotlock)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_warning("Our dotlock file %s was deleted", lock_path);
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen i_warning("Our dotlock file %s was overridden", lock_path);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_warning("Our dotlock file %s was modified (%s vs %s), "
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen dec2str(dotlock->mtime), dec2str(st.st_mtime));
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_warning("Our dotlock file %s was deleted", lock_path);