123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- // +build windows
- package client
- import (
- "bytes"
- "fmt"
- "io"
- "os"
- "strings"
- "time"
- "github.com/Microsoft/hcsshim"
- "github.com/sirupsen/logrus"
- )
- // 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
- Stderr io.ReadCloser
- }
- // 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 {
- return process{}, fmt.Errorf("failed to create process (%+v) in utility VM: %s", config, err)
- }
- if proc.Stdin, proc.Stdout, proc.Stderr, err = proc.Process.Stdio(); err != nil {
- proc.Process.Kill() // Should this have a timeout?
- proc.Process.Close()
- return process{}, fmt.Errorf("failed to get stdio pipes for process %+v: %s", config, err)
- }
- logrus.Debugf("opengcs: createUtilsProcess success: pid %d", proc.Process.Pid())
- return proc, nil
- }
- // 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.
- // 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) {
- logrus.Debugf("opengcs: RunProcess: %s", commandLine)
- process, err := config.createUtilsProcess(commandLine)
- if err != nil {
- return nil, err
- }
- // Send the data into the process's stdin
- if stdin != nil {
- if _, err = copyWithTimeout(process.Stdin,
- stdin,
- 0,
- config.UvmTimeoutSeconds,
- fmt.Sprintf("send to stdin of %s", commandLine)); err != nil {
- return nil, err
- }
- // Don't need stdin now we've sent everything. This signals GCS that we are finished sending data.
- 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
- }
- }
- }
- if stdout != nil {
- // Copy the data over to the writer.
- if _, err := copyWithTimeout(stdout,
- process.Stdout,
- 0,
- config.UvmTimeoutSeconds,
- fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil {
- 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
- }
- }
- logrus.Debugf("opengcs: runProcess success: %s", commandLine)
- return process.Process, nil
- }
- 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 "`
- cmd += debugCommand("kill -10 `pidof gcs`") // SIGUSR1 for stackdump
- cmd += debugCommand("ls -l /tmp")
- cmd += debugCommand("cat /tmp/gcs.log")
- cmd += debugCommand("cat /tmp/gcs/gcs-stacks*")
- cmd += debugCommand("cat /tmp/gcs/paniclog*")
- 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")
- cmd += debugCommand("cat /tmp/gcs/global-runc.log")
- cmd += debugCommand("cat /tmp/gcs/*/runc.log")
- 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 {
- proc.WaitTimeout(time.Second * 30)
- }
- logrus.Debugf("GCS Debugging:\n%s\n\nEnd GCS Debugging", strings.TrimSpace(out.String()))
- }
|