file-dotlock.c revision 0a3d3ca7db7cbdc947ccabe740c40561b3f5c066
/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "str.h"
#include "hex-binary.h"
#include "hostpid.h"
#include "randgen.h"
#include "write-full.h"
#include "safe-mkstemp.h"
#include "nfs-workarounds.h"
#include "file-dotlock.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <utime.h>
#define DEFAULT_LOCK_SUFFIX ".lock"
/* 0.1 .. 0.2msec */
/* If the dotlock is newer than this, don't verify that the PID it contains
is valid (since it most likely is). */
#define STALE_PID_CHECK_SECS 2
/* Maximum difference between current time and create file's ctime before
logging a warning. Should be less than a second in normal operation. */
#define MAX_TIME_DIFF 30
struct dotlock {
struct dotlock_settings settings;
char *path;
char *lock_path;
int fd;
};
struct file_change_info {
};
struct lock_info {
const struct dotlock_settings *set;
int fd;
struct file_change_info lock_info;
struct file_change_info file_info;
unsigned int have_pid:1;
unsigned int pid_read:1;
unsigned int use_io_notify:1;
unsigned int lock_stated:1;
};
static struct dotlock *
{
return dotlock;
}
{
int fd;
if (fd == -1)
return -1; /* ignore the actual error */
/* read line */
if (ret <= 0)
return -1;
/* fix the string */
ret--;
/* it should contain pid:host */
return -1;
*host++ = '\0';
/* host must be ours */
return -1;
return -1;
if (pid <= 0)
return -1;
return pid;
}
static bool
{
/* ctime is checked only if we're not doing NFS attribute cache
flushes. it changes them. */
/* First check, set last_change to file's change time.
Use mtime instead if it's higher, but only if it's
not higher than current time, because the mtime
can also be used for keeping metadata. */
}
if (*last_change_r < change_time)
return TRUE;
}
return FALSE;
}
bool *changed_r)
{
/* don't waste time flushing attribute cache the first time we're here.
if it's stale we'll get back here soon. */
}
return -1;
}
return 1;
}
return 0;
}
{
i_error("unlink(%s) failed: %m",
return -1;
}
/* make sure we sleep for a while after overriding the lock file.
otherwise another process might try to override it at the same time
and unlink our newly created dotlock. */
if (lock_info->use_io_notify)
return 0;
}
{
bool changed;
int ret;
return ret;
/* either our first check or someone else got the lock file.
if the dotlock was created only a couple of seconds ago,
don't bother to read its PID. */
else {
}
/* no pid checking */
} else {
/* we just checked the pid */
return 0;
}
/* re-read the pid. even if all times and inodes are the same,
the PID in the file might have changed if lock files were
rapidly being recreated. */
}
/* we've local PID. Check if it exists. */
/* process exists, don't override */
return 0;
}
/* it's us. either we're locking it again, or it's a
stale lock file with same pid than us. either way,
recreate it.. */
}
/* doesn't exist - now check again if the dotlock was just
deleted or replaced */
return ret;
if (!changed) {
/* still there, go ahead and override it */
return dotlock_override(lock_info);
}
return 1;
}
if (stale_timeout == 0) {
/* no change checking */
return 0;
}
/* possibly stale lock file. check also the timestamp of the
file we're protecting. */
}
/* file doesn't exist. treat it as if
it hasn't changed */
} else {
return -1;
}
} else {
}
}
/* no changes for a while, assume stale lock */
return dotlock_override(lock_info);
}
return 0;
}
{
const char *str;
/* write our pid and host, if possible */
/* failed, leave it empty then */
return -1;
}
}
return 0;
}
{
const char *p;
/* we'll need our temp file first. */
str_truncate(tmp_path, 0);
if (temp_prefix != NULL) {
/* add directory */
}
} else {
if (p != NULL) {
/* add directory */
}
}
return -1;
if (write_pid) {
return -1;
}
}
}
return 0;
i_error("link(%s, %s) failed: %m",
}
return -1;
}
/* non-fatal, continue */
}
return 1;
}
{
int fd;
if (fd == -1) {
return 0;
return -1;
}
if (write_pid) {
return -1;
}
}
return 1;
}
{
}
{
if (!lock_info->use_io_notify) {
return;
}
ioloop = io_loop_create();
case IO_NOTIFY_ADDED:
break;
case IO_NOTIFY_NOTFOUND:
/* the lock file doesn't exist anymore, don't sleep */
return;
case IO_NOTIFY_NOSUPPORT:
/* listening for files not supported */
return;
}
/* timeout after a random time even when using notify, since it
doesn't work reliably with e.g. NFS. */
timeout_remove(&to);
}
const char **lock_path_r)
{
const char *lock_path;
unsigned int stale_notify_threshold;
unsigned int change_secs, wait_left;
int ret;
bool do_wait;
do {
if (do_wait) {
}
if (ret < 0)
break;
if (ret == 1) {
if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
break;
tmp_path);
if (ret != 0)
break;
}
last_notify = now;
if (change_secs >= stale_notify_threshold &&
change_secs <= wait_left) {
unsigned int secs_left =
/* we don't want to override */
}
} else {
}
}
} while (now < max_wait_time);
if (ret > 0) {
ret = -1;
} else {
/* successful dotlock creation */
i_warning("Created dotlock file's timestamp is "
"different than current time "
}
}
}
}
}
if (ret == 0)
return ret;
}
{
int old_errno;
}
}
enum dotlock_create_flags flags)
{
const char *lock_path;
return ret;
return -1;
}
/* With NFS the writes may have been flushed only when closing the
file. Get the mtime again after that to avoid "dotlock was modified"
errors. */
else {
i_error("dotlock %s was immediately deleted under us",
}
return -1;
}
/* extra sanity check won't hurt.. */
i_error("dotlock %s was immediately recreated under us",
return -1;
}
return 1;
}
enum dotlock_create_flags flags,
{
int ret;
T_BEGIN {
} T_END;
return ret;
}
{
const char *lock_path;
i_warning("Our dotlock file %s was deleted "
"(kept it %d secs)", lock_path,
return 0;
}
return -1;
}
i_warning("Our dotlock file %s was overridden "
"(kept it %d secs)", lock_path,
return 0;
}
i_warning("Our dotlock file %s was modified (%s vs %s), "
"assuming it wasn't overridden (kept it %d secs)",
}
i_warning("Our dotlock file %s was deleted "
"(kept it %d secs)", lock_path,
return 0;
}
return -1;
}
return 1;
}
enum dotlock_create_flags flags,
{
int ret;
T_BEGIN {
const char *lock_path;
} T_END;
if (ret <= 0) {
return -1;
}
}
enum dotlock_replace_flags flags)
{
const char *lock_path;
int fd;
if ((flags & DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) != 0)
if ((flags & DOTLOCK_REPLACE_FLAG_VERIFY_OWNER) != 0 &&
i_warning("Our dotlock file %s was overridden "
"(kept it %d secs)", lock_path,
return 0;
}
return -1;
}
return 1;
}
{
int ret = 0;
return 0;
T_BEGIN {
ret = -1;
}
} T_END;
return ret;
}
{
const char *lock_path;
return FALSE;
}
return FALSE;
}
}
{
}
}