mirror of
https://github.com/reactos/syzkaller.git
synced 2024-12-03 17:01:28 +00:00
c4d43f4773
When manager is stopped there are sometimes runaway qemu processes still running. Set PDEATHSIG for all subprocesses. We never need child processes outliving parents.
123 lines
3.0 KiB
Go
123 lines
3.0 KiB
Go
// Copyright 2017 syzkaller project authors. All rights reserved.
|
|
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
"time"
|
|
|
|
. "github.com/google/syzkaller/pkg/log"
|
|
"github.com/google/syzkaller/pkg/osutil"
|
|
)
|
|
|
|
// ManagerCmd encapsulates a single instance of syz-manager process.
|
|
// It automatically restarts syz-manager if it exits unexpectedly,
|
|
// and supports graceful shutdown via SIGINT.
|
|
type ManagerCmd struct {
|
|
name string
|
|
log string
|
|
bin string
|
|
args []string
|
|
closing chan bool
|
|
}
|
|
|
|
// NewManagerCmd starts new syz-manager process.
|
|
// name - name for logging.
|
|
// log - manager log file with stdout/stderr.
|
|
// bin/args - process binary/args.
|
|
func NewManagerCmd(name, log, bin string, args ...string) *ManagerCmd {
|
|
mc := &ManagerCmd{
|
|
name: name,
|
|
log: log,
|
|
bin: bin,
|
|
args: args,
|
|
closing: make(chan bool),
|
|
}
|
|
go mc.loop()
|
|
return mc
|
|
}
|
|
|
|
// Close gracefully shutdowns the process and waits for its termination.
|
|
func (mc *ManagerCmd) Close() {
|
|
mc.closing <- true
|
|
<-mc.closing
|
|
}
|
|
|
|
func (mc *ManagerCmd) loop() {
|
|
const (
|
|
restartPeriod = time.Minute // don't restart crashing manager more frequently than that
|
|
interruptTimeout = time.Minute // give manager that much time to react to SIGINT
|
|
)
|
|
var (
|
|
cmd *exec.Cmd
|
|
started time.Time
|
|
interrupted time.Time
|
|
stopped = make(chan error, 1)
|
|
closing = mc.closing
|
|
ticker1 = time.NewTicker(restartPeriod)
|
|
ticker2 = time.NewTicker(interruptTimeout)
|
|
)
|
|
defer func() {
|
|
ticker1.Stop()
|
|
ticker2.Stop()
|
|
}()
|
|
for closing != nil || cmd != nil {
|
|
if cmd == nil {
|
|
// cmd is not running
|
|
// don't restart too frequently (in case it instantly exits with an error)
|
|
if time.Since(started) > restartPeriod {
|
|
started = time.Now()
|
|
os.Rename(mc.log, mc.log+".old")
|
|
logfile, err := os.Create(mc.log)
|
|
if err != nil {
|
|
Logf(0, "%v: failed to create manager log: %v", mc.name, err)
|
|
} else {
|
|
cmd = osutil.Command(mc.bin, mc.args...)
|
|
cmd.Stdout = logfile
|
|
cmd.Stderr = logfile
|
|
err := cmd.Start()
|
|
logfile.Close()
|
|
if err != nil {
|
|
Logf(0, "%v: failed to start manager: %v", mc.name, err)
|
|
cmd = nil
|
|
} else {
|
|
Logf(1, "%v: started manager", mc.name)
|
|
go func() {
|
|
stopped <- cmd.Wait()
|
|
}()
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// cmd is running
|
|
if closing == nil && time.Since(interrupted) > interruptTimeout {
|
|
Logf(1, "%v: killing manager", mc.name)
|
|
cmd.Process.Kill()
|
|
interrupted = time.Now()
|
|
}
|
|
}
|
|
|
|
select {
|
|
case <-closing:
|
|
closing = nil
|
|
if cmd != nil {
|
|
Logf(1, "%v: stopping manager", mc.name)
|
|
cmd.Process.Signal(syscall.SIGINT)
|
|
interrupted = time.Now()
|
|
}
|
|
case err := <-stopped:
|
|
if cmd == nil {
|
|
panic("spurious stop signal")
|
|
}
|
|
cmd = nil
|
|
Logf(1, "%v: manager exited with %v", mc.name, err)
|
|
case <-ticker1.C:
|
|
case <-ticker2.C:
|
|
}
|
|
}
|
|
close(mc.closing)
|
|
}
|