package httpd import ( "net/http" "os" "path/filepath" "runtime" "strings" ) // runnerBinary is github.com/barerepo/runner, which ships beside the server and is handed out by it. const runnerBinary = "barerepo-runner" // platform is what this binary was built for, which is the only one the server can hand out. func platform() string { return runtime.GOOS + "/" + runtime.GOARCH } // serveRunnerScript is the other half of the line on the setup page, which 404'd until now. func (s *Server) serveRunnerScript(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/x-shellscript; charset=utf-8") // A script is not a page, and a stale copy of an install script is a support ticket. w.Header().Set("Cache-Control", "no-store") w.Write([]byte(runnerScript(s.Cfg.Server.ExternalURL))) } // serveRunnerPS1 is the same script for the platform that has no sh. Chapter 15 shows all three. func (s *Server) serveRunnerPS1(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Cache-Control", "no-store") w.Write([]byte(runnerPS1(s.Cfg.Server.ExternalURL))) } // serveRunnerBinary hands out the runner installed beside this server, so the pair cannot skew. func (s *Server) serveRunnerBinary(w http.ResponseWriter, r *http.Request) { // The token is the whole authorisation, because the script has one and a stranger does not. if _, err := s.runnerAuth(r, r.URL.Query().Get("token")); err != nil { http.Error(w, "that token is not valid", http.StatusUnauthorized) return } self, err := os.Executable() if err != nil { http.Error(w, "this server cannot find its own binary", http.StatusInternalServerError) return } // The runner is its own program now, installed beside this one, so no path has to be configured. f, err := os.Open(filepath.Join(filepath.Dir(self), runnerBinary)) if err != nil { http.Error(w, "no "+runnerBinary+" is installed beside this server", http.StatusNotFound) return } defer f.Close() info, err := f.Stat() if err != nil { http.Error(w, "this server cannot stat its own binary", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Barerepo-Platform", platform()) http.ServeContent(w, r, runnerBinary, info.ModTime(), f) } // runnerScript builds the posix half. The token arrives as $1, from sh -s . func runnerScript(external string) string { base := strings.TrimRight(external, "/") return `#!/bin/sh # barerepo runner install. one paste, one machine attached. chapter 15. set -eu TOKEN="${1:-}" if [ -z "$TOKEN" ]; then echo "usage: curl -sL ` + base + `/runner.sh | sh -s " >&2 exit 1 fi SERVER="` + base + `" HAVE="` + platform() + `" OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) case "$ARCH" in x86_64 | amd64) ARCH=amd64 ;; aarch64 | arm64) ARCH=arm64 ;; esac WANT="$OS/$ARCH" # The server can only hand out the binary it is running, so say so rather than install the wrong one. if [ "$WANT" != "$HAVE" ]; then echo "this barerepo runs $HAVE and cannot hand you a $WANT binary." >&2 echo "build one and run it yourself:" >&2 echo " GOOS=$OS GOARCH=$ARCH go build -o barerepo-runner ./cmd/barerepo-runner" >&2 echo " ./barerepo-runner $TOKEN --server $SERVER" >&2 exit 1 fi DIR="${BAREREPO_BIN_DIR:-$HOME/.local/bin}" mkdir -p "$DIR" # Downloaded beside the target and moved, so an interrupted install leaves no half a binary. TMP="$DIR/.barerepo-runner.$$" trap 'rm -f "$TMP"' EXIT INT TERM if command -v curl >/dev/null 2>&1; then curl -fsSL "$SERVER/runner/binary?token=$TOKEN" -o "$TMP" elif command -v wget >/dev/null 2>&1; then wget -qO "$TMP" "$SERVER/runner/binary?token=$TOKEN" else echo "neither curl nor wget is installed." >&2 exit 1 fi chmod +x "$TMP" mv -f "$TMP" "$DIR/barerepo-runner" trap - EXIT INT TERM echo "the runner is at $DIR/barerepo-runner. attaching." exec "$DIR/barerepo-runner" "$TOKEN" --server "$SERVER" ` } // runnerPS1 is the windows half, which the mockup shows beside the others rather than behind a tab. func runnerPS1(external string) string { base := strings.TrimRight(external, "/") return `# barerepo runner install. one paste, one machine attached. chapter 15. $ErrorActionPreference = "Stop" function barerepo-runner { param([Parameter(Mandatory=$true)][string]$Token) $server = "` + base + `" $have = "` + platform() + `" $arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { "386" } $want = "windows/$arch" if ($want -ne $have) { Write-Error "this barerepo runs $have and cannot hand you a $want binary. build one: GOOS=windows GOARCH=$arch go build -o barerepo-runner.exe ./cmd/barerepo-runner" return } $dir = Join-Path $env:LOCALAPPDATA "barerepo" New-Item -ItemType Directory -Force -Path $dir | Out-Null $exe = Join-Path $dir "barerepo-runner.exe" Invoke-WebRequest -Uri "$server/runner/binary?token=$Token" -OutFile $exe Write-Host "the runner is at $exe. attaching." & $exe $Token --server $server } ` }