diff --git a/statescript/executor.go b/statescript/executor.go index 9d7a26e5a..94fc8a332 100644 --- a/statescript/executor.go +++ b/statescript/executor.go @@ -15,6 +15,7 @@ package statescript import ( + "io" "io/ioutil" "os" "os/exec" @@ -169,6 +170,16 @@ func execute(name string, timeout time.Duration) int { cmd := exec.Command(name) + var stderr io.ReadCloser + var err error + + if !strings.HasPrefix(name, "Idle") && !strings.HasPrefix(name, "Sync") { + stderr, err = cmd.StderrPipe() + if err != nil { + log.Errorf("statescript: %v", err) + } + } + // As child process gets the same PGID as the parent by default, in order // to avoid killing Mender when killing process group we are setting // new PGID for the executed script and its children. @@ -178,6 +189,22 @@ func execute(name string, timeout time.Duration) int { return retCode(err) } + var bts []byte + if stderr != nil { + bts, err = ioutil.ReadAll(stderr) + if err != nil { + log.Error(err) + } + } + + if len(bts) > 0 { + if len(bts) > 10*1024 { + log.Errorf("stderr collected while running script %s [%s] (Truncated to 10KB)", name, bts[:10*1024]) + } else { + log.Errorf("stderr collected while running script %s [%s]", name, string(bts)) + } + } + timer := time.AfterFunc(timeout, func() { // In addition to kill a single process we are sending SIGKILL to // process group making sure we are killing the hanging script and diff --git a/statescript/statescript_test.go b/statescript/statescript_test.go index 75d04220f..8c720389b 100644 --- a/statescript/statescript_test.go +++ b/statescript/statescript_test.go @@ -22,6 +22,7 @@ import ( "strconv" "testing" + "github.com/mendersoftware/log" "github.com/stretchr/testify/assert" ) @@ -190,6 +191,26 @@ func TestExecutor(t *testing.T) { sysInstallScripts, _, err = e.get("ArtifactInstall", "Leave") testArtifactArrayEquals(t, scriptArr[1:], sysInstallScripts) assert.NoError(t, err) + + // Test script logging + var buf bytes.Buffer + oldOut := log.Log.Out + defer log.SetOutput(oldOut) + log.SetOutput(&buf) + fileP, err := createArtifactTestScript(tmpArt, "ArtifactInstall_Leave_00", "#!/bin/bash \necho 'error data' >&2") + assert.NoError(t, err) + res := execute(fileP.Name(), 100) // give the script plenty of time to run + assert.Equal(t, 0, res) + assert.Contains(t, buf.String(), "error data") + + buf.Reset() + + // write more than 10KB to stderr + fileP, err = createArtifactTestScript(tmpArt, "ArtifactInstall_Leave_11", "#!/bin/bash \nhead -c 89999 &2\n exit 1") + assert.NoError(t, err) + res = execute(fileP.Name(), 100) + assert.Equal(t, 1, res) + assert.Contains(t, buf.String(), "Truncated to 10KB") } func TestVersion(t *testing.T) {