133728e barerepo 1mo
1
package httpd
133728e barerepo 1mo
3
import (
133728e barerepo 1mo
4
"net/http"
133728e barerepo 1mo
5
"os"
133728e barerepo 1mo
6
"path/filepath"
133728e barerepo 1mo
7
"runtime"
133728e barerepo 1mo
8
"strings"
133728e barerepo 1mo
11
// runnerBinary is github.com/barerepo/runner, which ships beside the server and is handed out by it.
133728e barerepo 1mo
12
const runnerBinary = "barerepo-runner"
133728e barerepo 1mo
14
// platform is what this binary was built for, which is the only one the server can hand out.
133728e barerepo 1mo
15
func platform() string { return runtime.GOOS + "/" + runtime.GOARCH }
133728e barerepo 1mo
17
// serveRunnerScript is the other half of the line on the setup page, which 404'd until now.
133728e barerepo 1mo
18
func (s *Server) serveRunnerScript(w http.ResponseWriter, r *http.Request) {
133728e barerepo 1mo
19
w.Header().Set("Content-Type", "text/x-shellscript; charset=utf-8")
133728e barerepo 1mo
20
// A script is not a page, and a stale copy of an install script is a support ticket.
133728e barerepo 1mo
21
w.Header().Set("Cache-Control", "no-store")
133728e barerepo 1mo
22
w.Write([]byte(runnerScript(s.Cfg.Server.ExternalURL)))
133728e barerepo 1mo
25
// serveRunnerPS1 is the same script for the platform that has no sh. Chapter 15 shows all three.
133728e barerepo 1mo
26
func (s *Server) serveRunnerPS1(w http.ResponseWriter, r *http.Request) {
133728e barerepo 1mo
27
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
133728e barerepo 1mo
28
w.Header().Set("Cache-Control", "no-store")
133728e barerepo 1mo
29
w.Write([]byte(runnerPS1(s.Cfg.Server.ExternalURL)))
133728e barerepo 1mo
32
// serveRunnerBinary hands out the runner installed beside this server, so the pair cannot skew.
133728e barerepo 1mo
33
func (s *Server) serveRunnerBinary(w http.ResponseWriter, r *http.Request) {
133728e barerepo 1mo
34
// The token is the whole authorisation, because the script has one and a stranger does not.
133728e barerepo 1mo
35
if _, err := s.runnerAuth(r, r.URL.Query().Get("token")); err != nil {
133728e barerepo 1mo
36
http.Error(w, "that token is not valid", http.StatusUnauthorized)
133728e barerepo 1mo
37
return
133728e barerepo 1mo
39
self, err := os.Executable()
133728e barerepo 1mo
40
if err != nil {
133728e barerepo 1mo
41
http.Error(w, "this server cannot find its own binary", http.StatusInternalServerError)
133728e barerepo 1mo
42
return
133728e barerepo 1mo
44
// The runner is its own program now, installed beside this one, so no path has to be configured.
133728e barerepo 1mo
45
f, err := os.Open(filepath.Join(filepath.Dir(self), runnerBinary))
133728e barerepo 1mo
46
if err != nil {
133728e barerepo 1mo
47
http.Error(w, "no "+runnerBinary+" is installed beside this server", http.StatusNotFound)
133728e barerepo 1mo
48
return
133728e barerepo 1mo
50
defer f.Close()
133728e barerepo 1mo
51
info, err := f.Stat()
133728e barerepo 1mo
52
if err != nil {
133728e barerepo 1mo
53
http.Error(w, "this server cannot stat its own binary", http.StatusInternalServerError)
133728e barerepo 1mo
54
return
133728e barerepo 1mo
56
w.Header().Set("Content-Type", "application/octet-stream")
133728e barerepo 1mo
57
w.Header().Set("Barerepo-Platform", platform())
133728e barerepo 1mo
58
http.ServeContent(w, r, runnerBinary, info.ModTime(), f)
133728e barerepo 1mo
61
// runnerScript builds the posix half. The token arrives as $1, from sh -s <token>.
133728e barerepo 1mo
62
func runnerScript(external string) string {
133728e barerepo 1mo
63
base := strings.TrimRight(external, "/")
133728e barerepo 1mo
64
return `#!/bin/sh
133728e barerepo 1mo
65
# barerepo runner install. one paste, one machine attached. chapter 15.
133728e barerepo 1mo
66
set -eu
133728e barerepo 1mo
68
TOKEN="${1:-}"
133728e barerepo 1mo
69
if [ -z "$TOKEN" ]; then
133728e barerepo 1mo
70
echo "usage: curl -sL ` + base + `/runner.sh | sh -s <token>" >&2
133728e barerepo 1mo
71
exit 1
133728e barerepo 1mo
74
SERVER="` + base + `"
133728e barerepo 1mo
75
HAVE="` + platform() + `"
133728e barerepo 1mo
77
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
133728e barerepo 1mo
78
ARCH=$(uname -m)
133728e barerepo 1mo
79
case "$ARCH" in
133728e barerepo 1mo
80
x86_64 | amd64) ARCH=amd64 ;;
133728e barerepo 1mo
81
aarch64 | arm64) ARCH=arm64 ;;
133728e barerepo 1mo
82
esac
133728e barerepo 1mo
83
WANT="$OS/$ARCH"
133728e barerepo 1mo
85
# The server can only hand out the binary it is running, so say so rather than install the wrong one.
133728e barerepo 1mo
86
if [ "$WANT" != "$HAVE" ]; then
133728e barerepo 1mo
87
echo "this barerepo runs $HAVE and cannot hand you a $WANT binary." >&2
133728e barerepo 1mo
88
echo "build one and run it yourself:" >&2
133728e barerepo 1mo
89
echo " GOOS=$OS GOARCH=$ARCH go build -o barerepo-runner ./cmd/barerepo-runner" >&2
133728e barerepo 1mo
90
echo " ./barerepo-runner $TOKEN --server $SERVER" >&2
133728e barerepo 1mo
91
exit 1
133728e barerepo 1mo
94
DIR="${BAREREPO_BIN_DIR:-$HOME/.local/bin}"
133728e barerepo 1mo
95
mkdir -p "$DIR"
133728e barerepo 1mo
97
# Downloaded beside the target and moved, so an interrupted install leaves no half a binary.
133728e barerepo 1mo
98
TMP="$DIR/.barerepo-runner.$$"
133728e barerepo 1mo
99
trap 'rm -f "$TMP"' EXIT INT TERM
133728e barerepo 1mo
100
if command -v curl >/dev/null 2>&1; then
133728e barerepo 1mo
101
curl -fsSL "$SERVER/runner/binary?token=$TOKEN" -o "$TMP"
133728e barerepo 1mo
102
elif command -v wget >/dev/null 2>&1; then
133728e barerepo 1mo
103
wget -qO "$TMP" "$SERVER/runner/binary?token=$TOKEN"
133728e barerepo 1mo
104
else
133728e barerepo 1mo
105
echo "neither curl nor wget is installed." >&2
133728e barerepo 1mo
106
exit 1
133728e barerepo 1mo
107
fi
133728e barerepo 1mo
108
chmod +x "$TMP"
133728e barerepo 1mo
109
mv -f "$TMP" "$DIR/barerepo-runner"
133728e barerepo 1mo
110
trap - EXIT INT TERM
133728e barerepo 1mo
112
echo "the runner is at $DIR/barerepo-runner. attaching."
133728e barerepo 1mo
113
exec "$DIR/barerepo-runner" "$TOKEN" --server "$SERVER"
133728e barerepo 1mo
117
// runnerPS1 is the windows half, which the mockup shows beside the others rather than behind a tab.
133728e barerepo 1mo
118
func runnerPS1(external string) string {
133728e barerepo 1mo
119
base := strings.TrimRight(external, "/")
133728e barerepo 1mo
120
return `# barerepo runner install. one paste, one machine attached. chapter 15.
133728e barerepo 1mo
121
$ErrorActionPreference = "Stop"
133728e barerepo 1mo
123
function barerepo-runner {
133728e barerepo 1mo
124
param([Parameter(Mandatory=$true)][string]$Token)
133728e barerepo 1mo
126
$server = "` + base + `"
133728e barerepo 1mo
127
$have = "` + platform() + `"
133728e barerepo 1mo
129
$arch = if ([Environment]::Is64BitOperatingSystem) { "amd64" } else { "386" }
133728e barerepo 1mo
130
$want = "windows/$arch"
133728e barerepo 1mo
131
if ($want -ne $have) {
133728e barerepo 1mo
132
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"
133728e barerepo 1mo
133
return
133728e barerepo 1mo
134
}
133728e barerepo 1mo
136
$dir = Join-Path $env:LOCALAPPDATA "barerepo"
133728e barerepo 1mo
137
New-Item -ItemType Directory -Force -Path $dir | Out-Null
133728e barerepo 1mo
138
$exe = Join-Path $dir "barerepo-runner.exe"
133728e barerepo 1mo
139
Invoke-WebRequest -Uri "$server/runner/binary?token=$Token" -OutFile $exe
133728e barerepo 1mo
141
Write-Host "the runner is at $exe. attaching."
133728e barerepo 1mo
142
& $exe $Token --server $server