-rw-r--r-- | cgit.c | 73 |
1 files changed, 73 insertions, 0 deletions
@@ -42,2 +42,4 @@ struct cgit_filter *new_filter(const char *cmd, int extra_args) +static void process_cached_repolist(const char *path); + void config_cb(const char *name, const char *value) @@ -98,2 +100,4 @@ void config_cb(const char *name, const char *value) ctx.cfg.cache_repo_ttl = atoi(value); + else if (!strcmp(name, "cache-scanrc-ttl")) + ctx.cfg.cache_scanrc_ttl = atoi(value); else if (!strcmp(name, "cache-static-ttl")) @@ -139,2 +143,5 @@ void config_cb(const char *name, const char *value) else if (!strcmp(name, "repo.scan")) + if (!ctx.cfg.nocache && ctx.cfg.cache_size) + process_cached_repolist(value); + else scan_tree(value); @@ -238,2 +245,3 @@ static void prepare_context(struct cgit_context *ctx) ctx->cfg.cache_root_ttl = 5; + ctx->cfg.cache_scanrc_ttl = 15; ctx->cfg.cache_static_ttl = -1; @@ -440,2 +448,67 @@ void print_repolist(FILE *f, struct cgit_repolist *list, int start) +/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' + * and return 0 on success. + */ +static int generate_cached_repolist(const char *path, const char *cached_rc) +{ + char *locked_rc; + int idx; + FILE *f; + + locked_rc = xstrdup(fmt("%s.lock", cached_rc)); + f = fopen(locked_rc, "wx"); + if (!f) { + /* Inform about the error unless the lockfile already existed, + * since that only means we've got concurrent requests. + */ + if (errno != EEXIST) + fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", + locked_rc, strerror(errno), errno); + return errno; + } + idx = cgit_repolist.count; + scan_tree(path); + print_repolist(f, &cgit_repolist, idx); + if (rename(locked_rc, cached_rc)) + fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", + locked_rc, cached_rc, strerror(errno), errno); + fclose(f); + return 0; +} + +static void process_cached_repolist(const char *path) +{ + struct stat st; + char *cached_rc; + time_t age; + + cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, + hash_str(path))); + + if (stat(cached_rc, &st)) { + /* Nothing is cached, we need to scan without forking. And + * if we fail to generate a cached repolist, we need to + * invoke scan_tree manually. + */ + if (generate_cached_repolist(path, cached_rc)) + scan_tree(path); + return; + } + + parse_configfile(cached_rc, config_cb); + + /* If the cached configfile hasn't expired, lets exit now */ + age = time(NULL) - st.st_mtime; + if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) + return; + + /* The cached repolist has been parsed, but it was old. So lets + * rescan the specified path and generate a new cached repolist + * in a child-process to avoid latency for the current request. + */ + if (fork()) + return; + + exit(generate_cached_repolist(path, cached_rc)); +} |