Files
Privatae e4c9b69b46
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s
bastion 0.53.2
2026-10-20 04:42:14 +00:00

64 lines
2.2 KiB
Go

//go:build linux || darwin
package main
import (
"errors"
"os"
"path/filepath"
"syscall"
)
// tryRunLock opens path and takes an exclusive flock on it without waiting.
// held reports that another open file already holds it. The lock belongs to
// the returned file and the kernel releases it when the process ends, however
// it ends, so a crashed daemon never leaves a stale lock behind.
func tryRunLock(path string) (f *os.File, held bool, err error) {
f, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600)
if err != nil {
return nil, false, err
}
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
_ = f.Close()
if errors.Is(err, syscall.EWOULDBLOCK) {
return nil, true, nil
}
return nil, false, err
}
return f, false, nil
}
// guardRunBesideService is the macOS single-run rule (runguard_darwin.go),
// kept here so the suite runs it on Linux as well.
//
// With no system service it does nothing and touches nothing: a Mac without
// the daemon behaves as it always has. With the service installed, an
// administrator's commands read the daemon's own state (sysservice.go), so an
// administrator's `run` beside the daemon has the same pairings and the same
// WireGuard keys. It would bring its tunnel up before reaching the loopback
// port it then fails to bind, and the host's peer follows whichever copy
// handshook last, which takes the daemon's tunnel down. So every `run` takes a
// lock in the state directory, and one that finds it held stops before it
// starts anything.
func guardRunBesideService(installed bool, stateDir func() (string, error),
try func(string) (*os.File, bool, error)) (*os.File, error) {
if !installed {
return nil, nil
}
d, err := stateDir()
if err != nil {
return nil, err
}
f, held, err := try(filepath.Join(d, "run.lock"))
if err != nil {
return nil, errors.New("cannot open the run lock: " + err.Error())
}
if held {
return nil, errors.New("this machine's bastion is already running as the system service, " +
"with these same pairings.\n" +
" A second copy would bring up the same tunnel and knock the service's off it.\n" +
" " + serviceRestartHintFor("darwin"))
}
return f, nil
}