Run a verb from the controller's own running image, and refuse what it cannot run as a handover (hq issue 289)
The witness moves a running build aside into a directory the controller's user cannot enter, then deletes it; a verb exec'd from os.Executable() in that window failed with permission denied. /proc/self/exe stays valid while the process lives. A verb that still cannot start, or arrives while the controller stops, is refused with link.ErrHandingOver and marked retry: handing-over.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// startedFrom is the path this process's executable had when it started: what a verb's command line
|
||||
// is named in a process listing, and what is run where the image cannot be named otherwise.
|
||||
var startedFrom, _ = os.Executable()
|
||||
|
||||
// ownImage is how a process names the executable it is running, as long as it runs, wherever the file
|
||||
// has gone since. On Linux the kernel keeps it: /proc/self/exe is the running image itself, not a path,
|
||||
// so it is valid after the file is renamed into a directory this process may not enter, or deleted —
|
||||
// which is what the node-engine's witness does to a build it replaces (novox/hq issue 289). Read in the
|
||||
// child, it names the child's image, which until the exec is this process's.
|
||||
//
|
||||
// os.Executable reads the same link and returns the path it points at *now*: correct at start and
|
||||
// wrong the moment the file moves, which is the fault. A variable so a test can name another.
|
||||
var ownImage = func() string {
|
||||
if runtime.GOOS == "linux" {
|
||||
if _, err := os.Stat("/proc/self/exe"); err == nil {
|
||||
return "/proc/self/exe"
|
||||
}
|
||||
}
|
||||
return startedFrom
|
||||
}
|
||||
|
||||
// selfCommand is this binary run with a command line: the image this process runs, named in a process
|
||||
// listing as the path it started from.
|
||||
func selfCommand(ctx context.Context, argv []string) *exec.Cmd {
|
||||
cmd := exec.CommandContext(ctx, ownImage(), argv...)
|
||||
if startedFrom != "" {
|
||||
cmd.Args[0] = startedFrom
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// handingOver is set when the serving controller begins to stop — a signal from its supervisor, a lease
|
||||
// lost. From then a verb that would run a command is refused as a handover rather than started and
|
||||
// killed with this process: the caller asks again, and the controller after this one answers
|
||||
// (novox/hq issue 289).
|
||||
var handingOver atomic.Bool
|
||||
Reference in New Issue
Block a user