lockprefix = "crunch-run-"
locksuffix = ".lock"
brokenfile = "crunch-run-broken"
+ pricesfile = "crunch-run-prices.json"
)
// procinfo is saved in each process's lockfile.
//
// Stdout and stderr in the child process are sent to the systemd
// journal using the systemd-cat program.
-func Detach(uuid string, prog string, args []string, stdout, stderr io.Writer) int {
- return exitcode(stderr, detach(uuid, prog, args, stdout, stderr))
+func Detach(uuid string, prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ return exitcode(stderr, detach(uuid, prog, args, stdin, stdout))
}
-func detach(uuid string, prog string, args []string, stdout, stderr io.Writer) error {
+func detach(uuid string, prog string, args []string, stdin io.Reader, stdout io.Writer) error {
lockfile, err := func() (*os.File, error) {
// We must hold the dir-level lock between
// opening/creating the lockfile and acquiring LOCK_EX
// invoked as "/path/to/crunch-run"
execargs = append([]string{prog}, execargs...)
}
- execargs = append([]string{
- // Here, if the inner systemd-cat can't exec
- // crunch-run, it writes an error message to stderr,
- // and the outer systemd-cat writes it to the journal
- // where the operator has a chance to discover it. (If
- // we only used one systemd-cat command, it would be
- // up to us to report the error -- but we are going to
- // detach and exit, not wait for something to appear
- // on stderr.) Note these systemd-cat calls don't
- // result in additional processes -- they just connect
- // stderr/stdout to sockets and call exec().
- "systemd-cat", "--identifier=crunch-run",
- "systemd-cat", "--identifier=crunch-run",
- }, execargs...)
+ if _, err := exec.LookPath("systemd-cat"); err == nil {
+ execargs = append([]string{
+ // Here, if the inner systemd-cat can't exec
+ // crunch-run, it writes an error message to
+ // stderr, and the outer systemd-cat writes it
+ // to the journal where the operator has a
+ // chance to discover it. (If we only used one
+ // systemd-cat command, it would be up to us
+ // to report the error -- but we are going to
+ // detach and exit, not wait for something to
+ // appear on stderr.) Note these systemd-cat
+ // calls don't result in additional processes
+ // -- they just connect stderr/stdout to
+ // sockets and call exec().
+ "systemd-cat", "--identifier=crunch-run",
+ "systemd-cat", "--identifier=crunch-run",
+ }, execargs...)
+ }
cmd := exec.Command(execargs[0], execargs[1:]...)
// Child inherits lockfile.
// from parent (sshd) while sending lockfile content to
// caller.
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ // We need to manage our own OS pipe here to ensure the child
+ // process reads all of our stdin pipe before we return.
+ piper, pipew, err := os.Pipe()
+ if err != nil {
+ return err
+ }
+ defer pipew.Close()
+ cmd.Stdin = piper
err = cmd.Start()
if err != nil {
return fmt.Errorf("exec %s: %s", cmd.Path, err)
}
+ _, err = io.Copy(pipew, stdin)
+ if err != nil {
+ return err
+ }
+ err = pipew.Close()
+ if err != nil {
+ return err
+ }
w := io.MultiWriter(stdout, lockfile)
return json.NewEncoder(w).Encode(procinfo{
var pi procinfo
err = json.NewDecoder(f).Decode(&pi)
if err != nil {
- return fmt.Errorf("decode %s: %s\n", path, err)
+ return fmt.Errorf("decode %s: %s", path, err)
}
if pi.UUID != uuid || pi.PID == 0 {
return nil
}
-// List UUIDs of active crunch-run processes.
-func ListProcesses(stdout, stderr io.Writer) int {
+// ListProcesses lists UUIDs of active crunch-run processes.
+func ListProcesses(stdin io.Reader, stdout, stderr io.Writer) int {
+ if buf, err := io.ReadAll(stdin); err == nil && len(buf) > 0 {
+ // write latest pricing data to disk where
+ // current/future crunch-run processes can load it
+ fnm := filepath.Join(lockdir, pricesfile)
+ fnmtmp := fmt.Sprintf("%s~%d", fnm, os.Getpid())
+ err := os.WriteFile(fnmtmp, buf, 0777)
+ if err != nil {
+ fmt.Fprintf(stderr, "error writing price data to %s: %s", fnmtmp, err)
+ } else if err = os.Rename(fnmtmp, fnm); err != nil {
+ fmt.Fprintf(stderr, "error renaming %s to %s: %s", fnmtmp, fnm, err)
+ os.Remove(fnmtmp)
+ }
+ }
// filepath.Walk does not follow symlinks, so we must walk
// lockdir+"/." in case lockdir itself is a symlink.
walkdir := lockdir + "/."
fmt.Fprintf(stderr, "%s: find process %d: %s", path, pi.PID, err)
return nil
}
- err = proc.Signal(syscall.Signal(0))
+ err = proc.Signal(syscall.SIGUSR2)
if err != nil {
// Process is dead, even though lockfile was
// still locked. Most likely a stuck arv-mount