aboutsummaryrefslogtreecommitdiffstats
path: root/syz-ci/managercmd.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-06-19 17:23:03 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-06-20 19:59:55 +0200
commit6573032fffc201ee69dfe22d0d678eda8f915436 (patch)
tree2f35ad8c63b7b474b3072b1ce71dd0877b6d9b71 /syz-ci/managercmd.go
parentfea266b33f5e4170b75d678d5e355f947aeb50cc (diff)
syz-ci: add continuous integration system
Diffstat (limited to 'syz-ci/managercmd.go')
-rw-r--r--syz-ci/managercmd.go120
1 files changed, 120 insertions, 0 deletions
diff --git a/syz-ci/managercmd.go b/syz-ci/managercmd.go
new file mode 100644
index 000000000..2f495b73f
--- /dev/null
+++ b/syz-ci/managercmd.go
@@ -0,0 +1,120 @@
+// 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"
+)
+
+// 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()
+ logfile, err := os.Create(mc.log)
+ if err != nil {
+ Logf(1, "%v: failed to create manager log: %v", mc.name, err)
+ } else {
+ cmd = exec.Command(mc.bin, mc.args...)
+ cmd.Stdout = logfile
+ cmd.Stderr = logfile
+ err := cmd.Start()
+ logfile.Close()
+ if err != nil {
+ Logf(1, "%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)
+}