Signed-off-by: John Howard <jhoward@microsoft.com> Upstream-commit: a3c43964cf994cc1315af1590155f63dc588e32c Component: engine
167 lines
4.2 KiB
Go
167 lines
4.2 KiB
Go
package hcsshim
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"runtime"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"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
|
|
}
|
|
|
|
// pipe struct used for the stdin/stdout/stderr pipes
|
|
type pipe struct {
|
|
handle syscall.Handle
|
|
}
|
|
|
|
func makePipe(h syscall.Handle) *pipe {
|
|
p := &pipe{h}
|
|
runtime.SetFinalizer(p, (*pipe).closeHandle)
|
|
return p
|
|
}
|
|
|
|
func (p *pipe) closeHandle() {
|
|
if p.handle != 0 {
|
|
syscall.CloseHandle(p.handle)
|
|
p.handle = 0
|
|
}
|
|
}
|
|
|
|
func (p *pipe) Close() error {
|
|
p.closeHandle()
|
|
runtime.SetFinalizer(p, nil)
|
|
return nil
|
|
}
|
|
|
|
func (p *pipe) Read(b []byte) (int, error) {
|
|
// syscall.Read returns 0, nil on ERROR_BROKEN_PIPE, but for
|
|
// our purposes this should indicate EOF. This may be a go bug.
|
|
var read uint32
|
|
err := syscall.ReadFile(p.handle, b, &read, nil)
|
|
if err != nil {
|
|
if err == syscall.ERROR_BROKEN_PIPE {
|
|
return 0, io.EOF
|
|
}
|
|
return 0, err
|
|
}
|
|
return int(read), nil
|
|
}
|
|
|
|
func (p *pipe) Write(b []byte) (int, error) {
|
|
return syscall.Write(p.handle, b)
|
|
}
|
|
|
|
// 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, uint32, error) {
|
|
|
|
var (
|
|
stdin io.WriteCloser
|
|
stdout, stderr io.ReadCloser
|
|
)
|
|
|
|
title := "HCSShim::CreateProcessInComputeSystem"
|
|
logrus.Debugf(title+" id=%s", id)
|
|
|
|
// Load the DLL and get a handle to the procedure we need
|
|
dll, proc, err := loadAndFind(procCreateProcessWithStdHandlesInComputeSystem)
|
|
if dll != nil {
|
|
defer dll.Release()
|
|
}
|
|
if err != nil {
|
|
return 0, nil, nil, nil, 0xFFFFFFFF, err
|
|
}
|
|
|
|
// Convert id to uint16 pointer for calling the procedure
|
|
idp, err := syscall.UTF16PtrFromString(id)
|
|
if err != nil {
|
|
err = fmt.Errorf(title+" - Failed conversion of id %s to pointer %s", id, err)
|
|
logrus.Error(err)
|
|
return 0, nil, nil, nil, 0xFFFFFFFF, err
|
|
}
|
|
|
|
// 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 {
|
|
err = fmt.Errorf(title+" - Failed to marshall params %v %s", params, err)
|
|
return 0, nil, nil, nil, 0xFFFFFFFF, err
|
|
}
|
|
|
|
// Convert paramsJson to uint16 pointer for calling the procedure
|
|
paramsJsonp, err := syscall.UTF16PtrFromString(string(paramsJson))
|
|
if err != nil {
|
|
return 0, nil, nil, nil, 0xFFFFFFFF, err
|
|
}
|
|
|
|
// Get a POINTER to variable to take the pid outparm
|
|
pid := new(uint32)
|
|
|
|
logrus.Debugf(title+" - Calling Win32 %s %s", id, paramsJson)
|
|
|
|
var stdinHandle, stdoutHandle, stderrHandle syscall.Handle
|
|
var stdinParam, stdoutParam, stderrParam uintptr
|
|
if useStdin {
|
|
stdinParam = uintptr(unsafe.Pointer(&stdinHandle))
|
|
}
|
|
if useStdout {
|
|
stdoutParam = uintptr(unsafe.Pointer(&stdoutHandle))
|
|
}
|
|
if useStderr {
|
|
stderrParam = uintptr(unsafe.Pointer(&stderrHandle))
|
|
}
|
|
|
|
// Call the procedure itself.
|
|
r1, _, _ := proc.Call(
|
|
uintptr(unsafe.Pointer(idp)),
|
|
uintptr(unsafe.Pointer(paramsJsonp)),
|
|
uintptr(unsafe.Pointer(pid)),
|
|
stdinParam,
|
|
stdoutParam,
|
|
stderrParam)
|
|
|
|
use(unsafe.Pointer(idp))
|
|
use(unsafe.Pointer(paramsJsonp))
|
|
|
|
if r1 != 0 {
|
|
err = fmt.Errorf(title+" - Win32 API call returned error r1=%d err=%s id=%s params=%v", r1, syscall.Errno(r1), 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 uint32(r1) != Win32InvalidArgument {
|
|
logrus.Error(err)
|
|
}
|
|
return 0, nil, nil, nil, uint32(r1), err
|
|
}
|
|
|
|
if useStdin {
|
|
stdin = makePipe(stdinHandle)
|
|
}
|
|
if useStdout {
|
|
stdout = makePipe(stdoutHandle)
|
|
}
|
|
if useStderr {
|
|
stderr = makePipe(stderrHandle)
|
|
}
|
|
|
|
logrus.Debugf(title+" - succeeded id=%s params=%s pid=%d", id, paramsJson, *pid)
|
|
return *pid, stdin, stdout, stderr, 0, nil
|
|
}
|