Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
mxq/mx_flock.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
167 lines (128 sloc)
2.94 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/file.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <errno.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include "mx_log.h" | |
#include "mx_flock.h" | |
#define FLOCK_MODE 0600 | |
#if !defined mx_free_null | |
# define mx_free_null(a) do { free((a)); (a) = NULL; } while(0) | |
#endif | |
static int _flock_open(struct mx_flock *lock, mode_t mode) | |
{ | |
int fd; | |
assert(lock); | |
assert(lock->fname); | |
fd = open(lock->fname, O_RDONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, mode); | |
if (fd < 0) { | |
mx_log_err("%s: %m",lock->fname); | |
return -1; | |
} | |
lock->fd = fd; | |
return fd; | |
} | |
static int _flock_close(struct mx_flock *lock) | |
{ | |
int res; | |
assert(lock); | |
res = close(lock->fd); | |
if (res < 0) | |
mx_log_debug("close(): %m"); | |
lock->fd = -1; | |
return res; | |
} | |
static void _flock_free(struct mx_flock *lock) | |
{ | |
if (!lock) | |
return; | |
mx_free_null(lock->fname); | |
free(lock); | |
} | |
struct mx_flock *mx_flock(int operation, char *fmt, ...) | |
{ | |
struct mx_flock *lock; | |
va_list ap; | |
char *fname; | |
int res; | |
struct stat s0, s1; | |
assert(fmt); | |
lock = malloc(sizeof(*lock)); | |
if (!lock) { | |
mx_log_err("malloc(): %m"); | |
return NULL; | |
} | |
lock->fd = -1; | |
lock->operation = -1; | |
lock->locked = 0; | |
va_start(ap, fmt); | |
res = vasprintf(&fname, fmt, ap); | |
va_end(ap); | |
if (res == -1) { | |
mx_log_err("vasprintf(): %m"); | |
free(lock); | |
return NULL; | |
} | |
lock->fname = fname; | |
while (1) { | |
res = _flock_open(lock, FLOCK_MODE); | |
if (res < 0) { | |
_flock_free(lock); | |
return NULL; | |
} | |
res = flock(lock->fd, operation|LOCK_NB); | |
if (res < 0) { | |
if (errno == EWOULDBLOCK) | |
return lock; | |
mx_log_err("flock(): %m"); | |
_flock_close(lock); | |
_flock_free(lock); | |
return NULL; | |
} | |
fstat(lock->fd, &s0); | |
stat(lock->fname, &s1); | |
if (s0.st_ino == s1.st_ino) { | |
lock->operation = operation; | |
break; | |
} | |
mx_log_warning("failed to acquire lock on %s - file changed on disk - retrying", lock->fname); | |
_flock_close(lock); | |
} | |
lock->locked = 1; | |
return lock; | |
} | |
int mx_funlock(struct mx_flock *lock) | |
{ | |
int res; | |
if (!lock) | |
return 0; | |
if (!lock->locked) { | |
_flock_free(lock); | |
return 0; | |
} | |
assert(lock->fname); | |
assert(lock->fd >= 0); | |
assert(lock->operation >= 0); | |
res = unlink(lock->fname); | |
if (res < 0) | |
mx_log_warning("unlink(): %m"); | |
_flock_close(lock); | |
_flock_free(lock); | |
return res; | |
} | |
/* as above, but don't delete lock file | |
*/ | |
void mx_funlock_nodelete(struct mx_flock *lock) { | |
_flock_close(lock); | |
_flock_free(lock); | |
} | |
void mx_flock_free(struct mx_flock *lock) | |
{ | |
_flock_free(lock); | |
} |