Change Docker to use the new HCS RPC API

Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
Darren Stahl 2016-05-23 16:12:06 -07:00
parent 91bc4cca58
commit 959c1a52bf
16 changed files with 2123 additions and 235 deletions

View File

@ -43,7 +43,7 @@ esac
# the following lines are in sorted order, FYI
clone git github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
clone git github.com/Microsoft/hcsshim v0.2.2
clone git github.com/Microsoft/hcsshim v0.3.0
clone git github.com/Microsoft/go-winio v0.3.4
clone git github.com/Sirupsen/logrus v0.10.0 # logrus is a common dependency among multiple deps
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a

View File

@ -1,7 +1,6 @@
package libcontainerd
import (
"encoding/json"
"errors"
"fmt"
"io"
@ -29,76 +28,6 @@ const (
ErrorInvalidObject = syscall.Errno(0x800710D8) // The object identifier does not represent a valid object
)
type layer struct {
ID string
Path string
}
type defConfig struct {
DefFile string
}
type portBinding struct {
Protocol string
InternalPort int
ExternalPort int
}
type natSettings struct {
Name string
PortBindings []portBinding
}
type networkConnection struct {
NetworkName string
Nat natSettings
}
type networkSettings struct {
MacAddress string
}
type device struct {
DeviceType string
Connection interface{}
Settings interface{}
}
type mappedDir struct {
HostPath string
ContainerPath string
ReadOnly bool
}
type hvRuntime struct {
ImagePath string `json:",omitempty"`
}
// TODO Windows: @darrenstahlmsft Add ProcessorCount
type containerInit struct {
SystemType string // HCS requires this to be hard-coded to "Container"
Name string // Name of the container. We use the docker ID.
Owner string // The management platform that created this container
IsDummy bool // Used for development purposes.
VolumePath string // Windows volume path for scratch space
Devices []device // Devices used by the container
IgnoreFlushesDuringBoot bool // Optimization hint for container startup in Windows
LayerFolderPath string // Where the layer folders are located
Layers []layer // List of storage layers
ProcessorWeight uint64 `json:",omitempty"` // CPU Shares 0..10000 on Windows; where 0 will be omitted and HCS will default.
ProcessorMaximum int64 `json:",omitempty"` // CPU maximum usage percent 1..100
StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS
StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second
StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller
MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes
HostName string // Hostname
MappedDirectories []mappedDir // List of mapped directories (volumes/mounts)
SandboxPath string // Location of unmounted sandbox (used for Hyper-V containers)
HvPartition bool // True if it a Hyper-V Container
EndpointList []string // List of networking endpoints to be attached to container
HvRuntime *hvRuntime // Hyper-V container settings
Servicing bool // True if this container is for servicing
}
// defaultOwner is a tag passed to HCS to allow it to differentiate between
// container creator management stacks. We hard code "docker" in the case
// of docker.
@ -109,7 +38,7 @@ const defaultOwner = "docker"
func (clnt *client) Create(containerID string, spec Spec, options ...CreateOption) error {
logrus.Debugln("LCD client.Create() with spec", spec)
cu := &containerInit{
configuration := &hcsshim.ContainerConfig{
SystemType: "Container",
Name: containerID,
Owner: defaultOwner,
@ -121,55 +50,55 @@ func (clnt *client) Create(containerID string, spec Spec, options ...CreateOptio
}
if spec.Windows.Networking != nil {
cu.EndpointList = spec.Windows.Networking.EndpointList
configuration.EndpointList = spec.Windows.Networking.EndpointList
}
if spec.Windows.Resources != nil {
if spec.Windows.Resources.CPU != nil {
if spec.Windows.Resources.CPU.Shares != nil {
cu.ProcessorWeight = *spec.Windows.Resources.CPU.Shares
configuration.ProcessorWeight = *spec.Windows.Resources.CPU.Shares
}
if spec.Windows.Resources.CPU.Percent != nil {
cu.ProcessorMaximum = *spec.Windows.Resources.CPU.Percent * 100 // ProcessorMaximum is a value between 1 and 10000
configuration.ProcessorMaximum = *spec.Windows.Resources.CPU.Percent * 100 // ProcessorMaximum is a value between 1 and 10000
}
}
if spec.Windows.Resources.Memory != nil {
if spec.Windows.Resources.Memory.Limit != nil {
cu.MemoryMaximumInMB = *spec.Windows.Resources.Memory.Limit / 1024 / 1024
configuration.MemoryMaximumInMB = *spec.Windows.Resources.Memory.Limit / 1024 / 1024
}
}
if spec.Windows.Resources.Storage != nil {
if spec.Windows.Resources.Storage.Bps != nil {
cu.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
configuration.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
}
if spec.Windows.Resources.Storage.Iops != nil {
cu.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
configuration.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
}
if spec.Windows.Resources.Storage.SandboxSize != nil {
cu.StorageSandboxSize = *spec.Windows.Resources.Storage.SandboxSize
configuration.StorageSandboxSize = *spec.Windows.Resources.Storage.SandboxSize
}
}
}
if spec.Windows.HvRuntime != nil {
cu.HvPartition = true
cu.HvRuntime = &hvRuntime{
configuration.HvPartition = true
configuration.HvRuntime = &hcsshim.HvRuntime{
ImagePath: spec.Windows.HvRuntime.ImagePath,
}
}
for _, option := range options {
if s, ok := option.(*ServicingOption); ok {
cu.Servicing = s.IsServicing
break
}
if configuration.HvPartition {
configuration.SandboxPath = filepath.Dir(spec.Windows.LayerFolder)
} else {
configuration.VolumePath = spec.Root.Path
configuration.LayerFolderPath = spec.Windows.LayerFolder
}
if cu.HvPartition {
cu.SandboxPath = filepath.Dir(spec.Windows.LayerFolder)
} else {
cu.VolumePath = spec.Root.Path
cu.LayerFolderPath = spec.Windows.LayerFolder
for _, option := range options {
if s, ok := option.(*ServicingOption); ok {
configuration.Servicing = s.IsServicing
break
}
}
for _, layerPath := range spec.Windows.LayerPaths {
@ -178,33 +107,27 @@ func (clnt *client) Create(containerID string, spec Spec, options ...CreateOptio
if err != nil {
return err
}
cu.Layers = append(cu.Layers, layer{
configuration.Layers = append(configuration.Layers, hcsshim.Layer{
ID: g.ToString(),
Path: layerPath,
})
}
// Add the mounts (volumes, bind mounts etc) to the structure
mds := make([]mappedDir, len(spec.Mounts))
mds := make([]hcsshim.MappedDir, len(spec.Mounts))
for i, mount := range spec.Mounts {
mds[i] = mappedDir{
mds[i] = hcsshim.MappedDir{
HostPath: mount.Source,
ContainerPath: mount.Destination,
ReadOnly: mount.Readonly}
}
cu.MappedDirectories = mds
configuration.MappedDirectories = mds
configurationb, err := json.Marshal(cu)
hcsContainer, err := hcsshim.CreateContainer(containerID, configuration)
if err != nil {
return err
}
// Create the compute system
configuration := string(configurationb)
if err := hcsshim.CreateComputeSystem(containerID, configuration); err != nil {
return err
}
// Construct a container object for calling start on it.
container := &container{
containerCommon: containerCommon{
@ -218,7 +141,8 @@ func (clnt *client) Create(containerID string, spec Spec, options ...CreateOptio
},
processes: make(map[string]*process),
},
ociSpec: spec,
ociSpec: spec,
hcsContainer: hcsContainer,
}
container.options = options
@ -252,10 +176,17 @@ func (clnt *client) AddProcess(containerID, processFriendlyName string, procToAd
if err != nil {
return err
}
createProcessParms := hcsshim.CreateProcessParams{
EmulateConsole: procToAdd.Terminal,
ConsoleSize: procToAdd.InitialConsoleSize,
// Note we always tell HCS to
// create stdout as it's required regardless of '-i' or '-t' options, so that
// docker can always grab the output through logs. We also tell HCS to always
// create stdin, even if it's not used - it will be closed shortly. Stderr
// is only created if it we're not -t.
createProcessParms := hcsshim.ProcessConfig{
EmulateConsole: procToAdd.Terminal,
ConsoleSize: procToAdd.InitialConsoleSize,
CreateStdInPipe: true,
CreateStdOutPipe: true,
CreateStdErrPipe: !procToAdd.Terminal,
}
// Take working directory from the process to add if it is defined,
@ -272,25 +203,24 @@ func (clnt *client) AddProcess(containerID, processFriendlyName string, procToAd
logrus.Debugf("commandLine: %s", createProcessParms.CommandLine)
// Start the command running in the container. Note we always tell HCS to
// create stdout as it's required regardless of '-i' or '-t' options, so that
// docker can always grab the output through logs. We also tell HCS to always
// create stdin, even if it's not used - it will be closed shortly. Stderr
// is only created if it we're not -t.
// Start the command running in the container.
var stdout, stderr io.ReadCloser
var pid uint32
iopipe := &IOPipe{Terminal: procToAdd.Terminal}
pid, iopipe.Stdin, stdout, stderr, err = hcsshim.CreateProcessInComputeSystem(
containerID,
true,
true,
!procToAdd.Terminal,
createProcessParms)
var stdin io.WriteCloser
newProcess, err := container.hcsContainer.CreateProcess(&createProcessParms)
if err != nil {
logrus.Errorf("AddProcess %s CreateProcessInComputeSystem() failed %s", containerID, err)
logrus.Errorf("AddProcess %s CreateProcess() failed %s", containerID, err)
return err
}
stdin, stdout, stderr, err = newProcess.Stdio()
if err != nil {
logrus.Errorf("%s getting std pipes failed %s", containerID, err)
return err
}
iopipe := &IOPipe{Terminal: procToAdd.Terminal}
iopipe.Stdin = createStdInCloser(stdin, newProcess)
// TEMP: Work around Windows BS/DEL behavior.
iopipe.Stdin = fixStdinBackspaceBehavior(iopipe.Stdin, procToAdd.Terminal)
@ -302,17 +232,21 @@ func (clnt *client) AddProcess(containerID, processFriendlyName string, procToAd
iopipe.Stderr = openReaderFromPipe(stderr)
}
// Add the process to the containers list of processes
container.processes[processFriendlyName] =
&process{
processCommon: processCommon{
containerID: containerID,
friendlyName: processFriendlyName,
client: clnt,
systemPid: pid,
},
commandLine: createProcessParms.CommandLine,
}
pid := newProcess.Pid()
proc := &process{
processCommon: processCommon{
containerID: containerID,
friendlyName: processFriendlyName,
client: clnt,
systemPid: uint32(pid),
},
commandLine: createProcessParms.CommandLine,
hcsProcess: newProcess,
}
// Add the process to the container's list of processes
container.processes[processFriendlyName] = proc
// Make sure the lock is not held while calling back into the daemon
clnt.unlock(containerID)
@ -326,7 +260,7 @@ func (clnt *client) AddProcess(containerID, processFriendlyName string, procToAd
clnt.lock(containerID)
// Spin up a go routine waiting for exit to handle cleanup
go container.waitExit(pid, processFriendlyName, false)
go container.waitExit(proc, false)
return nil
}
@ -350,16 +284,17 @@ func (clnt *client) Signal(containerID string, sig int) error {
cont.manualStopRequested = true
logrus.Debugf("lcd: Signal() containerID=%s sig=%d pid=%d", containerID, sig, cont.systemPid)
context := fmt.Sprintf("Signal: sig=%d pid=%d", sig, cont.systemPid)
if syscall.Signal(sig) == syscall.SIGKILL {
// Terminate the compute system
if err := hcsshim.TerminateComputeSystem(containerID, hcsshim.TimeoutInfinite, context); err != nil {
logrus.Errorf("Failed to terminate %s - %q", containerID, err)
if err := cont.hcsContainer.Terminate(); err != nil {
if err != hcsshim.ErrVmcomputeOperationPending {
logrus.Errorf("Failed to terminate %s - %q", containerID, err)
}
}
} else {
// Terminate Process
if err = hcsshim.TerminateProcessInComputeSystem(containerID, cont.systemPid); err != nil {
if err := cont.hcsProcess.Kill(); err != nil {
logrus.Warnf("Failed to terminate pid %d in %s: %q", cont.systemPid, containerID, err)
// Ignore errors
err = nil
@ -380,15 +315,17 @@ func (clnt *client) Resize(containerID, processFriendlyName string, width, heigh
return err
}
h, w := uint16(height), uint16(width)
if processFriendlyName == InitFriendlyName {
logrus.Debugln("Resizing systemPID in", containerID, cont.process.systemPid)
return hcsshim.ResizeConsoleInComputeSystem(containerID, cont.process.systemPid, height, width)
return cont.process.hcsProcess.ResizeConsole(w, h)
}
for _, p := range cont.processes {
if p.friendlyName == processFriendlyName {
logrus.Debugln("Resizing exec'd process", containerID, p.systemPid)
return hcsshim.ResizeConsoleInComputeSystem(containerID, p.systemPid, height, width)
return p.hcsProcess.ResizeConsole(w, h)
}
}

View File

@ -22,6 +22,7 @@ type container struct {
ociSpec Spec
manualStopRequested bool
hcsContainer hcsshim.Container
}
func (ctr *container) newProcess(friendlyName string) *process {
@ -40,7 +41,7 @@ func (ctr *container) start() error {
// Start the container. If this is a servicing container, this call will block
// until the container is done with the servicing execution.
logrus.Debugln("Starting container ", ctr.containerID)
if err = hcsshim.StartComputeSystem(ctr.containerID); err != nil {
if err = ctr.hcsContainer.Start(); err != nil {
logrus.Errorf("Failed to start compute system: %s", err)
return err
}
@ -49,59 +50,61 @@ func (ctr *container) start() error {
if s, ok := option.(*ServicingOption); ok && s.IsServicing {
// Since the servicing operation is complete when StartCommputeSystem returns without error,
// we can shutdown (which triggers merge) and exit early.
const shutdownTimeout = 5 * 60 * 1000 // 4 minutes
const terminateTimeout = 1 * 60 * 1000 // 1 minute
if err := hcsshim.ShutdownComputeSystem(ctr.containerID, shutdownTimeout, ""); err != nil {
logrus.Errorf("Failed during cleanup of servicing container: %s", err)
// Terminate the container, ignoring errors.
if err2 := hcsshim.TerminateComputeSystem(ctr.containerID, terminateTimeout, ""); err2 != nil {
logrus.Errorf("Failed to terminate container %s after shutdown failure: %q", ctr.containerID, err2)
}
return err
}
return nil
return ctr.shutdown()
}
}
createProcessParms := hcsshim.CreateProcessParams{
// Note we always tell HCS to
// create stdout as it's required regardless of '-i' or '-t' options, so that
// docker can always grab the output through logs. We also tell HCS to always
// create stdin, even if it's not used - it will be closed shortly. Stderr
// is only created if it we're not -t.
createProcessParms := &hcsshim.ProcessConfig{
EmulateConsole: ctr.ociSpec.Process.Terminal,
WorkingDirectory: ctr.ociSpec.Process.Cwd,
ConsoleSize: ctr.ociSpec.Process.InitialConsoleSize,
CreateStdInPipe: true,
CreateStdOutPipe: true,
CreateStdErrPipe: !ctr.ociSpec.Process.Terminal,
}
// Configure the environment for the process
createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
createProcessParms.CommandLine = strings.Join(ctr.ociSpec.Process.Args, " ")
iopipe := &IOPipe{Terminal: ctr.ociSpec.Process.Terminal}
// Start the command running in the container. Note we always tell HCS to
// create stdout as it's required regardless of '-i' or '-t' options, so that
// docker can always grab the output through logs. We also tell HCS to always
// create stdin, even if it's not used - it will be closed shortly. Stderr
// is only created if it we're not -t.
var pid uint32
var stdout, stderr io.ReadCloser
pid, iopipe.Stdin, stdout, stderr, err = hcsshim.CreateProcessInComputeSystem(
ctr.containerID,
true,
true,
!ctr.ociSpec.Process.Terminal,
createProcessParms)
// Start the command running in the container.
hcsProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
if err != nil {
logrus.Errorf("CreateProcessInComputeSystem() failed %s", err)
// Explicitly terminate the compute system here.
if err2 := hcsshim.TerminateComputeSystem(ctr.containerID, hcsshim.TimeoutInfinite, "CreateProcessInComputeSystem failed"); err2 != nil {
// Ignore this error, there's not a lot we can do except log it
logrus.Warnf("Failed to TerminateComputeSystem after a failed CreateProcessInComputeSystem. Ignoring this.", err2)
logrus.Errorf("CreateProcess() failed %s", err)
if err2 := ctr.terminate(); err2 != nil {
logrus.Debugf("Failed to cleanup after a failed CreateProcess. Ignoring this. %s", err2)
} else {
logrus.Debugln("Cleaned up after failed CreateProcessInComputeSystem by calling TerminateComputeSystem")
logrus.Debugln("Cleaned up after failed CreateProcess by calling Terminate")
}
return err
}
ctr.startedAt = time.Now()
// Save the hcs Process and PID
ctr.process.friendlyName = InitFriendlyName
pid := hcsProcess.Pid()
ctr.process.hcsProcess = hcsProcess
var stdout, stderr io.ReadCloser
var stdin io.WriteCloser
stdin, stdout, stderr, err = hcsProcess.Stdio()
if err != nil {
logrus.Errorf("failed to get stdio pipes: %s", err)
if err := ctr.terminate(); err != nil {
logrus.Debugf("Failed to cleanup after a failed CreateProcess. Ignoring this. %s", err)
}
return err
}
iopipe := &IOPipe{Terminal: ctr.ociSpec.Process.Terminal}
iopipe.Stdin = createStdInCloser(stdin, hcsProcess)
// TEMP: Work around Windows BS/DEL behavior.
iopipe.Stdin = fixStdinBackspaceBehavior(iopipe.Stdin, ctr.ociSpec.Process.Terminal)
@ -118,7 +121,7 @@ func (ctr *container) start() error {
ctr.systemPid = uint32(pid)
// Spin up a go routine waiting for exit to handle cleanup
go ctr.waitExit(pid, InitFriendlyName, true)
go ctr.waitExit(&ctr.process, true)
ctr.client.appendContainer(ctr)
@ -140,17 +143,27 @@ func (ctr *container) start() error {
// waitExit runs as a goroutine waiting for the process to exit. It's
// equivalent to (in the linux containerd world) where events come in for
// state change notifications from containerd.
func (ctr *container) waitExit(pid uint32, processFriendlyName string, isFirstProcessToStart bool) error {
logrus.Debugln("waitExit on pid", pid)
func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error {
logrus.Debugln("waitExit on pid", process.systemPid)
// Block indefinitely for the process to exit.
exitCode, err := hcsshim.WaitForProcessInComputeSystem(ctr.containerID, pid, hcsshim.TimeoutInfinite)
err := process.hcsProcess.Wait()
if err != nil {
if herr, ok := err.(*hcsshim.HcsError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
logrus.Warnf("WaitForProcessInComputeSystem failed (container may have been killed): %s", err)
}
// Fall through here, do not return. This ensures we attempt to continue the
// shutdown in HCS nad tell the docker engine that the process/container
// shutdown in HCS and tell the docker engine that the process/container
// has exited to avoid a container being dropped on the floor.
}
exitCode, err := process.hcsProcess.ExitCode()
if err != nil {
if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
logrus.Warnf("Unable to get exit code from container %s", ctr.containerID)
}
// Fall through here, do not return. This ensures we attempt to continue the
// shutdown in HCS and tell the docker engine that the process/container
// has exited to avoid a container being dropped on the floor.
}
@ -159,46 +172,35 @@ func (ctr *container) waitExit(pid uint32, processFriendlyName string, isFirstPr
CommonStateInfo: CommonStateInfo{
State: StateExit,
ExitCode: uint32(exitCode),
Pid: pid,
ProcessID: processFriendlyName,
Pid: process.systemPid,
ProcessID: process.friendlyName,
},
UpdatePending: false,
}
// But it could have been an exec'd process which exited
if !isFirstProcessToStart {
if err := process.hcsProcess.Close(); err != nil {
logrus.Error(err)
}
si.State = StateExitProcess
} else {
// Since this is the init process, always call into vmcompute.dll to
// shutdown the container after we have completed.
propertyCheckFlag := 1 // Include update pending check.
csProperties, err := hcsshim.GetComputeSystemProperties(ctr.containerID, uint32(propertyCheckFlag))
updatePending, err := ctr.hcsContainer.HasPendingUpdates()
if err != nil {
logrus.Warnf("GetComputeSystemProperties failed (container may have been killed): %s", err)
logrus.Warnf("HasPendingUpdates failed (container may have been killed): %s", err)
} else {
si.UpdatePending = csProperties.AreUpdatesPending
si.UpdatePending = updatePending
}
logrus.Debugf("Shutting down container %s", ctr.containerID)
// Explicit timeout here rather than hcsshim.TimeoutInfinte to avoid a
// (remote) possibility that ShutdownComputeSystem hangs indefinitely.
const shutdownTimeout = 5 * 60 * 1000 // 5 minutes
if err := hcsshim.ShutdownComputeSystem(ctr.containerID, shutdownTimeout, "waitExit"); err != nil {
if herr, ok := err.(*hcsshim.HcsError); !ok ||
(herr.Err != hcsshim.ERROR_SHUTDOWN_IN_PROGRESS &&
herr.Err != ErrorBadPathname &&
herr.Err != syscall.ERROR_PATH_NOT_FOUND) {
logrus.Debugf("waitExit - error from ShutdownComputeSystem on %s %v. Calling TerminateComputeSystem", ctr.containerCommon, err)
if err := hcsshim.TerminateComputeSystem(ctr.containerID, shutdownTimeout, "waitExit"); err != nil {
logrus.Debugf("waitExit - ignoring error from TerminateComputeSystem %s %v", ctr.containerID, err)
} else {
logrus.Debugf("Successful TerminateComputeSystem after failed ShutdownComputeSystem on %s in waitExit", ctr.containerID)
}
}
if err := ctr.shutdown(); err != nil {
logrus.Debugf("Failed to shutdown container %s", ctr.containerID)
} else {
logrus.Debugf("Completed shutting down container %s", ctr.containerID)
}
if err := ctr.hcsContainer.Close(); err != nil {
logrus.Error(err)
}
if !ctr.manualStopRequested && ctr.restartManager != nil {
restart, wait, err := ctr.restartManager.ShouldRestart(uint32(exitCode), false, time.Since(ctr.startedAt))
@ -227,6 +229,9 @@ func (ctr *container) waitExit(pid uint32, processFriendlyName string, isFirstPr
// Remove process from list if we have exited
// We need to do so here in case the Message Handler decides to restart it.
if si.State == StateExit {
if err := ctr.hcsContainer.Close(); err != nil {
logrus.Error(err)
}
ctr.client.deleteContainer(ctr.friendlyName)
}
}
@ -240,3 +245,37 @@ func (ctr *container) waitExit(pid uint32, processFriendlyName string, isFirstPr
logrus.Debugln("waitExit() completed OK")
return nil
}
func (ctr *container) shutdown() error {
const shutdownTimeout = time.Minute * 5
err := ctr.hcsContainer.Shutdown()
if err == hcsshim.ErrVmcomputeOperationPending {
// Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely.
err = ctr.hcsContainer.WaitTimeout(shutdownTimeout)
}
if err != nil {
logrus.Debugf("error shutting down container %s %v calling terminate", ctr.containerID, err)
if err := ctr.terminate(); err != nil {
return err
}
return err
}
return nil
}
func (ctr *container) terminate() error {
const terminateTimeout = time.Minute * 5
err := ctr.hcsContainer.Terminate()
if err == hcsshim.ErrVmcomputeOperationPending {
err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
}
if err != nil {
return err
}
return nil
}

View File

@ -3,6 +3,7 @@ package libcontainerd
import (
"io"
"github.com/Microsoft/hcsshim"
"github.com/docker/docker/pkg/system"
)
@ -14,6 +15,7 @@ type process struct {
// commandLine is to support returning summary information for docker top
commandLine string
hcsProcess hcsshim.Process
}
func openReaderFromPipe(p io.ReadCloser) io.Reader {
@ -57,3 +59,27 @@ func (w *delToBsWriter) Write(b []byte) (int, error) {
}
return w.WriteCloser.Write(bc)
}
type stdInCloser struct {
io.WriteCloser
hcsshim.Process
}
func createStdInCloser(pipe io.WriteCloser, process hcsshim.Process) *stdInCloser {
return &stdInCloser{
WriteCloser: pipe,
Process: process,
}
}
func (stdin *stdInCloser) Close() error {
if err := stdin.WriteCloser.Close(); err != nil {
return err
}
return stdin.Process.CloseStdin()
}
func (stdin *stdInCloser) Write(p []byte) (n int, err error) {
return stdin.WriteCloser.Write(p)
}

View File

@ -62,20 +62,17 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
}
}()
err = winio.RunWithPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}, func() (err error) {
createmode := uint32(syscall.CREATE_NEW)
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
err := os.Mkdir(path, 0)
if err != nil && !os.IsExist(err) {
return err
}
createmode = syscall.OPEN_EXISTING
createmode := uint32(syscall.CREATE_NEW)
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
err := os.Mkdir(path, 0)
if err != nil && !os.IsExist(err) {
return err
}
createmode = syscall.OPEN_EXISTING
}
mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
return
})
mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
if err != nil {
return err
}
@ -113,9 +110,7 @@ func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
return err
}
return winio.RunWithPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}, func() (err error) {
return os.Link(linktarget, linkpath)
})
return os.Link(linktarget, linkpath)
}
func (w *baseLayerWriter) Remove(name string) error {
@ -123,11 +118,7 @@ func (w *baseLayerWriter) Remove(name string) error {
}
func (w *baseLayerWriter) Write(b []byte) (int, error) {
var n int
err := winio.RunWithPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}, func() (err error) {
n, err = w.bw.Write(b)
return
})
n, err := w.bw.Write(b)
if err != nil {
w.err = err
}

View File

@ -0,0 +1,78 @@
package hcsshim
import (
"errors"
"sync"
"syscall"
)
var (
nextCallback uintptr
callbackMap = map[uintptr]*notifcationWatcherContext{}
callbackMapLock = sync.RWMutex{}
notificationWatcherCallback = syscall.NewCallback(notificationWatcher)
// Notifications for HCS_SYSTEM handles
hcsNotificationSystemExited hcsNotification = 0x00000001
hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002
hcsNotificationSystemStartCompleted hcsNotification = 0x00000003
hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004
hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005
// Notifications for HCS_PROCESS handles
hcsNotificationProcessExited hcsNotification = 0x00010000
// Common notifications
hcsNotificationInvalid hcsNotification = 0x00000000
hcsNotificationServiceDisconnect hcsNotification = 0x01000000
// ErrUnexpectedContainerExit is the error returned when a container exits while waiting for
// a different expected notification
ErrUnexpectedContainerExit = errors.New("unexpected container exit")
// ErrUnexpectedProcessAbort is the error returned when communication with the compute service
// is lost while waiting for a notification
ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")
)
type hcsNotification uint32
type notificationChannel chan error
type notifcationWatcherContext struct {
channel notificationChannel
expectedNotification hcsNotification
handle hcsCallback
}
func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr {
var (
result error
completeWait = false
)
callbackMapLock.RLock()
context := callbackMap[callbackNumber]
callbackMapLock.RUnlock()
if notificationType == context.expectedNotification {
if int32(notificationStatus) < 0 {
result = syscall.Errno(win32FromHresult(notificationStatus))
} else {
result = nil
}
completeWait = true
} else if notificationType == hcsNotificationSystemExited {
result = ErrUnexpectedContainerExit
completeWait = true
} else if notificationType == hcsNotificationServiceDisconnect {
result = ErrUnexpectedProcessAbort
completeWait = true
}
if completeWait {
context.channel <- result
}
return 0
}

View File

@ -0,0 +1,513 @@
package hcsshim
import (
"encoding/json"
"errors"
"fmt"
"runtime"
"syscall"
"time"
"github.com/Sirupsen/logrus"
)
var (
defaultTimeout = time.Minute * 4
// ErrTimeout is an error encountered when waiting on a notification times out
ErrTimeout = errors.New("hcsshim: timeout waiting for notification")
)
type ContainerError struct {
Container *container
Operation string
ExtraInfo string
Err error
}
type container struct {
handle hcsSystem
id string
}
type containerProperties struct {
ID string `json:"Id"`
Name string
SystemType string
Owner string
SiloGUID string `json:"SiloGuid,omitempty"`
IsDummy bool `json:",omitempty"`
RuntimeID string `json:"RuntimeId,omitempty"`
Stopped bool `json:",omitempty"`
ExitType string `json:",omitempty"`
AreUpdatesPending bool `json:",omitempty"`
}
// CreateContainer creates a new container with the given configuration but does not start it.
func CreateContainer(id string, c *ContainerConfig) (Container, error) {
operation := "CreateContainer"
title := "HCSShim::" + operation
logrus.Debugf(title+" id=%s", id)
container := &container{
id: id,
}
configurationb, err := json.Marshal(c)
if err != nil {
return nil, err
}
configuration := string(configurationb)
var (
handle hcsSystem
resultp *uint16
createError error
)
if hcsCallbacksSupported {
var identity syscall.Handle
createError = hcsCreateComputeSystem(id, configuration, identity, &handle, &resultp)
} else {
createError = hcsCreateComputeSystemTP5(id, configuration, &handle, &resultp)
}
container.handle = handle
err = processAsyncHcsResult(container, createError, resultp, hcsNotificationSystemCreateCompleted, &defaultTimeout)
if err != nil {
err := &ContainerError{Container: container, Operation: operation, ExtraInfo: configuration, Err: err}
logrus.Error(err)
return nil, err
}
logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle)
runtime.SetFinalizer(container, closeContainer)
return container, nil
}
// OpenContainer opens an existing container by ID.
func OpenContainer(id string) (Container, error) {
operation := "OpenContainer"
title := "HCSShim::" + operation
logrus.Debugf(title+" id=%s", id)
container := &container{
id: id,
}
var (
handle hcsSystem
resultp *uint16
)
err := hcsOpenComputeSystem(id, &handle, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err = &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return nil, err
}
container.handle = handle
logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle)
runtime.SetFinalizer(container, closeContainer)
return container, nil
}
// Start synchronously starts the container.
func (container *container) Start() error {
operation := "Start"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
var resultp *uint16
err := hcsStartComputeSystemTP5(container.handle, nil, &resultp)
err = processAsyncHcsResult(container, err, resultp, hcsNotificationSystemStartCompleted, &defaultTimeout)
if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
// Shutdown requests a container shutdown, but it may not actually be shut down until Wait() succeeds.
// It returns ErrVmcomputeOperationPending if the shutdown is in progress, nil if the shutdown is complete.
func (container *container) Shutdown() error {
operation := "Shutdown"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
var resultp *uint16
err := hcsShutdownComputeSystemTP5(container.handle, nil, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
if err == ErrVmcomputeOperationPending {
return ErrVmcomputeOperationPending
}
err = &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
// It returns ErrVmcomputeOperationPending if the shutdown is in progress, nil if the shutdown is complete.
func (container *container) Terminate() error {
operation := "Terminate"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
var resultp *uint16
err := hcsTerminateComputeSystemTP5(container.handle, nil, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
if err == ErrVmcomputeOperationPending {
return ErrVmcomputeOperationPending
}
err = &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
// Wait synchronously waits for the container to shutdown or terminate.
func (container *container) Wait() error {
operation := "Wait"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
if hcsCallbacksSupported {
err := registerAndWaitForCallback(container, hcsNotificationSystemExited)
if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
} else {
_, err := container.waitTimeoutInternal(syscall.INFINITE)
if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
func (container *container) waitTimeoutInternal(timeout uint32) (bool, error) {
return waitTimeoutInternalHelper(container, timeout)
}
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It returns
// ErrTimeout if the timeout duration expires before the container is shut down.
func (container *container) WaitTimeout(timeout time.Duration) error {
operation := "WaitTimeout"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
if hcsCallbacksSupported {
err := registerAndWaitForCallbackTimeout(container, hcsNotificationSystemExited, timeout)
if err == ErrTimeout {
return ErrTimeout
} else if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
} else {
finished, err := waitTimeoutHelper(container, timeout)
if !finished {
return ErrTimeout
} else if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
func (container *container) hcsWait(timeout uint32) (bool, error) {
var (
resultp *uint16
exitEvent syscall.Handle
)
err := hcsCreateComputeSystemWait(container.handle, &exitEvent, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
return false, err
}
defer syscall.CloseHandle(exitEvent)
return waitForSingleObject(exitEvent, timeout)
}
func (container *container) properties() (*containerProperties, error) {
var (
resultp *uint16
propertiesp *uint16
)
err := hcsGetComputeSystemProperties(container.handle, "", &propertiesp, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
return nil, err
}
if propertiesp == nil {
return nil, errors.New("Unexpected result from hcsGetComputeSystemProperties, properties should never be nil")
}
propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
properties := &containerProperties{}
if err := json.Unmarshal(propertiesRaw, properties); err != nil {
return nil, err
}
return properties, nil
}
// HasPendingUpdates returns true if the container has updates pending to install
func (container *container) HasPendingUpdates() (bool, error) {
operation := "HasPendingUpdates"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
properties, err := container.properties()
if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return false, err
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return properties.AreUpdatesPending, nil
}
// Pause pauses the execution of the container. This feature is not enabled in TP5.
func (container *container) Pause() error {
operation := "Pause"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
var resultp *uint16
err := hcsPauseComputeSystemTP5(container.handle, nil, &resultp)
err = processAsyncHcsResult(container, err, resultp, hcsNotificationSystemPauseCompleted, &defaultTimeout)
if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
// Resume resumes the execution of the container. This feature is not enabled in TP5.
func (container *container) Resume() error {
operation := "Resume"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
var (
resultp *uint16
)
err := hcsResumeComputeSystemTP5(container.handle, nil, &resultp)
err = processAsyncHcsResult(container, err, resultp, hcsNotificationSystemResumeCompleted, &defaultTimeout)
if err != nil {
err := &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
// CreateProcess launches a new process within the container.
func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
operation := "CreateProcess"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
var (
processInfo hcsProcessInformation
processHandle hcsProcess
resultp *uint16
)
// If we are not emulating a console, ignore any console size passed to us
if !c.EmulateConsole {
c.ConsoleSize[0] = 0
c.ConsoleSize[1] = 0
}
configurationb, err := json.Marshal(c)
if err != nil {
return nil, err
}
configuration := string(configurationb)
err = hcsCreateProcess(container.handle, configuration, &processInfo, &processHandle, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err = &ContainerError{Container: container, Operation: operation, ExtraInfo: configuration, Err: err}
logrus.Error(err)
return nil, err
}
process := &process{
handle: processHandle,
processID: int(processInfo.ProcessId),
container: container,
cachedPipes: &cachedPipes{
stdIn: processInfo.StdInput,
stdOut: processInfo.StdOutput,
stdErr: processInfo.StdError,
},
}
logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
runtime.SetFinalizer(process, closeProcess)
return process, nil
}
// OpenProcess gets an interface to an existing process within the container.
func (container *container) OpenProcess(pid int) (Process, error) {
operation := "OpenProcess"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s, processid=%d", container.id, pid)
var (
processHandle hcsProcess
resultp *uint16
)
err := hcsOpenProcess(container.handle, uint32(pid), &processHandle, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err = &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return nil, err
}
process := &process{
handle: processHandle,
processID: pid,
container: container,
}
logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
runtime.SetFinalizer(process, closeProcess)
return process, nil
}
// Close cleans up any state associated with the container but does not terminate or wait for it.
func (container *container) Close() error {
operation := "Close"
title := "HCSShim::Container::" + operation
logrus.Debugf(title+" id=%s", container.id)
// Don't double free this
if container.handle == 0 {
return nil
}
if err := hcsCloseComputeSystem(container.handle); err != nil {
err = &ContainerError{Container: container, Operation: operation, Err: err}
logrus.Error(err)
return err
}
container.handle = 0
logrus.Debugf(title+" succeeded id=%s", container.id)
return nil
}
// closeContainer wraps container.Close for use by a finalizer
func closeContainer(container *container) {
container.Close()
}
func (container *container) registerCallback(expectedNotification hcsNotification) (uintptr, error) {
callbackMapLock.Lock()
defer callbackMapLock.Unlock()
callbackNumber := nextCallback
nextCallback++
context := &notifcationWatcherContext{
expectedNotification: expectedNotification,
channel: make(chan error, 1),
}
callbackMap[callbackNumber] = context
var callbackHandle hcsCallback
err := hcsRegisterComputeSystemCallback(container.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
if err != nil {
return 0, err
}
context.handle = callbackHandle
return callbackNumber, nil
}
func (container *container) unregisterCallback(callbackNumber uintptr) error {
callbackMapLock.Lock()
defer callbackMapLock.Unlock()
handle := callbackMap[callbackNumber].handle
if handle == 0 {
return nil
}
err := hcsUnregisterComputeSystemCallback(handle)
if err != nil {
return err
}
callbackMap[callbackNumber] = nil
handle = 0
return nil
}
func (e *ContainerError) Error() string {
if e == nil {
return "<nil>"
}
if e.Container == nil {
return "unexpected nil container for error: " + e.Err.Error()
}
s := "container " + e.Container.id
if e.Operation != "" {
s += " encountered an error during " + e.Operation
}
if e.Err != nil {
s += fmt.Sprintf(" failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err))
}
if e.ExtraInfo != "" {
s += " extra info: " + e.ExtraInfo
}
return s
}

View File

@ -112,7 +112,9 @@ func (r *FilterLayerReader) Close() (err error) {
}
// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer.
func NewLayerReader(info DriverInfo, layerId string, parentLayerPaths []string) (LayerReader, error) {
// The caller must have taken the SeBackupPrivilege privilege
// to call this and any methods on the resulting LayerReader.
func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) {
if procExportLayerBegin.Find() != nil {
// The new layer reader is not available on this Windows build. Fall back to the
// legacy export code path.
@ -120,7 +122,7 @@ func NewLayerReader(info DriverInfo, layerId string, parentLayerPaths []string)
if err != nil {
return nil, err
}
err = ExportLayer(info, layerId, path, parentLayerPaths)
err = ExportLayer(info, layerID, path, parentLayerPaths)
if err != nil {
os.RemoveAll(path)
return nil, err
@ -137,7 +139,7 @@ func NewLayerReader(info DriverInfo, layerId string, parentLayerPaths []string)
return nil, err
}
r := &FilterLayerReader{}
err = exportLayerBegin(&infop, layerId, layers, &r.context)
err = exportLayerBegin(&infop, layerID, layers, &r.context)
if err != nil {
return nil, makeError(err, "ExportLayerBegin", "")
}

View File

@ -7,6 +7,8 @@ import (
"fmt"
"syscall"
"unsafe"
"github.com/Sirupsen/logrus"
)
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go
@ -50,6 +52,40 @@ import (
//sys waitForProcessInComputeSystem(id string, pid uint32, timeout uint32, exitCode *uint32) (hr error) = vmcompute.WaitForProcessInComputeSystem?
//sys getComputeSystemProperties(id string, flags uint32, properties **uint16) (hr error) = vmcompute.GetComputeSystemProperties?
//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems?
//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem?
//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem?
//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem?
//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem?
//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem?
//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem?
//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem?
//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem?
//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties?
//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem?
//sys hcsCreateComputeSystemWait(computeSystem hcsSystem, exitEvent *syscall.Handle, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystemWait?
//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess?
//sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess?
//sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess?
//sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess?
//sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo?
//sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties?
//sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess?
//sys hcsCreateProcessWait(process hcsProcess, settings *syscall.Handle, result **uint16) (hr error) = vmcompute.HcsCreateProcessWait?
//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties?
//sys hcsModifyServiceSettings(settings string, result **uint16) (hr error) = vmcompute.HcsModifyServiceSettings?
//sys hcsCreateComputeSystemTP5(id string, configuration string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem?
//sys hcsStartComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem?
//sys hcsShutdownComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem?
//sys hcsTerminateComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem?
//sys hcsPauseComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem?
//sys hcsResumeComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem?
//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback?
//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback?
//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback?
//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback?
//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall?
const (
@ -60,6 +96,8 @@ const (
ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115)
WSAEINVAL = syscall.Errno(10022)
ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)
// Timeout on wait calls
TimeoutInfinite = 0xFFFFFFFF
)
@ -70,6 +108,18 @@ type HcsError struct {
Err error
}
type hcsSystem syscall.Handle
type hcsProcess syscall.Handle
type hcsCallback syscall.Handle
type hcsProcessInformation struct {
ProcessId uint32
Reserved uint32
StdInput syscall.Handle
StdOutput syscall.Handle
StdError syscall.Handle
}
func makeError(err error, title, rest string) error {
// Pass through DLL errors directly since they do not originate from HCS.
if _, ok := err.(*syscall.DLLError); ok {
@ -119,3 +169,15 @@ func convertAndFreeCoTaskMemString(buffer *uint16) string {
coTaskMemFree(unsafe.Pointer(buffer))
return str
}
func convertAndFreeCoTaskMemBytes(buffer *uint16) []byte {
return []byte(convertAndFreeCoTaskMemString(buffer))
}
func processHcsResult(err error, resultp *uint16) error {
if resultp != nil {
result := convertAndFreeCoTaskMemString(resultp)
logrus.Debugf("Result: %s", result)
}
return err
}

View File

@ -148,6 +148,8 @@ func (r *legacyLayerWriterWrapper) Close() error {
}
// NewLayerWriter returns a new layer writer for creating a layer on disk.
// The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges
// to call this and any methods on the resulting LayerWriter.
func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
if len(parentLayerPaths) == 0 {
// This is a base layer. It gets imported differently.

View File

@ -0,0 +1,147 @@
package hcsshim
import (
"io"
"time"
)
// ProcessConfig is used as both the input of Container.CreateProcess
// and to convert the parameters to JSON for passing onto the HCS
type ProcessConfig struct {
ApplicationName string
CommandLine string
WorkingDirectory string
Environment map[string]string
EmulateConsole bool
CreateStdInPipe bool
CreateStdOutPipe bool
CreateStdErrPipe bool
ConsoleSize [2]int
}
type Layer struct {
ID string
Path string
}
type MappedDir struct {
HostPath string
ContainerPath string
ReadOnly bool
}
type HvRuntime struct {
ImagePath string `json:",omitempty"`
}
// ContainerConfig is used as both the input of CreateContainer
// and to convert the parameters to JSON for passing onto the HCS
// TODO Windows: @darrenstahlmsft Add ProcessorCount
type ContainerConfig struct {
SystemType string // HCS requires this to be hard-coded to "Container"
Name string // Name of the container. We use the docker ID.
Owner string // The management platform that created this container
IsDummy bool // Used for development purposes.
VolumePath string // Windows volume path for scratch space
IgnoreFlushesDuringBoot bool // Optimization hint for container startup in Windows
LayerFolderPath string // Where the layer folders are located
Layers []Layer // List of storage layers
ProcessorWeight uint64 `json:",omitempty"` // CPU Shares 0..10000 on Windows; where 0 will be omitted and HCS will default.
ProcessorMaximum int64 `json:",omitempty"` // CPU maximum usage percent 1..100
StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS
StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second
StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller
MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes
HostName string // Hostname
MappedDirectories []MappedDir // List of mapped directories (volumes/mounts)
SandboxPath string // Location of unmounted sandbox (used for Hyper-V containers)
HvPartition bool // True if it a Hyper-V Container
EndpointList []string // List of networking endpoints to be attached to container
HvRuntime *HvRuntime // Hyper-V container settings
Servicing bool // True if this container is for servicing
}
const (
notificationTypeNone string = "None"
notificationTypeGracefulExit string = "GracefulExit"
notificationTypeForcedExit string = "ForcedExit"
notificationTypeUnexpectedExit string = "UnexpectedExit"
notificationTypeReboot string = "Reboot"
notificationTypeConstructed string = "Constructed"
notificationTypeStarted string = "Started"
notificationTypePaused string = "Paused"
notificationTypeUnknown string = "Unknown"
)
// Container represents a created (but not necessarily running) container.
type Container interface {
// Start synchronously starts the container.
Start() error
// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
Shutdown() error
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
Terminate() error
// Waits synchronously waits for the container to shutdown or terminate.
Wait() error
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
// returns false if timeout occurs.
WaitTimeout(time.Duration) error
// Pause pauses the execution of a container.
Pause() error
// Resume resumes the execution of a container.
Resume() error
// HasPendingUpdates returns true if the container has updates pending to install.
HasPendingUpdates() (bool, error)
// CreateProcess launches a new process within the container.
CreateProcess(c *ProcessConfig) (Process, error)
// OpenProcess gets an interface to an existing process within the container.
OpenProcess(pid int) (Process, error)
// Close cleans up any state associated with the container but does not terminate or wait for it.
Close() error
}
// Process represents a running or exited process.
type Process interface {
// Pid returns the process ID of the process within the container.
Pid() int
// Kill signals the process to terminate but does not wait for it to finish terminating.
Kill() error
// Wait waits for the process to exit.
Wait() error
// WaitTimeout waits for the process to exit or the duration to elapse. It returns
// false if timeout occurs.
WaitTimeout(time.Duration) error
// ExitCode returns the exit code of the process. The process must have
// already terminated.
ExitCode() (int, error)
// ResizeConsole resizes the console of the process.
ResizeConsole(width, height uint16) error
// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
// these pipes does not close the underlying pipes; it should be possible to
// call this multiple times to get multiple interfaces.
Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error)
// CloseStdin closes the write side of the stdin pipe so that the process is
// notified on the read side that there is no more data in stdin.
CloseStdin() error
// Close cleans up any state associated with the process but does not kill
// or wait on it.
Close() error
}

View File

@ -598,6 +598,19 @@ func (f *Fn) HasStringParam() bool {
return false
}
var uniqDllFuncName = make(map[string]bool)
// IsNotDuplicate is true if f is not a duplicated function
func (f *Fn) IsNotDuplicate() bool {
funcName := f.DLLFuncName()
if uniqDllFuncName[funcName] == false {
uniqDllFuncName[funcName] = true
return true
}
return false
}
// HelperName returns name of function f helper.
func (f *Fn) HelperName() string {
if !f.HasStringParam() {
@ -748,6 +761,7 @@ const srcTemplate = `
package {{packagename}}
import "github.com/Microsoft/go-winio"
import "unsafe"{{if syscalldot}}
import "syscall"{{end}}
@ -764,7 +778,7 @@ var (
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{syscalldot}}NewLazyDLL("{{.}}.dll")
{{end}}{{end}}
{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
{{define "funcnames"}}{{range .Funcs}}{{if .IsNotDuplicate}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}"){{end}}
{{end}}{{end}}
{{define "helperbody"}}

View File

@ -0,0 +1,432 @@
package hcsshim
import (
"encoding/json"
"errors"
"fmt"
"io"
"syscall"
"time"
"github.com/Sirupsen/logrus"
)
var (
ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")
)
type ProcessError struct {
Process *process
Operation string
Err error
}
type process struct {
handle hcsProcess
processID int
container *container
cachedPipes *cachedPipes
killCallbackNumber uintptr
}
type cachedPipes struct {
stdIn syscall.Handle
stdOut syscall.Handle
stdErr syscall.Handle
}
type processModifyRequest struct {
Operation string
ConsoleSize *consoleSize `json:",omitempty"`
CloseHandle *closeHandle `json:",omitempty"`
}
type consoleSize struct {
Height uint16
Width uint16
}
type closeHandle struct {
Handle string
}
type processStatus struct {
ProcessId uint32
Exited bool
ExitCode uint32
LastWaitResult int32
}
const (
stdIn string = "StdIn"
stdOut string = "StdOut"
stdErr string = "StdErr"
)
const (
modifyConsoleSize string = "ConsoleSize"
modifyCloseHandle string = "CloseHandle"
)
// Pid returns the process ID of the process within the container.
func (process *process) Pid() int {
return process.processID
}
// Kill signals the process to terminate but does not wait for it to finish terminating.
func (process *process) Kill() error {
operation := "Kill"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
var resultp *uint16
err := hcsTerminateProcess(process.handle, &resultp)
err = processHcsResult(err, resultp)
if err == ErrVmcomputeOperationPending {
return ErrVmcomputeOperationPending
} else if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return nil
}
// Wait waits for the process to exit.
func (process *process) Wait() error {
operation := "Wait"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
if hcsCallbacksSupported {
err := registerAndWaitForCallback(process, hcsNotificationProcessExited)
if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
} else {
_, err := process.waitTimeoutInternal(syscall.INFINITE)
if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
}
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return nil
}
// WaitTimeout waits for the process to exit or the duration to elapse. It returns
// false if timeout occurs.
func (process *process) WaitTimeout(timeout time.Duration) error {
operation := "WaitTimeout"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
if hcsCallbacksSupported {
err := registerAndWaitForCallbackTimeout(process, hcsNotificationProcessExited, timeout)
if err == ErrTimeout {
return ErrTimeout
} else if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
} else {
finished, err := waitTimeoutHelper(process, timeout)
if !finished {
return ErrTimeout
} else if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
}
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return nil
}
func (process *process) hcsWait(timeout uint32) (bool, error) {
var (
resultp *uint16
exitEvent syscall.Handle
)
err := hcsCreateProcessWait(process.handle, &exitEvent, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
return false, err
}
defer syscall.CloseHandle(exitEvent)
return waitForSingleObject(exitEvent, timeout)
}
func (process *process) waitTimeoutInternal(timeout uint32) (bool, error) {
return waitTimeoutInternalHelper(process, timeout)
}
// ExitCode returns the exit code of the process. The process must have
// already terminated.
func (process *process) ExitCode() (int, error) {
operation := "ExitCode"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
properties, err := process.properties()
if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return 0, err
}
if properties.Exited == false {
return 0, ErrInvalidProcessState
}
logrus.Debugf(title+" succeeded processid=%d exitCode=%d", process.processID, properties.ExitCode)
return int(properties.ExitCode), nil
}
// ResizeConsole resizes the console of the process.
func (process *process) ResizeConsole(width, height uint16) error {
operation := "ResizeConsole"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
modifyRequest := processModifyRequest{
Operation: modifyConsoleSize,
ConsoleSize: &consoleSize{
Height: height,
Width: width,
},
}
modifyRequestb, err := json.Marshal(modifyRequest)
if err != nil {
return err
}
modifyRequestStr := string(modifyRequestb)
var resultp *uint16
err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return nil
}
func (process *process) properties() (*processStatus, error) {
operation := "properties"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
var (
resultp *uint16
propertiesp *uint16
)
err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err := &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return nil, err
}
if propertiesp == nil {
return nil, errors.New("Unexpected result from hcsGetProcessProperties, properties should never be nil")
}
propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
properties := &processStatus{}
if err := json.Unmarshal(propertiesRaw, properties); err != nil {
return nil, err
}
logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw)
return properties, nil
}
// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
// these pipes does not close the underlying pipes; it should be possible to
// call this multiple times to get multiple interfaces.
func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
operation := "Stdio"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
var stdIn, stdOut, stdErr syscall.Handle
if process.cachedPipes == nil {
var (
processInfo hcsProcessInformation
resultp *uint16
)
err := hcsGetProcessInfo(process.handle, &processInfo, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err = &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return nil, nil, nil, err
}
stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError
} else {
// Use cached pipes
stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr
// Invalidate the cache
process.cachedPipes = nil
}
pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr})
if err != nil {
return nil, nil, nil, err
}
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return pipes[0], pipes[1], pipes[2], nil
}
// CloseStdin closes the write side of the stdin pipe so that the process is
// notified on the read side that there is no more data in stdin.
func (process *process) CloseStdin() error {
operation := "CloseStdin"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
modifyRequest := processModifyRequest{
Operation: modifyCloseHandle,
CloseHandle: &closeHandle{
Handle: stdIn,
},
}
modifyRequestb, err := json.Marshal(modifyRequest)
if err != nil {
return err
}
modifyRequestStr := string(modifyRequestb)
var resultp *uint16
err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
err = processHcsResult(err, resultp)
if err != nil {
err = &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return nil
}
// Close cleans up any state associated with the process but does not kill
// or wait on it.
func (process *process) Close() error {
operation := "Close"
title := "HCSShim::Process::" + operation
logrus.Debugf(title+" processid=%d", process.processID)
// Don't double free this
if process.handle == 0 {
return nil
}
if err := hcsCloseProcess(process.handle); err != nil {
err = &ProcessError{Operation: operation, Process: process, Err: err}
logrus.Error(err)
return err
}
process.handle = 0
logrus.Debugf(title+" succeeded processid=%d", process.processID)
return nil
}
// closeProcess wraps process.Close for use by a finalizer
func closeProcess(process *process) {
process.Close()
}
func (process *process) registerCallback(expectedNotification hcsNotification) (uintptr, error) {
callbackMapLock.Lock()
defer callbackMapLock.Unlock()
callbackNumber := nextCallback
nextCallback++
context := &notifcationWatcherContext{
expectedNotification: expectedNotification,
channel: make(chan error, 1),
}
callbackMap[callbackNumber] = context
var callbackHandle hcsCallback
err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
if err != nil {
return 0, err
}
context.handle = callbackHandle
return callbackNumber, nil
}
func (process *process) unregisterCallback(callbackNumber uintptr) error {
callbackMapLock.Lock()
defer callbackMapLock.Unlock()
handle := callbackMap[callbackNumber].handle
if handle == 0 {
return nil
}
err := hcsUnregisterProcessCallback(handle)
if err != nil {
return err
}
callbackMap[callbackNumber] = nil
handle = 0
return nil
}
func (e *ProcessError) Error() string {
if e == nil {
return "<nil>"
}
if e.Process == nil {
return "Unexpected nil process for error: " + e.Err.Error()
}
s := fmt.Sprintf("process %d", e.Process.processID)
if e.Process.container != nil {
s += " in container " + e.Process.container.id
}
if e.Operation != "" {
s += " " + e.Operation
}
if e.Err != nil {
s += fmt.Sprintf(" failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err))
}
return s
}

View File

@ -0,0 +1,11 @@
package hcsshim
import (
"syscall"
)
var (
vmcomputedll = syscall.NewLazyDLL("vmcompute.dll")
hcsCallbackAPI = vmcomputedll.NewProc("HcsRegisterComputeSystemCallback")
hcsCallbacksSupported = hcsCallbackAPI.Find() == nil
)

View File

@ -0,0 +1,113 @@
package hcsshim
import (
"syscall"
"time"
)
type waitable interface {
waitTimeoutInternal(timeout uint32) (bool, error)
hcsWait(timeout uint32) (bool, error)
}
type callbackable interface {
registerCallback(expectedNotification hcsNotification) (uintptr, error)
unregisterCallback(callbackNumber uintptr) error
}
func waitTimeoutHelper(object waitable, timeout time.Duration) (bool, error) {
var (
millis uint32
)
for totalMillis := uint64(timeout / time.Millisecond); totalMillis > 0; totalMillis = totalMillis - uint64(millis) {
if totalMillis >= syscall.INFINITE {
millis = syscall.INFINITE - 1
} else {
millis = uint32(totalMillis)
}
result, err := object.waitTimeoutInternal(millis)
if err != nil {
return result, err
}
}
return true, nil
}
func waitTimeoutInternalHelper(object waitable, timeout uint32) (bool, error) {
return object.hcsWait(timeout)
}
func waitForSingleObject(handle syscall.Handle, timeout uint32) (bool, error) {
s, e := syscall.WaitForSingleObject(handle, timeout)
switch s {
case syscall.WAIT_OBJECT_0:
return true, nil
case syscall.WAIT_TIMEOUT:
return false, nil
default:
return false, e
}
}
func processAsyncHcsResult(object callbackable, err error, resultp *uint16, expectedNotification hcsNotification, timeout *time.Duration) error {
err = processHcsResult(err, resultp)
if err == ErrVmcomputeOperationPending {
if timeout != nil {
err = registerAndWaitForCallbackTimeout(object, expectedNotification, *timeout)
} else {
err = registerAndWaitForCallback(object, expectedNotification)
}
}
return err
}
func registerAndWaitForCallbackTimeout(object callbackable, expectedNotification hcsNotification, timeout time.Duration) error {
callbackNumber, err := object.registerCallback(expectedNotification)
if err != nil {
return err
}
defer object.unregisterCallback(callbackNumber)
return waitForNotificationTimeout(callbackNumber, timeout)
}
func registerAndWaitForCallback(object callbackable, expectedNotification hcsNotification) error {
callbackNumber, err := object.registerCallback(expectedNotification)
if err != nil {
return err
}
defer object.unregisterCallback(callbackNumber)
return waitForNotification(callbackNumber)
}
func waitForNotificationTimeout(callbackNumber uintptr, timeout time.Duration) error {
callbackMapLock.RLock()
channel := callbackMap[callbackNumber].channel
callbackMapLock.RUnlock()
timer := time.NewTimer(timeout)
defer timer.Stop()
select {
case err := <-channel:
return err
case <-timer.C:
return ErrTimeout
}
}
func waitForNotification(callbackNumber uintptr) error {
callbackMapLock.RLock()
channel := callbackMap[callbackNumber].channel
callbackMapLock.RUnlock()
select {
case err := <-channel:
return err
}
}

View File

@ -2,11 +2,8 @@
package hcsshim
import (
"unsafe"
"github.com/Microsoft/go-winio"
)
import "github.com/Microsoft/go-winio"
import "unsafe"
import "syscall"
var _ unsafe.Pointer
@ -49,7 +46,34 @@ var (
procTerminateProcessInComputeSystem = modvmcompute.NewProc("TerminateProcessInComputeSystem")
procWaitForProcessInComputeSystem = modvmcompute.NewProc("WaitForProcessInComputeSystem")
procGetComputeSystemProperties = modvmcompute.NewProc("GetComputeSystemProperties")
procHNSCall = modvmcompute.NewProc("HNSCall")
procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems")
procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem")
procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem")
procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem")
procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem")
procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem")
procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem")
procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem")
procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem")
procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties")
procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem")
procHcsCreateComputeSystemWait = modvmcompute.NewProc("HcsCreateComputeSystemWait")
procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess")
procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess")
procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess")
procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess")
procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo")
procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties")
procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess")
procHcsCreateProcessWait = modvmcompute.NewProc("HcsCreateProcessWait")
procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties")
procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings")
procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback")
procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback")
procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback")
procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback")
procHNSCall = modvmcompute.NewProc("HNSCall")
)
func coTaskMemFree(buffer unsafe.Pointer) {
@ -734,6 +758,503 @@ func _getComputeSystemProperties(id *uint16, flags uint32, properties **uint16)
return
}
func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(query)
if hr != nil {
return
}
return _hcsEnumerateComputeSystems(_p0, computeSystems, result)
}
func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) {
if hr = procHcsEnumerateComputeSystems.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(id)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(configuration)
if hr != nil {
return
}
return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result)
}
func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) {
if hr = procHcsCreateComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(id)
if hr != nil {
return
}
return _hcsOpenComputeSystem(_p0, computeSystem, result)
}
func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16) (hr error) {
if hr = procHcsOpenComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) {
if hr = procHcsCloseComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(options)
if hr != nil {
return
}
return _hcsStartComputeSystem(computeSystem, _p0, result)
}
func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsStartComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(options)
if hr != nil {
return
}
return _hcsShutdownComputeSystem(computeSystem, _p0, result)
}
func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsShutdownComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(options)
if hr != nil {
return
}
return _hcsTerminateComputeSystem(computeSystem, _p0, result)
}
func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsTerminateComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(options)
if hr != nil {
return
}
return _hcsPauseComputeSystem(computeSystem, _p0, result)
}
func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsPauseComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(options)
if hr != nil {
return
}
return _hcsResumeComputeSystem(computeSystem, _p0, result)
}
func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsResumeComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(propertyQuery)
if hr != nil {
return
}
return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result)
}
func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) {
if hr = procHcsGetComputeSystemProperties.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(configuration)
if hr != nil {
return
}
return _hcsModifyComputeSystem(computeSystem, _p0, result)
}
func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, result **uint16) (hr error) {
if hr = procHcsModifyComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCreateComputeSystemWait(computeSystem hcsSystem, exitEvent *syscall.Handle, result **uint16) (hr error) {
if hr = procHcsCreateComputeSystemWait.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsCreateComputeSystemWait.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(exitEvent)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(processParameters)
if hr != nil {
return
}
return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result)
}
func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) {
if hr = procHcsCreateProcess.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) {
if hr = procHcsOpenProcess.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCloseProcess(process hcsProcess) (hr error) {
if hr = procHcsCloseProcess.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) {
if hr = procHcsTerminateProcess.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) {
if hr = procHcsGetProcessInfo.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) {
if hr = procHcsGetProcessProperties.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(settings)
if hr != nil {
return
}
return _hcsModifyProcess(process, _p0, result)
}
func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (hr error) {
if hr = procHcsModifyProcess.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCreateProcessWait(process hcsProcess, settings *syscall.Handle, result **uint16) (hr error) {
if hr = procHcsCreateProcessWait.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsCreateProcessWait.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(propertyQuery)
if hr != nil {
return
}
return _hcsGetServiceProperties(_p0, properties, result)
}
func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) {
if hr = procHcsGetServiceProperties.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsModifyServiceSettings(settings string, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(settings)
if hr != nil {
return
}
return _hcsModifyServiceSettings(_p0, result)
}
func _hcsModifyServiceSettings(settings *uint16, result **uint16) (hr error) {
if hr = procHcsModifyServiceSettings.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsModifyServiceSettings.Addr(), 2, uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)), 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsCreateComputeSystemTP5(id string, configuration string, computeSystem *hcsSystem, result **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(id)
if hr != nil {
return
}
var _p1 *uint16
_p1, hr = syscall.UTF16PtrFromString(configuration)
if hr != nil {
return
}
return _hcsCreateComputeSystemTP5(_p0, _p1, computeSystem, result)
}
func _hcsCreateComputeSystemTP5(id *uint16, configuration *uint16, computeSystem *hcsSystem, result **uint16) (hr error) {
if hr = procHcsCreateComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 4, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsStartComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsStartComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsShutdownComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsShutdownComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsTerminateComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsTerminateComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsPauseComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsPauseComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsResumeComputeSystemTP5(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
if hr = procHcsResumeComputeSystem.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) {
if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) {
if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) {
if hr = procHcsRegisterProcessCallback.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) {
if hr = procHcsUnregisterProcessCallback.Find(); hr != nil {
return
}
r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0)
if int32(r0) < 0 {
hr = syscall.Errno(win32FromHresult(r0))
}
return
}
func _hnsCall(method string, path string, object string, response **uint16) (hr error) {
var _p0 *uint16
_p0, hr = syscall.UTF16PtrFromString(method)