aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/cgitd/main.go200
-rw-r--r--go.mod11
-rw-r--r--go.sum4
3 files changed, 215 insertions, 0 deletions
diff --git a/cmd/cgitd/main.go b/cmd/cgitd/main.go
new file mode 100644
index 0000000..9abced6
--- /dev/null
+++ b/cmd/cgitd/main.go
@@ -0,0 +1,200 @@
+// Copyright (C) 2026 cgit Development Team <cgit@lists.zx2c4.com>
+// Licensed under GNU General Public License v2
+// (see COPYING for full license text)
+
+// Program cgitd is the HTTP server for cgit CGI.
+// This program can be built into single binary, provides as an alternatives
+// to common setup that require second application (web server with their own
+// configuration).
+//
+// cgitd application can be customized through environment variables or by
+// modifying this code directly before build.
+//
+// # Environment variables
+//
+// The following environment variables affect on cgitd only.
+//
+// CGITD_CGIT_BIN - The full path to cgit.cgi.
+// It should be in the same directory that contains the "filters" scripts.
+// This environment is required, default to "/usr/lib/cgit/cgit.cgi".
+//
+// CGITD_CGIT_STATIC_DIR - The full path to directory that contains cgit
+// resources (cgit.css, cgit.js, cgit.png).
+// This environment is required, default to "/usr/share/webapps/cgit/".
+//
+// CGITD_LISTEN - IP address and port to accept HTTP connections.
+// This environment is required, default to "127.0.0.1:10010".
+//
+// CGITD_REPO_PREFIXES - Comma separated repo URL prefixes that will be
+// handled by cgit.
+// This environment is optional, no default value.
+//
+// CGITD_STATIC_DIR - The full path to serve static files, like favicon and
+// logo.
+// If set, the content of this directory will be served under "/static/" path,
+// so make sure to add prefix to favicon= and logo= in cgitrc.
+// This environment is optional, no default value.
+//
+// # Environment passed to cgit
+//
+// The following environment variables is passed to cgit process.
+//
+// CGIT_CONFIG - The full path to cgitrc file.
+// This environment is optional.
+//
+// # Environment passed as arguments to cgit
+//
+// The following environment variables is paseed to cgit as arguments.
+//
+// CGIT_CACHE_ROOT - The directory for storing cgit caches, passed in
+// "--cache" argument.
+// This environment is optional.
+package main
+
+import (
+ "log"
+ "net"
+ "net/http"
+ "net/http/cgi"
+ "os"
+ "strings"
+
+ "git.sr.ht/~shulhan/pakakeh.go/lib/systemd"
+)
+
+// List of known environment variables handled by cgitd.
+const (
+ envCgitBin = `CGITD_CGIT_BIN`
+ envCgitStaticDir = `CGITD_CGIT_STATIC_DIR`
+ envListen = `CGITD_LISTEN`
+ envRepoPrefixes = `CGITD_REPO_PREFIXES`
+ envStaticDir = `CGITD_STATIC_DIR`
+)
+
+const (
+ defCgitBin = `/usr/lib/cgit/cgit.cgi`
+ defCgitStaticDir = `/usr/share/webapps/cgit/`
+ defListen = `127.0.0.1:10010`
+ defStaticPrefix = `/static`
+)
+
+// List of known environment variables passed to cgit process.
+var listCgitEnv = []string{
+ `CGIT_CONFIG`,
+}
+
+type config struct {
+ cgitBin string // Path to cgit.cgi.
+ cgitStaticDir string // Path to cgit static resources.
+ listen string // Address for accepting connection.
+ staticDir string // Path to non-cgit resources like favicon and logo.
+
+ cgitEnvs []string // List of environment variables for cgit.
+ repoPrefixes []string // List of repo URL prefixes.
+}
+
+func initConfig() (cfg config) {
+ cfg.cgitBin = os.Getenv(envCgitBin)
+ if cfg.cgitBin == "" {
+ cfg.cgitBin = defCgitBin
+ }
+
+ cfg.cgitStaticDir = os.Getenv(envCgitStaticDir)
+ if cfg.cgitStaticDir == "" {
+ cfg.cgitStaticDir = defCgitStaticDir
+ }
+
+ cfg.listen = os.Getenv(envListen)
+ if cfg.listen == "" {
+ cfg.listen = defListen
+ }
+
+ repoPrefixes := os.Getenv(envRepoPrefixes)
+ for _, item := range strings.Split(repoPrefixes, ",") {
+ item = strings.TrimSpace(item)
+ if item != "" {
+ cfg.repoPrefixes = append(cfg.repoPrefixes, item)
+ }
+ }
+
+ cfg.staticDir = os.Getenv(envStaticDir)
+
+ for _, key := range listCgitEnv {
+ val := os.Getenv(key)
+ if val != "" {
+ cfg.cgitEnvs = append(cfg.cgitEnvs, key+`=`+val)
+ }
+ }
+
+ return cfg
+}
+
+func main() {
+ logp := `cgitd`
+ cfg := initConfig()
+
+ log.Printf(`%s: %s=%s`, logp, envCgitBin, cfg.cgitBin)
+ log.Printf(`%s: %s=%s`, logp, envCgitStaticDir, cfg.cgitStaticDir)
+ log.Printf(`%s: %s=%s`, logp, envListen, cfg.listen)
+ log.Printf(`%s: %s=%s`, logp, envRepoPrefixes, cfg.repoPrefixes)
+ log.Printf(`%s: %s=%s`, logp, envStaticDir, cfg.staticDir)
+ log.Printf(`%s: cgitEnvs=%s`, logp, cfg.cgitEnvs)
+
+ rootfs, err := os.OpenRoot(cfg.cgitStaticDir)
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+ cgitStaticFS := http.FileServerFS(rootfs.FS())
+
+ mux := http.NewServeMux()
+ mux.Handle(`GET /cgit.css`, cgitStaticFS)
+ mux.Handle(`GET /cgit.js`, cgitStaticFS)
+ mux.Handle(`GET /cgit.png`, cgitStaticFS)
+ mux.Handle(`GET /robots.txt`, cgitStaticFS)
+
+ if cfg.staticDir != "" {
+ rootfs, err = os.OpenRoot(cfg.staticDir)
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+ staticFS := http.FileServerFS(rootfs.FS())
+ mux.Handle(`GET `+defStaticPrefix+`/`,
+ http.StripPrefix(defStaticPrefix, staticFS))
+ }
+
+ cgitHandler := &cgi.Handler{
+ Path: cfg.cgitBin,
+ Env: cfg.cgitEnvs,
+ }
+ for _, prefix := range cfg.repoPrefixes {
+ mux.Handle(`GET `+prefix, cgitHandler)
+ }
+ mux.Handle(`GET /`, cgitHandler)
+
+ httpd := http.Server{
+ Addr: cfg.listen,
+ Handler: mux,
+ }
+
+ // Allow activation through systemd socket.
+ var listener net.Listener
+ listeners, err := systemd.Listeners(true)
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+ for _, l := range listeners {
+ if l.Addr().String() == cfg.listen {
+ listener = l
+ }
+ }
+ if listener == nil {
+ log.Printf(`%s: listening on address %s`, logp, cfg.listen)
+ err = httpd.ListenAndServe()
+ } else {
+ log.Printf(`%s: activated throught systemd socket`, logp)
+ err = httpd.Serve(listener)
+ }
+ if err != nil {
+ log.Fatalf(`%s: %s`, logp, err)
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..6807df0
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,11 @@
+// Copyright (C) 2026 cgit Development Team <cgit@lists.zx2c4.com>
+// Licensed under GNU General Public License v2
+// (see COPYING for full license text)
+
+module git.zx2c4.com/cgit
+
+go 1.26.0
+
+require git.sr.ht/~shulhan/pakakeh.go v0.62.0
+
+require golang.org/x/sys v0.42.0 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..c6b687e
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+git.sr.ht/~shulhan/pakakeh.go v0.62.0 h1:rAX0NpfxGbRfe6YhG7I9mmETmcafAreXUZqwPWNDvw4=
+git.sr.ht/~shulhan/pakakeh.go v0.62.0/go.mod h1:M8FG29UN+TMqRsOvKFTjsBuZDiGeBzYlAFxHjBM03Rs=
+golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
+golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=