summaryrefslogtreecommitdiffabout
path: root/cgit.c
authorLars Hjemli <hjemli@gmail.com>2008-04-28 09:32:42 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-04-28 09:32:42 (UTC)
commit939d32fda70ea66c9db51687beb3cea6da7b0599 (patch) (unidiff)
tree50915facf89b78e3856fe6b0564a26c3678c01ba /cgit.c
parent9ec5cd7944a7099515b7d41107007d6332a2540e (diff)
downloadcgit-939d32fda70ea66c9db51687beb3cea6da7b0599.zip
cgit-939d32fda70ea66c9db51687beb3cea6da7b0599.tar.gz
cgit-939d32fda70ea66c9db51687beb3cea6da7b0599.tar.bz2
Redesign the caching layer
The original caching layer in cgit has no upper bound on the number of concurrent cache entries, so when cgit is traversed by a spider (like the googlebot), the cache might end up filling your disk. Also, if any error occurs in the cache layer, no content is returned to the client. This patch redesigns the caching layer to avoid these flaws by * giving the cache a bound number of slots * disabling the cache for the current request when errors occur The cache size limit is implemented by hashing the querystring (the cache lookup key) and generating a cache filename based on this hash modulo the cache size. In order to detect hash collisions, the full lookup key (i.e. the querystring) is stored in the cache file (separated from its associated content by ascii 0). The cache filename is the reversed 8-digit hexadecimal representation of hash(key) % cache_size which should make the filesystem lookup pretty fast (if directory content is indexed/sorted); reversing the representation avoids the problem where all keys have equal prefix. There is a new config option, cache-size, which sets the upper bound for the cache. Default value for this option is 0, which has the same effect as setting nocache=1 (hence nocache is now deprecated). Included in this patch is also a new testfile which verifies that the new option works as intended. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (limited to 'cgit.c') (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c166
1 files changed, 37 insertions, 129 deletions
diff --git a/cgit.c b/cgit.c
index 38b0ba5..4dc8eec 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,461 +1,369 @@
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, "css")) 22 else if (!strcmp(name, "css"))
23 ctx.cfg.css = xstrdup(value); 23 ctx.cfg.css = xstrdup(value);
24 else if (!strcmp(name, "logo")) 24 else if (!strcmp(name, "logo"))
25 ctx.cfg.logo = xstrdup(value); 25 ctx.cfg.logo = xstrdup(value);
26 else if (!strcmp(name, "index-header")) 26 else if (!strcmp(name, "index-header"))
27 ctx.cfg.index_header = xstrdup(value); 27 ctx.cfg.index_header = xstrdup(value);
28 else if (!strcmp(name, "index-info")) 28 else if (!strcmp(name, "index-info"))
29 ctx.cfg.index_info = xstrdup(value); 29 ctx.cfg.index_info = xstrdup(value);
30 else if (!strcmp(name, "logo-link")) 30 else if (!strcmp(name, "logo-link"))
31 ctx.cfg.logo_link = xstrdup(value); 31 ctx.cfg.logo_link = xstrdup(value);
32 else if (!strcmp(name, "module-link")) 32 else if (!strcmp(name, "module-link"))
33 ctx.cfg.module_link = xstrdup(value); 33 ctx.cfg.module_link = xstrdup(value);
34 else if (!strcmp(name, "virtual-root")) { 34 else if (!strcmp(name, "virtual-root")) {
35 ctx.cfg.virtual_root = trim_end(value, '/'); 35 ctx.cfg.virtual_root = trim_end(value, '/');
36 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 36 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
37 ctx.cfg.virtual_root = ""; 37 ctx.cfg.virtual_root = "";
38 } else if (!strcmp(name, "nocache")) 38 } else if (!strcmp(name, "nocache"))
39 ctx.cfg.nocache = atoi(value); 39 ctx.cfg.nocache = atoi(value);
40 else if (!strcmp(name, "snapshots")) 40 else if (!strcmp(name, "snapshots"))
41 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 41 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
42 else if (!strcmp(name, "enable-index-links")) 42 else if (!strcmp(name, "enable-index-links"))
43 ctx.cfg.enable_index_links = atoi(value); 43 ctx.cfg.enable_index_links = atoi(value);
44 else if (!strcmp(name, "enable-log-filecount")) 44 else if (!strcmp(name, "enable-log-filecount"))
45 ctx.cfg.enable_log_filecount = atoi(value); 45 ctx.cfg.enable_log_filecount = atoi(value);
46 else if (!strcmp(name, "enable-log-linecount")) 46 else if (!strcmp(name, "enable-log-linecount"))
47 ctx.cfg.enable_log_linecount = atoi(value); 47 ctx.cfg.enable_log_linecount = atoi(value);
48 else if (!strcmp(name, "cache-size"))
49 ctx.cfg.cache_size = atoi(value);
48 else if (!strcmp(name, "cache-root")) 50 else if (!strcmp(name, "cache-root"))
49 ctx.cfg.cache_root = xstrdup(value); 51 ctx.cfg.cache_root = xstrdup(value);
50 else if (!strcmp(name, "cache-root-ttl")) 52 else if (!strcmp(name, "cache-root-ttl"))
51 ctx.cfg.cache_root_ttl = atoi(value); 53 ctx.cfg.cache_root_ttl = atoi(value);
52 else if (!strcmp(name, "cache-repo-ttl")) 54 else if (!strcmp(name, "cache-repo-ttl"))
53 ctx.cfg.cache_repo_ttl = atoi(value); 55 ctx.cfg.cache_repo_ttl = atoi(value);
54 else if (!strcmp(name, "cache-static-ttl")) 56 else if (!strcmp(name, "cache-static-ttl"))
55 ctx.cfg.cache_static_ttl = atoi(value); 57 ctx.cfg.cache_static_ttl = atoi(value);
56 else if (!strcmp(name, "cache-dynamic-ttl")) 58 else if (!strcmp(name, "cache-dynamic-ttl"))
57 ctx.cfg.cache_dynamic_ttl = atoi(value); 59 ctx.cfg.cache_dynamic_ttl = atoi(value);
58 else if (!strcmp(name, "max-message-length")) 60 else if (!strcmp(name, "max-message-length"))
59 ctx.cfg.max_msg_len = atoi(value); 61 ctx.cfg.max_msg_len = atoi(value);
60 else if (!strcmp(name, "max-repodesc-length")) 62 else if (!strcmp(name, "max-repodesc-length"))
61 ctx.cfg.max_repodesc_len = atoi(value); 63 ctx.cfg.max_repodesc_len = atoi(value);
62 else if (!strcmp(name, "max-commit-count")) 64 else if (!strcmp(name, "max-commit-count"))
63 ctx.cfg.max_commit_count = atoi(value); 65 ctx.cfg.max_commit_count = atoi(value);
64 else if (!strcmp(name, "summary-log")) 66 else if (!strcmp(name, "summary-log"))
65 ctx.cfg.summary_log = atoi(value); 67 ctx.cfg.summary_log = atoi(value);
66 else if (!strcmp(name, "summary-branches")) 68 else if (!strcmp(name, "summary-branches"))
67 ctx.cfg.summary_branches = atoi(value); 69 ctx.cfg.summary_branches = atoi(value);
68 else if (!strcmp(name, "summary-tags")) 70 else if (!strcmp(name, "summary-tags"))
69 ctx.cfg.summary_tags = atoi(value); 71 ctx.cfg.summary_tags = atoi(value);
70 else if (!strcmp(name, "agefile")) 72 else if (!strcmp(name, "agefile"))
71 ctx.cfg.agefile = xstrdup(value); 73 ctx.cfg.agefile = xstrdup(value);
72 else if (!strcmp(name, "renamelimit")) 74 else if (!strcmp(name, "renamelimit"))
73 ctx.cfg.renamelimit = atoi(value); 75 ctx.cfg.renamelimit = atoi(value);
74 else if (!strcmp(name, "robots")) 76 else if (!strcmp(name, "robots"))
75 ctx.cfg.robots = xstrdup(value); 77 ctx.cfg.robots = xstrdup(value);
76 else if (!strcmp(name, "clone-prefix")) 78 else if (!strcmp(name, "clone-prefix"))
77 ctx.cfg.clone_prefix = xstrdup(value); 79 ctx.cfg.clone_prefix = xstrdup(value);
78 else if (!strcmp(name, "repo.group")) 80 else if (!strcmp(name, "repo.group"))
79 ctx.cfg.repo_group = xstrdup(value); 81 ctx.cfg.repo_group = xstrdup(value);
80 else if (!strcmp(name, "repo.url")) 82 else if (!strcmp(name, "repo.url"))
81 ctx.repo = cgit_add_repo(value); 83 ctx.repo = cgit_add_repo(value);
82 else if (!strcmp(name, "repo.name")) 84 else if (!strcmp(name, "repo.name"))
83 ctx.repo->name = xstrdup(value); 85 ctx.repo->name = xstrdup(value);
84 else if (ctx.repo && !strcmp(name, "repo.path")) 86 else if (ctx.repo && !strcmp(name, "repo.path"))
85 ctx.repo->path = trim_end(value, '/'); 87 ctx.repo->path = trim_end(value, '/');
86 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 88 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
87 ctx.repo->clone_url = xstrdup(value); 89 ctx.repo->clone_url = xstrdup(value);
88 else if (ctx.repo && !strcmp(name, "repo.desc")) 90 else if (ctx.repo && !strcmp(name, "repo.desc"))
89 ctx.repo->desc = xstrdup(value); 91 ctx.repo->desc = xstrdup(value);
90 else if (ctx.repo && !strcmp(name, "repo.owner")) 92 else if (ctx.repo && !strcmp(name, "repo.owner"))
91 ctx.repo->owner = xstrdup(value); 93 ctx.repo->owner = xstrdup(value);
92 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 94 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
93 ctx.repo->defbranch = xstrdup(value); 95 ctx.repo->defbranch = xstrdup(value);
94 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 96 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
95 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 97 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
96 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 98 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
97 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 99 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
98 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 100 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
99 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 101 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
100 else if (ctx.repo && !strcmp(name, "repo.module-link")) 102 else if (ctx.repo && !strcmp(name, "repo.module-link"))
101 ctx.repo->module_link= xstrdup(value); 103 ctx.repo->module_link= xstrdup(value);
102 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 104 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
103 if (*value == '/') 105 if (*value == '/')
104 ctx.repo->readme = xstrdup(value); 106 ctx.repo->readme = xstrdup(value);
105 else 107 else
106 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 108 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
107 } else if (!strcmp(name, "include")) 109 } else if (!strcmp(name, "include"))
108 parse_configfile(value, config_cb); 110 parse_configfile(value, config_cb);
109} 111}
110 112
111static void querystring_cb(const char *name, const char *value) 113static void querystring_cb(const char *name, const char *value)
112{ 114{
113 if (!strcmp(name,"r")) { 115 if (!strcmp(name,"r")) {
114 ctx.qry.repo = xstrdup(value); 116 ctx.qry.repo = xstrdup(value);
115 ctx.repo = cgit_get_repoinfo(value); 117 ctx.repo = cgit_get_repoinfo(value);
116 } else if (!strcmp(name, "p")) { 118 } else if (!strcmp(name, "p")) {
117 ctx.qry.page = xstrdup(value); 119 ctx.qry.page = xstrdup(value);
118 } else if (!strcmp(name, "url")) { 120 } else if (!strcmp(name, "url")) {
119 cgit_parse_url(value); 121 cgit_parse_url(value);
120 } else if (!strcmp(name, "qt")) { 122 } else if (!strcmp(name, "qt")) {
121 ctx.qry.grep = xstrdup(value); 123 ctx.qry.grep = xstrdup(value);
122 } else if (!strcmp(name, "q")) { 124 } else if (!strcmp(name, "q")) {
123 ctx.qry.search = xstrdup(value); 125 ctx.qry.search = xstrdup(value);
124 } else if (!strcmp(name, "h")) { 126 } else if (!strcmp(name, "h")) {
125 ctx.qry.head = xstrdup(value); 127 ctx.qry.head = xstrdup(value);
126 ctx.qry.has_symref = 1; 128 ctx.qry.has_symref = 1;
127 } else if (!strcmp(name, "id")) { 129 } else if (!strcmp(name, "id")) {
128 ctx.qry.sha1 = xstrdup(value); 130 ctx.qry.sha1 = xstrdup(value);
129 ctx.qry.has_sha1 = 1; 131 ctx.qry.has_sha1 = 1;
130 } else if (!strcmp(name, "id2")) { 132 } else if (!strcmp(name, "id2")) {
131 ctx.qry.sha2 = xstrdup(value); 133 ctx.qry.sha2 = xstrdup(value);
132 ctx.qry.has_sha1 = 1; 134 ctx.qry.has_sha1 = 1;
133 } else if (!strcmp(name, "ofs")) { 135 } else if (!strcmp(name, "ofs")) {
134 ctx.qry.ofs = atoi(value); 136 ctx.qry.ofs = atoi(value);
135 } else if (!strcmp(name, "path")) { 137 } else if (!strcmp(name, "path")) {
136 ctx.qry.path = trim_end(value, '/'); 138 ctx.qry.path = trim_end(value, '/');
137 } else if (!strcmp(name, "name")) { 139 } else if (!strcmp(name, "name")) {
138 ctx.qry.name = xstrdup(value); 140 ctx.qry.name = xstrdup(value);
139 } 141 }
140} 142}
141 143
142static void prepare_context(struct cgit_context *ctx) 144static void prepare_context(struct cgit_context *ctx)
143{ 145{
144 memset(ctx, 0, sizeof(ctx)); 146 memset(ctx, 0, sizeof(ctx));
145 ctx->cfg.agefile = "info/web/last-modified"; 147 ctx->cfg.agefile = "info/web/last-modified";
148 ctx->cfg.nocache = 0;
149 ctx->cfg.cache_size = 0;
146 ctx->cfg.cache_dynamic_ttl = 5; 150 ctx->cfg.cache_dynamic_ttl = 5;
147 ctx->cfg.cache_max_create_time = 5; 151 ctx->cfg.cache_max_create_time = 5;
148 ctx->cfg.cache_repo_ttl = 5; 152 ctx->cfg.cache_repo_ttl = 5;
149 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 153 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
150 ctx->cfg.cache_root_ttl = 5; 154 ctx->cfg.cache_root_ttl = 5;
151 ctx->cfg.cache_static_ttl = -1; 155 ctx->cfg.cache_static_ttl = -1;
152 ctx->cfg.css = "/cgit.css"; 156 ctx->cfg.css = "/cgit.css";
153 ctx->cfg.logo = "/git-logo.png"; 157 ctx->cfg.logo = "/git-logo.png";
154 ctx->cfg.max_commit_count = 50; 158 ctx->cfg.max_commit_count = 50;
155 ctx->cfg.max_lock_attempts = 5; 159 ctx->cfg.max_lock_attempts = 5;
156 ctx->cfg.max_msg_len = 60; 160 ctx->cfg.max_msg_len = 60;
157 ctx->cfg.max_repodesc_len = 60; 161 ctx->cfg.max_repodesc_len = 60;
158 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 162 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
159 ctx->cfg.renamelimit = -1; 163 ctx->cfg.renamelimit = -1;
160 ctx->cfg.robots = "index, nofollow"; 164 ctx->cfg.robots = "index, nofollow";
161 ctx->cfg.root_title = "Git repository browser"; 165 ctx->cfg.root_title = "Git repository browser";
162 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 166 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
163 ctx->page.mimetype = "text/html"; 167 ctx->page.mimetype = "text/html";
164 ctx->page.charset = PAGE_ENCODING; 168 ctx->page.charset = PAGE_ENCODING;
165 ctx->page.filename = NULL; 169 ctx->page.filename = NULL;
166} 170 ctx->page.modified = time(NULL);
167 171 ctx->page.expires = ctx->page.modified;
168static int cgit_prepare_cache(struct cacheitem *item)
169{
170 if (!ctx.repo && ctx.qry.repo) {
171 ctx.page.title = fmt("%s - %s", ctx.cfg.root_title,
172 "Bad request");
173 cgit_print_http_headers(&ctx);
174 cgit_print_docstart(&ctx);
175 cgit_print_pageheader(&ctx);
176 cgit_print_error(fmt("Unknown repo: %s", ctx.qry.repo));
177 cgit_print_docend();
178 return 0;
179 }
180
181 if (!ctx.repo) {
182 item->name = xstrdup(fmt("%s/index.%s.html",
183 ctx.cfg.cache_root,
184 cache_safe_filename(ctx.qry.raw)));
185 item->ttl = ctx.cfg.cache_root_ttl;
186 return 1;
187 }
188
189 if (!ctx.qry.page) {
190 item->name = xstrdup(fmt("%s/%s/index.%s.html", ctx.cfg.cache_root,
191 cache_safe_filename(ctx.repo->url),
192 cache_safe_filename(ctx.qry.raw)));
193 item->ttl = ctx.cfg.cache_repo_ttl;
194 } else {
195 item->name = xstrdup(fmt("%s/%s/%s/%s.html", ctx.cfg.cache_root,
196 cache_safe_filename(ctx.repo->url),
197 ctx.qry.page,
198 cache_safe_filename(ctx.qry.raw)));
199 if (ctx.qry.has_symref)
200 item->ttl = ctx.cfg.cache_dynamic_ttl;
201 else if (ctx.qry.has_sha1)
202 item->ttl = ctx.cfg.cache_static_ttl;
203 else
204 item->ttl = ctx.cfg.cache_repo_ttl;
205 }
206 return 1;
207} 172}
208 173
209struct refmatch { 174struct refmatch {
210 char *req_ref; 175 char *req_ref;
211 char *first_ref; 176 char *first_ref;
212 int match; 177 int match;
213}; 178};
214 179
215int find_current_ref(const char *refname, const unsigned char *sha1, 180int find_current_ref(const char *refname, const unsigned char *sha1,
216 int flags, void *cb_data) 181 int flags, void *cb_data)
217{ 182{
218 struct refmatch *info; 183 struct refmatch *info;
219 184
220 info = (struct refmatch *)cb_data; 185 info = (struct refmatch *)cb_data;
221 if (!strcmp(refname, info->req_ref)) 186 if (!strcmp(refname, info->req_ref))
222 info->match = 1; 187 info->match = 1;
223 if (!info->first_ref) 188 if (!info->first_ref)
224 info->first_ref = xstrdup(refname); 189 info->first_ref = xstrdup(refname);
225 return info->match; 190 return info->match;
226} 191}
227 192
228char *find_default_branch(struct cgit_repo *repo) 193char *find_default_branch(struct cgit_repo *repo)
229{ 194{
230 struct refmatch info; 195 struct refmatch info;
231 196
232 info.req_ref = repo->defbranch; 197 info.req_ref = repo->defbranch;
233 info.first_ref = NULL; 198 info.first_ref = NULL;
234 info.match = 0; 199 info.match = 0;
235 for_each_branch_ref(find_current_ref, &info); 200 for_each_branch_ref(find_current_ref, &info);
236 if (info.match) 201 if (info.match)
237 return info.req_ref; 202 return info.req_ref;
238 else 203 else
239 return info.first_ref; 204 return info.first_ref;
240} 205}
241 206
242static int prepare_repo_cmd(struct cgit_context *ctx) 207static int prepare_repo_cmd(struct cgit_context *ctx)
243{ 208{
244 char *tmp; 209 char *tmp;
245 unsigned char sha1[20]; 210 unsigned char sha1[20];
246 int nongit = 0; 211 int nongit = 0;
247 212
248 setenv("GIT_DIR", ctx->repo->path, 1); 213 setenv("GIT_DIR", ctx->repo->path, 1);
249 setup_git_directory_gently(&nongit); 214 setup_git_directory_gently(&nongit);
250 if (nongit) { 215 if (nongit) {
251 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 216 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
252 "config error"); 217 "config error");
253 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 218 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
254 ctx->repo = NULL; 219 ctx->repo = NULL;
255 cgit_print_http_headers(ctx); 220 cgit_print_http_headers(ctx);
256 cgit_print_docstart(ctx); 221 cgit_print_docstart(ctx);
257 cgit_print_pageheader(ctx); 222 cgit_print_pageheader(ctx);
258 cgit_print_error(tmp); 223 cgit_print_error(tmp);
259 cgit_print_docend(); 224 cgit_print_docend();
260 return 1; 225 return 1;
261 } 226 }
262 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 227 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
263 228
264 if (!ctx->qry.head) { 229 if (!ctx->qry.head) {
265 ctx->qry.head = xstrdup(find_default_branch(ctx->repo)); 230 ctx->qry.head = xstrdup(find_default_branch(ctx->repo));
266 ctx->repo->defbranch = ctx->qry.head; 231 ctx->repo->defbranch = ctx->qry.head;
267 } 232 }
268 233
269 if (!ctx->qry.head) { 234 if (!ctx->qry.head) {
270 cgit_print_http_headers(ctx); 235 cgit_print_http_headers(ctx);
271 cgit_print_docstart(ctx); 236 cgit_print_docstart(ctx);
272 cgit_print_pageheader(ctx); 237 cgit_print_pageheader(ctx);
273 cgit_print_error("Repository seems to be empty"); 238 cgit_print_error("Repository seems to be empty");
274 cgit_print_docend(); 239 cgit_print_docend();
275 return 1; 240 return 1;
276 } 241 }
277 242
278 if (get_sha1(ctx->qry.head, sha1)) { 243 if (get_sha1(ctx->qry.head, sha1)) {
279 tmp = xstrdup(ctx->qry.head); 244 tmp = xstrdup(ctx->qry.head);
280 ctx->qry.head = ctx->repo->defbranch; 245 ctx->qry.head = ctx->repo->defbranch;
281 cgit_print_http_headers(ctx); 246 cgit_print_http_headers(ctx);
282 cgit_print_docstart(ctx); 247 cgit_print_docstart(ctx);
283 cgit_print_pageheader(ctx); 248 cgit_print_pageheader(ctx);
284 cgit_print_error(fmt("Invalid branch: %s", tmp)); 249 cgit_print_error(fmt("Invalid branch: %s", tmp));
285 cgit_print_docend(); 250 cgit_print_docend();
286 return 1; 251 return 1;
287 } 252 }
288 return 0; 253 return 0;
289} 254}
290 255
291static void process_request(struct cgit_context *ctx) 256static void process_request(void *cbdata)
292{ 257{
258 struct cgit_context *ctx = cbdata;
293 struct cgit_cmd *cmd; 259 struct cgit_cmd *cmd;
294 260
295 cmd = cgit_get_cmd(ctx); 261 cmd = cgit_get_cmd(ctx);
296 if (!cmd) { 262 if (!cmd) {
297 ctx->page.title = "cgit error"; 263 ctx->page.title = "cgit error";
298 ctx->repo = NULL; 264 ctx->repo = NULL;
299 cgit_print_http_headers(ctx); 265 cgit_print_http_headers(ctx);
300 cgit_print_docstart(ctx); 266 cgit_print_docstart(ctx);
301 cgit_print_pageheader(ctx); 267 cgit_print_pageheader(ctx);
302 cgit_print_error("Invalid request"); 268 cgit_print_error("Invalid request");
303 cgit_print_docend(); 269 cgit_print_docend();
304 return; 270 return;
305 } 271 }
306 272
307 if (cmd->want_repo && prepare_repo_cmd(ctx)) 273 if (cmd->want_repo && prepare_repo_cmd(ctx))
308 return; 274 return;
309 275
310 if (cmd->want_layout) { 276 if (cmd->want_layout) {
311 cgit_print_http_headers(ctx); 277 cgit_print_http_headers(ctx);
312 cgit_print_docstart(ctx); 278 cgit_print_docstart(ctx);
313 cgit_print_pageheader(ctx); 279 cgit_print_pageheader(ctx);
314 } 280 }
315 281
316 cmd->fn(ctx); 282 cmd->fn(ctx);
317 283
318 if (cmd->want_layout) 284 if (cmd->want_layout)
319 cgit_print_docend(); 285 cgit_print_docend();
320} 286}
321 287
322static long ttl_seconds(long ttl)
323{
324 if (ttl<0)
325 return 60 * 60 * 24 * 365;
326 else
327 return ttl * 60;
328}
329
330static void cgit_fill_cache(struct cacheitem *item, int use_cache)
331{
332 int stdout2;
333
334 if (use_cache) {
335 stdout2 = chk_positive(dup(STDOUT_FILENO),
336 "Preserving STDOUT");
337 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
338 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
339 }
340
341 ctx.page.modified = time(NULL);
342 ctx.page.expires = ctx.page.modified + ttl_seconds(item->ttl);
343 process_request(&ctx);
344
345 if (use_cache) {
346 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
347 chk_positive(dup2(stdout2, STDOUT_FILENO),
348 "Restoring original STDOUT");
349 chk_zero(close(stdout2), "Closing temporary STDOUT");
350 }
351}
352
353static void cgit_check_cache(struct cacheitem *item)
354{
355 int i = 0;
356
357 top:
358 if (++i > ctx.cfg.max_lock_attempts) {
359 die("cgit_refresh_cache: unable to lock %s: %s",
360 item->name, strerror(errno));
361 }
362 if (!cache_exist(item)) {
363 if (!cache_lock(item)) {
364 sleep(1);
365 goto top;
366 }
367 if (!cache_exist(item)) {
368 cgit_fill_cache(item, 1);
369 cache_unlock(item);
370 } else {
371 cache_cancel_lock(item);
372 }
373 } else if (cache_expired(item) && cache_lock(item)) {
374 if (cache_expired(item)) {
375 cgit_fill_cache(item, 1);
376 cache_unlock(item);
377 } else {
378 cache_cancel_lock(item);
379 }
380 }
381}
382
383static void cgit_print_cache(struct cacheitem *item)
384{
385 static char buf[4096];
386 ssize_t i;
387
388 int fd = open(item->name, O_RDONLY);
389 if (fd<0)
390 die("Unable to open cached file %s", item->name);
391
392 while((i=read(fd, buf, sizeof(buf))) > 0)
393 write(STDOUT_FILENO, buf, i);
394
395 close(fd);
396}
397
398static void cgit_parse_args(int argc, const char **argv) 288static void cgit_parse_args(int argc, const char **argv)
399{ 289{
400 int i; 290 int i;
401 291
402 for (i = 1; i < argc; i++) { 292 for (i = 1; i < argc; i++) {
403 if (!strncmp(argv[i], "--cache=", 8)) { 293 if (!strncmp(argv[i], "--cache=", 8)) {
404 ctx.cfg.cache_root = xstrdup(argv[i]+8); 294 ctx.cfg.cache_root = xstrdup(argv[i]+8);
405 } 295 }
406 if (!strcmp(argv[i], "--nocache")) { 296 if (!strcmp(argv[i], "--nocache")) {
407 ctx.cfg.nocache = 1; 297 ctx.cfg.nocache = 1;
408 } 298 }
409 if (!strncmp(argv[i], "--query=", 8)) { 299 if (!strncmp(argv[i], "--query=", 8)) {
410 ctx.qry.raw = xstrdup(argv[i]+8); 300 ctx.qry.raw = xstrdup(argv[i]+8);
411 } 301 }
412 if (!strncmp(argv[i], "--repo=", 7)) { 302 if (!strncmp(argv[i], "--repo=", 7)) {
413 ctx.qry.repo = xstrdup(argv[i]+7); 303 ctx.qry.repo = xstrdup(argv[i]+7);
414 } 304 }
415 if (!strncmp(argv[i], "--page=", 7)) { 305 if (!strncmp(argv[i], "--page=", 7)) {
416 ctx.qry.page = xstrdup(argv[i]+7); 306 ctx.qry.page = xstrdup(argv[i]+7);
417 } 307 }
418 if (!strncmp(argv[i], "--head=", 7)) { 308 if (!strncmp(argv[i], "--head=", 7)) {
419 ctx.qry.head = xstrdup(argv[i]+7); 309 ctx.qry.head = xstrdup(argv[i]+7);
420 ctx.qry.has_symref = 1; 310 ctx.qry.has_symref = 1;
421 } 311 }
422 if (!strncmp(argv[i], "--sha1=", 7)) { 312 if (!strncmp(argv[i], "--sha1=", 7)) {
423 ctx.qry.sha1 = xstrdup(argv[i]+7); 313 ctx.qry.sha1 = xstrdup(argv[i]+7);
424 ctx.qry.has_sha1 = 1; 314 ctx.qry.has_sha1 = 1;
425 } 315 }
426 if (!strncmp(argv[i], "--ofs=", 6)) { 316 if (!strncmp(argv[i], "--ofs=", 6)) {
427 ctx.qry.ofs = atoi(argv[i]+6); 317 ctx.qry.ofs = atoi(argv[i]+6);
428 } 318 }
429 } 319 }
430} 320}
431 321
322static int calc_ttl()
323{
324 if (!ctx.repo)
325 return ctx.cfg.cache_root_ttl;
326
327 if (!ctx.qry.page)
328 return ctx.cfg.cache_repo_ttl;
329
330 if (ctx.qry.has_symref)
331 return ctx.cfg.cache_dynamic_ttl;
332
333 if (ctx.qry.has_sha1)
334 return ctx.cfg.cache_static_ttl;
335
336 return ctx.cfg.cache_repo_ttl;
337}
338
432int main(int argc, const char **argv) 339int main(int argc, const char **argv)
433{ 340{
434 struct cacheitem item;
435 const char *cgit_config_env = getenv("CGIT_CONFIG"); 341 const char *cgit_config_env = getenv("CGIT_CONFIG");
342 int err, ttl;
436 343
437 prepare_context(&ctx); 344 prepare_context(&ctx);
438 item.st.st_mtime = time(NULL);
439 cgit_repolist.length = 0; 345 cgit_repolist.length = 0;
440 cgit_repolist.count = 0; 346 cgit_repolist.count = 0;
441 cgit_repolist.repos = NULL; 347 cgit_repolist.repos = NULL;
442 348
443 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 349 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
444 config_cb); 350 config_cb);
445 ctx.repo = NULL; 351 ctx.repo = NULL;
446 if (getenv("SCRIPT_NAME")) 352 if (getenv("SCRIPT_NAME"))
447 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); 353 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
448 if (getenv("QUERY_STRING")) 354 if (getenv("QUERY_STRING"))
449 ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); 355 ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
450 cgit_parse_args(argc, argv); 356 cgit_parse_args(argc, argv);
451 http_parse_querystring(ctx.qry.raw, querystring_cb); 357 http_parse_querystring(ctx.qry.raw, querystring_cb);
452 if (!cgit_prepare_cache(&item)) 358
453 return 0; 359 ttl = calc_ttl();
454 if (ctx.cfg.nocache) { 360 ctx.page.expires += ttl*60;
455 cgit_fill_cache(&item, 0); 361 if (ctx.cfg.nocache)
456 } else { 362 ctx.cfg.cache_size = 0;
457 cgit_check_cache(&item); 363 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
458 cgit_print_cache(&item); 364 ctx.qry.raw, ttl, process_request, &ctx);
459 } 365 if (err)
460 return 0; 366 cache_log("[cgit] error %d - %s\n",
367 err, strerror(err));
368 return err;
461} 369}