Skip to content

Commit

Permalink
Make Win32 errors visible to callers
Browse files Browse the repository at this point in the history
This makes hcsError public so that callers can inspect the internal
error and check it against certain known Win32 error codes.
  • Loading branch information
jstarks committed Feb 3, 2016
1 parent 35ad4d8 commit fd9d5fb
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 42 deletions.
14 changes: 6 additions & 8 deletions createprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) {
// 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, hr uint32, err error) {
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)
hr = 0xFFFFFFFF

// If we are not emulating a console, ignore any console size passed to us
if !params.EmulateConsole {
Expand Down Expand Up @@ -82,14 +81,13 @@ func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useS

err = createProcessWithStdHandlesInComputeSystem(id, string(paramsJson), &pid, stdinParam, stdoutParam, stderrParam)
if err != nil {
winerr := makeErrorf(err, title, "id=%s params=%v", id, params)
hr = winerr.HResult()
herr := makeErrorf(err, title, "id=%s params=%v", id, params)
err = herr
// Windows TP4: Hyper-V Containers may return this error with more than one
// concurrent exec. Do not log it as an error
if hr != Win32InvalidArgument {
logrus.Error(winerr)
if herr.Err != WSAEINVAL {
logrus.Error(err)
}
err = winerr
return
}

Expand All @@ -99,5 +97,5 @@ func CreateProcessInComputeSystem(id string, useStdin bool, useStdout bool, useS
}

logrus.Debugf(title+" - succeeded id=%s params=%s pid=%d", id, paramsJson, pid)
return pid, pipes[0], pipes[1], pipes[2], 0, nil
return pid, pipes[0], pipes[1], pipes[2], nil
}
51 changes: 28 additions & 23 deletions hcsshim.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,47 +43,52 @@ const (
// Specific user-visible exit codes
WaitErrExecFailed = 32767

// Known Win32 RC values which should be trapped
Win32PipeHasBeenEnded = 0x8007006d // WaitForProcessInComputeSystem: The pipe has been ended
Win32SystemShutdownIsInProgress = 0x8007045B // ShutdownComputeSystem: A system shutdown is in progress
Win32SpecifiedPathInvalid = 0x800700A1 // ShutdownComputeSystem: The specified path is invalid
Win32SystemCannotFindThePathSpecified = 0x80070003 // ShutdownComputeSystem: The system cannot find the path specified
Win32InvalidArgument = 0x80072726 // CreateProcessInComputeSystem: An invalid argument was supplied
EFail = 0x80004005
ERROR_GEN_FAILURE = syscall.Errno(31)
ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115)
WSAEINVAL = syscall.Errno(10022)

// Timeout on wait calls
TimeoutInfinite = 0xFFFFFFFF
)

type hcsError struct {
type HcsError struct {
title string
rest string
err error
Err error
}

type Win32Error interface {
error
HResult() uint32
func makeError(err error, title, rest string) *HcsError {
if hr, ok := err.(syscall.Errno); ok {
// Convert the HRESULT to a Win32 error code so that it better matches
// error codes returned from go and other packages.
err = syscall.Errno(win32FromHresult(uint32(hr)))
}
return &HcsError{title, rest, err}
}

func makeError(err error, title, rest string) Win32Error {
return &hcsError{title, rest, err}
func makeErrorf(err error, title, format string, a ...interface{}) *HcsError {
return makeError(err, title, fmt.Sprintf(format, a...))
}

func makeErrorf(err error, title, format string, a ...interface{}) Win32Error {
return makeError(err, title, fmt.Sprintf(format, a...))
func win32FromError(err error) uint32 {
if herr, ok := err.(*HcsError); ok {
return win32FromError(herr.Err)
}
if code, ok := err.(syscall.Errno); ok {
return win32FromHresult(uint32(code))
}
return uint32(ERROR_GEN_FAILURE)
}

func (e *hcsError) HResult() uint32 {
if hr, ok := e.err.(syscall.Errno); ok {
return uint32(hr)
} else {
return EFail
func win32FromHresult(hr uint32) uint32 {
if hr&0x1fff0000 == 0x00070000 {
return hr & 0xffff
}
return hr
}

func (e *hcsError) Error() string {
return fmt.Sprintf("%s- Win32 API call returned error r1=0x%x err=%s%s", e.title, e.HResult(), e.err, e.rest)
func (e *HcsError) Error() string {
return fmt.Sprintf("%s- Win32 API call returned error r1=0x%x err=%s%s", e.title, win32FromError(e.Err), e.Err, e.rest)
}

func convertAndFreeCoTaskMemString(buffer *uint16) string {
Expand Down
11 changes: 5 additions & 6 deletions shutdownterminatecomputesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ package hcsshim
import "github.com/Sirupsen/logrus"

// TerminateComputeSystem force terminates a container.
func TerminateComputeSystem(id string, timeout uint32, context string) (uint32, error) {
func TerminateComputeSystem(id string, timeout uint32, context string) error {
return shutdownTerminate(false, id, timeout, context)
}

// ShutdownComputeSystem shuts down a container by requesting a shutdown within
// the container operating system.
func ShutdownComputeSystem(id string, timeout uint32, context string) (uint32, error) {
func ShutdownComputeSystem(id string, timeout uint32, context string) error {
return shutdownTerminate(true, id, timeout, context)
}

// shutdownTerminate is a wrapper for ShutdownComputeSystem and TerminateComputeSystem
// which have very similar calling semantics
func shutdownTerminate(shutdown bool, id string, timeout uint32, context string) (uint32, error) {
func shutdownTerminate(shutdown bool, id string, timeout uint32, context string) error {

var (
title = "HCSShim::"
Expand All @@ -35,10 +35,9 @@ func shutdownTerminate(shutdown bool, id string, timeout uint32, context string)
}

if err != nil {
err := makeErrorf(err, title, "id=%s context=%s", id, context)
return err.HResult(), err
return makeErrorf(err, title, "id=%s context=%s", id, context)
}

logrus.Debugf(title+" succeeded id=%s context=%s", id, context)
return 0, nil
return nil
}
9 changes: 4 additions & 5 deletions waitprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ package hcsshim
import "github.com/Sirupsen/logrus"

// WaitForProcessInComputeSystem waits for a process ID to terminate and returns
// the exit code. Returns exitcode, errno, error
func WaitForProcessInComputeSystem(id string, processid uint32, timeout uint32) (int32, uint32, error) {
// the exit code. Returns exitcode, error
func WaitForProcessInComputeSystem(id string, processid uint32, timeout uint32) (int32, error) {

title := "HCSShim::WaitForProcessInComputeSystem"
logrus.Debugf(title+" id=%s processid=%d", id, processid)

var exitCode uint32
err := waitForProcessInComputeSystem(id, processid, timeout, &exitCode)
if err != nil {
err := makeErrorf(err, title, "id=%s", id)
return 0, err.HResult(), err
return 0, makeErrorf(err, title, "id=%s", id)
}

logrus.Debugf(title+" succeeded id=%s processid=%d exitcode=%d", id, processid, exitCode)
return int32(exitCode), 0, nil
return int32(exitCode), nil
}

0 comments on commit fd9d5fb

Please sign in to comment.