about summary refs log tree commit diff stats
path: root/cache.c
diff options
context:
space:
mode:
authorJohn Keeping2015-03-03 19:22:31 +0000
committerJason A. Donenfeld2015-03-03 23:55:27 +0100
commitdb9a70b159a107da29f88865d63ba14dd127556f (patch)
tree8dd6c73a41bb49206c6bd2c4f1bbee08f295a6bf /cache.c
parentMake root handling sane again. (diff)
downloadcgit-db9a70b159a107da29f88865d63ba14dd127556f.tar.gz
cgit-db9a70b159a107da29f88865d63ba14dd127556f.zip
cache: use F_SETLK to avoid stale lock files
If CGit is killed while it holds a lock on a cache slot (for example
because it is taking too long to generate a page), the lock file will be
left in place.  This prevents any future attempt to use the same slot
since it will fail to exclusively create the lock file.

Since CGit is the only program that should be manipulating lock files,
we can use advisory locking to detect whether another process is
actually using the lock file or if it is now stale.

I have confirmed that this works on Linux by setting a short TTL in a
custom cgitrc and running the following with CGit patched to print a
message to stderr if the fcntl(2) fails:

	$ export CGIT_CONFIG=$PWD/cgitrc
	$ export QUERY_STRING=url=cgit/tree/ui-shared.c
	$ ./cgit |
		grep -v -e '^<div class=.footer.>' \
			-e '^Last-Modified: ' \
			-e ^'Expires: ' >expect
	$ seq 50000 | dd bs=8192 |
		parallel -j200 "diff -u expect <(./cgit |
			grep -v -e '^<div class=.footer.>' \
				-e '^Last-Modified: ' \
				-e ^'Expires: ') || echo BAD"

This printed the fail message several times without ever printing "BAD".

Signed-off-by: John Keeping <john@keeping.me.uk>
Diffstat (limited to 'cache.c')
-rw-r--r--cache.c15
1 files changed, 14 insertions, 1 deletions
diff --git a/cache.c b/cache.c index 801e63f..900b161 100644 --- a/cache.c +++ b/cache.c
@@ -161,10 +161,23 @@ static int close_lock(struct cache_slot *slot)
161 */ 161 */
162static int lock_slot(struct cache_slot *slot) 162static int lock_slot(struct cache_slot *slot)
163{ 163{
164 slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT | O_EXCL, 164 struct flock lock = {
165 .l_type = F_WRLCK,
166 .l_whence = SEEK_SET,
167 .l_start = 0,
168 .l_len = 0,
169 };
170
171 slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT,
165 S_IRUSR | S_IWUSR); 172 S_IRUSR | S_IWUSR);
166 if (slot->lock_fd == -1) 173 if (slot->lock_fd == -1)
167 return errno; 174 return errno;
175 if (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) {
176 int saved_errno = errno;
177 close(slot->lock_fd);
178 slot->lock_fd = -1;
179 return saved_errno;
180 }
168 if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) 181 if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0)
169 return errno; 182 return errno;
170 return 0; 183 return 0;