2017-06-20 11:37:36 -04:00
|
|
|
// +build windows
|
|
|
|
|
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2017-09-07 20:02:17 -04:00
|
|
|
"bytes"
|
2017-06-20 11:37:36 -04:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-09-07 20:02:17 -04:00
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2017-06-20 11:37:36 -04:00
|
|
|
|
|
|
|
"github.com/Microsoft/hcsshim"
|
2017-07-26 18:03:47 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-06-20 11:37:36 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// Process is the structure pertaining to a process running in a utility VM.
|
|
|
|
type process struct {
|
|
|
|
Process hcsshim.Process
|
|
|
|
Stdin io.WriteCloser
|
|
|
|
Stdout io.ReadCloser
|
2017-07-18 21:58:39 -04:00
|
|
|
Stderr io.ReadCloser
|
2017-06-20 11:37:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// createUtilsProcess is a convenient wrapper for hcsshim.createUtilsProcess to use when
|
|
|
|
// communicating with a utility VM.
|
|
|
|
func (config *Config) createUtilsProcess(commandLine string) (process, error) {
|
|
|
|
logrus.Debugf("opengcs: createUtilsProcess")
|
|
|
|
|
|
|
|
if config.Uvm == nil {
|
|
|
|
return process{}, fmt.Errorf("cannot create utils process as no utility VM is in configuration")
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
proc process
|
|
|
|
)
|
|
|
|
|
|
|
|
env := make(map[string]string)
|
|
|
|
env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:"
|
|
|
|
processConfig := &hcsshim.ProcessConfig{
|
|
|
|
EmulateConsole: false,
|
|
|
|
CreateStdInPipe: true,
|
|
|
|
CreateStdOutPipe: true,
|
|
|
|
CreateStdErrPipe: true,
|
|
|
|
CreateInUtilityVm: true,
|
|
|
|
WorkingDirectory: "/bin",
|
|
|
|
Environment: env,
|
|
|
|
CommandLine: commandLine,
|
|
|
|
}
|
|
|
|
proc.Process, err = config.Uvm.CreateProcess(processConfig)
|
|
|
|
if err != nil {
|
2017-07-18 21:58:39 -04:00
|
|
|
return process{}, fmt.Errorf("failed to create process (%+v) in utility VM: %s", config, err)
|
2017-06-20 11:37:36 -04:00
|
|
|
}
|
|
|
|
|
2017-07-18 21:58:39 -04:00
|
|
|
if proc.Stdin, proc.Stdout, proc.Stderr, err = proc.Process.Stdio(); err != nil {
|
2017-06-20 11:37:36 -04:00
|
|
|
proc.Process.Kill() // Should this have a timeout?
|
|
|
|
proc.Process.Close()
|
2017-07-18 21:58:39 -04:00
|
|
|
return process{}, fmt.Errorf("failed to get stdio pipes for process %+v: %s", config, err)
|
2017-06-20 11:37:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
logrus.Debugf("opengcs: createUtilsProcess success: pid %d", proc.Process.Pid())
|
|
|
|
return proc, nil
|
|
|
|
}
|
2017-07-06 17:29:43 -04:00
|
|
|
|
|
|
|
// RunProcess runs the given command line program in the utilityVM. It takes in
|
|
|
|
// an input to the reader to feed into stdin and returns stdout to output.
|
2017-07-18 21:58:39 -04:00
|
|
|
// IMPORTANT: It is the responsibility of the caller to call Close() on the returned process.
|
|
|
|
func (config *Config) RunProcess(commandLine string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (hcsshim.Process, error) {
|
2017-07-06 17:29:43 -04:00
|
|
|
logrus.Debugf("opengcs: RunProcess: %s", commandLine)
|
|
|
|
process, err := config.createUtilsProcess(commandLine)
|
|
|
|
if err != nil {
|
2017-07-18 21:58:39 -04:00
|
|
|
return nil, err
|
2017-07-06 17:29:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Send the data into the process's stdin
|
2017-07-18 21:58:39 -04:00
|
|
|
if stdin != nil {
|
2017-07-06 17:29:43 -04:00
|
|
|
if _, err = copyWithTimeout(process.Stdin,
|
2017-07-18 21:58:39 -04:00
|
|
|
stdin,
|
2017-07-06 17:29:43 -04:00
|
|
|
0,
|
|
|
|
config.UvmTimeoutSeconds,
|
|
|
|
fmt.Sprintf("send to stdin of %s", commandLine)); err != nil {
|
2017-07-18 21:58:39 -04:00
|
|
|
return nil, err
|
2017-07-06 17:29:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Don't need stdin now we've sent everything. This signals GCS that we are finished sending data.
|
2018-01-24 19:08:16 -05:00
|
|
|
if err := process.Process.CloseStdin(); err != nil && !hcsshim.IsNotExist(err) && !hcsshim.IsAlreadyClosed(err) {
|
|
|
|
// This error will occur if the compute system is currently shutting down
|
|
|
|
if perr, ok := err.(*hcsshim.ProcessError); ok && perr.Err != hcsshim.ErrVmcomputeOperationInvalidState {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-07-06 17:29:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:58:39 -04:00
|
|
|
if stdout != nil {
|
2017-07-06 17:29:43 -04:00
|
|
|
// Copy the data over to the writer.
|
2017-07-18 21:58:39 -04:00
|
|
|
if _, err := copyWithTimeout(stdout,
|
2017-07-06 17:29:43 -04:00
|
|
|
process.Stdout,
|
|
|
|
0,
|
|
|
|
config.UvmTimeoutSeconds,
|
|
|
|
fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil {
|
2017-07-18 21:58:39 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if stderr != nil {
|
|
|
|
// Copy the data over to the writer.
|
|
|
|
if _, err := copyWithTimeout(stderr,
|
|
|
|
process.Stderr,
|
|
|
|
0,
|
|
|
|
config.UvmTimeoutSeconds,
|
|
|
|
fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil {
|
|
|
|
return nil, err
|
2017-07-06 17:29:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logrus.Debugf("opengcs: runProcess success: %s", commandLine)
|
2017-07-18 21:58:39 -04:00
|
|
|
return process.Process, nil
|
2017-07-06 17:29:43 -04:00
|
|
|
}
|
2017-09-07 20:02:17 -04:00
|
|
|
|
|
|
|
func debugCommand(s string) string {
|
|
|
|
return fmt.Sprintf(`echo -e 'DEBUG COMMAND: %s\\n--------------\\n';%s;echo -e '\\n\\n';`, s, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DebugGCS extracts logs from the GCS. It's a useful hack for debugging,
|
|
|
|
// but not necessarily optimal, but all that is available to us in RS3.
|
|
|
|
func (config *Config) DebugGCS() {
|
|
|
|
if logrus.GetLevel() < logrus.DebugLevel || len(os.Getenv("OPENGCS_DEBUG_ENABLE")) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var out bytes.Buffer
|
|
|
|
cmd := os.Getenv("OPENGCS_DEBUG_COMMAND")
|
|
|
|
if cmd == "" {
|
|
|
|
cmd = `sh -c "`
|
2018-01-18 20:52:40 -05:00
|
|
|
cmd += debugCommand("kill -10 `pidof gcs`") // SIGUSR1 for stackdump
|
2017-09-07 20:02:17 -04:00
|
|
|
cmd += debugCommand("ls -l /tmp")
|
|
|
|
cmd += debugCommand("cat /tmp/gcs.log")
|
2018-01-18 20:52:40 -05:00
|
|
|
cmd += debugCommand("cat /tmp/gcs/gcs-stacks*")
|
|
|
|
cmd += debugCommand("cat /tmp/gcs/paniclog*")
|
2017-09-07 20:02:17 -04:00
|
|
|
cmd += debugCommand("ls -l /tmp/gcs")
|
|
|
|
cmd += debugCommand("ls -l /tmp/gcs/*")
|
|
|
|
cmd += debugCommand("cat /tmp/gcs/*/config.json")
|
|
|
|
cmd += debugCommand("ls -lR /var/run/gcsrunc")
|
2018-01-18 20:52:40 -05:00
|
|
|
cmd += debugCommand("cat /tmp/gcs/global-runc.log")
|
|
|
|
cmd += debugCommand("cat /tmp/gcs/*/runc.log")
|
2017-09-07 20:02:17 -04:00
|
|
|
cmd += debugCommand("ps -ef")
|
|
|
|
cmd += `"`
|
|
|
|
}
|
|
|
|
proc, err := config.RunProcess(cmd, nil, &out, nil)
|
|
|
|
defer func() {
|
|
|
|
if proc != nil {
|
|
|
|
proc.Kill()
|
|
|
|
proc.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugln("benign failure getting gcs logs: ", err)
|
|
|
|
}
|
|
|
|
if proc != nil {
|
2018-09-18 11:52:33 -04:00
|
|
|
proc.WaitTimeout(time.Second * 30)
|
2017-09-07 20:02:17 -04:00
|
|
|
}
|
2018-01-18 20:52:40 -05:00
|
|
|
logrus.Debugf("GCS Debugging:\n%s\n\nEnd GCS Debugging", strings.TrimSpace(out.String()))
|
2017-09-07 20:02:17 -04:00
|
|
|
}
|