summaryrefslogtreecommitdiffabout
path: root/cgit.c
Unidiff
Diffstat (limited to 'cgit.c') (more/less context) (show whitespace changes)
-rw-r--r--cgit.c5
1 files changed, 3 insertions, 2 deletions
diff --git a/cgit.c b/cgit.c
index 916feb4..f4dd6ef 100644
--- a/cgit.c
+++ b/cgit.c
@@ -563,229 +563,230 @@ void print_repo(FILE *f, struct cgit_repo *repo)
563 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd); 563 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd);
564 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter) 564 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
565 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); 565 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd);
566 if (repo->snapshots != ctx.cfg.snapshots) { 566 if (repo->snapshots != ctx.cfg.snapshots) {
567 char *tmp = build_snapshot_setting(repo->snapshots); 567 char *tmp = build_snapshot_setting(repo->snapshots);
568 fprintf(f, "repo.snapshots=%s\n", tmp); 568 fprintf(f, "repo.snapshots=%s\n", tmp);
569 free(tmp); 569 free(tmp);
570 } 570 }
571 if (repo->max_stats != ctx.cfg.max_stats) 571 if (repo->max_stats != ctx.cfg.max_stats)
572 fprintf(f, "repo.max-stats=%s\n", 572 fprintf(f, "repo.max-stats=%s\n",
573 cgit_find_stats_periodname(repo->max_stats)); 573 cgit_find_stats_periodname(repo->max_stats));
574 fprintf(f, "\n"); 574 fprintf(f, "\n");
575} 575}
576 576
577void print_repolist(FILE *f, struct cgit_repolist *list, int start) 577void print_repolist(FILE *f, struct cgit_repolist *list, int start)
578{ 578{
579 int i; 579 int i;
580 580
581 for(i = start; i < list->count; i++) 581 for(i = start; i < list->count; i++)
582 print_repo(f, &list->repos[i]); 582 print_repo(f, &list->repos[i]);
583} 583}
584 584
585/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 585/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
586 * and return 0 on success. 586 * and return 0 on success.
587 */ 587 */
588static int generate_cached_repolist(const char *path, const char *cached_rc) 588static int generate_cached_repolist(const char *path, const char *cached_rc)
589{ 589{
590 char *locked_rc; 590 char *locked_rc;
591 int idx; 591 int idx;
592 FILE *f; 592 FILE *f;
593 593
594 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 594 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
595 f = fopen(locked_rc, "wx"); 595 f = fopen(locked_rc, "wx");
596 if (!f) { 596 if (!f) {
597 /* Inform about the error unless the lockfile already existed, 597 /* Inform about the error unless the lockfile already existed,
598 * since that only means we've got concurrent requests. 598 * since that only means we've got concurrent requests.
599 */ 599 */
600 if (errno != EEXIST) 600 if (errno != EEXIST)
601 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 601 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
602 locked_rc, strerror(errno), errno); 602 locked_rc, strerror(errno), errno);
603 return errno; 603 return errno;
604 } 604 }
605 idx = cgit_repolist.count; 605 idx = cgit_repolist.count;
606 if (ctx.cfg.project_list) 606 if (ctx.cfg.project_list)
607 scan_projects(path, ctx.cfg.project_list, repo_config); 607 scan_projects(path, ctx.cfg.project_list, repo_config);
608 else 608 else
609 scan_tree(path, repo_config); 609 scan_tree(path, repo_config);
610 print_repolist(f, &cgit_repolist, idx); 610 print_repolist(f, &cgit_repolist, idx);
611 if (rename(locked_rc, cached_rc)) 611 if (rename(locked_rc, cached_rc))
612 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 612 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
613 locked_rc, cached_rc, strerror(errno), errno); 613 locked_rc, cached_rc, strerror(errno), errno);
614 fclose(f); 614 fclose(f);
615 return 0; 615 return 0;
616} 616}
617 617
618static void process_cached_repolist(const char *path) 618static void process_cached_repolist(const char *path)
619{ 619{
620 struct stat st; 620 struct stat st;
621 char *cached_rc; 621 char *cached_rc;
622 time_t age; 622 time_t age;
623 unsigned long hash; 623 unsigned long hash;
624 624
625 hash = hash_str(path); 625 hash = hash_str(path);
626 if (ctx.cfg.project_list) 626 if (ctx.cfg.project_list)
627 hash += hash_str(ctx.cfg.project_list); 627 hash += hash_str(ctx.cfg.project_list);
628 cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash)); 628 cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash));
629 629
630 if (stat(cached_rc, &st)) { 630 if (stat(cached_rc, &st)) {
631 /* Nothing is cached, we need to scan without forking. And 631 /* Nothing is cached, we need to scan without forking. And
632 * if we fail to generate a cached repolist, we need to 632 * if we fail to generate a cached repolist, we need to
633 * invoke scan_tree manually. 633 * invoke scan_tree manually.
634 */ 634 */
635 if (generate_cached_repolist(path, cached_rc)) { 635 if (generate_cached_repolist(path, cached_rc)) {
636 if (ctx.cfg.project_list) 636 if (ctx.cfg.project_list)
637 scan_projects(path, ctx.cfg.project_list, 637 scan_projects(path, ctx.cfg.project_list,
638 repo_config); 638 repo_config);
639 else 639 else
640 scan_tree(path, repo_config); 640 scan_tree(path, repo_config);
641 } 641 }
642 return; 642 return;
643 } 643 }
644 644
645 parse_configfile(cached_rc, config_cb); 645 parse_configfile(cached_rc, config_cb);
646 646
647 /* If the cached configfile hasn't expired, lets exit now */ 647 /* If the cached configfile hasn't expired, lets exit now */
648 age = time(NULL) - st.st_mtime; 648 age = time(NULL) - st.st_mtime;
649 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 649 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
650 return; 650 return;
651 651
652 /* The cached repolist has been parsed, but it was old. So lets 652 /* The cached repolist has been parsed, but it was old. So lets
653 * rescan the specified path and generate a new cached repolist 653 * rescan the specified path and generate a new cached repolist
654 * in a child-process to avoid latency for the current request. 654 * in a child-process to avoid latency for the current request.
655 */ 655 */
656 if (fork()) 656 if (fork())
657 return; 657 return;
658 658
659 exit(generate_cached_repolist(path, cached_rc)); 659 exit(generate_cached_repolist(path, cached_rc));
660} 660}
661 661
662static void cgit_parse_args(int argc, const char **argv) 662static void cgit_parse_args(int argc, const char **argv)
663{ 663{
664 int i; 664 int i;
665 int scan = 0; 665 int scan = 0;
666 666
667 for (i = 1; i < argc; i++) { 667 for (i = 1; i < argc; i++) {
668 if (!strncmp(argv[i], "--cache=", 8)) { 668 if (!strncmp(argv[i], "--cache=", 8)) {
669 ctx.cfg.cache_root = xstrdup(argv[i]+8); 669 ctx.cfg.cache_root = xstrdup(argv[i]+8);
670 } 670 }
671 if (!strcmp(argv[i], "--nocache")) { 671 if (!strcmp(argv[i], "--nocache")) {
672 ctx.cfg.nocache = 1; 672 ctx.cfg.nocache = 1;
673 } 673 }
674 if (!strcmp(argv[i], "--nohttp")) { 674 if (!strcmp(argv[i], "--nohttp")) {
675 ctx.env.no_http = "1"; 675 ctx.env.no_http = "1";
676 } 676 }
677 if (!strncmp(argv[i], "--query=", 8)) { 677 if (!strncmp(argv[i], "--query=", 8)) {
678 ctx.qry.raw = xstrdup(argv[i]+8); 678 ctx.qry.raw = xstrdup(argv[i]+8);
679 } 679 }
680 if (!strncmp(argv[i], "--repo=", 7)) { 680 if (!strncmp(argv[i], "--repo=", 7)) {
681 ctx.qry.repo = xstrdup(argv[i]+7); 681 ctx.qry.repo = xstrdup(argv[i]+7);
682 } 682 }
683 if (!strncmp(argv[i], "--page=", 7)) { 683 if (!strncmp(argv[i], "--page=", 7)) {
684 ctx.qry.page = xstrdup(argv[i]+7); 684 ctx.qry.page = xstrdup(argv[i]+7);
685 } 685 }
686 if (!strncmp(argv[i], "--head=", 7)) { 686 if (!strncmp(argv[i], "--head=", 7)) {
687 ctx.qry.head = xstrdup(argv[i]+7); 687 ctx.qry.head = xstrdup(argv[i]+7);
688 ctx.qry.has_symref = 1; 688 ctx.qry.has_symref = 1;
689 } 689 }
690 if (!strncmp(argv[i], "--sha1=", 7)) { 690 if (!strncmp(argv[i], "--sha1=", 7)) {
691 ctx.qry.sha1 = xstrdup(argv[i]+7); 691 ctx.qry.sha1 = xstrdup(argv[i]+7);
692 ctx.qry.has_sha1 = 1; 692 ctx.qry.has_sha1 = 1;
693 } 693 }
694 if (!strncmp(argv[i], "--ofs=", 6)) { 694 if (!strncmp(argv[i], "--ofs=", 6)) {
695 ctx.qry.ofs = atoi(argv[i]+6); 695 ctx.qry.ofs = atoi(argv[i]+6);
696 } 696 }
697 if (!strncmp(argv[i], "--scan-tree=", 12) || 697 if (!strncmp(argv[i], "--scan-tree=", 12) ||
698 !strncmp(argv[i], "--scan-path=", 12)) { 698 !strncmp(argv[i], "--scan-path=", 12)) {
699 /* HACK: the global snapshot bitmask defines the 699 /* HACK: the global snapshot bitmask defines the
700 * set of allowed snapshot formats, but the config 700 * set of allowed snapshot formats, but the config
701 * file hasn't been parsed yet so the mask is 701 * file hasn't been parsed yet so the mask is
702 * currently 0. By setting all bits high before 702 * currently 0. By setting all bits high before
703 * scanning we make sure that any in-repo cgitrc 703 * scanning we make sure that any in-repo cgitrc
704 * snapshot setting is respected by scan_tree(). 704 * snapshot setting is respected by scan_tree().
705 * BTW: we assume that there'll never be more than 705 * BTW: we assume that there'll never be more than
706 * 255 different snapshot formats supported by cgit... 706 * 255 different snapshot formats supported by cgit...
707 */ 707 */
708 ctx.cfg.snapshots = 0xFF; 708 ctx.cfg.snapshots = 0xFF;
709 scan++; 709 scan++;
710 scan_tree(argv[i] + 12, repo_config); 710 scan_tree(argv[i] + 12, repo_config);
711 } 711 }
712 } 712 }
713 if (scan) { 713 if (scan) {
714 qsort(cgit_repolist.repos, cgit_repolist.count, 714 qsort(cgit_repolist.repos, cgit_repolist.count,
715 sizeof(struct cgit_repo), cmp_repos); 715 sizeof(struct cgit_repo), cmp_repos);
716 print_repolist(stdout, &cgit_repolist, 0); 716 print_repolist(stdout, &cgit_repolist, 0);
717 exit(0); 717 exit(0);
718 } 718 }
719} 719}
720 720
721static int calc_ttl() 721static int calc_ttl()
722{ 722{
723 if (!ctx.repo) 723 if (!ctx.repo)
724 return ctx.cfg.cache_root_ttl; 724 return ctx.cfg.cache_root_ttl;
725 725
726 if (!ctx.qry.page) 726 if (!ctx.qry.page)
727 return ctx.cfg.cache_repo_ttl; 727 return ctx.cfg.cache_repo_ttl;
728 728
729 if (ctx.qry.has_symref) 729 if (ctx.qry.has_symref)
730 return ctx.cfg.cache_dynamic_ttl; 730 return ctx.cfg.cache_dynamic_ttl;
731 731
732 if (ctx.qry.has_sha1) 732 if (ctx.qry.has_sha1)
733 return ctx.cfg.cache_static_ttl; 733 return ctx.cfg.cache_static_ttl;
734 734
735 return ctx.cfg.cache_repo_ttl; 735 return ctx.cfg.cache_repo_ttl;
736} 736}
737 737
738int main(int argc, const char **argv) 738int main(int argc, const char **argv)
739{ 739{
740 const char *path; 740 const char *path;
741 char *qry; 741 char *qry;
742 int err, ttl; 742 int err, ttl;
743 743
744 prepare_context(&ctx); 744 prepare_context(&ctx);
745 cgit_repolist.length = 0; 745 cgit_repolist.length = 0;
746 cgit_repolist.count = 0; 746 cgit_repolist.count = 0;
747 cgit_repolist.repos = NULL; 747 cgit_repolist.repos = NULL;
748 748
749 cgit_parse_args(argc, argv); 749 cgit_parse_args(argc, argv);
750 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb); 750 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
751 ctx.repo = NULL; 751 ctx.repo = NULL;
752 http_parse_querystring(ctx.qry.raw, querystring_cb); 752 http_parse_querystring(ctx.qry.raw, querystring_cb);
753 753
754 /* If virtual-root isn't specified in cgitrc, lets pretend 754 /* If virtual-root isn't specified in cgitrc, lets pretend
755 * that virtual-root equals SCRIPT_NAME. 755 * that virtual-root equals SCRIPT_NAME, minus any possibly
756 * trailing slashes.
756 */ 757 */
757 if (!ctx.cfg.virtual_root) 758 if (!ctx.cfg.virtual_root)
758 ctx.cfg.virtual_root = ctx.cfg.script_name; 759 ctx.cfg.virtual_root = trim_end(ctx.cfg.script_name, '/');
759 760
760 /* If no url parameter is specified on the querystring, lets 761 /* If no url parameter is specified on the querystring, lets
761 * use PATH_INFO as url. This allows cgit to work with virtual 762 * use PATH_INFO as url. This allows cgit to work with virtual
762 * urls without the need for rewriterules in the webserver (as 763 * urls without the need for rewriterules in the webserver (as
763 * long as PATH_INFO is included in the cache lookup key). 764 * long as PATH_INFO is included in the cache lookup key).
764 */ 765 */
765 path = ctx.env.path_info; 766 path = ctx.env.path_info;
766 if (!ctx.qry.url && path) { 767 if (!ctx.qry.url && path) {
767 if (path[0] == '/') 768 if (path[0] == '/')
768 path++; 769 path++;
769 ctx.qry.url = xstrdup(path); 770 ctx.qry.url = xstrdup(path);
770 if (ctx.qry.raw) { 771 if (ctx.qry.raw) {
771 qry = ctx.qry.raw; 772 qry = ctx.qry.raw;
772 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 773 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
773 free(qry); 774 free(qry);
774 } else 775 } else
775 ctx.qry.raw = xstrdup(ctx.qry.url); 776 ctx.qry.raw = xstrdup(ctx.qry.url);
776 cgit_parse_url(ctx.qry.url); 777 cgit_parse_url(ctx.qry.url);
777 } 778 }
778 779
779 ttl = calc_ttl(); 780 ttl = calc_ttl();
780 ctx.page.expires += ttl*60; 781 ctx.page.expires += ttl*60;
781 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")) 782 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
782 ctx.cfg.nocache = 1; 783 ctx.cfg.nocache = 1;
783 if (ctx.cfg.nocache) 784 if (ctx.cfg.nocache)
784 ctx.cfg.cache_size = 0; 785 ctx.cfg.cache_size = 0;
785 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 786 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
786 ctx.qry.raw, ttl, process_request, &ctx); 787 ctx.qry.raw, ttl, process_request, &ctx);
787 if (err) 788 if (err)
788 cgit_print_error(fmt("Error processing page: %s (%d)", 789 cgit_print_error(fmt("Error processing page: %s (%d)",
789 strerror(err), err)); 790 strerror(err), err));
790 return err; 791 return err;
791} 792}