X-Git-Url: https://gitweb.ps.run/ps-cgit/blobdiff_plain/f75900b04f73725c00abb46405b51ade59313ecc..HEAD:/cache.c diff --git a/cache.c b/cache.c index 74a1795..1c843ba 100644 --- a/cache.c +++ b/cache.c @@ -1,6 +1,6 @@ /* cache.c: cache management * - * Copyright (C) 2006 Lars Hjemli + * Copyright (C) 2006-2014 cgit Development Team * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -15,22 +15,25 @@ #include "cgit.h" #include "cache.h" +#include "html.h" +#ifdef HAVE_LINUX_SENDFILE +#include +#endif #define CACHE_BUFSIZE (1024 * 4) struct cache_slot { const char *key; - int keylen; + size_t keylen; int ttl; cache_fill_fn fn; - void *cbdata; int cache_fd; int lock_fd; + int stdout_fd; const char *cache_name; const char *lock_name; int match; struct stat cache_st; - struct stat lock_st; int bufsize; char buf[CACHE_BUFSIZE]; }; @@ -42,7 +45,7 @@ struct cache_slot { static int open_slot(struct cache_slot *slot) { char *bufz; - int bufkeylen = -1; + ssize_t bufkeylen = -1; slot->cache_fd = open(slot->cache_name, O_RDONLY); if (slot->cache_fd == -1) @@ -59,8 +62,9 @@ static int open_slot(struct cache_slot *slot) if (bufz) bufkeylen = bufz - slot->buf; - slot->match = bufkeylen == slot->keylen && - !memcmp(slot->key, slot->buf, bufkeylen + 1); + if (slot->key) + slot->match = bufkeylen == slot->keylen && + !memcmp(slot->key, slot->buf, bufkeylen + 1); return 0; } @@ -81,22 +85,45 @@ static int close_slot(struct cache_slot *slot) /* Print the content of the active cache slot (but skip the key). */ static int print_slot(struct cache_slot *slot) { - ssize_t i, j; + off_t off; +#ifdef HAVE_LINUX_SENDFILE + off_t size; +#endif - i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); - if (i != slot->keylen + 1) - return errno; + off = slot->keylen + 1; + +#ifdef HAVE_LINUX_SENDFILE + size = slot->cache_st.st_size; do { - i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); - if (i > 0) - j = xwrite(STDOUT_FILENO, slot->buf, i); - } while (i > 0 && j == i); + ssize_t ret; + ret = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + /* Fall back to read/write on EINVAL or ENOSYS */ + if (errno == EINVAL || errno == ENOSYS) + break; + return errno; + } + if (off == size) + return 0; + } while (1); +#endif - if (i < 0 || j != i) + if (lseek(slot->cache_fd, off, SEEK_SET) != off) return errno; - else - return 0; + + do { + ssize_t ret; + ret = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); + if (ret < 0) + return errno; + if (ret == 0) + return 0; + if (write_in_full(STDOUT_FILENO, slot->buf, ret) < 0) + return errno; + } while (1); } /* Check if the slot has expired */ @@ -141,10 +168,23 @@ static int close_lock(struct cache_slot *slot) */ static int lock_slot(struct cache_slot *slot) { - slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT | O_EXCL, + struct flock lock = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0, + }; + + slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (slot->lock_fd == -1) return errno; + if (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) { + int saved_errno = errno; + close(slot->lock_fd); + slot->lock_fd = -1; + return saved_errno; + } if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) return errno; return 0; @@ -163,6 +203,13 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot) else err = unlink(slot->lock_name); + /* Restore stdout and close the temporary FD. */ + if (slot->stdout_fd >= 0) { + dup2(slot->stdout_fd, STDOUT_FILENO); + close(slot->stdout_fd); + slot->stdout_fd = -1; + } + if (err) return errno; @@ -174,11 +221,9 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot) */ static int fill_slot(struct cache_slot *slot) { - int tmp; - /* Preserve stdout */ - tmp = dup(STDOUT_FILENO); - if (tmp == -1) + slot->stdout_fd = dup(STDOUT_FILENO); + if (slot->stdout_fd == -1) return errno; /* Redirect stdout to lockfile */ @@ -186,14 +231,14 @@ static int fill_slot(struct cache_slot *slot) return errno; /* Generate cache content */ - slot->fn(slot->cbdata); + slot->fn(); - /* Restore stdout */ - if (dup2(tmp, STDOUT_FILENO) == -1) + /* Make sure any buffered data is flushed to the file */ + if (fflush(stdout)) return errno; - /* Close the temporary filedescriptor */ - if (close(tmp)) + /* update stat info */ + if (fstat(slot->lock_fd, &slot->cache_st)) return errno; return 0; @@ -268,7 +313,7 @@ static int process_slot(struct cache_slot *slot) /* If the cache slot does not exist (or its key doesn't match the * current key), lets try to create a new cache slot for this * request. If this fails (for whatever reason), lets just generate - * the content without caching it and fool the caller to belive + * the content without caching it and fool the caller to believe * everything worked out (but print a warning on stdout). */ @@ -276,7 +321,7 @@ static int process_slot(struct cache_slot *slot) if ((err = lock_slot(slot)) != 0) { cache_log("[cgit] Unable to lock slot %s: %s (%d)\n", slot->lock_name, strerror(err), err); - slot->fn(slot->cbdata); + slot->fn(); return 0; } @@ -285,7 +330,7 @@ static int process_slot(struct cache_slot *slot) slot->lock_name, strerror(err), err); unlock_slot(slot, 0); close_lock(slot); - slot->fn(slot->cbdata); + slot->fn(); return 0; } // We've got a valid cache slot in the lock file, which @@ -309,7 +354,7 @@ static int process_slot(struct cache_slot *slot) /* Print cached content to stdout, generate the content if necessary. */ int cache_process(int size, const char *path, const char *key, int ttl, - cache_fill_fn fn, void *cbdata) + cache_fill_fn fn) { unsigned long hash; int i; @@ -319,15 +364,15 @@ int cache_process(int size, const char *path, const char *key, int ttl, int result; /* If the cache is disabled, just generate the content */ - if (size <= 0) { - fn(cbdata); + if (size <= 0 || ttl == 0) { + fn(); return 0; } /* Verify input, calculate filenames */ if (!path) { cache_log("[cgit] Cache path not specified, caching is disabled\n"); - fn(cbdata); + fn(); return 0; } if (!key) @@ -342,8 +387,8 @@ int cache_process(int size, const char *path, const char *key, int ttl, strbuf_addbuf(&lockname, &filename); strbuf_addstr(&lockname, ".lock"); slot.fn = fn; - slot.cbdata = cbdata; slot.ttl = ttl; + slot.stdout_fd = -1; slot.cache_name = filename.buf; slot.lock_name = lockname.buf; slot.key = key; @@ -361,12 +406,12 @@ int cache_process(int size, const char *path, const char *key, int ttl, static char *sprintftime(const char *format, time_t time) { static char buf[64]; - struct tm *tm; + struct tm tm; if (!time) return NULL; - tm = gmtime(&time); - strftime(buf, sizeof(buf)-1, format, tm); + gmtime_r(&time, &tm); + strftime(buf, sizeof(buf)-1, format, &tm); return buf; } @@ -375,7 +420,7 @@ int cache_ls(const char *path) DIR *dir; struct dirent *ent; int err = 0; - struct cache_slot slot; + struct cache_slot slot = { NULL }; struct strbuf fullname = STRBUF_INIT; size_t prefixlen; @@ -404,12 +449,12 @@ int cache_ls(const char *path) fullname.buf, strerror(err), err); continue; } - printf("%s %s %10"PRIuMAX" %s\n", - fullname.buf, - sprintftime("%Y-%m-%d %H:%M:%S", - slot.cache_st.st_mtime), - (uintmax_t)slot.cache_st.st_size, - slot.buf); + htmlf("%s %s %10"PRIuMAX" %s\n", + fullname.buf, + sprintftime("%Y-%m-%d %H:%M:%S", + slot.cache_st.st_mtime), + (uintmax_t)slot.cache_st.st_size, + slot.buf); close_slot(&slot); } closedir(dir);