summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-05-18 21:59:11 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-05-18 21:59:11 (UTC)
commitaf2e75616d1bfb7dc79d299d10ae0bd39bef47bc (patch) (unidiff)
tree6330a6f9bc1b2b16434df055ee48129e2e3b827e
parentcdc6b2f8e7a8d43dcfe0475a9d3498333ea686b8 (diff)
downloadcgit-af2e75616d1bfb7dc79d299d10ae0bd39bef47bc.zip
cgit-af2e75616d1bfb7dc79d299d10ae0bd39bef47bc.tar.gz
cgit-af2e75616d1bfb7dc79d299d10ae0bd39bef47bc.tar.bz2
cache.c: do not ignore errors from print_slot()
If print_slot() fails, the client will be served an inferior response. This patch makes sure that such an error will be returned to main(), which in turn will try to inform about the error in the response itself. The error is also printed to the cache_log, i.e. stderr, which will make the error message appear in error_log (atleast when httpd==apache). Noticed-by: Jim Meyering <jim@meyering.net> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cache.c16
-rw-r--r--cgit.c4
2 files changed, 15 insertions, 5 deletions
diff --git a/cache.c b/cache.c
index a996109..aa97ae1 100644
--- a/cache.c
+++ b/cache.c
@@ -1,426 +1,436 @@
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 = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); 54 slot->bufsize = xread(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 int close_slot(struct cache_slot *slot) 69static int close_slot(struct cache_slot *slot)
70{ 70{
71 int err = 0; 71 int err = 0;
72 if (slot->cache_fd > 0) { 72 if (slot->cache_fd > 0) {
73 if (close(slot->cache_fd)) 73 if (close(slot->cache_fd))
74 err = errno; 74 err = errno;
75 else 75 else
76 slot->cache_fd = -1; 76 slot->cache_fd = -1;
77 } 77 }
78 return err; 78 return err;
79} 79}
80 80
81/* Print the content of the active cache slot (but skip the key). */ 81/* Print the content of the active cache slot (but skip the key). */
82static int print_slot(struct cache_slot *slot) 82static int print_slot(struct cache_slot *slot)
83{ 83{
84 ssize_t i; 84 ssize_t i;
85 85
86 i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); 86 i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
87 if (i != slot->keylen + 1) 87 if (i != slot->keylen + 1)
88 return errno; 88 return errno;
89 89
90 while((i = xread(slot->cache_fd, slot->buf, sizeof(slot->buf))) > 0) 90 while((i = xread(slot->cache_fd, slot->buf, sizeof(slot->buf))) > 0)
91 i = xwrite(STDOUT_FILENO, slot->buf, i); 91 i = xwrite(STDOUT_FILENO, slot->buf, i);
92 92
93 if (i < 0) 93 if (i < 0)
94 return errno; 94 return errno;
95 else 95 else
96 return 0; 96 return 0;
97} 97}
98 98
99/* Check if the slot has expired */ 99/* Check if the slot has expired */
100static int is_expired(struct cache_slot *slot) 100static int is_expired(struct cache_slot *slot)
101{ 101{
102 if (slot->ttl < 0) 102 if (slot->ttl < 0)
103 return 0; 103 return 0;
104 else 104 else
105 return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL); 105 return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL);
106} 106}
107 107
108/* Check if the slot has been modified since we opened it. 108/* Check if the slot has been modified since we opened it.
109 * NB: If stat() fails, we pretend the file is modified. 109 * NB: If stat() fails, we pretend the file is modified.
110 */ 110 */
111static int is_modified(struct cache_slot *slot) 111static int is_modified(struct cache_slot *slot)
112{ 112{
113 struct stat st; 113 struct stat st;
114 114
115 if (stat(slot->cache_name, &st)) 115 if (stat(slot->cache_name, &st))
116 return 1; 116 return 1;
117 return (st.st_ino != slot->cache_st.st_ino || 117 return (st.st_ino != slot->cache_st.st_ino ||
118 st.st_mtime != slot->cache_st.st_mtime || 118 st.st_mtime != slot->cache_st.st_mtime ||
119 st.st_size != slot->cache_st.st_size); 119 st.st_size != slot->cache_st.st_size);
120} 120}
121 121
122/* Close an open lockfile */ 122/* Close an open lockfile */
123static int close_lock(struct cache_slot *slot) 123static int close_lock(struct cache_slot *slot)
124{ 124{
125 int err = 0; 125 int err = 0;
126 if (slot->lock_fd > 0) { 126 if (slot->lock_fd > 0) {
127 if (close(slot->lock_fd)) 127 if (close(slot->lock_fd))
128 err = errno; 128 err = errno;
129 else 129 else
130 slot->lock_fd = -1; 130 slot->lock_fd = -1;
131 } 131 }
132 return err; 132 return err;
133} 133}
134 134
135/* Create a lockfile used to store the generated content for a cache 135/* Create a lockfile used to store the generated content for a cache
136 * slot, and write the slot key + \0 into it. 136 * slot, and write the slot key + \0 into it.
137 * Returns 0 on success and errno otherwise. 137 * Returns 0 on success and errno otherwise.
138 */ 138 */
139static int lock_slot(struct cache_slot *slot) 139static int lock_slot(struct cache_slot *slot)
140{ 140{
141 slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL, 141 slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL,
142 S_IRUSR|S_IWUSR); 142 S_IRUSR|S_IWUSR);
143 if (slot->lock_fd == -1) 143 if (slot->lock_fd == -1)
144 return errno; 144 return errno;
145 if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) 145 if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0)
146 return errno; 146 return errno;
147 return 0; 147 return 0;
148} 148}
149 149
150/* Release the current lockfile. If `replace_old_slot` is set the 150/* Release the current lockfile. If `replace_old_slot` is set the
151 * lockfile replaces the old cache slot, otherwise the lockfile is 151 * lockfile replaces the old cache slot, otherwise the lockfile is
152 * just deleted. 152 * just deleted.
153 */ 153 */
154static int unlock_slot(struct cache_slot *slot, int replace_old_slot) 154static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
155{ 155{
156 int err; 156 int err;
157 157
158 if (replace_old_slot) 158 if (replace_old_slot)
159 err = rename(slot->lock_name, slot->cache_name); 159 err = rename(slot->lock_name, slot->cache_name);
160 else 160 else
161 err = unlink(slot->lock_name); 161 err = unlink(slot->lock_name);
162 162
163 if (err) 163 if (err)
164 return errno; 164 return errno;
165 165
166 return 0; 166 return 0;
167} 167}
168 168
169/* Generate the content for the current cache slot by redirecting 169/* Generate the content for the current cache slot by redirecting
170 * stdout to the lock-fd and invoking the callback function 170 * stdout to the lock-fd and invoking the callback function
171 */ 171 */
172static int fill_slot(struct cache_slot *slot) 172static int fill_slot(struct cache_slot *slot)
173{ 173{
174 int tmp; 174 int tmp;
175 175
176 /* Preserve stdout */ 176 /* Preserve stdout */
177 tmp = dup(STDOUT_FILENO); 177 tmp = dup(STDOUT_FILENO);
178 if (tmp == -1) 178 if (tmp == -1)
179 return errno; 179 return errno;
180 180
181 /* Redirect stdout to lockfile */ 181 /* Redirect stdout to lockfile */
182 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1) 182 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1)
183 return errno; 183 return errno;
184 184
185 /* Generate cache content */ 185 /* Generate cache content */
186 slot->fn(slot->cbdata); 186 slot->fn(slot->cbdata);
187 187
188 /* Restore stdout */ 188 /* Restore stdout */
189 if (dup2(tmp, STDOUT_FILENO) == -1) 189 if (dup2(tmp, STDOUT_FILENO) == -1)
190 return errno; 190 return errno;
191 191
192 /* Close the temporary filedescriptor */ 192 /* Close the temporary filedescriptor */
193 if (close(tmp)) 193 if (close(tmp))
194 return errno; 194 return errno;
195 195
196 return 0; 196 return 0;
197} 197}
198 198
199/* Crude implementation of 32-bit FNV-1 hash algorithm, 199/* Crude implementation of 32-bit FNV-1 hash algorithm,
200 * see http://www.isthe.com/chongo/tech/comp/fnv/ for details 200 * see http://www.isthe.com/chongo/tech/comp/fnv/ for details
201 * about the magic numbers. 201 * about the magic numbers.
202 */ 202 */
203#define FNV_OFFSET 0x811c9dc5 203#define FNV_OFFSET 0x811c9dc5
204#define FNV_PRIME 0x01000193 204#define FNV_PRIME 0x01000193
205 205
206unsigned long hash_str(const char *str) 206unsigned long hash_str(const char *str)
207{ 207{
208 unsigned long h = FNV_OFFSET; 208 unsigned long h = FNV_OFFSET;
209 unsigned char *s = (unsigned char *)str; 209 unsigned char *s = (unsigned char *)str;
210 210
211 if (!s) 211 if (!s)
212 return h; 212 return h;
213 213
214 while(*s) { 214 while(*s) {
215 h *= FNV_PRIME; 215 h *= FNV_PRIME;
216 h ^= *s++; 216 h ^= *s++;
217 } 217 }
218 return h; 218 return h;
219} 219}
220 220
221static int process_slot(struct cache_slot *slot) 221static int process_slot(struct cache_slot *slot)
222{ 222{
223 int err; 223 int err;
224 224
225 err = open_slot(slot); 225 err = open_slot(slot);
226 if (!err && slot->match) { 226 if (!err && slot->match) {
227 if (is_expired(slot)) { 227 if (is_expired(slot)) {
228 if (!lock_slot(slot)) { 228 if (!lock_slot(slot)) {
229 /* If the cachefile has been replaced between 229 /* If the cachefile has been replaced between
230 * `open_slot` and `lock_slot`, we'll just 230 * `open_slot` and `lock_slot`, we'll just
231 * serve the stale content from the original 231 * serve the stale content from the original
232 * cachefile. This way we avoid pruning the 232 * cachefile. This way we avoid pruning the
233 * newly generated slot. The same code-path 233 * newly generated slot. The same code-path
234 * is chosen if fill_slot() fails for some 234 * is chosen if fill_slot() fails for some
235 * reason. 235 * reason.
236 * 236 *
237 * TODO? check if the new slot contains the 237 * TODO? check if the new slot contains the
238 * same key as the old one, since we would 238 * same key as the old one, since we would
239 * prefer to serve the newest content. 239 * prefer to serve the newest content.
240 * This will require us to open yet another 240 * This will require us to open yet another
241 * file-descriptor and read and compare the 241 * file-descriptor and read and compare the
242 * key from the new file, so for now we're 242 * key from the new file, so for now we're
243 * lazy and just ignore the new file. 243 * lazy and just ignore the new file.
244 */ 244 */
245 if (is_modified(slot) || fill_slot(slot)) { 245 if (is_modified(slot) || fill_slot(slot)) {
246 unlock_slot(slot, 0); 246 unlock_slot(slot, 0);
247 close_lock(slot); 247 close_lock(slot);
248 } else { 248 } else {
249 close_slot(slot); 249 close_slot(slot);
250 unlock_slot(slot, 1); 250 unlock_slot(slot, 1);
251 slot->cache_fd = slot->lock_fd; 251 slot->cache_fd = slot->lock_fd;
252 } 252 }
253 } 253 }
254 } 254 }
255 print_slot(slot); 255 if ((err = print_slot(slot)) != 0) {
256 cache_log("[cgit] error printing cache %s: %s (%d)\n",
257 slot->cache_name,
258 strerror(err),
259 err);
260 }
256 close_slot(slot); 261 close_slot(slot);
257 return 0; 262 return err;
258 } 263 }
259 264
260 /* If the cache slot does not exist (or its key doesn't match the 265 /* If the cache slot does not exist (or its key doesn't match the
261 * current key), lets try to create a new cache slot for this 266 * current key), lets try to create a new cache slot for this
262 * request. If this fails (for whatever reason), lets just generate 267 * request. If this fails (for whatever reason), lets just generate
263 * the content without caching it and fool the caller to belive 268 * the content without caching it and fool the caller to belive
264 * everything worked out (but print a warning on stdout). 269 * everything worked out (but print a warning on stdout).
265 */ 270 */
266 271
267 close_slot(slot); 272 close_slot(slot);
268 if ((err = lock_slot(slot)) != 0) { 273 if ((err = lock_slot(slot)) != 0) {
269 cache_log("[cgit] Unable to lock slot %s: %s (%d)\n", 274 cache_log("[cgit] Unable to lock slot %s: %s (%d)\n",
270 slot->lock_name, strerror(err), err); 275 slot->lock_name, strerror(err), err);
271 slot->fn(slot->cbdata); 276 slot->fn(slot->cbdata);
272 return 0; 277 return 0;
273 } 278 }
274 279
275 if ((err = fill_slot(slot)) != 0) { 280 if ((err = fill_slot(slot)) != 0) {
276 cache_log("[cgit] Unable to fill slot %s: %s (%d)\n", 281 cache_log("[cgit] Unable to fill slot %s: %s (%d)\n",
277 slot->lock_name, strerror(err), err); 282 slot->lock_name, strerror(err), err);
278 unlock_slot(slot, 0); 283 unlock_slot(slot, 0);
279 close_lock(slot); 284 close_lock(slot);
280 slot->fn(slot->cbdata); 285 slot->fn(slot->cbdata);
281 return 0; 286 return 0;
282 } 287 }
283 // We've got a valid cache slot in the lock file, which 288 // We've got a valid cache slot in the lock file, which
284 // is about to replace the old cache slot. But if we 289 // is about to replace the old cache slot. But if we
285 // release the lockfile and then try to open the new cache 290 // release the lockfile and then try to open the new cache
286 // slot, we might get a race condition with a concurrent 291 // slot, we might get a race condition with a concurrent
287 // writer for the same cache slot (with a different key). 292 // writer for the same cache slot (with a different key).
288 // Lets avoid such a race by just printing the content of 293 // Lets avoid such a race by just printing the content of
289 // the lock file. 294 // the lock file.
290 slot->cache_fd = slot->lock_fd; 295 slot->cache_fd = slot->lock_fd;
291 unlock_slot(slot, 1); 296 unlock_slot(slot, 1);
292 err = print_slot(slot); 297 if ((err = print_slot(slot)) != 0) {
298 cache_log("[cgit] error printing cache %s: %s (%d)\n",
299 slot->cache_name,
300 strerror(err),
301 err);
302 }
293 close_slot(slot); 303 close_slot(slot);
294 return err; 304 return err;
295} 305}
296 306
297/* Print cached content to stdout, generate the content if necessary. */ 307/* Print cached content to stdout, generate the content if necessary. */
298int cache_process(int size, const char *path, const char *key, int ttl, 308int cache_process(int size, const char *path, const char *key, int ttl,
299 cache_fill_fn fn, void *cbdata) 309 cache_fill_fn fn, void *cbdata)
300{ 310{
301 unsigned long hash; 311 unsigned long hash;
302 int len, i; 312 int len, i;
303 char filename[1024]; 313 char filename[1024];
304 char lockname[1024 + 5]; /* 5 = ".lock" */ 314 char lockname[1024 + 5]; /* 5 = ".lock" */
305 struct cache_slot slot; 315 struct cache_slot slot;
306 316
307 /* If the cache is disabled, just generate the content */ 317 /* If the cache is disabled, just generate the content */
308 if (size <= 0) { 318 if (size <= 0) {
309 fn(cbdata); 319 fn(cbdata);
310 return 0; 320 return 0;
311 } 321 }
312 322
313 /* Verify input, calculate filenames */ 323 /* Verify input, calculate filenames */
314 if (!path) { 324 if (!path) {
315 cache_log("[cgit] Cache path not specified, caching is disabled\n"); 325 cache_log("[cgit] Cache path not specified, caching is disabled\n");
316 fn(cbdata); 326 fn(cbdata);
317 return 0; 327 return 0;
318 } 328 }
319 len = strlen(path); 329 len = strlen(path);
320 if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */ 330 if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */
321 cache_log("[cgit] Cache path too long, caching is disabled: %s\n", 331 cache_log("[cgit] Cache path too long, caching is disabled: %s\n",
322 path); 332 path);
323 fn(cbdata); 333 fn(cbdata);
324 return 0; 334 return 0;
325 } 335 }
326 if (!key) 336 if (!key)
327 key = ""; 337 key = "";
328 hash = hash_str(key) % size; 338 hash = hash_str(key) % size;
329 strcpy(filename, path); 339 strcpy(filename, path);
330 if (filename[len - 1] != '/') 340 if (filename[len - 1] != '/')
331 filename[len++] = '/'; 341 filename[len++] = '/';
332 for(i = 0; i < 8; i++) { 342 for(i = 0; i < 8; i++) {
333 sprintf(filename + len++, "%x", 343 sprintf(filename + len++, "%x",
334 (unsigned char)(hash & 0xf)); 344 (unsigned char)(hash & 0xf));
335 hash >>= 4; 345 hash >>= 4;
336 } 346 }
337 filename[len] = '\0'; 347 filename[len] = '\0';
338 strcpy(lockname, filename); 348 strcpy(lockname, filename);
339 strcpy(lockname + len, ".lock"); 349 strcpy(lockname + len, ".lock");
340 slot.fn = fn; 350 slot.fn = fn;
341 slot.cbdata = cbdata; 351 slot.cbdata = cbdata;
342 slot.ttl = ttl; 352 slot.ttl = ttl;
343 slot.cache_name = filename; 353 slot.cache_name = filename;
344 slot.lock_name = lockname; 354 slot.lock_name = lockname;
345 slot.key = key; 355 slot.key = key;
346 slot.keylen = strlen(key); 356 slot.keylen = strlen(key);
347 return process_slot(&slot); 357 return process_slot(&slot);
348} 358}
349 359
350/* Return a strftime formatted date/time 360/* Return a strftime formatted date/time
351 * NB: the result from this function is to shared memory 361 * NB: the result from this function is to shared memory
352 */ 362 */
353char *sprintftime(const char *format, time_t time) 363char *sprintftime(const char *format, time_t time)
354{ 364{
355 static char buf[64]; 365 static char buf[64];
356 struct tm *tm; 366 struct tm *tm;
357 367
358 if (!time) 368 if (!time)
359 return NULL; 369 return NULL;
360 tm = gmtime(&time); 370 tm = gmtime(&time);
361 strftime(buf, sizeof(buf)-1, format, tm); 371 strftime(buf, sizeof(buf)-1, format, tm);
362 return buf; 372 return buf;
363} 373}
364 374
365int cache_ls(const char *path) 375int cache_ls(const char *path)
366{ 376{
367 DIR *dir; 377 DIR *dir;
368 struct dirent *ent; 378 struct dirent *ent;
369 int err = 0; 379 int err = 0;
370 struct cache_slot slot; 380 struct cache_slot slot;
371 char fullname[1024]; 381 char fullname[1024];
372 char *name; 382 char *name;
373 383
374 if (!path) { 384 if (!path) {
375 cache_log("[cgit] cache path not specified\n"); 385 cache_log("[cgit] cache path not specified\n");
376 return -1; 386 return -1;
377 } 387 }
378 if (strlen(path) > 1024 - 10) { 388 if (strlen(path) > 1024 - 10) {
379 cache_log("[cgit] cache path too long: %s\n", 389 cache_log("[cgit] cache path too long: %s\n",
380 path); 390 path);
381 return -1; 391 return -1;
382 } 392 }
383 dir = opendir(path); 393 dir = opendir(path);
384 if (!dir) { 394 if (!dir) {
385 err = errno; 395 err = errno;
386 cache_log("[cgit] unable to open path %s: %s (%d)\n", 396 cache_log("[cgit] unable to open path %s: %s (%d)\n",
387 path, strerror(err), err); 397 path, strerror(err), err);
388 return err; 398 return err;
389 } 399 }
390 strcpy(fullname, path); 400 strcpy(fullname, path);
391 name = fullname + strlen(path); 401 name = fullname + strlen(path);
392 if (*(name - 1) != '/') { 402 if (*(name - 1) != '/') {
393 *name++ = '/'; 403 *name++ = '/';
394 *name = '\0'; 404 *name = '\0';
395 } 405 }
396 slot.cache_name = fullname; 406 slot.cache_name = fullname;
397 while((ent = readdir(dir)) != NULL) { 407 while((ent = readdir(dir)) != NULL) {
398 if (strlen(ent->d_name) != 8) 408 if (strlen(ent->d_name) != 8)
399 continue; 409 continue;
400 strcpy(name, ent->d_name); 410 strcpy(name, ent->d_name);
401 if ((err = open_slot(&slot)) != 0) { 411 if ((err = open_slot(&slot)) != 0) {
402 cache_log("[cgit] unable to open path %s: %s (%d)\n", 412 cache_log("[cgit] unable to open path %s: %s (%d)\n",
403 fullname, strerror(err), err); 413 fullname, strerror(err), err);
404 continue; 414 continue;
405 } 415 }
406 printf("%s %s %10lld %s\n", 416 printf("%s %s %10lld %s\n",
407 name, 417 name,
408 sprintftime("%Y-%m-%d %H:%M:%S", 418 sprintftime("%Y-%m-%d %H:%M:%S",
409 slot.cache_st.st_mtime), 419 slot.cache_st.st_mtime),
410 slot.cache_st.st_size, 420 slot.cache_st.st_size,
411 slot.buf); 421 slot.buf);
412 close_slot(&slot); 422 close_slot(&slot);
413 } 423 }
414 closedir(dir); 424 closedir(dir);
415 return 0; 425 return 0;
416} 426}
417 427
418/* Print a message to stdout */ 428/* Print a message to stdout */
419void cache_log(const char *format, ...) 429void cache_log(const char *format, ...)
420{ 430{
421 va_list args; 431 va_list args;
422 va_start(args, format); 432 va_start(args, format);
423 vfprintf(stderr, format, args); 433 vfprintf(stderr, format, args);
424 va_end(args); 434 va_end(args);
425} 435}
426 436
diff --git a/cgit.c b/cgit.c
index 2036ceb..ac882c3 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,386 +1,386 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
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#include "cgit.h" 9#include "cgit.h"
10#include "cache.h" 10#include "cache.h"
11#include "cmd.h" 11#include "cmd.h"
12#include "configfile.h" 12#include "configfile.h"
13#include "html.h" 13#include "html.h"
14#include "ui-shared.h" 14#include "ui-shared.h"
15 15
16const char *cgit_version = CGIT_VERSION; 16const char *cgit_version = CGIT_VERSION;
17 17
18void config_cb(const char *name, const char *value) 18void config_cb(const char *name, const char *value)
19{ 19{
20 if (!strcmp(name, "root-title")) 20 if (!strcmp(name, "root-title"))
21 ctx.cfg.root_title = xstrdup(value); 21 ctx.cfg.root_title = xstrdup(value);
22 else if (!strcmp(name, "root-desc")) 22 else if (!strcmp(name, "root-desc"))
23 ctx.cfg.root_desc = xstrdup(value); 23 ctx.cfg.root_desc = xstrdup(value);
24 else if (!strcmp(name, "root-readme")) 24 else if (!strcmp(name, "root-readme"))
25 ctx.cfg.root_readme = xstrdup(value); 25 ctx.cfg.root_readme = xstrdup(value);
26 else if (!strcmp(name, "css")) 26 else if (!strcmp(name, "css"))
27 ctx.cfg.css = xstrdup(value); 27 ctx.cfg.css = xstrdup(value);
28 else if (!strcmp(name, "logo")) 28 else if (!strcmp(name, "logo"))
29 ctx.cfg.logo = xstrdup(value); 29 ctx.cfg.logo = xstrdup(value);
30 else if (!strcmp(name, "index-header")) 30 else if (!strcmp(name, "index-header"))
31 ctx.cfg.index_header = xstrdup(value); 31 ctx.cfg.index_header = xstrdup(value);
32 else if (!strcmp(name, "index-info")) 32 else if (!strcmp(name, "index-info"))
33 ctx.cfg.index_info = xstrdup(value); 33 ctx.cfg.index_info = xstrdup(value);
34 else if (!strcmp(name, "logo-link")) 34 else if (!strcmp(name, "logo-link"))
35 ctx.cfg.logo_link = xstrdup(value); 35 ctx.cfg.logo_link = xstrdup(value);
36 else if (!strcmp(name, "module-link")) 36 else if (!strcmp(name, "module-link"))
37 ctx.cfg.module_link = xstrdup(value); 37 ctx.cfg.module_link = xstrdup(value);
38 else if (!strcmp(name, "virtual-root")) { 38 else if (!strcmp(name, "virtual-root")) {
39 ctx.cfg.virtual_root = trim_end(value, '/'); 39 ctx.cfg.virtual_root = trim_end(value, '/');
40 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 40 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
41 ctx.cfg.virtual_root = ""; 41 ctx.cfg.virtual_root = "";
42 } else if (!strcmp(name, "nocache")) 42 } else if (!strcmp(name, "nocache"))
43 ctx.cfg.nocache = atoi(value); 43 ctx.cfg.nocache = atoi(value);
44 else if (!strcmp(name, "snapshots")) 44 else if (!strcmp(name, "snapshots"))
45 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 45 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
46 else if (!strcmp(name, "enable-index-links")) 46 else if (!strcmp(name, "enable-index-links"))
47 ctx.cfg.enable_index_links = atoi(value); 47 ctx.cfg.enable_index_links = atoi(value);
48 else if (!strcmp(name, "enable-log-filecount")) 48 else if (!strcmp(name, "enable-log-filecount"))
49 ctx.cfg.enable_log_filecount = atoi(value); 49 ctx.cfg.enable_log_filecount = atoi(value);
50 else if (!strcmp(name, "enable-log-linecount")) 50 else if (!strcmp(name, "enable-log-linecount"))
51 ctx.cfg.enable_log_linecount = atoi(value); 51 ctx.cfg.enable_log_linecount = atoi(value);
52 else if (!strcmp(name, "cache-size")) 52 else if (!strcmp(name, "cache-size"))
53 ctx.cfg.cache_size = atoi(value); 53 ctx.cfg.cache_size = atoi(value);
54 else if (!strcmp(name, "cache-root")) 54 else if (!strcmp(name, "cache-root"))
55 ctx.cfg.cache_root = xstrdup(value); 55 ctx.cfg.cache_root = xstrdup(value);
56 else if (!strcmp(name, "cache-root-ttl")) 56 else if (!strcmp(name, "cache-root-ttl"))
57 ctx.cfg.cache_root_ttl = atoi(value); 57 ctx.cfg.cache_root_ttl = atoi(value);
58 else if (!strcmp(name, "cache-repo-ttl")) 58 else if (!strcmp(name, "cache-repo-ttl"))
59 ctx.cfg.cache_repo_ttl = atoi(value); 59 ctx.cfg.cache_repo_ttl = atoi(value);
60 else if (!strcmp(name, "cache-static-ttl")) 60 else if (!strcmp(name, "cache-static-ttl"))
61 ctx.cfg.cache_static_ttl = atoi(value); 61 ctx.cfg.cache_static_ttl = atoi(value);
62 else if (!strcmp(name, "cache-dynamic-ttl")) 62 else if (!strcmp(name, "cache-dynamic-ttl"))
63 ctx.cfg.cache_dynamic_ttl = atoi(value); 63 ctx.cfg.cache_dynamic_ttl = atoi(value);
64 else if (!strcmp(name, "max-message-length")) 64 else if (!strcmp(name, "max-message-length"))
65 ctx.cfg.max_msg_len = atoi(value); 65 ctx.cfg.max_msg_len = atoi(value);
66 else if (!strcmp(name, "max-repodesc-length")) 66 else if (!strcmp(name, "max-repodesc-length"))
67 ctx.cfg.max_repodesc_len = atoi(value); 67 ctx.cfg.max_repodesc_len = atoi(value);
68 else if (!strcmp(name, "max-repo-count")) 68 else if (!strcmp(name, "max-repo-count"))
69 ctx.cfg.max_repo_count = atoi(value); 69 ctx.cfg.max_repo_count = atoi(value);
70 else if (!strcmp(name, "max-commit-count")) 70 else if (!strcmp(name, "max-commit-count"))
71 ctx.cfg.max_commit_count = atoi(value); 71 ctx.cfg.max_commit_count = atoi(value);
72 else if (!strcmp(name, "summary-log")) 72 else if (!strcmp(name, "summary-log"))
73 ctx.cfg.summary_log = atoi(value); 73 ctx.cfg.summary_log = atoi(value);
74 else if (!strcmp(name, "summary-branches")) 74 else if (!strcmp(name, "summary-branches"))
75 ctx.cfg.summary_branches = atoi(value); 75 ctx.cfg.summary_branches = atoi(value);
76 else if (!strcmp(name, "summary-tags")) 76 else if (!strcmp(name, "summary-tags"))
77 ctx.cfg.summary_tags = atoi(value); 77 ctx.cfg.summary_tags = atoi(value);
78 else if (!strcmp(name, "agefile")) 78 else if (!strcmp(name, "agefile"))
79 ctx.cfg.agefile = xstrdup(value); 79 ctx.cfg.agefile = xstrdup(value);
80 else if (!strcmp(name, "renamelimit")) 80 else if (!strcmp(name, "renamelimit"))
81 ctx.cfg.renamelimit = atoi(value); 81 ctx.cfg.renamelimit = atoi(value);
82 else if (!strcmp(name, "robots")) 82 else if (!strcmp(name, "robots"))
83 ctx.cfg.robots = xstrdup(value); 83 ctx.cfg.robots = xstrdup(value);
84 else if (!strcmp(name, "clone-prefix")) 84 else if (!strcmp(name, "clone-prefix"))
85 ctx.cfg.clone_prefix = xstrdup(value); 85 ctx.cfg.clone_prefix = xstrdup(value);
86 else if (!strcmp(name, "repo.group")) 86 else if (!strcmp(name, "repo.group"))
87 ctx.cfg.repo_group = xstrdup(value); 87 ctx.cfg.repo_group = xstrdup(value);
88 else if (!strcmp(name, "repo.url")) 88 else if (!strcmp(name, "repo.url"))
89 ctx.repo = cgit_add_repo(value); 89 ctx.repo = cgit_add_repo(value);
90 else if (!strcmp(name, "repo.name")) 90 else if (!strcmp(name, "repo.name"))
91 ctx.repo->name = xstrdup(value); 91 ctx.repo->name = xstrdup(value);
92 else if (ctx.repo && !strcmp(name, "repo.path")) 92 else if (ctx.repo && !strcmp(name, "repo.path"))
93 ctx.repo->path = trim_end(value, '/'); 93 ctx.repo->path = trim_end(value, '/');
94 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 94 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
95 ctx.repo->clone_url = xstrdup(value); 95 ctx.repo->clone_url = xstrdup(value);
96 else if (ctx.repo && !strcmp(name, "repo.desc")) 96 else if (ctx.repo && !strcmp(name, "repo.desc"))
97 ctx.repo->desc = xstrdup(value); 97 ctx.repo->desc = xstrdup(value);
98 else if (ctx.repo && !strcmp(name, "repo.owner")) 98 else if (ctx.repo && !strcmp(name, "repo.owner"))
99 ctx.repo->owner = xstrdup(value); 99 ctx.repo->owner = xstrdup(value);
100 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 100 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
101 ctx.repo->defbranch = xstrdup(value); 101 ctx.repo->defbranch = xstrdup(value);
102 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 102 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
103 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 103 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
104 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 104 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
105 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 105 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
106 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 106 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
107 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 107 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
108 else if (ctx.repo && !strcmp(name, "repo.module-link")) 108 else if (ctx.repo && !strcmp(name, "repo.module-link"))
109 ctx.repo->module_link= xstrdup(value); 109 ctx.repo->module_link= xstrdup(value);
110 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 110 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
111 if (*value == '/') 111 if (*value == '/')
112 ctx.repo->readme = xstrdup(value); 112 ctx.repo->readme = xstrdup(value);
113 else 113 else
114 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 114 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
115 } else if (!strcmp(name, "include")) 115 } else if (!strcmp(name, "include"))
116 parse_configfile(value, config_cb); 116 parse_configfile(value, config_cb);
117} 117}
118 118
119static void querystring_cb(const char *name, const char *value) 119static void querystring_cb(const char *name, const char *value)
120{ 120{
121 if (!strcmp(name,"r")) { 121 if (!strcmp(name,"r")) {
122 ctx.qry.repo = xstrdup(value); 122 ctx.qry.repo = xstrdup(value);
123 ctx.repo = cgit_get_repoinfo(value); 123 ctx.repo = cgit_get_repoinfo(value);
124 } else if (!strcmp(name, "p")) { 124 } else if (!strcmp(name, "p")) {
125 ctx.qry.page = xstrdup(value); 125 ctx.qry.page = xstrdup(value);
126 } else if (!strcmp(name, "url")) { 126 } else if (!strcmp(name, "url")) {
127 cgit_parse_url(value); 127 cgit_parse_url(value);
128 } else if (!strcmp(name, "qt")) { 128 } else if (!strcmp(name, "qt")) {
129 ctx.qry.grep = xstrdup(value); 129 ctx.qry.grep = xstrdup(value);
130 } else if (!strcmp(name, "q")) { 130 } else if (!strcmp(name, "q")) {
131 ctx.qry.search = xstrdup(value); 131 ctx.qry.search = xstrdup(value);
132 } else if (!strcmp(name, "h")) { 132 } else if (!strcmp(name, "h")) {
133 ctx.qry.head = xstrdup(value); 133 ctx.qry.head = xstrdup(value);
134 ctx.qry.has_symref = 1; 134 ctx.qry.has_symref = 1;
135 } else if (!strcmp(name, "id")) { 135 } else if (!strcmp(name, "id")) {
136 ctx.qry.sha1 = xstrdup(value); 136 ctx.qry.sha1 = xstrdup(value);
137 ctx.qry.has_sha1 = 1; 137 ctx.qry.has_sha1 = 1;
138 } else if (!strcmp(name, "id2")) { 138 } else if (!strcmp(name, "id2")) {
139 ctx.qry.sha2 = xstrdup(value); 139 ctx.qry.sha2 = xstrdup(value);
140 ctx.qry.has_sha1 = 1; 140 ctx.qry.has_sha1 = 1;
141 } else if (!strcmp(name, "ofs")) { 141 } else if (!strcmp(name, "ofs")) {
142 ctx.qry.ofs = atoi(value); 142 ctx.qry.ofs = atoi(value);
143 } else if (!strcmp(name, "path")) { 143 } else if (!strcmp(name, "path")) {
144 ctx.qry.path = trim_end(value, '/'); 144 ctx.qry.path = trim_end(value, '/');
145 } else if (!strcmp(name, "name")) { 145 } else if (!strcmp(name, "name")) {
146 ctx.qry.name = xstrdup(value); 146 ctx.qry.name = xstrdup(value);
147 } 147 }
148} 148}
149 149
150static void prepare_context(struct cgit_context *ctx) 150static void prepare_context(struct cgit_context *ctx)
151{ 151{
152 memset(ctx, 0, sizeof(ctx)); 152 memset(ctx, 0, sizeof(ctx));
153 ctx->cfg.agefile = "info/web/last-modified"; 153 ctx->cfg.agefile = "info/web/last-modified";
154 ctx->cfg.nocache = 0; 154 ctx->cfg.nocache = 0;
155 ctx->cfg.cache_size = 0; 155 ctx->cfg.cache_size = 0;
156 ctx->cfg.cache_dynamic_ttl = 5; 156 ctx->cfg.cache_dynamic_ttl = 5;
157 ctx->cfg.cache_max_create_time = 5; 157 ctx->cfg.cache_max_create_time = 5;
158 ctx->cfg.cache_repo_ttl = 5; 158 ctx->cfg.cache_repo_ttl = 5;
159 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 159 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
160 ctx->cfg.cache_root_ttl = 5; 160 ctx->cfg.cache_root_ttl = 5;
161 ctx->cfg.cache_static_ttl = -1; 161 ctx->cfg.cache_static_ttl = -1;
162 ctx->cfg.css = "/cgit.css"; 162 ctx->cfg.css = "/cgit.css";
163 ctx->cfg.logo = "/git-logo.png"; 163 ctx->cfg.logo = "/git-logo.png";
164 ctx->cfg.max_repo_count = 50; 164 ctx->cfg.max_repo_count = 50;
165 ctx->cfg.max_commit_count = 50; 165 ctx->cfg.max_commit_count = 50;
166 ctx->cfg.max_lock_attempts = 5; 166 ctx->cfg.max_lock_attempts = 5;
167 ctx->cfg.max_msg_len = 60; 167 ctx->cfg.max_msg_len = 60;
168 ctx->cfg.max_repodesc_len = 60; 168 ctx->cfg.max_repodesc_len = 60;
169 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 169 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
170 ctx->cfg.renamelimit = -1; 170 ctx->cfg.renamelimit = -1;
171 ctx->cfg.robots = "index, nofollow"; 171 ctx->cfg.robots = "index, nofollow";
172 ctx->cfg.root_title = "Git repository browser"; 172 ctx->cfg.root_title = "Git repository browser";
173 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 173 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
174 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 174 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
175 ctx->page.mimetype = "text/html"; 175 ctx->page.mimetype = "text/html";
176 ctx->page.charset = PAGE_ENCODING; 176 ctx->page.charset = PAGE_ENCODING;
177 ctx->page.filename = NULL; 177 ctx->page.filename = NULL;
178 ctx->page.modified = time(NULL); 178 ctx->page.modified = time(NULL);
179 ctx->page.expires = ctx->page.modified; 179 ctx->page.expires = ctx->page.modified;
180} 180}
181 181
182struct refmatch { 182struct refmatch {
183 char *req_ref; 183 char *req_ref;
184 char *first_ref; 184 char *first_ref;
185 int match; 185 int match;
186}; 186};
187 187
188int find_current_ref(const char *refname, const unsigned char *sha1, 188int find_current_ref(const char *refname, const unsigned char *sha1,
189 int flags, void *cb_data) 189 int flags, void *cb_data)
190{ 190{
191 struct refmatch *info; 191 struct refmatch *info;
192 192
193 info = (struct refmatch *)cb_data; 193 info = (struct refmatch *)cb_data;
194 if (!strcmp(refname, info->req_ref)) 194 if (!strcmp(refname, info->req_ref))
195 info->match = 1; 195 info->match = 1;
196 if (!info->first_ref) 196 if (!info->first_ref)
197 info->first_ref = xstrdup(refname); 197 info->first_ref = xstrdup(refname);
198 return info->match; 198 return info->match;
199} 199}
200 200
201char *find_default_branch(struct cgit_repo *repo) 201char *find_default_branch(struct cgit_repo *repo)
202{ 202{
203 struct refmatch info; 203 struct refmatch info;
204 204
205 info.req_ref = repo->defbranch; 205 info.req_ref = repo->defbranch;
206 info.first_ref = NULL; 206 info.first_ref = NULL;
207 info.match = 0; 207 info.match = 0;
208 for_each_branch_ref(find_current_ref, &info); 208 for_each_branch_ref(find_current_ref, &info);
209 if (info.match) 209 if (info.match)
210 return info.req_ref; 210 return info.req_ref;
211 else 211 else
212 return info.first_ref; 212 return info.first_ref;
213} 213}
214 214
215static int prepare_repo_cmd(struct cgit_context *ctx) 215static int prepare_repo_cmd(struct cgit_context *ctx)
216{ 216{
217 char *tmp; 217 char *tmp;
218 unsigned char sha1[20]; 218 unsigned char sha1[20];
219 int nongit = 0; 219 int nongit = 0;
220 220
221 setenv("GIT_DIR", ctx->repo->path, 1); 221 setenv("GIT_DIR", ctx->repo->path, 1);
222 setup_git_directory_gently(&nongit); 222 setup_git_directory_gently(&nongit);
223 if (nongit) { 223 if (nongit) {
224 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 224 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
225 "config error"); 225 "config error");
226 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 226 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
227 ctx->repo = NULL; 227 ctx->repo = NULL;
228 cgit_print_http_headers(ctx); 228 cgit_print_http_headers(ctx);
229 cgit_print_docstart(ctx); 229 cgit_print_docstart(ctx);
230 cgit_print_pageheader(ctx); 230 cgit_print_pageheader(ctx);
231 cgit_print_error(tmp); 231 cgit_print_error(tmp);
232 cgit_print_docend(); 232 cgit_print_docend();
233 return 1; 233 return 1;
234 } 234 }
235 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 235 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
236 236
237 if (!ctx->qry.head) { 237 if (!ctx->qry.head) {
238 ctx->qry.head = xstrdup(find_default_branch(ctx->repo)); 238 ctx->qry.head = xstrdup(find_default_branch(ctx->repo));
239 ctx->repo->defbranch = ctx->qry.head; 239 ctx->repo->defbranch = ctx->qry.head;
240 } 240 }
241 241
242 if (!ctx->qry.head) { 242 if (!ctx->qry.head) {
243 cgit_print_http_headers(ctx); 243 cgit_print_http_headers(ctx);
244 cgit_print_docstart(ctx); 244 cgit_print_docstart(ctx);
245 cgit_print_pageheader(ctx); 245 cgit_print_pageheader(ctx);
246 cgit_print_error("Repository seems to be empty"); 246 cgit_print_error("Repository seems to be empty");
247 cgit_print_docend(); 247 cgit_print_docend();
248 return 1; 248 return 1;
249 } 249 }
250 250
251 if (get_sha1(ctx->qry.head, sha1)) { 251 if (get_sha1(ctx->qry.head, sha1)) {
252 tmp = xstrdup(ctx->qry.head); 252 tmp = xstrdup(ctx->qry.head);
253 ctx->qry.head = ctx->repo->defbranch; 253 ctx->qry.head = ctx->repo->defbranch;
254 cgit_print_http_headers(ctx); 254 cgit_print_http_headers(ctx);
255 cgit_print_docstart(ctx); 255 cgit_print_docstart(ctx);
256 cgit_print_pageheader(ctx); 256 cgit_print_pageheader(ctx);
257 cgit_print_error(fmt("Invalid branch: %s", tmp)); 257 cgit_print_error(fmt("Invalid branch: %s", tmp));
258 cgit_print_docend(); 258 cgit_print_docend();
259 return 1; 259 return 1;
260 } 260 }
261 return 0; 261 return 0;
262} 262}
263 263
264static void process_request(void *cbdata) 264static void process_request(void *cbdata)
265{ 265{
266 struct cgit_context *ctx = cbdata; 266 struct cgit_context *ctx = cbdata;
267 struct cgit_cmd *cmd; 267 struct cgit_cmd *cmd;
268 268
269 cmd = cgit_get_cmd(ctx); 269 cmd = cgit_get_cmd(ctx);
270 if (!cmd) { 270 if (!cmd) {
271 ctx->page.title = "cgit error"; 271 ctx->page.title = "cgit error";
272 ctx->repo = NULL; 272 ctx->repo = NULL;
273 cgit_print_http_headers(ctx); 273 cgit_print_http_headers(ctx);
274 cgit_print_docstart(ctx); 274 cgit_print_docstart(ctx);
275 cgit_print_pageheader(ctx); 275 cgit_print_pageheader(ctx);
276 cgit_print_error("Invalid request"); 276 cgit_print_error("Invalid request");
277 cgit_print_docend(); 277 cgit_print_docend();
278 return; 278 return;
279 } 279 }
280 280
281 if (cmd->want_repo && !ctx->repo) { 281 if (cmd->want_repo && !ctx->repo) {
282 cgit_print_http_headers(ctx); 282 cgit_print_http_headers(ctx);
283 cgit_print_docstart(ctx); 283 cgit_print_docstart(ctx);
284 cgit_print_pageheader(ctx); 284 cgit_print_pageheader(ctx);
285 cgit_print_error(fmt("No repository selected")); 285 cgit_print_error(fmt("No repository selected"));
286 cgit_print_docend(); 286 cgit_print_docend();
287 return; 287 return;
288 } 288 }
289 289
290 if (ctx->repo && prepare_repo_cmd(ctx)) 290 if (ctx->repo && prepare_repo_cmd(ctx))
291 return; 291 return;
292 292
293 if (cmd->want_layout) { 293 if (cmd->want_layout) {
294 cgit_print_http_headers(ctx); 294 cgit_print_http_headers(ctx);
295 cgit_print_docstart(ctx); 295 cgit_print_docstart(ctx);
296 cgit_print_pageheader(ctx); 296 cgit_print_pageheader(ctx);
297 } 297 }
298 298
299 cmd->fn(ctx); 299 cmd->fn(ctx);
300 300
301 if (cmd->want_layout) 301 if (cmd->want_layout)
302 cgit_print_docend(); 302 cgit_print_docend();
303} 303}
304 304
305static void cgit_parse_args(int argc, const char **argv) 305static void cgit_parse_args(int argc, const char **argv)
306{ 306{
307 int i; 307 int i;
308 308
309 for (i = 1; i < argc; i++) { 309 for (i = 1; i < argc; i++) {
310 if (!strncmp(argv[i], "--cache=", 8)) { 310 if (!strncmp(argv[i], "--cache=", 8)) {
311 ctx.cfg.cache_root = xstrdup(argv[i]+8); 311 ctx.cfg.cache_root = xstrdup(argv[i]+8);
312 } 312 }
313 if (!strcmp(argv[i], "--nocache")) { 313 if (!strcmp(argv[i], "--nocache")) {
314 ctx.cfg.nocache = 1; 314 ctx.cfg.nocache = 1;
315 } 315 }
316 if (!strncmp(argv[i], "--query=", 8)) { 316 if (!strncmp(argv[i], "--query=", 8)) {
317 ctx.qry.raw = xstrdup(argv[i]+8); 317 ctx.qry.raw = xstrdup(argv[i]+8);
318 } 318 }
319 if (!strncmp(argv[i], "--repo=", 7)) { 319 if (!strncmp(argv[i], "--repo=", 7)) {
320 ctx.qry.repo = xstrdup(argv[i]+7); 320 ctx.qry.repo = xstrdup(argv[i]+7);
321 } 321 }
322 if (!strncmp(argv[i], "--page=", 7)) { 322 if (!strncmp(argv[i], "--page=", 7)) {
323 ctx.qry.page = xstrdup(argv[i]+7); 323 ctx.qry.page = xstrdup(argv[i]+7);
324 } 324 }
325 if (!strncmp(argv[i], "--head=", 7)) { 325 if (!strncmp(argv[i], "--head=", 7)) {
326 ctx.qry.head = xstrdup(argv[i]+7); 326 ctx.qry.head = xstrdup(argv[i]+7);
327 ctx.qry.has_symref = 1; 327 ctx.qry.has_symref = 1;
328 } 328 }
329 if (!strncmp(argv[i], "--sha1=", 7)) { 329 if (!strncmp(argv[i], "--sha1=", 7)) {
330 ctx.qry.sha1 = xstrdup(argv[i]+7); 330 ctx.qry.sha1 = xstrdup(argv[i]+7);
331 ctx.qry.has_sha1 = 1; 331 ctx.qry.has_sha1 = 1;
332 } 332 }
333 if (!strncmp(argv[i], "--ofs=", 6)) { 333 if (!strncmp(argv[i], "--ofs=", 6)) {
334 ctx.qry.ofs = atoi(argv[i]+6); 334 ctx.qry.ofs = atoi(argv[i]+6);
335 } 335 }
336 } 336 }
337} 337}
338 338
339static int calc_ttl() 339static int calc_ttl()
340{ 340{
341 if (!ctx.repo) 341 if (!ctx.repo)
342 return ctx.cfg.cache_root_ttl; 342 return ctx.cfg.cache_root_ttl;
343 343
344 if (!ctx.qry.page) 344 if (!ctx.qry.page)
345 return ctx.cfg.cache_repo_ttl; 345 return ctx.cfg.cache_repo_ttl;
346 346
347 if (ctx.qry.has_symref) 347 if (ctx.qry.has_symref)
348 return ctx.cfg.cache_dynamic_ttl; 348 return ctx.cfg.cache_dynamic_ttl;
349 349
350 if (ctx.qry.has_sha1) 350 if (ctx.qry.has_sha1)
351 return ctx.cfg.cache_static_ttl; 351 return ctx.cfg.cache_static_ttl;
352 352
353 return ctx.cfg.cache_repo_ttl; 353 return ctx.cfg.cache_repo_ttl;
354} 354}
355 355
356int main(int argc, const char **argv) 356int main(int argc, const char **argv)
357{ 357{
358 const char *cgit_config_env = getenv("CGIT_CONFIG"); 358 const char *cgit_config_env = getenv("CGIT_CONFIG");
359 int err, ttl; 359 int err, ttl;
360 360
361 prepare_context(&ctx); 361 prepare_context(&ctx);
362 cgit_repolist.length = 0; 362 cgit_repolist.length = 0;
363 cgit_repolist.count = 0; 363 cgit_repolist.count = 0;
364 cgit_repolist.repos = NULL; 364 cgit_repolist.repos = NULL;
365 365
366 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 366 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
367 config_cb); 367 config_cb);
368 ctx.repo = NULL; 368 ctx.repo = NULL;
369 if (getenv("SCRIPT_NAME")) 369 if (getenv("SCRIPT_NAME"))
370 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); 370 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
371 if (getenv("QUERY_STRING")) 371 if (getenv("QUERY_STRING"))
372 ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); 372 ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
373 cgit_parse_args(argc, argv); 373 cgit_parse_args(argc, argv);
374 http_parse_querystring(ctx.qry.raw, querystring_cb); 374 http_parse_querystring(ctx.qry.raw, querystring_cb);
375 375
376 ttl = calc_ttl(); 376 ttl = calc_ttl();
377 ctx.page.expires += ttl*60; 377 ctx.page.expires += ttl*60;
378 if (ctx.cfg.nocache) 378 if (ctx.cfg.nocache)
379 ctx.cfg.cache_size = 0; 379 ctx.cfg.cache_size = 0;
380 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 380 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
381 ctx.qry.raw, ttl, process_request, &ctx); 381 ctx.qry.raw, ttl, process_request, &ctx);
382 if (err) 382 if (err)
383 cache_log("[cgit] error %d - %s\n", 383 cgit_print_error(fmt("Error processing page: %s (%d)",
384 err, strerror(err)); 384 strerror(err), err));
385 return err; 385 return err;
386} 386}