summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-04-28 10:10:13 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-04-28 10:10:13 (UTC)
commit9000bbf865cb3578ba5ed3810dc44253cb46ec7f (patch) (unidiff)
treea9917628c40301862d1b1d6f5ecaf803ecc0f714
parent939d32fda70ea66c9db51687beb3cea6da7b0599 (diff)
downloadcgit-9000bbf865cb3578ba5ed3810dc44253cb46ec7f.zip
cgit-9000bbf865cb3578ba5ed3810dc44253cb46ec7f.tar.gz
cgit-9000bbf865cb3578ba5ed3810dc44253cb46ec7f.tar.bz2
Add page 'ls_cache'
This new page will list all entries found in the current cache, which is useful when reviewing the new cache implementation. There are no links to the new page, but it's reachable by adding 'p=ls_cache' to any cgit url. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cache.c68
-rw-r--r--cache.h3
-rw-r--r--cmd.c21
3 files changed, 87 insertions, 5 deletions
diff --git a/cache.c b/cache.c
index e590d7b..b701e13 100644
--- a/cache.c
+++ b/cache.c
@@ -1,343 +1,411 @@
1/* cache.c: cache management 1/* cache.c: cache management
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 * 7 *
8 * 8 *
9 * The cache is just a directory structure where each file is a cache slot, 9 * The cache is just a directory structure where each file is a cache slot,
10 * and each filename is based on the hash of some key (e.g. the cgit url). 10 * and each filename is based on the hash of some key (e.g. the cgit url).
11 * Each file contains the full key followed by the cached content for that 11 * Each file contains the full key followed by the cached content for that
12 * key. 12 * key.
13 * 13 *
14 */ 14 */
15 15
16#include "cgit.h" 16#include "cgit.h"
17#include "cache.h" 17#include "cache.h"
18 18
19#define CACHE_BUFSIZE (1024 * 4) 19#define CACHE_BUFSIZE (1024 * 4)
20 20
21struct cache_slot { 21struct cache_slot {
22 const char *key; 22 const char *key;
23 int keylen; 23 int keylen;
24 int ttl; 24 int ttl;
25 cache_fill_fn fn; 25 cache_fill_fn fn;
26 void *cbdata; 26 void *cbdata;
27 int cache_fd; 27 int cache_fd;
28 int lock_fd; 28 int lock_fd;
29 const char *cache_name; 29 const char *cache_name;
30 const char *lock_name; 30 const char *lock_name;
31 int match; 31 int match;
32 struct stat cache_st; 32 struct stat cache_st;
33 struct stat lock_st; 33 struct stat lock_st;
34 int bufsize; 34 int bufsize;
35 char buf[CACHE_BUFSIZE]; 35 char buf[CACHE_BUFSIZE];
36}; 36};
37 37
38/* Open an existing cache slot and fill the cache buffer with 38/* Open an existing cache slot and fill the cache buffer with
39 * (part of) the content of the cache file. Return 0 on success 39 * (part of) the content of the cache file. Return 0 on success
40 * and errno otherwise. 40 * and errno otherwise.
41 */ 41 */
42static int open_slot(struct cache_slot *slot) 42static int open_slot(struct cache_slot *slot)
43{ 43{
44 char *bufz; 44 char *bufz;
45 int bufkeylen = -1; 45 int bufkeylen = -1;
46 46
47 slot->cache_fd = open(slot->cache_name, O_RDONLY); 47 slot->cache_fd = open(slot->cache_name, O_RDONLY);
48 if (slot->cache_fd == -1) 48 if (slot->cache_fd == -1)
49 return errno; 49 return errno;
50 50
51 if (fstat(slot->cache_fd, &slot->cache_st)) 51 if (fstat(slot->cache_fd, &slot->cache_st))
52 return errno; 52 return errno;
53 53
54 slot->bufsize = read(slot->cache_fd, slot->buf, sizeof(slot->buf)); 54 slot->bufsize = read(slot->cache_fd, slot->buf, sizeof(slot->buf));
55 if (slot->bufsize == 0) 55 if (slot->bufsize == 0)
56 return errno; 56 return errno;
57 57
58 bufz = memchr(slot->buf, 0, slot->bufsize); 58 bufz = memchr(slot->buf, 0, slot->bufsize);
59 if (bufz) 59 if (bufz)
60 bufkeylen = bufz - slot->buf; 60 bufkeylen = bufz - slot->buf;
61 61
62 slot->match = bufkeylen == slot->keylen && 62 slot->match = bufkeylen == slot->keylen &&
63 !memcmp(slot->key, slot->buf, bufkeylen + 1); 63 !memcmp(slot->key, slot->buf, bufkeylen + 1);
64 64
65 return 0; 65 return 0;
66} 66}
67 67
68/* Close the active cache slot */ 68/* Close the active cache slot */
69static void close_slot(struct cache_slot *slot) 69static void close_slot(struct cache_slot *slot)
70{ 70{
71 if (slot->cache_fd > 0) { 71 if (slot->cache_fd > 0) {
72 close(slot->cache_fd); 72 close(slot->cache_fd);
73 slot->cache_fd = -1; 73 slot->cache_fd = -1;
74 } 74 }
75} 75}
76 76
77/* Print the content of the active cache slot (but skip the key). */ 77/* Print the content of the active cache slot (but skip the key). */
78static int print_slot(struct cache_slot *slot) 78static int print_slot(struct cache_slot *slot)
79{ 79{
80 ssize_t i, j = 0; 80 ssize_t i, j = 0;
81 81
82 i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); 82 i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
83 if (i != slot->keylen + 1) 83 if (i != slot->keylen + 1)
84 return errno; 84 return errno;
85 85
86 while((i=read(slot->cache_fd, slot->buf, sizeof(slot->buf))) > 0) 86 while((i=read(slot->cache_fd, slot->buf, sizeof(slot->buf))) > 0)
87 j = write(STDOUT_FILENO, slot->buf, i); 87 j = write(STDOUT_FILENO, slot->buf, i);
88 88
89 if (j < 0) 89 if (j < 0)
90 return errno; 90 return errno;
91 else 91 else
92 return 0; 92 return 0;
93} 93}
94 94
95/* Check if the slot has expired */ 95/* Check if the slot has expired */
96static int is_expired(struct cache_slot *slot) 96static int is_expired(struct cache_slot *slot)
97{ 97{
98 if (slot->ttl < 0) 98 if (slot->ttl < 0)
99 return 0; 99 return 0;
100 else 100 else
101 return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL); 101 return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL);
102} 102}
103 103
104/* Check if the slot has been modified since we opened it. 104/* Check if the slot has been modified since we opened it.
105 * NB: If stat() fails, we pretend the file is modified. 105 * NB: If stat() fails, we pretend the file is modified.
106 */ 106 */
107static int is_modified(struct cache_slot *slot) 107static int is_modified(struct cache_slot *slot)
108{ 108{
109 struct stat st; 109 struct stat st;
110 110
111 if (stat(slot->cache_name, &st)) 111 if (stat(slot->cache_name, &st))
112 return 1; 112 return 1;
113 return (st.st_ino != slot->cache_st.st_ino || 113 return (st.st_ino != slot->cache_st.st_ino ||
114 st.st_mtime != slot->cache_st.st_mtime || 114 st.st_mtime != slot->cache_st.st_mtime ||
115 st.st_size != slot->cache_st.st_size); 115 st.st_size != slot->cache_st.st_size);
116} 116}
117 117
118/* Close an open lockfile */ 118/* Close an open lockfile */
119static void close_lock(struct cache_slot *slot) 119static void close_lock(struct cache_slot *slot)
120{ 120{
121 if (slot->lock_fd > 0) { 121 if (slot->lock_fd > 0) {
122 close(slot->lock_fd); 122 close(slot->lock_fd);
123 slot->lock_fd = -1; 123 slot->lock_fd = -1;
124 } 124 }
125} 125}
126 126
127/* Create a lockfile used to store the generated content for a cache 127/* Create a lockfile used to store the generated content for a cache
128 * slot, and write the slot key + \0 into it. 128 * slot, and write the slot key + \0 into it.
129 * Returns 0 on success and errno otherwise. 129 * Returns 0 on success and errno otherwise.
130 */ 130 */
131static int lock_slot(struct cache_slot *slot) 131static int lock_slot(struct cache_slot *slot)
132{ 132{
133 slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL, 133 slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL,
134 S_IRUSR|S_IWUSR); 134 S_IRUSR|S_IWUSR);
135 if (slot->lock_fd == -1) 135 if (slot->lock_fd == -1)
136 return errno; 136 return errno;
137 write(slot->lock_fd, slot->key, slot->keylen + 1); 137 write(slot->lock_fd, slot->key, slot->keylen + 1);
138 return 0; 138 return 0;
139} 139}
140 140
141/* Release the current lockfile. If `replace_old_slot` is set the 141/* Release the current lockfile. If `replace_old_slot` is set the
142 * lockfile replaces the old cache slot, otherwise the lockfile is 142 * lockfile replaces the old cache slot, otherwise the lockfile is
143 * just deleted. 143 * just deleted.
144 */ 144 */
145static int unlock_slot(struct cache_slot *slot, int replace_old_slot) 145static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
146{ 146{
147 int err; 147 int err;
148 148
149 if (replace_old_slot) 149 if (replace_old_slot)
150 err = rename(slot->lock_name, slot->cache_name); 150 err = rename(slot->lock_name, slot->cache_name);
151 else 151 else
152 err = unlink(slot->lock_name); 152 err = unlink(slot->lock_name);
153 return err; 153 return err;
154} 154}
155 155
156/* Generate the content for the current cache slot by redirecting 156/* Generate the content for the current cache slot by redirecting
157 * stdout to the lock-fd and invoking the callback function 157 * stdout to the lock-fd and invoking the callback function
158 */ 158 */
159static int fill_slot(struct cache_slot *slot) 159static int fill_slot(struct cache_slot *slot)
160{ 160{
161 int tmp; 161 int tmp;
162 162
163 /* Preserve stdout */ 163 /* Preserve stdout */
164 tmp = dup(STDOUT_FILENO); 164 tmp = dup(STDOUT_FILENO);
165 if (tmp == -1) 165 if (tmp == -1)
166 return errno; 166 return errno;
167 167
168 /* Redirect stdout to lockfile */ 168 /* Redirect stdout to lockfile */
169 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1) 169 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1)
170 return errno; 170 return errno;
171 171
172 /* Generate cache content */ 172 /* Generate cache content */
173 slot->fn(slot->cbdata); 173 slot->fn(slot->cbdata);
174 174
175 /* Restore stdout */ 175 /* Restore stdout */
176 if (dup2(tmp, STDOUT_FILENO) == -1) 176 if (dup2(tmp, STDOUT_FILENO) == -1)
177 return errno; 177 return errno;
178 178
179 /* Close the temporary filedescriptor */ 179 /* Close the temporary filedescriptor */
180 close(tmp); 180 close(tmp);
181 return 0; 181 return 0;
182} 182}
183 183
184/* Crude implementation of 32-bit FNV-1 hash algorithm, 184/* Crude implementation of 32-bit FNV-1 hash algorithm,
185 * see http://www.isthe.com/chongo/tech/comp/fnv/ for details 185 * see http://www.isthe.com/chongo/tech/comp/fnv/ for details
186 * about the magic numbers. 186 * about the magic numbers.
187 */ 187 */
188#define FNV_OFFSET 0x811c9dc5 188#define FNV_OFFSET 0x811c9dc5
189#define FNV_PRIME 0x01000193 189#define FNV_PRIME 0x01000193
190 190
191unsigned long hash_str(const char *str) 191unsigned long hash_str(const char *str)
192{ 192{
193 unsigned long h = FNV_OFFSET; 193 unsigned long h = FNV_OFFSET;
194 unsigned char *s = (unsigned char *)str; 194 unsigned char *s = (unsigned char *)str;
195 195
196 if (!s) 196 if (!s)
197 return h; 197 return h;
198 198
199 while(*s) { 199 while(*s) {
200 h *= FNV_PRIME; 200 h *= FNV_PRIME;
201 h ^= *s++; 201 h ^= *s++;
202 } 202 }
203 return h; 203 return h;
204} 204}
205 205
206static int process_slot(struct cache_slot *slot) 206static int process_slot(struct cache_slot *slot)
207{ 207{
208 int err; 208 int err;
209 209
210 err = open_slot(slot); 210 err = open_slot(slot);
211 if (!err && slot->match) { 211 if (!err && slot->match) {
212 if (is_expired(slot)) { 212 if (is_expired(slot)) {
213 if (!lock_slot(slot)) { 213 if (!lock_slot(slot)) {
214 /* If the cachefile has been replaced between 214 /* If the cachefile has been replaced between
215 * `open_slot` and `lock_slot`, we'll just 215 * `open_slot` and `lock_slot`, we'll just
216 * serve the stale content from the original 216 * serve the stale content from the original
217 * cachefile. This way we avoid pruning the 217 * cachefile. This way we avoid pruning the
218 * newly generated slot. The same code-path 218 * newly generated slot. The same code-path
219 * is chosen if fill_slot() fails for some 219 * is chosen if fill_slot() fails for some
220 * reason. 220 * reason.
221 * 221 *
222 * TODO? check if the new slot contains the 222 * TODO? check if the new slot contains the
223 * same key as the old one, since we would 223 * same key as the old one, since we would
224 * prefer to serve the newest content. 224 * prefer to serve the newest content.
225 * This will require us to open yet another 225 * This will require us to open yet another
226 * file-descriptor and read and compare the 226 * file-descriptor and read and compare the
227 * key from the new file, so for now we're 227 * key from the new file, so for now we're
228 * lazy and just ignore the new file. 228 * lazy and just ignore the new file.
229 */ 229 */
230 if (is_modified(slot) || fill_slot(slot)) { 230 if (is_modified(slot) || fill_slot(slot)) {
231 unlock_slot(slot, 0); 231 unlock_slot(slot, 0);
232 close_lock(slot); 232 close_lock(slot);
233 } else { 233 } else {
234 close_slot(slot); 234 close_slot(slot);
235 unlock_slot(slot, 1); 235 unlock_slot(slot, 1);
236 slot->cache_fd = slot->lock_fd; 236 slot->cache_fd = slot->lock_fd;
237 } 237 }
238 } 238 }
239 } 239 }
240 print_slot(slot); 240 print_slot(slot);
241 close_slot(slot); 241 close_slot(slot);
242 return 0; 242 return 0;
243 } 243 }
244 244
245 /* If the cache slot does not exist (or its key doesn't match the 245 /* If the cache slot does not exist (or its key doesn't match the
246 * current key), lets try to create a new cache slot for this 246 * current key), lets try to create a new cache slot for this
247 * request. If this fails (for whatever reason), lets just generate 247 * request. If this fails (for whatever reason), lets just generate
248 * the content without caching it and fool the caller to belive 248 * the content without caching it and fool the caller to belive
249 * everything worked out (but print a warning on stdout). 249 * everything worked out (but print a warning on stdout).
250 */ 250 */
251 251
252 close_slot(slot); 252 close_slot(slot);
253 if ((err = lock_slot(slot)) != 0) { 253 if ((err = lock_slot(slot)) != 0) {
254 cache_log("[cgit] Unable to lock slot %s: %s (%d)\n", 254 cache_log("[cgit] Unable to lock slot %s: %s (%d)\n",
255 slot->lock_name, strerror(err), err); 255 slot->lock_name, strerror(err), err);
256 slot->fn(slot->cbdata); 256 slot->fn(slot->cbdata);
257 return 0; 257 return 0;
258 } 258 }
259 259
260 if ((err = fill_slot(slot)) != 0) { 260 if ((err = fill_slot(slot)) != 0) {
261 cache_log("[cgit] Unable to fill slot %s: %s (%d)\n", 261 cache_log("[cgit] Unable to fill slot %s: %s (%d)\n",
262 slot->lock_name, strerror(err), err); 262 slot->lock_name, strerror(err), err);
263 unlock_slot(slot, 0); 263 unlock_slot(slot, 0);
264 close_lock(slot); 264 close_lock(slot);
265 slot->fn(slot->cbdata); 265 slot->fn(slot->cbdata);
266 return 0; 266 return 0;
267 } 267 }
268 // We've got a valid cache slot in the lock file, which 268 // We've got a valid cache slot in the lock file, which
269 // is about to replace the old cache slot. But if we 269 // is about to replace the old cache slot. But if we
270 // release the lockfile and then try to open the new cache 270 // release the lockfile and then try to open the new cache
271 // slot, we might get a race condition with a concurrent 271 // slot, we might get a race condition with a concurrent
272 // writer for the same cache slot (with a different key). 272 // writer for the same cache slot (with a different key).
273 // Lets avoid such a race by just printing the content of 273 // Lets avoid such a race by just printing the content of
274 // the lock file. 274 // the lock file.
275 slot->cache_fd = slot->lock_fd; 275 slot->cache_fd = slot->lock_fd;
276 unlock_slot(slot, 1); 276 unlock_slot(slot, 1);
277 err = print_slot(slot); 277 err = print_slot(slot);
278 close_slot(slot); 278 close_slot(slot);
279 return err; 279 return err;
280} 280}
281 281
282/* Print cached content to stdout, generate the content if necessary. */ 282/* Print cached content to stdout, generate the content if necessary. */
283int cache_process(int size, const char *path, const char *key, int ttl, 283int cache_process(int size, const char *path, const char *key, int ttl,
284 cache_fill_fn fn, void *cbdata) 284 cache_fill_fn fn, void *cbdata)
285{ 285{
286 unsigned long hash; 286 unsigned long hash;
287 int len, i; 287 int len, i;
288 char filename[1024]; 288 char filename[1024];
289 char lockname[1024 + 5]; /* 5 = ".lock" */ 289 char lockname[1024 + 5]; /* 5 = ".lock" */
290 struct cache_slot slot; 290 struct cache_slot slot;
291 291
292 /* If the cache is disabled, just generate the content */ 292 /* If the cache is disabled, just generate the content */
293 if (size <= 0) { 293 if (size <= 0) {
294 fn(cbdata); 294 fn(cbdata);
295 return 0; 295 return 0;
296 } 296 }
297 297
298 /* Verify input, calculate filenames */ 298 /* Verify input, calculate filenames */
299 if (!path) { 299 if (!path) {
300 cache_log("[cgit] Cache path not specified, caching is disabled\n"); 300 cache_log("[cgit] Cache path not specified, caching is disabled\n");
301 fn(cbdata); 301 fn(cbdata);
302 return 0; 302 return 0;
303 } 303 }
304 len = strlen(path); 304 len = strlen(path);
305 if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */ 305 if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */
306 cache_log("[cgit] Cache path too long, caching is disabled: %s\n", 306 cache_log("[cgit] Cache path too long, caching is disabled: %s\n",
307 path); 307 path);
308 fn(cbdata); 308 fn(cbdata);
309 return 0; 309 return 0;
310 } 310 }
311 if (!key) 311 if (!key)
312 key = ""; 312 key = "";
313 hash = hash_str(key) % size; 313 hash = hash_str(key) % size;
314 strcpy(filename, path); 314 strcpy(filename, path);
315 if (filename[len - 1] != '/') 315 if (filename[len - 1] != '/')
316 filename[len++] = '/'; 316 filename[len++] = '/';
317 for(i = 0; i < 8; i++) { 317 for(i = 0; i < 8; i++) {
318 sprintf(filename + len++, "%x", 318 sprintf(filename + len++, "%x",
319 (unsigned char)(hash & 0xf)); 319 (unsigned char)(hash & 0xf));
320 hash >>= 4; 320 hash >>= 4;
321 } 321 }
322 filename[len] = '\0'; 322 filename[len] = '\0';
323 strcpy(lockname, filename); 323 strcpy(lockname, filename);
324 strcpy(lockname + len, ".lock"); 324 strcpy(lockname + len, ".lock");
325 slot.fn = fn; 325 slot.fn = fn;
326 slot.cbdata = cbdata; 326 slot.cbdata = cbdata;
327 slot.ttl = ttl; 327 slot.ttl = ttl;
328 slot.cache_name = filename; 328 slot.cache_name = filename;
329 slot.lock_name = lockname; 329 slot.lock_name = lockname;
330 slot.key = key; 330 slot.key = key;
331 slot.keylen = strlen(key); 331 slot.keylen = strlen(key);
332 return process_slot(&slot); 332 return process_slot(&slot);
333} 333}
334 334
335/* Return a strftime formatted date/time
336 * NB: the result from this function is to shared memory
337 */
338char *sprintftime(const char *format, time_t time)
339{
340 static char buf[64];
341 struct tm *tm;
342
343 if (!time)
344 return NULL;
345 tm = gmtime(&time);
346 strftime(buf, sizeof(buf)-1, format, tm);
347 return buf;
348}
349
350int cache_ls(const char *path)
351{
352 DIR *dir;
353 struct dirent *ent;
354 int err = 0;
355 struct cache_slot slot;
356 char fullname[1024];
357 char *name;
358
359 if (!path) {
360 cache_log("[cgit] cache path not specified\n");
361 return -1;
362 }
363 if (strlen(path) > 1024 - 10) {
364 cache_log("[cgit] cache path too long: %s\n",
365 path);
366 return -1;
367 }
368 dir = opendir(path);
369 if (!dir) {
370 err = errno;
371 cache_log("[cgit] unable to open path %s: %s (%d)\n",
372 path, strerror(err), err);
373 return err;
374 }
375 strcpy(fullname, path);
376 name = fullname + strlen(path);
377 if (*(name - 1) != '/') {
378 *name++ = '/';
379 *name = '\0';
380 }
381 slot.cache_name = fullname;
382 while((ent = readdir(dir)) != NULL) {
383 if (strlen(ent->d_name) != 8)
384 continue;
385 strcpy(name, ent->d_name);
386 if ((err = open_slot(&slot)) != 0) {
387 cache_log("[cgit] unable to open path %s: %s (%d)\n",
388 fullname, strerror(err), err);
389 continue;
390 }
391 printf("%s %s %10lld %s\n",
392 name,
393 sprintftime("%Y-%m-%d %H:%M:%S",
394 slot.cache_st.st_mtime),
395 slot.cache_st.st_size,
396 slot.buf);
397 close_slot(&slot);
398 }
399 closedir(dir);
400 return 0;
401}
402
335/* Print a message to stdout */ 403/* Print a message to stdout */
336void cache_log(const char *format, ...) 404void cache_log(const char *format, ...)
337{ 405{
338 va_list args; 406 va_list args;
339 va_start(args, format); 407 va_start(args, format);
340 vfprintf(stderr, format, args); 408 vfprintf(stderr, format, args);
341 va_end(args); 409 va_end(args);
342} 410}
343 411
diff --git a/cache.h b/cache.h
index 5f2178d..66cc41f 100644
--- a/cache.h
+++ b/cache.h
@@ -1,32 +1,35 @@
1/* 1/*
2 * Since git has it's own cache.h which we include, 2 * Since git has it's own cache.h which we include,
3 * lets test on CGIT_CACHE_H to avoid confusion 3 * lets test on CGIT_CACHE_H to avoid confusion
4 */ 4 */
5 5
6#ifndef CGIT_CACHE_H 6#ifndef CGIT_CACHE_H
7#define CGIT_CACHE_H 7#define CGIT_CACHE_H
8 8
9typedef void (*cache_fill_fn)(void *cbdata); 9typedef void (*cache_fill_fn)(void *cbdata);
10 10
11 11
12/* Print cached content to stdout, generate the content if necessary. 12/* Print cached content to stdout, generate the content if necessary.
13 * 13 *
14 * Parameters 14 * Parameters
15 * size max number of cache files 15 * size max number of cache files
16 * path directory used to store cache files 16 * path directory used to store cache files
17 * key the key used to lookup cache files 17 * key the key used to lookup cache files
18 * ttl max cache time in seconds for this key 18 * ttl max cache time in seconds for this key
19 * fn content generator function for this key 19 * fn content generator function for this key
20 * cbdata user-supplied data to the content generator function 20 * cbdata user-supplied data to the content generator function
21 * 21 *
22 * Return value 22 * Return value
23 * 0 indicates success, everyting else is an error 23 * 0 indicates success, everyting else is an error
24 */ 24 */
25extern int cache_process(int size, const char *path, const char *key, int ttl, 25extern int cache_process(int size, const char *path, const char *key, int ttl,
26 cache_fill_fn fn, void *cbdata); 26 cache_fill_fn fn, void *cbdata);
27 27
28 28
29/* List info about all cache entries on stdout */
30extern int cache_ls(const char *path);
31
29/* Print a message to stdout */ 32/* Print a message to stdout */
30extern void cache_log(const char *format, ...); 33extern void cache_log(const char *format, ...);
31 34
32#endif /* CGIT_CACHE_H */ 35#endif /* CGIT_CACHE_H */
diff --git a/cmd.c b/cmd.c
index e0eacbe..07f4707 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,112 +1,123 @@
1/* cmd.c: the cgit command dispatcher 1/* cmd.c: the cgit command dispatcher
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "cache.h"
12#include "ui-shared.h"
11#include "ui-blob.h" 13#include "ui-blob.h"
12#include "ui-commit.h" 14#include "ui-commit.h"
13#include "ui-diff.h" 15#include "ui-diff.h"
14#include "ui-log.h" 16#include "ui-log.h"
15#include "ui-patch.h" 17#include "ui-patch.h"
16#include "ui-refs.h" 18#include "ui-refs.h"
17#include "ui-repolist.h" 19#include "ui-repolist.h"
18#include "ui-snapshot.h" 20#include "ui-snapshot.h"
19#include "ui-summary.h" 21#include "ui-summary.h"
20#include "ui-tag.h" 22#include "ui-tag.h"
21#include "ui-tree.h" 23#include "ui-tree.h"
22 24
23static void blob_fn(struct cgit_context *ctx) 25static void blob_fn(struct cgit_context *ctx)
24{ 26{
25 cgit_print_blob(ctx->qry.sha1, ctx->qry.path); 27 cgit_print_blob(ctx->qry.sha1, ctx->qry.path);
26} 28}
27 29
28static void commit_fn(struct cgit_context *ctx) 30static void commit_fn(struct cgit_context *ctx)
29{ 31{
30 cgit_print_commit(ctx->qry.sha1); 32 cgit_print_commit(ctx->qry.sha1);
31} 33}
32 34
33static void diff_fn(struct cgit_context *ctx) 35static void diff_fn(struct cgit_context *ctx)
34{ 36{
35 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 37 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
36} 38}
37 39
38static void repolist_fn(struct cgit_context *ctx)
39{
40 cgit_print_repolist();
41}
42
43static void log_fn(struct cgit_context *ctx) 40static void log_fn(struct cgit_context *ctx)
44{ 41{
45 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 42 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
46 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 43 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
47} 44}
48 45
46static void ls_cache_fn(struct cgit_context *ctx)
47{
48 ctx->page.mimetype = "text/plain";
49 ctx->page.filename = "ls-cache.txt";
50 cgit_print_http_headers(ctx);
51 cache_ls(ctx->cfg.cache_root);
52}
53
54static void repolist_fn(struct cgit_context *ctx)
55{
56 cgit_print_repolist();
57}
58
49static void patch_fn(struct cgit_context *ctx) 59static void patch_fn(struct cgit_context *ctx)
50{ 60{
51 cgit_print_patch(ctx->qry.sha1); 61 cgit_print_patch(ctx->qry.sha1);
52} 62}
53 63
54static void refs_fn(struct cgit_context *ctx) 64static void refs_fn(struct cgit_context *ctx)
55{ 65{
56 cgit_print_refs(); 66 cgit_print_refs();
57} 67}
58 68
59static void snapshot_fn(struct cgit_context *ctx) 69static void snapshot_fn(struct cgit_context *ctx)
60{ 70{
61 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, 71 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1,
62 cgit_repobasename(ctx->repo->url), ctx->qry.path, 72 cgit_repobasename(ctx->repo->url), ctx->qry.path,
63 ctx->repo->snapshots); 73 ctx->repo->snapshots);
64} 74}
65 75
66static void summary_fn(struct cgit_context *ctx) 76static void summary_fn(struct cgit_context *ctx)
67{ 77{
68 cgit_print_summary(); 78 cgit_print_summary();
69} 79}
70 80
71static void tag_fn(struct cgit_context *ctx) 81static void tag_fn(struct cgit_context *ctx)
72{ 82{
73 cgit_print_tag(ctx->qry.sha1); 83 cgit_print_tag(ctx->qry.sha1);
74} 84}
75 85
76static void tree_fn(struct cgit_context *ctx) 86static void tree_fn(struct cgit_context *ctx)
77{ 87{
78 cgit_print_tree(ctx->qry.sha1, ctx->qry.path); 88 cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
79} 89}
80 90
81#define def_cmd(name, want_repo, want_layout) \ 91#define def_cmd(name, want_repo, want_layout) \
82 {#name, name##_fn, want_repo, want_layout} 92 {#name, name##_fn, want_repo, want_layout}
83 93
84struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) 94struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
85{ 95{
86 static struct cgit_cmd cmds[] = { 96 static struct cgit_cmd cmds[] = {
87 def_cmd(blob, 1, 0), 97 def_cmd(blob, 1, 0),
88 def_cmd(commit, 1, 1), 98 def_cmd(commit, 1, 1),
89 def_cmd(diff, 1, 1), 99 def_cmd(diff, 1, 1),
90 def_cmd(log, 1, 1), 100 def_cmd(log, 1, 1),
101 def_cmd(ls_cache, 0, 0),
91 def_cmd(patch, 1, 0), 102 def_cmd(patch, 1, 0),
92 def_cmd(refs, 1, 1), 103 def_cmd(refs, 1, 1),
93 def_cmd(repolist, 0, 0), 104 def_cmd(repolist, 0, 0),
94 def_cmd(snapshot, 1, 0), 105 def_cmd(snapshot, 1, 0),
95 def_cmd(summary, 1, 1), 106 def_cmd(summary, 1, 1),
96 def_cmd(tag, 1, 1), 107 def_cmd(tag, 1, 1),
97 def_cmd(tree, 1, 1), 108 def_cmd(tree, 1, 1),
98 }; 109 };
99 int i; 110 int i;
100 111
101 if (ctx->qry.page == NULL) { 112 if (ctx->qry.page == NULL) {
102 if (ctx->repo) 113 if (ctx->repo)
103 ctx->qry.page = "summary"; 114 ctx->qry.page = "summary";
104 else 115 else
105 ctx->qry.page = "repolist"; 116 ctx->qry.page = "repolist";
106 } 117 }
107 118
108 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) 119 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
109 if (!strcmp(ctx->qry.page, cmds[i].name)) 120 if (!strcmp(ctx->qry.page, cmds[i].name))
110 return &cmds[i]; 121 return &cmds[i];
111 return NULL; 122 return NULL;
112} 123}