author | Lars Hjemli <hjemli@gmail.com> | 2008-05-18 21:59:11 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2008-05-18 21:59:11 (UTC) |
commit | af2e75616d1bfb7dc79d299d10ae0bd39bef47bc (patch) (unidiff) | |
tree | 6330a6f9bc1b2b16434df055ee48129e2e3b827e | |
parent | cdc6b2f8e7a8d43dcfe0475a9d3498333ea686b8 (diff) | |
download | cgit-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>
-rw-r--r-- | cache.c | 16 | ||||
-rw-r--r-- | cgit.c | 4 |
2 files changed, 15 insertions, 5 deletions
@@ -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 | ||
21 | struct cache_slot { | 21 | struct 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 | */ |
42 | static int open_slot(struct cache_slot *slot) | 42 | static 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 */ |
69 | static int close_slot(struct cache_slot *slot) | 69 | static 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). */ |
82 | static int print_slot(struct cache_slot *slot) | 82 | static 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 */ |
100 | static int is_expired(struct cache_slot *slot) | 100 | static 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 | */ |
111 | static int is_modified(struct cache_slot *slot) | 111 | static 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 */ |
123 | static int close_lock(struct cache_slot *slot) | 123 | static 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 | */ |
139 | static int lock_slot(struct cache_slot *slot) | 139 | static 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 | */ |
154 | static int unlock_slot(struct cache_slot *slot, int replace_old_slot) | 154 | static 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 | */ |
172 | static int fill_slot(struct cache_slot *slot) | 172 | static 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 | ||
206 | unsigned long hash_str(const char *str) | 206 | unsigned 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 | ||
221 | static int process_slot(struct cache_slot *slot) | 221 | static 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. */ |
298 | int cache_process(int size, const char *path, const char *key, int ttl, | 308 | int 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 | */ |
353 | char *sprintftime(const char *format, time_t time) | 363 | char *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 | ||
365 | int cache_ls(const char *path) | 375 | int 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 */ |
419 | void cache_log(const char *format, ...) | 429 | void 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 | ||
@@ -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 | ||
16 | const char *cgit_version = CGIT_VERSION; | 16 | const char *cgit_version = CGIT_VERSION; |
17 | 17 | ||
18 | void config_cb(const char *name, const char *value) | 18 | void 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 | ||
119 | static void querystring_cb(const char *name, const char *value) | 119 | static 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 | ||
150 | static void prepare_context(struct cgit_context *ctx) | 150 | static 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 | ||
182 | struct refmatch { | 182 | struct 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 | ||
188 | int find_current_ref(const char *refname, const unsigned char *sha1, | 188 | int 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 | ||
201 | char *find_default_branch(struct cgit_repo *repo) | 201 | char *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 | ||
215 | static int prepare_repo_cmd(struct cgit_context *ctx) | 215 | static 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 | ||
264 | static void process_request(void *cbdata) | 264 | static 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 | ||
305 | static void cgit_parse_args(int argc, const char **argv) | 305 | static 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 | ||
339 | static int calc_ttl() | 339 | static 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 | ||
356 | int main(int argc, const char **argv) | 356 | int 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 | } |