package hcsshim import ( "encoding/json" "io" "syscall" "github.com/Microsoft/go-winio" "github.com/Sirupsen/logrus" ) // CreateProcessParams is used as both the input of CreateProcessInComputeSystem // and to convert the parameters to JSON for passing onto the HCS type CreateProcessParams struct { ApplicationName string CommandLine string WorkingDirectory string Environment map[string]string EmulateConsole bool ConsoleSize [2]int } // makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles // if there is an error. func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { fs := make([]io.ReadWriteCloser, len(hs)) for i, h := range hs { if h != syscall.Handle(0) { if err == nil { fs[i], err = winio.MakeOpenFile(h) } if err != nil { syscall.Close(h) } } } if err != nil { for _, f := range fs { if f != nil { f.Close() } } return nil, err } return fs, nil } // CreateProcessInComputeSystem starts a process in a container. This is invoked, for example, // as a result of docker run, docker exec, or RUN in Dockerfile. If successful, // it returns the PID of the process. func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useStderr bool, params CreateProcessParams) (_ uint32, _ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { title := "HCSShim::CreateProcessInComputeSystem" logrus.Debugf(title+" id=%s", id) // If we are not emulating a console, ignore any console size passed to us if !params.EmulateConsole { params.ConsoleSize[0] = 0 params.ConsoleSize[1] = 0 } paramsJson, err := json.Marshal(params) if err != nil { return } logrus.Debugf(title+" - Calling Win32 %s %s", id, paramsJson) var pid uint32 handles := make([]syscall.Handle, 3) var stdinParam, stdoutParam, stderrParam *syscall.Handle if useStdin { stdinParam = &handles[0] } if useStdout { stdoutParam = &handles[1] } if useStderr { stderrParam = &handles[2] } err = createProcessWithStdHandlesInComputeSystem(id, string(paramsJson), &pid, stdinParam, stdoutParam, stderrParam) if err != nil { herr := makeErrorf(err, title, "id=%s params=%v", id, params) // Windows TP4: Hyper-V Containers may return this error with more than one // concurrent exec. Do not log it as an error if err != WSAEINVAL { logrus.Error(herr) } err = herr return } pipes, err := makeOpenFiles(handles) if err != nil { return } logrus.Debugf(title+" - succeeded id=%s params=%s pid=%d", id, paramsJson, pid) return pid, pipes[0], pipes[1], pipes[2], nil }