summaryrefslogtreecommitdiff
path: root/misc/dashboard/builder/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'misc/dashboard/builder/exec.go')
-rw-r--r--misc/dashboard/builder/exec.go70
1 files changed, 43 insertions, 27 deletions
diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go
index 802d5f079..a4aabd284 100644
--- a/misc/dashboard/builder/exec.go
+++ b/misc/dashboard/builder/exec.go
@@ -6,14 +6,16 @@ package main
import (
"bytes"
+ "fmt"
"io"
"log"
"os"
"os/exec"
+ "time"
)
// run is a simple wrapper for exec.Run/Close
-func run(envv []string, dir string, argv ...string) error {
+func run(timeout time.Duration, envv []string, dir string, argv ...string) error {
if *verbose {
log.Println("run", argv)
}
@@ -21,43 +23,57 @@ func run(envv []string, dir string, argv ...string) error {
cmd.Dir = dir
cmd.Env = envv
cmd.Stderr = os.Stderr
- return cmd.Run()
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ return waitWithTimeout(timeout, cmd)
}
-// runLog runs a process and returns the combined stdout/stderr,
-// as well as writing it to logfile (if specified). It returns
-// process combined stdout and stderr output, exit status and error.
-// The error returned is nil, if process is started successfully,
-// even if exit status is not successful.
-func runLog(envv []string, logfile, dir string, argv ...string) (string, int, error) {
- if *verbose {
- log.Println("runLog", argv)
- }
+// runLog runs a process and returns the combined stdout/stderr. It returns
+// process combined stdout and stderr output, exit status and error. The
+// error returned is nil, if process is started successfully, even if exit
+// status is not successful.
+func runLog(timeout time.Duration, envv []string, dir string, argv ...string) (string, bool, error) {
+ var b bytes.Buffer
+ ok, err := runOutput(timeout, envv, &b, dir, argv...)
+ return b.String(), ok, err
+}
- b := new(bytes.Buffer)
- var w io.Writer = b
- if logfile != "" {
- f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
- if err != nil {
- return "", 0, err
- }
- defer f.Close()
- w = io.MultiWriter(f, b)
+// runOutput runs a process and directs any output to the supplied writer.
+// It returns exit status and error. The error returned is nil, if process
+// is started successfully, even if exit status is not successful.
+func runOutput(timeout time.Duration, envv []string, out io.Writer, dir string, argv ...string) (bool, error) {
+ if *verbose {
+ log.Println("runOutput", argv)
}
cmd := exec.Command(argv[0], argv[1:]...)
cmd.Dir = dir
cmd.Env = envv
- cmd.Stdout = w
- cmd.Stderr = w
+ cmd.Stdout = out
+ cmd.Stderr = out
startErr := cmd.Start()
if startErr != nil {
- return "", 1, startErr
+ return false, startErr
+ }
+ if err := waitWithTimeout(timeout, cmd); err != nil {
+ return false, err
}
- exitStatus := 0
- if err := cmd.Wait(); err != nil {
- exitStatus = 1 // TODO(bradfitz): this is fake. no callers care, so just return a bool instead.
+ return true, nil
+}
+
+func waitWithTimeout(timeout time.Duration, cmd *exec.Cmd) error {
+ errc := make(chan error, 1)
+ go func() {
+ errc <- cmd.Wait()
+ }()
+ var err error
+ select {
+ case <-time.After(timeout):
+ cmd.Process.Kill()
+ err = fmt.Errorf("timed out after %v", timeout)
+ case err = <-errc:
}
- return b.String(), exitStatus, nil
+ return err
}