Self-install into canonical layout on both platforms, --no-copy to opt out

Windows: --install-service creates %ProgramFiles%\gpu-turnstile and
%ProgramData%\gpu-turnstile, copies the exe and (if absent) the env
file in, and registers the copy. Linux: binary goes to
/var/lib/gpu-turnstile (not /usr/local/sbin: replacing a running binary
needs directory write, which must not be granted on a shared system dir
to a sandboxed service). --no-copy registers the current location
as-is on both platforms.
This commit is contained in:
mram
2026-09-21 07:52:33 +02:00
parent a88955e35c
commit 802a64280f
6 changed files with 183 additions and 77 deletions
+29 -20
View File
@@ -35,21 +35,28 @@ var version = "dev"
const exitCodeUpdate = 3
func main() {
configPath, install, remove, args := parseFlags(os.Args[1:])
configPath, install, remove, noCopy, args := parseFlags(os.Args[1:])
if len(args) > 0 && args[0] == "service" {
// Legacy subcommand form: gpu-turnstile service install|remove.
if len(args) != 2 || (args[1] != "install" && args[1] != "remove") {
fmt.Fprintf(os.Stderr, "usage: gpu-turnstile service install|remove [-config path]\n")
os.Exit(2)
}
install = args[1] == "install"
remove = !install
args = nil
}
switch {
case install && remove:
fmt.Fprintf(os.Stderr, "gpu-turnstile: --install-service and --remove-service are mutually exclusive\n")
os.Exit(2)
case install:
os.Exit(serviceCommand(configPath, []string{"install"}))
os.Exit(serviceCommand(configPath, true, noCopy))
case remove:
os.Exit(serviceCommand(configPath, []string{"remove"}))
}
if len(args) > 0 && args[0] == "service" {
os.Exit(serviceCommand(configPath, args[1:]))
os.Exit(serviceCommand(configPath, false, noCopy))
}
if len(args) > 0 {
fmt.Fprintf(os.Stderr, "usage: gpu-turnstile [-config path] [--install-service | --remove-service]\n")
fmt.Fprintf(os.Stderr, "usage: gpu-turnstile [-config path] [--install-service [--no-copy] | --remove-service]\n")
fmt.Fprintf(os.Stderr, " gpu-turnstile service install|remove [-config path]\n")
os.Exit(2)
}
@@ -81,9 +88,9 @@ func main() {
}
}
// parseFlags extracts -config <path> (or -config=<path>) and the
// --install-service / --remove-service switches from args.
func parseFlags(args []string) (configPath string, install, remove bool, rest []string) {
// parseFlags extracts -config <path> (or -config=<path>), the
// --install-service / --remove-service switches and --no-copy from args.
func parseFlags(args []string) (configPath string, install, remove, noCopy bool, rest []string) {
rest = args[:0]
for i := 0; i < len(args); i++ {
switch {
@@ -96,11 +103,13 @@ func parseFlags(args []string) (configPath string, install, remove bool, rest []
install = true
case args[i] == "--remove-service" || args[i] == "-remove-service":
remove = true
case args[i] == "--no-copy" || args[i] == "-no-copy":
noCopy = true
default:
rest = append(rest, args[i])
}
}
return configPath, install, remove, rest
return configPath, install, remove, noCopy, rest
}
// defaultConfigPath returns gpu-turnstile.env next to the executable.
@@ -172,26 +181,26 @@ func newLogger(cfg config.Config) (*slog.Logger, io.Writer, io.Closer) {
return log, out, closer
}
func serviceCommand(configPath string, args []string) int {
if len(args) != 1 || (args[0] != "install" && args[0] != "remove") {
fmt.Fprintf(os.Stderr, "usage: gpu-turnstile service install|remove [-config path]\n")
return 2
}
// serviceCommand installs (copyBin = register the canonical-layout copy)
// or removes the service and reports the result.
func serviceCommand(configPath string, install, noCopy bool) int {
verb := "remove"
var err error
if args[0] == "install" {
if install {
verb = "install"
path := resolveConfigPath(configPath)
if abs, absErr := filepath.Abs(path); absErr == nil {
path = abs
}
err = service.Install(path)
err = service.Install(path, !noCopy)
} else {
err = service.Remove()
}
if err != nil {
fmt.Fprintf(os.Stderr, "gpu-turnstile service %s: %v\n", args[0], err)
fmt.Fprintf(os.Stderr, "gpu-turnstile service %s: %v\n", verb, err)
return 1
}
fmt.Printf("service %s: %sd\n", service.Name, args[0])
fmt.Printf("service %s: %sd\n", service.Name, verb)
return 0
}