diff options
| author | Shulhan <ms@kilabit.info> | 2026-04-14 17:09:12 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2026-04-14 21:17:11 +0700 |
| commit | 3cf0fd6460e828b2d39afe5b5673c4d0d2fdf24a (patch) | |
| tree | 7411fdcf10ef91bff027254acaed9ed3276745a0 | |
| parent | 829eb0711305e8946fa2f4a1c57c43354f35e208 (diff) | |
| download | cgit-3cf0fd6460e828b2d39afe5b5673c4d0d2fdf24a.tar.xz | |
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).
| -rw-r--r-- | cmd/cgitd/main.go | 200 | ||||
| -rw-r--r-- | go.mod | 11 | ||||
| -rw-r--r-- | go.sum | 4 |
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) + } +} @@ -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 @@ -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= |
