mirror of https://github.com/docker/docs.git
				
				
				
			Change Docker to use the new HCS RPC API
Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
		
							parent
							
								
									91bc4cca58
								
							
						
					
					
						commit
						959c1a52bf
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 := ¬ifcationWatcherContext{
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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", "")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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"}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 := ¬ifcationWatcherContext{
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
package hcsshim
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	vmcomputedll          = syscall.NewLazyDLL("vmcompute.dll")
 | 
			
		||||
	hcsCallbackAPI        = vmcomputedll.NewProc("HcsRegisterComputeSystemCallback")
 | 
			
		||||
	hcsCallbacksSupported = hcsCallbackAPI.Find() == nil
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue