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.
47 lines
1.8 KiB
Go
47 lines
1.8 KiB
Go
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
|