launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/utils/exec/exec.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package exec
     5  
     6  import (
     7  	"bytes"
     8  	"os/exec"
     9  	"syscall"
    10  
    11  	"github.com/loggo/loggo"
    12  )
    13  
    14  var logger = loggo.GetLogger("juju.util.exec")
    15  
    16  // Parameters for RunCommands.  Commands contains one or more commands to be
    17  // executed using '/bin/bash -s'.  If WorkingDir is set, this is passed
    18  // through to bash.  Similarly if the Environment is specified, this is used
    19  // for executing the command.
    20  type RunParams struct {
    21  	Commands    string
    22  	WorkingDir  string
    23  	Environment []string
    24  }
    25  
    26  // ExecResponse contains the return code and output generated by executing a
    27  // command.
    28  type ExecResponse struct {
    29  	Code   int
    30  	Stdout []byte
    31  	Stderr []byte
    32  }
    33  
    34  // RunCommands executes the Commands specified in the RunParams using
    35  // '/bin/bash -s', passing the commands through as stdin, and collecting
    36  // stdout and stderr.  If a non-zero return code is returned, this is
    37  // collected as the code for the response and this does not classify as an
    38  // error.
    39  func RunCommands(run RunParams) (*ExecResponse, error) {
    40  	ps := exec.Command("/bin/bash", "-s")
    41  	if run.Environment != nil {
    42  		ps.Env = run.Environment
    43  	}
    44  	if run.WorkingDir != "" {
    45  		ps.Dir = run.WorkingDir
    46  	}
    47  	ps.Stdin = bytes.NewBufferString(run.Commands)
    48  
    49  	stdout := &bytes.Buffer{}
    50  	stderr := &bytes.Buffer{}
    51  
    52  	ps.Stdout = stdout
    53  	ps.Stderr = stderr
    54  
    55  	err := ps.Start()
    56  	if err == nil {
    57  		err = ps.Wait()
    58  	}
    59  	result := &ExecResponse{
    60  		Stdout: stdout.Bytes(),
    61  		Stderr: stderr.Bytes(),
    62  	}
    63  	if ee, ok := err.(*exec.ExitError); ok && err != nil {
    64  		status := ee.ProcessState.Sys().(syscall.WaitStatus)
    65  		if status.Exited() {
    66  			// A non-zero return code isn't considered an error here.
    67  			result.Code = status.ExitStatus()
    68  			err = nil
    69  		}
    70  		logger.Infof("run result: %v", ee)
    71  	}
    72  	return result, err
    73  }