mirror of https://github.com/docker/docs.git
				
				
				
			Merge pull request #22958 from Microsoft/hcs_rpc
Windows: Use the new HCS RPC API
This commit is contained in:
		
						commit
						c7ee503082
					
				|  | @ -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