Monitor: add r hotkey for config reload

Generalizes the one-shot ask behind u into a shared helper; r sends
reload-env and reports the service's reply (unchanged / restarting with
the changed setting names / invalid config) in the note line. Footer now
lists q, u, r.
This commit is contained in:
mram
2026-09-22 13:33:46 +02:00
parent a07b6726ea
commit 2040b30c94
+28 -18
View File
@@ -22,13 +22,14 @@ const (
) )
// hotkeysLine is the monitor's footer. // hotkeysLine is the monitor's footer.
const hotkeysLine = " " + cDim + "q quit · u update now" + cReset + "\x1b[K\n" const hotkeysLine = " " + cDim + "q quit · u update now · r reload config" + cReset + "\x1b[K\n"
// monitorCommand renders a live status view of the running service, // monitorCommand renders a live status view of the running service,
// refreshed every second from the control channel. When the service // refreshed every second from the control channel. When the service
// reports a different version and the executable on disk changed (the // reports a different version and the executable on disk changed (the
// updater replaced it), the monitor restarts itself onto the new binary. // updater replaced it), the monitor restarts itself onto the new binary.
// Hotkeys: q quits, u triggers an update check on the service. // Hotkeys: q quits, u triggers an update check on the service, r asks the
// service to reload its config file.
func monitorCommand() int { func monitorCommand() int {
if !stdoutIsTerminal() { if !stdoutIsTerminal() {
fmt.Fprintln(os.Stderr, "gpu-turnstile: --monitor needs an interactive terminal") fmt.Fprintln(os.Stderr, "gpu-turnstile: --monitor needs an interactive terminal")
@@ -57,7 +58,27 @@ func monitorCommand() int {
var note string var note string
var noteAt time.Time var noteAt time.Time
noteCh := make(chan string, 1) noteCh := make(chan string, 1)
updatePending := false askPending := false
// ask sends a one-shot command to the service and reports the reply in
// the note line. Only one ask runs at a time.
ask := func(cmd, busy, label string) {
if askPending {
return
}
askPending = true
note, noteAt = busy, time.Now()
go func() {
reply, err := control.Ask(cmd)
if err != nil {
noteCh <- label + ": no answer from the service"
return
}
msg := strings.TrimPrefix(reply, "OK ")
msg = strings.TrimPrefix(msg, "ERR ")
noteCh <- label + ": " + msg
}()
}
poll := func() string { poll := func() string {
frame := renderWaiting() frame := renderWaiting()
@@ -104,23 +125,12 @@ func monitorCommand() int {
case 'q', 'Q', 3: // q or Ctrl+C (raw mode delivers it as a byte) case 'q', 'Q', 3: // q or Ctrl+C (raw mode delivers it as a byte)
return 0 return 0
case 'u', 'U': case 'u', 'U':
if !updatePending { ask(control.CmdUpdateNow, "checking for updates…", "update")
updatePending = true case 'r', 'R':
note, noteAt = "checking for updates…", time.Now() ask(control.CmdReloadEnv, "reloading config…", "reload")
go func() {
reply, err := control.Ask(control.CmdUpdateNow)
if err != nil {
noteCh <- "update: no answer from the service"
return
}
msg := strings.TrimPrefix(reply, "OK ")
msg = strings.TrimPrefix(msg, "ERR ")
noteCh <- "update: " + msg
}()
}
} }
case n := <-noteCh: case n := <-noteCh:
updatePending = false askPending = false
note, noteAt = n, time.Now() note, noteAt = n, time.Now()
} }
} }