github.com/moby/docker@v26.1.3+incompatible/libcontainerd/local/local_windows.go (about)

     1  package local // import "github.com/docker/docker/libcontainerd/local"
     2  
     3  // This package contains the legacy in-proc calls in HCS using the v1 schema
     4  // for Windows runtime purposes.
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"strings"
    14  	"sync"
    15  	"syscall"
    16  	"time"
    17  
    18  	"github.com/Microsoft/hcsshim"
    19  	"github.com/containerd/containerd"
    20  	"github.com/containerd/containerd/cio"
    21  	cerrdefs "github.com/containerd/containerd/errdefs"
    22  	"github.com/containerd/log"
    23  	"github.com/docker/docker/errdefs"
    24  	"github.com/docker/docker/libcontainerd/queue"
    25  	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
    26  	"github.com/docker/docker/pkg/sysinfo"
    27  	"github.com/docker/docker/pkg/system"
    28  	specs "github.com/opencontainers/runtime-spec/specs-go"
    29  	"github.com/pkg/errors"
    30  	"golang.org/x/sys/windows"
    31  )
    32  
    33  type process struct {
    34  	// mu guards the mutable fields of this struct.
    35  	//
    36  	// Always lock mu before ctr's mutex to prevent deadlocks.
    37  	mu         sync.Mutex
    38  	id         string                 // Invariants: immutable
    39  	ctr        *container             // Invariants: immutable, ctr != nil
    40  	hcsProcess hcsshim.Process        // Is set to nil on process exit
    41  	exited     *containerd.ExitStatus // Valid iff waitCh is closed
    42  	waitCh     chan struct{}
    43  }
    44  
    45  type task struct {
    46  	process
    47  }
    48  
    49  type container struct {
    50  	mu sync.Mutex
    51  
    52  	// The ociSpec is required, as client.Create() needs a spec, but can
    53  	// be called from the RestartManager context which does not otherwise
    54  	// have access to the Spec
    55  	//
    56  	// A container value with ociSpec == nil represents a container which
    57  	// has been loaded with (*client).LoadContainer, and is ineligible to
    58  	// be Start()ed.
    59  	ociSpec *specs.Spec
    60  
    61  	hcsContainer hcsshim.Container // Is set to nil on container delete
    62  	isPaused     bool
    63  
    64  	client           *client
    65  	id               string
    66  	terminateInvoked bool
    67  
    68  	// task is a reference to the current task for the container. As a
    69  	// corollary, when task == nil the container has no current task: the
    70  	// container was never Start()ed or the task was Delete()d.
    71  	task *task
    72  }
    73  
    74  // defaultOwner is a tag passed to HCS to allow it to differentiate between
    75  // container creator management stacks. We hard code "docker" in the case
    76  // of docker.
    77  const defaultOwner = "docker"
    78  
    79  type client struct {
    80  	stateDir string
    81  	backend  libcontainerdtypes.Backend
    82  	logger   *log.Entry
    83  	eventQ   queue.Queue
    84  }
    85  
    86  // NewClient creates a new local executor for windows
    87  func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
    88  	c := &client{
    89  		stateDir: stateDir,
    90  		backend:  b,
    91  		logger:   log.G(ctx).WithField("module", "libcontainerd").WithField("namespace", ns),
    92  	}
    93  
    94  	return c, nil
    95  }
    96  
    97  func (c *client) Version(ctx context.Context) (containerd.Version, error) {
    98  	return containerd.Version{}, errors.New("not implemented on Windows")
    99  }
   100  
   101  // NewContainer is the entrypoint to create a container from a spec.
   102  // Table below shows the fields required for HCS JSON calling parameters,
   103  // where if not populated, is omitted.
   104  // +-----------------+--------------------------------------------+---------------------------------------------------+
   105  // |                 | Isolation=Process                          | Isolation=Hyper-V                                 |
   106  // +-----------------+--------------------------------------------+---------------------------------------------------+
   107  // | VolumePath      | \\?\\Volume{GUIDa}                         |                                                   |
   108  // | LayerFolderPath | %root%\windowsfilter\containerID           |                                                   |
   109  // | Layers[]        | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID        |
   110  // | HvRuntime       |                                            | ImagePath=%root%\BaseLayerID\UtilityVM            |
   111  // +-----------------+--------------------------------------------+---------------------------------------------------+
   112  //
   113  // Isolation=Process example:
   114  //
   115  //	{
   116  //		"SystemType": "Container",
   117  //		"Name": "5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
   118  //		"Owner": "docker",
   119  //		"VolumePath": "\\\\\\\\?\\\\Volume{66d1ef4c-7a00-11e6-8948-00155ddbef9d}",
   120  //		"IgnoreFlushesDuringBoot": true,
   121  //		"LayerFolderPath": "C:\\\\control\\\\windowsfilter\\\\5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
   122  //		"Layers": [{
   123  //			"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
   124  //			"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
   125  //		}],
   126  //		"HostName": "5e0055c814a6",
   127  //		"MappedDirectories": [],
   128  //		"HvPartition": false,
   129  //		"EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
   130  //	}
   131  //
   132  // Isolation=Hyper-V example:
   133  //
   134  //	{
   135  //		"SystemType": "Container",
   136  //		"Name": "475c2c58933b72687a88a441e7e0ca4bd72d76413c5f9d5031fee83b98f6045d",
   137  //		"Owner": "docker",
   138  //		"IgnoreFlushesDuringBoot": true,
   139  //		"Layers": [{
   140  //			"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
   141  //			"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
   142  //		}],
   143  //		"HostName": "475c2c58933b",
   144  //		"MappedDirectories": [],
   145  //		"HvPartition": true,
   146  //		"EndpointList": ["e1bb1e61-d56f-405e-b75d-fd520cefa0cb"],
   147  //		"DNSSearchList": "a.com,b.com,c.com",
   148  //		"HvRuntime": {
   149  //			"ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
   150  //		},
   151  //	}
   152  func (c *client) NewContainer(_ context.Context, id string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) (libcontainerdtypes.Container, error) {
   153  	var err error
   154  	if spec.Linux != nil {
   155  		return nil, errors.New("linux containers are not supported on this platform")
   156  	}
   157  	ctr, err := c.createWindows(id, spec, runtimeOptions)
   158  
   159  	if err == nil {
   160  		c.eventQ.Append(id, func() {
   161  			ei := libcontainerdtypes.EventInfo{
   162  				ContainerID: id,
   163  			}
   164  			c.logger.WithFields(log.Fields{
   165  				"container": id,
   166  				"event":     libcontainerdtypes.EventCreate,
   167  			}).Info("sending event")
   168  			err := c.backend.ProcessEvent(id, libcontainerdtypes.EventCreate, ei)
   169  			if err != nil {
   170  				c.logger.WithError(err).WithFields(log.Fields{
   171  					"container": id,
   172  					"event":     libcontainerdtypes.EventCreate,
   173  				}).Error("failed to process event")
   174  			}
   175  		})
   176  	}
   177  	return ctr, err
   178  }
   179  
   180  func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions interface{}) (*container, error) {
   181  	logger := c.logger.WithField("container", id)
   182  	configuration := &hcsshim.ContainerConfig{
   183  		SystemType:              "Container",
   184  		Name:                    id,
   185  		Owner:                   defaultOwner,
   186  		IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
   187  		HostName:                spec.Hostname,
   188  		HvPartition:             false,
   189  	}
   190  
   191  	c.extractResourcesFromSpec(spec, configuration)
   192  
   193  	if spec.Windows.Resources != nil {
   194  		if spec.Windows.Resources.Storage != nil {
   195  			if spec.Windows.Resources.Storage.Bps != nil {
   196  				configuration.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
   197  			}
   198  			if spec.Windows.Resources.Storage.Iops != nil {
   199  				configuration.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
   200  			}
   201  		}
   202  	}
   203  
   204  	if spec.Windows.HyperV != nil {
   205  		configuration.HvPartition = true
   206  	}
   207  
   208  	if spec.Windows.Network != nil {
   209  		configuration.EndpointList = spec.Windows.Network.EndpointList
   210  		configuration.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
   211  		if spec.Windows.Network.DNSSearchList != nil {
   212  			configuration.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
   213  		}
   214  		configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
   215  	}
   216  
   217  	if cs, ok := spec.Windows.CredentialSpec.(string); ok {
   218  		configuration.Credentials = cs
   219  	}
   220  
   221  	// We must have least two layers in the spec, the bottom one being a
   222  	// base image, the top one being the RW layer.
   223  	if spec.Windows.LayerFolders == nil || len(spec.Windows.LayerFolders) < 2 {
   224  		return nil, fmt.Errorf("OCI spec is invalid - at least two LayerFolders must be supplied to the runtime")
   225  	}
   226  
   227  	// Strip off the top-most layer as that's passed in separately to HCS
   228  	configuration.LayerFolderPath = spec.Windows.LayerFolders[len(spec.Windows.LayerFolders)-1]
   229  	layerFolders := spec.Windows.LayerFolders[:len(spec.Windows.LayerFolders)-1]
   230  
   231  	if configuration.HvPartition {
   232  		// We don't currently support setting the utility VM image explicitly.
   233  		// TODO circa RS5, this may be re-locatable.
   234  		if spec.Windows.HyperV.UtilityVMPath != "" {
   235  			return nil, errors.New("runtime does not support an explicit utility VM path for Hyper-V containers")
   236  		}
   237  
   238  		// Find the upper-most utility VM image.
   239  		var uvmImagePath string
   240  		for _, path := range layerFolders {
   241  			fullPath := filepath.Join(path, "UtilityVM")
   242  			_, err := os.Stat(fullPath)
   243  			if err == nil {
   244  				uvmImagePath = fullPath
   245  				break
   246  			}
   247  			if !os.IsNotExist(err) {
   248  				return nil, err
   249  			}
   250  		}
   251  		if uvmImagePath == "" {
   252  			return nil, errors.New("utility VM image could not be found")
   253  		}
   254  		configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: uvmImagePath}
   255  
   256  		if spec.Root.Path != "" {
   257  			return nil, errors.New("OCI spec is invalid - Root.Path must be omitted for a Hyper-V container")
   258  		}
   259  	} else {
   260  		const volumeGUIDRegex = `^\\\\\?\\(Volume)\{{0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}\}\\$`
   261  		if _, err := regexp.MatchString(volumeGUIDRegex, spec.Root.Path); err != nil {
   262  			return nil, fmt.Errorf(`OCI spec is invalid - Root.Path '%s' must be a volume GUID path in the format '\\?\Volume{GUID}\'`, spec.Root.Path)
   263  		}
   264  		// HCS API requires the trailing backslash to be removed
   265  		configuration.VolumePath = spec.Root.Path[:len(spec.Root.Path)-1]
   266  	}
   267  
   268  	if spec.Root.Readonly {
   269  		return nil, errors.New(`OCI spec is invalid - Root.Readonly must not be set on Windows`)
   270  	}
   271  
   272  	for _, layerPath := range layerFolders {
   273  		_, filename := filepath.Split(layerPath)
   274  		g, err := hcsshim.NameToGuid(filename)
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  		configuration.Layers = append(configuration.Layers, hcsshim.Layer{
   279  			ID:   g.ToString(),
   280  			Path: layerPath,
   281  		})
   282  	}
   283  
   284  	// Add the mounts (volumes, bind mounts etc) to the structure
   285  	var mds []hcsshim.MappedDir
   286  	var mps []hcsshim.MappedPipe
   287  	for _, mount := range spec.Mounts {
   288  		const pipePrefix = `\\.\pipe\`
   289  		if mount.Type != "" {
   290  			return nil, fmt.Errorf("OCI spec is invalid - Mount.Type '%s' must not be set", mount.Type)
   291  		}
   292  		if strings.HasPrefix(mount.Destination, pipePrefix) {
   293  			mp := hcsshim.MappedPipe{
   294  				HostPath:          mount.Source,
   295  				ContainerPipeName: mount.Destination[len(pipePrefix):],
   296  			}
   297  			mps = append(mps, mp)
   298  		} else {
   299  			md := hcsshim.MappedDir{
   300  				HostPath:      mount.Source,
   301  				ContainerPath: mount.Destination,
   302  				ReadOnly:      false,
   303  			}
   304  			for _, o := range mount.Options {
   305  				if strings.ToLower(o) == "ro" {
   306  					md.ReadOnly = true
   307  				}
   308  			}
   309  			mds = append(mds, md)
   310  		}
   311  	}
   312  	configuration.MappedDirectories = mds
   313  	configuration.MappedPipes = mps
   314  
   315  	if len(spec.Windows.Devices) > 0 {
   316  		// Add any device assignments
   317  		if configuration.HvPartition {
   318  			return nil, errors.New("device assignment is not supported for HyperV containers")
   319  		}
   320  		for _, d := range spec.Windows.Devices {
   321  			// Per https://github.com/microsoft/hcsshim/blob/v0.9.2/internal/uvm/virtual_device.go#L17-L18,
   322  			// these represent an Interface Class GUID.
   323  			if d.IDType != "class" && d.IDType != "vpci-class-guid" {
   324  				return nil, errors.Errorf("device assignment of type '%s' is not supported", d.IDType)
   325  			}
   326  			configuration.AssignedDevices = append(configuration.AssignedDevices, hcsshim.AssignedDevice{InterfaceClassGUID: d.ID})
   327  		}
   328  	}
   329  
   330  	hcsContainer, err := hcsshim.CreateContainer(id, configuration)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	// Construct a container object for calling start on it.
   336  	ctr := &container{
   337  		client:       c,
   338  		id:           id,
   339  		ociSpec:      spec,
   340  		hcsContainer: hcsContainer,
   341  	}
   342  
   343  	logger.Debug("starting container")
   344  	if err := ctr.hcsContainer.Start(); err != nil {
   345  		logger.WithError(err).Error("failed to start container")
   346  		ctr.mu.Lock()
   347  		if err := ctr.terminateContainer(); err != nil {
   348  			logger.WithError(err).Error("failed to cleanup after a failed Start")
   349  		} else {
   350  			logger.Debug("cleaned up after failed Start by calling Terminate")
   351  		}
   352  		ctr.mu.Unlock()
   353  		return nil, err
   354  	}
   355  
   356  	logger.Debug("createWindows() completed successfully")
   357  	return ctr, nil
   358  }
   359  
   360  func (c *client) extractResourcesFromSpec(spec *specs.Spec, configuration *hcsshim.ContainerConfig) {
   361  	if spec.Windows.Resources != nil {
   362  		if spec.Windows.Resources.CPU != nil {
   363  			if spec.Windows.Resources.CPU.Count != nil {
   364  				// This check is being done here rather than in adaptContainerSettings
   365  				// because we don't want to update the HostConfig in case this container
   366  				// is moved to a host with more CPUs than this one.
   367  				cpuCount := *spec.Windows.Resources.CPU.Count
   368  				hostCPUCount := uint64(sysinfo.NumCPU())
   369  				if cpuCount > hostCPUCount {
   370  					c.logger.Warnf("Changing requested CPUCount of %d to current number of processors, %d", cpuCount, hostCPUCount)
   371  					cpuCount = hostCPUCount
   372  				}
   373  				configuration.ProcessorCount = uint32(cpuCount)
   374  			}
   375  			if spec.Windows.Resources.CPU.Shares != nil {
   376  				configuration.ProcessorWeight = uint64(*spec.Windows.Resources.CPU.Shares)
   377  			}
   378  			if spec.Windows.Resources.CPU.Maximum != nil {
   379  				configuration.ProcessorMaximum = int64(*spec.Windows.Resources.CPU.Maximum)
   380  			}
   381  		}
   382  		if spec.Windows.Resources.Memory != nil {
   383  			if spec.Windows.Resources.Memory.Limit != nil {
   384  				configuration.MemoryMaximumInMB = int64(*spec.Windows.Resources.Memory.Limit) / 1024 / 1024
   385  			}
   386  		}
   387  	}
   388  }
   389  
   390  func (ctr *container) NewTask(_ context.Context, _ string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (_ libcontainerdtypes.Task, retErr error) {
   391  	ctr.mu.Lock()
   392  	defer ctr.mu.Unlock()
   393  
   394  	switch {
   395  	case ctr.ociSpec == nil:
   396  		return nil, errors.WithStack(errdefs.NotImplemented(errors.New("a restored container cannot be started")))
   397  	case ctr.task != nil:
   398  		return nil, errors.WithStack(errdefs.NotModified(cerrdefs.ErrAlreadyExists))
   399  	}
   400  
   401  	logger := ctr.client.logger.WithField("container", ctr.id)
   402  
   403  	// Note we always tell HCS to create stdout as it's required
   404  	// regardless of '-i' or '-t' options, so that docker can always grab
   405  	// the output through logs. We also tell HCS to always create stdin,
   406  	// even if it's not used - it will be closed shortly. Stderr is only
   407  	// created if it we're not -t.
   408  	var (
   409  		emulateConsole   bool
   410  		createStdErrPipe bool
   411  	)
   412  	if ctr.ociSpec.Process != nil {
   413  		emulateConsole = ctr.ociSpec.Process.Terminal
   414  		createStdErrPipe = !ctr.ociSpec.Process.Terminal
   415  	}
   416  
   417  	createProcessParms := &hcsshim.ProcessConfig{
   418  		EmulateConsole:   emulateConsole,
   419  		WorkingDirectory: ctr.ociSpec.Process.Cwd,
   420  		CreateStdInPipe:  true,
   421  		CreateStdOutPipe: true,
   422  		CreateStdErrPipe: createStdErrPipe,
   423  	}
   424  
   425  	if ctr.ociSpec.Process != nil && ctr.ociSpec.Process.ConsoleSize != nil {
   426  		createProcessParms.ConsoleSize[0] = uint(ctr.ociSpec.Process.ConsoleSize.Height)
   427  		createProcessParms.ConsoleSize[1] = uint(ctr.ociSpec.Process.ConsoleSize.Width)
   428  	}
   429  
   430  	// Configure the environment for the process
   431  	createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
   432  
   433  	// Configure the CommandLine/CommandArgs
   434  	setCommandLineAndArgs(ctr.ociSpec.Process, createProcessParms)
   435  	logger.Debugf("start commandLine: %s", createProcessParms.CommandLine)
   436  
   437  	createProcessParms.User = ctr.ociSpec.Process.User.Username
   438  
   439  	// Start the command running in the container.
   440  	newProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
   441  	if err != nil {
   442  		logger.WithError(err).Error("CreateProcess() failed")
   443  		return nil, err
   444  	}
   445  
   446  	defer func() {
   447  		if retErr != nil {
   448  			if err := newProcess.Kill(); err != nil {
   449  				logger.WithError(err).Error("failed to kill process")
   450  			}
   451  			go func() {
   452  				if err := newProcess.Wait(); err != nil {
   453  					logger.WithError(err).Error("failed to wait for process")
   454  				}
   455  				if err := newProcess.Close(); err != nil {
   456  					logger.WithError(err).Error("failed to clean process resources")
   457  				}
   458  			}()
   459  		}
   460  	}()
   461  
   462  	pid := newProcess.Pid()
   463  	logger.WithField("pid", pid).Debug("init process started")
   464  
   465  	dio, err := newIOFromProcess(newProcess, ctr.ociSpec.Process.Terminal)
   466  	if err != nil {
   467  		logger.WithError(err).Error("failed to get stdio pipes")
   468  		return nil, err
   469  	}
   470  	_, err = attachStdio(dio)
   471  	if err != nil {
   472  		logger.WithError(err).Error("failed to attach stdio")
   473  		return nil, err
   474  	}
   475  
   476  	t := &task{process{
   477  		id:         ctr.id,
   478  		ctr:        ctr,
   479  		hcsProcess: newProcess,
   480  		waitCh:     make(chan struct{}),
   481  	}}
   482  
   483  	// All fallible operations have succeeded so it is now safe to set the
   484  	// container's current task.
   485  	ctr.task = t
   486  
   487  	// Spin up a goroutine to notify the backend and clean up resources when
   488  	// the task exits. Defer until after the start event is sent so that the
   489  	// exit event is not sent out-of-order.
   490  	defer func() { go t.reap() }()
   491  
   492  	// Generate the associated event
   493  	ctr.client.eventQ.Append(ctr.id, func() {
   494  		ei := libcontainerdtypes.EventInfo{
   495  			ContainerID: ctr.id,
   496  			ProcessID:   t.id,
   497  			Pid:         uint32(pid),
   498  		}
   499  		ctr.client.logger.WithFields(log.Fields{
   500  			"container":  ctr.id,
   501  			"event":      libcontainerdtypes.EventStart,
   502  			"event-info": ei,
   503  		}).Info("sending event")
   504  		err := ctr.client.backend.ProcessEvent(ei.ContainerID, libcontainerdtypes.EventStart, ei)
   505  		if err != nil {
   506  			ctr.client.logger.WithError(err).WithFields(log.Fields{
   507  				"container":  ei.ContainerID,
   508  				"event":      libcontainerdtypes.EventStart,
   509  				"event-info": ei,
   510  			}).Error("failed to process event")
   511  		}
   512  	})
   513  	logger.Debug("start() completed")
   514  	return t, nil
   515  }
   516  
   517  func (*task) Start(context.Context) error {
   518  	// No-op on Windows.
   519  	return nil
   520  }
   521  
   522  func (ctr *container) Task(context.Context) (libcontainerdtypes.Task, error) {
   523  	ctr.mu.Lock()
   524  	defer ctr.mu.Unlock()
   525  	if ctr.task == nil {
   526  		return nil, errdefs.NotFound(cerrdefs.ErrNotFound)
   527  	}
   528  	return ctr.task, nil
   529  }
   530  
   531  // setCommandLineAndArgs configures the HCS ProcessConfig based on an OCI process spec
   532  func setCommandLineAndArgs(process *specs.Process, createProcessParms *hcsshim.ProcessConfig) {
   533  	if process.CommandLine != "" {
   534  		createProcessParms.CommandLine = process.CommandLine
   535  	} else {
   536  		createProcessParms.CommandLine = system.EscapeArgs(process.Args)
   537  	}
   538  }
   539  
   540  func newIOFromProcess(newProcess hcsshim.Process, terminal bool) (*cio.DirectIO, error) {
   541  	stdin, stdout, stderr, err := newProcess.Stdio()
   542  	if err != nil {
   543  		return nil, err
   544  	}
   545  
   546  	dio := cio.NewDirectIO(createStdInCloser(stdin, newProcess), nil, nil, terminal)
   547  
   548  	// Convert io.ReadClosers to io.Readers
   549  	if stdout != nil {
   550  		dio.Stdout = io.NopCloser(&autoClosingReader{ReadCloser: stdout})
   551  	}
   552  	if stderr != nil {
   553  		dio.Stderr = io.NopCloser(&autoClosingReader{ReadCloser: stderr})
   554  	}
   555  	return dio, nil
   556  }
   557  
   558  // Exec launches a process in a running container.
   559  //
   560  // The processID argument is entirely informational. As there is no mechanism
   561  // (exposed through the libcontainerd interfaces) to enumerate or reference an
   562  // exec'd process by ID, uniqueness is not currently enforced.
   563  func (t *task) Exec(ctx context.Context, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (_ libcontainerdtypes.Process, retErr error) {
   564  	hcsContainer, err := t.getHCSContainer()
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  	logger := t.ctr.client.logger.WithFields(log.Fields{
   569  		"container": t.ctr.id,
   570  		"exec":      processID,
   571  	})
   572  
   573  	// Note we always tell HCS to
   574  	// create stdout as it's required regardless of '-i' or '-t' options, so that
   575  	// docker can always grab the output through logs. We also tell HCS to always
   576  	// create stdin, even if it's not used - it will be closed shortly. Stderr
   577  	// is only created if it we're not -t.
   578  	createProcessParms := &hcsshim.ProcessConfig{
   579  		CreateStdInPipe:  true,
   580  		CreateStdOutPipe: true,
   581  		CreateStdErrPipe: !spec.Terminal,
   582  	}
   583  	if spec.Terminal {
   584  		createProcessParms.EmulateConsole = true
   585  		if spec.ConsoleSize != nil {
   586  			createProcessParms.ConsoleSize[0] = uint(spec.ConsoleSize.Height)
   587  			createProcessParms.ConsoleSize[1] = uint(spec.ConsoleSize.Width)
   588  		}
   589  	}
   590  
   591  	// Take working directory from the process to add if it is defined,
   592  	// otherwise take from the first process.
   593  	if spec.Cwd != "" {
   594  		createProcessParms.WorkingDirectory = spec.Cwd
   595  	} else {
   596  		createProcessParms.WorkingDirectory = t.ctr.ociSpec.Process.Cwd
   597  	}
   598  
   599  	// Configure the environment for the process
   600  	createProcessParms.Environment = setupEnvironmentVariables(spec.Env)
   601  
   602  	// Configure the CommandLine/CommandArgs
   603  	setCommandLineAndArgs(spec, createProcessParms)
   604  	logger.Debugf("exec commandLine: %s", createProcessParms.CommandLine)
   605  
   606  	createProcessParms.User = spec.User.Username
   607  
   608  	// Start the command running in the container.
   609  	newProcess, err := hcsContainer.CreateProcess(createProcessParms)
   610  	if err != nil {
   611  		logger.WithError(err).Errorf("exec's CreateProcess() failed")
   612  		return nil, err
   613  	}
   614  	defer func() {
   615  		if retErr != nil {
   616  			if err := newProcess.Kill(); err != nil {
   617  				logger.WithError(err).Error("failed to kill process")
   618  			}
   619  			go func() {
   620  				if err := newProcess.Wait(); err != nil {
   621  					logger.WithError(err).Error("failed to wait for process")
   622  				}
   623  				if err := newProcess.Close(); err != nil {
   624  					logger.WithError(err).Error("failed to clean process resources")
   625  				}
   626  			}()
   627  		}
   628  	}()
   629  
   630  	dio, err := newIOFromProcess(newProcess, spec.Terminal)
   631  	if err != nil {
   632  		logger.WithError(err).Error("failed to get stdio pipes")
   633  		return nil, err
   634  	}
   635  	// Tell the engine to attach streams back to the client
   636  	_, err = attachStdio(dio)
   637  	if err != nil {
   638  		return nil, err
   639  	}
   640  
   641  	p := &process{
   642  		id:         processID,
   643  		ctr:        t.ctr,
   644  		hcsProcess: newProcess,
   645  		waitCh:     make(chan struct{}),
   646  	}
   647  
   648  	// Spin up a goroutine to notify the backend and clean up resources when
   649  	// the process exits. Defer until after the start event is sent so that
   650  	// the exit event is not sent out-of-order.
   651  	defer func() { go p.reap() }()
   652  
   653  	pid := newProcess.Pid()
   654  	t.ctr.client.eventQ.Append(t.ctr.id, func() {
   655  		ei := libcontainerdtypes.EventInfo{
   656  			ContainerID: t.ctr.id,
   657  			ProcessID:   p.id,
   658  			Pid:         uint32(pid),
   659  		}
   660  		t.ctr.client.logger.WithFields(log.Fields{
   661  			"container":  t.ctr.id,
   662  			"event":      libcontainerdtypes.EventExecAdded,
   663  			"event-info": ei,
   664  		}).Info("sending event")
   665  		err := t.ctr.client.backend.ProcessEvent(t.ctr.id, libcontainerdtypes.EventExecAdded, ei)
   666  		if err != nil {
   667  			t.ctr.client.logger.WithError(err).WithFields(log.Fields{
   668  				"container":  t.ctr.id,
   669  				"event":      libcontainerdtypes.EventExecAdded,
   670  				"event-info": ei,
   671  			}).Error("failed to process event")
   672  		}
   673  		err = t.ctr.client.backend.ProcessEvent(t.ctr.id, libcontainerdtypes.EventExecStarted, ei)
   674  		if err != nil {
   675  			t.ctr.client.logger.WithError(err).WithFields(log.Fields{
   676  				"container":  t.ctr.id,
   677  				"event":      libcontainerdtypes.EventExecStarted,
   678  				"event-info": ei,
   679  			}).Error("failed to process event")
   680  		}
   681  	})
   682  
   683  	return p, nil
   684  }
   685  
   686  func (p *process) Pid() uint32 {
   687  	p.mu.Lock()
   688  	hcsProcess := p.hcsProcess
   689  	p.mu.Unlock()
   690  	if hcsProcess == nil {
   691  		return 0
   692  	}
   693  	return uint32(hcsProcess.Pid())
   694  }
   695  
   696  func (p *process) Kill(_ context.Context, signal syscall.Signal) error {
   697  	p.mu.Lock()
   698  	hcsProcess := p.hcsProcess
   699  	p.mu.Unlock()
   700  	if hcsProcess == nil {
   701  		return errors.WithStack(errdefs.NotFound(errors.New("process not found")))
   702  	}
   703  	return hcsProcess.Kill()
   704  }
   705  
   706  // Kill handles `docker stop` on Windows. While Linux has support for
   707  // the full range of signals, signals aren't really implemented on Windows.
   708  // We fake supporting regular stop and -9 to force kill.
   709  func (t *task) Kill(_ context.Context, signal syscall.Signal) error {
   710  	hcsContainer, err := t.getHCSContainer()
   711  	if err != nil {
   712  		return err
   713  	}
   714  
   715  	logger := t.ctr.client.logger.WithFields(log.Fields{
   716  		"container": t.ctr.id,
   717  		"process":   t.id,
   718  		"pid":       t.Pid(),
   719  		"signal":    signal,
   720  	})
   721  	logger.Debug("Signal()")
   722  
   723  	var op string
   724  	if signal == syscall.SIGKILL {
   725  		// Terminate the compute system
   726  		t.ctr.mu.Lock()
   727  		t.ctr.terminateInvoked = true
   728  		t.ctr.mu.Unlock()
   729  		op, err = "terminate", hcsContainer.Terminate()
   730  	} else {
   731  		// Shut down the container
   732  		op, err = "shutdown", hcsContainer.Shutdown()
   733  	}
   734  	if err != nil {
   735  		if !hcsshim.IsPending(err) && !hcsshim.IsAlreadyStopped(err) {
   736  			// ignore errors
   737  			logger.WithError(err).Errorf("failed to %s hccshim container", op)
   738  		}
   739  	}
   740  
   741  	return nil
   742  }
   743  
   744  // Resize handles a CLI event to resize an interactive docker run or docker
   745  // exec window.
   746  func (p *process) Resize(_ context.Context, width, height uint32) error {
   747  	p.mu.Lock()
   748  	hcsProcess := p.hcsProcess
   749  	p.mu.Unlock()
   750  	if hcsProcess == nil {
   751  		return errors.WithStack(errdefs.NotFound(errors.New("process not found")))
   752  	}
   753  
   754  	p.ctr.client.logger.WithFields(log.Fields{
   755  		"container": p.ctr.id,
   756  		"process":   p.id,
   757  		"height":    height,
   758  		"width":     width,
   759  		"pid":       hcsProcess.Pid(),
   760  	}).Debug("resizing")
   761  	return hcsProcess.ResizeConsole(uint16(width), uint16(height))
   762  }
   763  
   764  func (p *process) CloseStdin(context.Context) error {
   765  	p.mu.Lock()
   766  	hcsProcess := p.hcsProcess
   767  	p.mu.Unlock()
   768  	if hcsProcess == nil {
   769  		return errors.WithStack(errdefs.NotFound(errors.New("process not found")))
   770  	}
   771  
   772  	return hcsProcess.CloseStdin()
   773  }
   774  
   775  // Pause handles pause requests for containers
   776  func (t *task) Pause(_ context.Context) error {
   777  	if t.ctr.ociSpec.Windows.HyperV == nil {
   778  		return cerrdefs.ErrNotImplemented
   779  	}
   780  
   781  	t.ctr.mu.Lock()
   782  	defer t.ctr.mu.Unlock()
   783  
   784  	if err := t.assertIsCurrentTask(); err != nil {
   785  		return err
   786  	}
   787  	if t.ctr.hcsContainer == nil {
   788  		return errdefs.NotFound(errors.WithStack(fmt.Errorf("container %q not found", t.ctr.id)))
   789  	}
   790  	if err := t.ctr.hcsContainer.Pause(); err != nil {
   791  		return err
   792  	}
   793  
   794  	t.ctr.isPaused = true
   795  
   796  	t.ctr.client.eventQ.Append(t.ctr.id, func() {
   797  		err := t.ctr.client.backend.ProcessEvent(t.ctr.id, libcontainerdtypes.EventPaused, libcontainerdtypes.EventInfo{
   798  			ContainerID: t.ctr.id,
   799  			ProcessID:   t.id,
   800  		})
   801  		t.ctr.client.logger.WithFields(log.Fields{
   802  			"container": t.ctr.id,
   803  			"event":     libcontainerdtypes.EventPaused,
   804  		}).Info("sending event")
   805  		if err != nil {
   806  			t.ctr.client.logger.WithError(err).WithFields(log.Fields{
   807  				"container": t.ctr.id,
   808  				"event":     libcontainerdtypes.EventPaused,
   809  			}).Error("failed to process event")
   810  		}
   811  	})
   812  
   813  	return nil
   814  }
   815  
   816  // Resume handles resume requests for containers
   817  func (t *task) Resume(ctx context.Context) error {
   818  	if t.ctr.ociSpec.Windows.HyperV == nil {
   819  		return errors.New("cannot resume Windows Server Containers")
   820  	}
   821  
   822  	t.ctr.mu.Lock()
   823  	defer t.ctr.mu.Unlock()
   824  
   825  	if err := t.assertIsCurrentTask(); err != nil {
   826  		return err
   827  	}
   828  	if t.ctr.hcsContainer == nil {
   829  		return errdefs.NotFound(errors.WithStack(fmt.Errorf("container %q not found", t.ctr.id)))
   830  	}
   831  	if err := t.ctr.hcsContainer.Resume(); err != nil {
   832  		return err
   833  	}
   834  
   835  	t.ctr.isPaused = false
   836  
   837  	t.ctr.client.eventQ.Append(t.ctr.id, func() {
   838  		err := t.ctr.client.backend.ProcessEvent(t.ctr.id, libcontainerdtypes.EventResumed, libcontainerdtypes.EventInfo{
   839  			ContainerID: t.ctr.id,
   840  			ProcessID:   t.id,
   841  		})
   842  		t.ctr.client.logger.WithFields(log.Fields{
   843  			"container": t.ctr.id,
   844  			"event":     libcontainerdtypes.EventResumed,
   845  		}).Info("sending event")
   846  		if err != nil {
   847  			t.ctr.client.logger.WithError(err).WithFields(log.Fields{
   848  				"container": t.ctr.id,
   849  				"event":     libcontainerdtypes.EventResumed,
   850  			}).Error("failed to process event")
   851  		}
   852  	})
   853  
   854  	return nil
   855  }
   856  
   857  // Stats handles stats requests for containers
   858  func (t *task) Stats(_ context.Context) (*libcontainerdtypes.Stats, error) {
   859  	hc, err := t.getHCSContainer()
   860  	if err != nil {
   861  		return nil, err
   862  	}
   863  
   864  	readAt := time.Now()
   865  	s, err := hc.Statistics()
   866  	if err != nil {
   867  		return nil, err
   868  	}
   869  	return &libcontainerdtypes.Stats{
   870  		Read:     readAt,
   871  		HCSStats: &s,
   872  	}, nil
   873  }
   874  
   875  // LoadContainer is the handler for restoring a container
   876  func (c *client) LoadContainer(ctx context.Context, id string) (libcontainerdtypes.Container, error) {
   877  	c.logger.WithField("container", id).Debug("LoadContainer()")
   878  
   879  	// TODO Windows: On RS1, a re-attach isn't possible.
   880  	// However, there is a scenario in which there is an issue.
   881  	// Consider a background container. The daemon dies unexpectedly.
   882  	// HCS will still have the compute service alive and running.
   883  	// For consistence, we call in to shoot it regardless if HCS knows about it
   884  	// We explicitly just log a warning if the terminate fails.
   885  	// Then we tell the backend the container exited.
   886  	hc, err := hcsshim.OpenContainer(id)
   887  	if err != nil {
   888  		return nil, errdefs.NotFound(errors.New("container not found"))
   889  	}
   890  	const terminateTimeout = time.Minute * 2
   891  	err = hc.Terminate()
   892  
   893  	if hcsshim.IsPending(err) {
   894  		err = hc.WaitTimeout(terminateTimeout)
   895  	} else if hcsshim.IsAlreadyStopped(err) {
   896  		err = nil
   897  	}
   898  
   899  	if err != nil {
   900  		c.logger.WithField("container", id).WithError(err).Debug("terminate failed on restore")
   901  		return nil, err
   902  	}
   903  	return &container{
   904  		client:       c,
   905  		hcsContainer: hc,
   906  		id:           id,
   907  	}, nil
   908  }
   909  
   910  // AttachTask is only called by the daemon when restoring containers. As
   911  // re-attach isn't possible (see LoadContainer), a NotFound error is
   912  // unconditionally returned to allow restore to make progress.
   913  func (*container) AttachTask(context.Context, libcontainerdtypes.StdioCallback) (libcontainerdtypes.Task, error) {
   914  	return nil, errdefs.NotFound(cerrdefs.ErrNotImplemented)
   915  }
   916  
   917  // Pids returns a list of process IDs running in a container. It is not
   918  // implemented on Windows.
   919  func (t *task) Pids(context.Context) ([]containerd.ProcessInfo, error) {
   920  	return nil, errors.New("not implemented on Windows")
   921  }
   922  
   923  // Summary returns a summary of the processes running in a container.
   924  // This is present in Windows to support docker top. In linux, the
   925  // engine shells out to ps to get process information. On Windows, as
   926  // the containers could be Hyper-V containers, they would not be
   927  // visible on the container host. However, libcontainerd does have
   928  // that information.
   929  func (t *task) Summary(_ context.Context) ([]libcontainerdtypes.Summary, error) {
   930  	hc, err := t.getHCSContainer()
   931  	if err != nil {
   932  		return nil, err
   933  	}
   934  
   935  	p, err := hc.ProcessList()
   936  	if err != nil {
   937  		return nil, err
   938  	}
   939  
   940  	pl := make([]libcontainerdtypes.Summary, len(p))
   941  	for i := range p {
   942  		pl[i] = libcontainerdtypes.Summary{
   943  			ImageName:                    p[i].ImageName,
   944  			CreatedAt:                    p[i].CreateTimestamp,
   945  			KernelTime_100Ns:             p[i].KernelTime100ns,
   946  			MemoryCommitBytes:            p[i].MemoryCommitBytes,
   947  			MemoryWorkingSetPrivateBytes: p[i].MemoryWorkingSetPrivateBytes,
   948  			MemoryWorkingSetSharedBytes:  p[i].MemoryWorkingSetSharedBytes,
   949  			ProcessID:                    p[i].ProcessId,
   950  			UserTime_100Ns:               p[i].UserTime100ns,
   951  			ExecID:                       "",
   952  		}
   953  	}
   954  	return pl, nil
   955  }
   956  
   957  func (p *process) Delete(ctx context.Context) (*containerd.ExitStatus, error) {
   958  	select {
   959  	case <-ctx.Done():
   960  		return nil, errors.WithStack(ctx.Err())
   961  	case <-p.waitCh:
   962  	default:
   963  		return nil, errdefs.Conflict(errors.New("process is running"))
   964  	}
   965  	return p.exited, nil
   966  }
   967  
   968  func (t *task) Delete(ctx context.Context) (*containerd.ExitStatus, error) {
   969  	select {
   970  	case <-ctx.Done():
   971  		return nil, errors.WithStack(ctx.Err())
   972  	case <-t.waitCh:
   973  	default:
   974  		return nil, errdefs.Conflict(errors.New("container is not stopped"))
   975  	}
   976  
   977  	t.ctr.mu.Lock()
   978  	defer t.ctr.mu.Unlock()
   979  	if err := t.assertIsCurrentTask(); err != nil {
   980  		return nil, err
   981  	}
   982  	t.ctr.task = nil
   983  	return t.exited, nil
   984  }
   985  
   986  func (t *task) ForceDelete(ctx context.Context) error {
   987  	select {
   988  	case <-t.waitCh: // Task is already stopped.
   989  		_, err := t.Delete(ctx)
   990  		return err
   991  	default:
   992  	}
   993  
   994  	if err := t.Kill(ctx, syscall.SIGKILL); err != nil {
   995  		return errors.Wrap(err, "could not force-kill task")
   996  	}
   997  
   998  	select {
   999  	case <-ctx.Done():
  1000  		return ctx.Err()
  1001  	case <-t.waitCh:
  1002  		_, err := t.Delete(ctx)
  1003  		return err
  1004  	}
  1005  }
  1006  
  1007  func (t *task) Status(ctx context.Context) (containerd.Status, error) {
  1008  	select {
  1009  	case <-t.waitCh:
  1010  		return containerd.Status{
  1011  			Status:     containerd.Stopped,
  1012  			ExitStatus: t.exited.ExitCode(),
  1013  			ExitTime:   t.exited.ExitTime(),
  1014  		}, nil
  1015  	default:
  1016  	}
  1017  
  1018  	t.ctr.mu.Lock()
  1019  	defer t.ctr.mu.Unlock()
  1020  	s := containerd.Running
  1021  	if t.ctr.isPaused {
  1022  		s = containerd.Paused
  1023  	}
  1024  	return containerd.Status{Status: s}, nil
  1025  }
  1026  
  1027  func (*task) UpdateResources(ctx context.Context, resources *libcontainerdtypes.Resources) error {
  1028  	// Updating resource isn't supported on Windows
  1029  	// but we should return nil for enabling updating container
  1030  	return nil
  1031  }
  1032  
  1033  func (*task) CreateCheckpoint(ctx context.Context, checkpointDir string, exit bool) error {
  1034  	return errors.New("Windows: Containers do not support checkpoints")
  1035  }
  1036  
  1037  // assertIsCurrentTask returns a non-nil error if the task has been deleted.
  1038  func (t *task) assertIsCurrentTask() error {
  1039  	if t.ctr.task != t {
  1040  		return errors.WithStack(errdefs.NotFound(fmt.Errorf("task %q not found", t.id)))
  1041  	}
  1042  	return nil
  1043  }
  1044  
  1045  // getHCSContainer returns a reference to the hcsshim Container for the task's
  1046  // container if neither the task nor container have been deleted.
  1047  //
  1048  // t.ctr.mu must not be locked by the calling goroutine when calling this
  1049  // function.
  1050  func (t *task) getHCSContainer() (hcsshim.Container, error) {
  1051  	t.ctr.mu.Lock()
  1052  	defer t.ctr.mu.Unlock()
  1053  	if err := t.assertIsCurrentTask(); err != nil {
  1054  		return nil, err
  1055  	}
  1056  	hc := t.ctr.hcsContainer
  1057  	if hc == nil {
  1058  		return nil, errors.WithStack(errdefs.NotFound(fmt.Errorf("container %q not found", t.ctr.id)))
  1059  	}
  1060  	return hc, nil
  1061  }
  1062  
  1063  // ctr mutex must be held when calling this function.
  1064  func (ctr *container) shutdownContainer() error {
  1065  	var err error
  1066  	const waitTimeout = time.Minute * 5
  1067  
  1068  	if !ctr.terminateInvoked {
  1069  		err = ctr.hcsContainer.Shutdown()
  1070  	}
  1071  
  1072  	if hcsshim.IsPending(err) || ctr.terminateInvoked {
  1073  		err = ctr.hcsContainer.WaitTimeout(waitTimeout)
  1074  	} else if hcsshim.IsAlreadyStopped(err) {
  1075  		err = nil
  1076  	}
  1077  
  1078  	if err != nil {
  1079  		ctr.client.logger.WithError(err).WithField("container", ctr.id).
  1080  			Debug("failed to shutdown container, terminating it")
  1081  		terminateErr := ctr.terminateContainer()
  1082  		if terminateErr != nil {
  1083  			ctr.client.logger.WithError(terminateErr).WithField("container", ctr.id).
  1084  				Error("failed to shutdown container, and subsequent terminate also failed")
  1085  			return fmt.Errorf("%s: subsequent terminate failed %s", err, terminateErr)
  1086  		}
  1087  		return err
  1088  	}
  1089  
  1090  	return nil
  1091  }
  1092  
  1093  // ctr mutex must be held when calling this function.
  1094  func (ctr *container) terminateContainer() error {
  1095  	const terminateTimeout = time.Minute * 5
  1096  	ctr.terminateInvoked = true
  1097  	err := ctr.hcsContainer.Terminate()
  1098  
  1099  	if hcsshim.IsPending(err) {
  1100  		err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
  1101  	} else if hcsshim.IsAlreadyStopped(err) {
  1102  		err = nil
  1103  	}
  1104  
  1105  	if err != nil {
  1106  		ctr.client.logger.WithError(err).WithField("container", ctr.id).
  1107  			Debug("failed to terminate container")
  1108  		return err
  1109  	}
  1110  
  1111  	return nil
  1112  }
  1113  
  1114  func (p *process) reap() {
  1115  	logger := p.ctr.client.logger.WithFields(log.Fields{
  1116  		"container": p.ctr.id,
  1117  		"process":   p.id,
  1118  	})
  1119  
  1120  	var eventErr error
  1121  
  1122  	// Block indefinitely for the process to exit.
  1123  	if err := p.hcsProcess.Wait(); err != nil {
  1124  		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
  1125  			logger.WithError(err).Warnf("Wait() failed (container may have been killed)")
  1126  		}
  1127  		// Fall through here, do not return. This ensures we tell the
  1128  		// docker engine that the process/container has exited to avoid
  1129  		// a container being dropped on the floor.
  1130  	}
  1131  	exitedAt := time.Now()
  1132  
  1133  	exitCode, err := p.hcsProcess.ExitCode()
  1134  	if err != nil {
  1135  		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
  1136  			logger.WithError(err).Warnf("unable to get exit code for process")
  1137  		}
  1138  		// Since we got an error retrieving the exit code, make sure that the
  1139  		// code we return doesn't incorrectly indicate success.
  1140  		exitCode = -1
  1141  
  1142  		// Fall through here, do not return. This ensures we tell the
  1143  		// docker engine that the process/container has exited to avoid
  1144  		// a container being dropped on the floor.
  1145  	}
  1146  
  1147  	p.mu.Lock()
  1148  	hcsProcess := p.hcsProcess
  1149  	p.hcsProcess = nil
  1150  	p.mu.Unlock()
  1151  
  1152  	if err := hcsProcess.Close(); err != nil {
  1153  		logger.WithError(err).Warnf("failed to cleanup hcs process resources")
  1154  		exitCode = -1
  1155  		eventErr = fmt.Errorf("hcsProcess.Close() failed %s", err)
  1156  	}
  1157  
  1158  	// Explicit locking is not required as reads from exited are
  1159  	// synchronized using waitCh.
  1160  	p.exited = containerd.NewExitStatus(uint32(exitCode), exitedAt, nil)
  1161  	close(p.waitCh)
  1162  
  1163  	p.ctr.client.eventQ.Append(p.ctr.id, func() {
  1164  		ei := libcontainerdtypes.EventInfo{
  1165  			ContainerID: p.ctr.id,
  1166  			ProcessID:   p.id,
  1167  			Pid:         uint32(hcsProcess.Pid()),
  1168  			ExitCode:    uint32(exitCode),
  1169  			ExitedAt:    exitedAt,
  1170  			Error:       eventErr,
  1171  		}
  1172  		p.ctr.client.logger.WithFields(log.Fields{
  1173  			"container":  p.ctr.id,
  1174  			"event":      libcontainerdtypes.EventExit,
  1175  			"event-info": ei,
  1176  		}).Info("sending event")
  1177  		err := p.ctr.client.backend.ProcessEvent(p.ctr.id, libcontainerdtypes.EventExit, ei)
  1178  		if err != nil {
  1179  			p.ctr.client.logger.WithError(err).WithFields(log.Fields{
  1180  				"container":  p.ctr.id,
  1181  				"event":      libcontainerdtypes.EventExit,
  1182  				"event-info": ei,
  1183  			}).Error("failed to process event")
  1184  		}
  1185  	})
  1186  }
  1187  
  1188  func (ctr *container) Delete(context.Context) error {
  1189  	ctr.mu.Lock()
  1190  	defer ctr.mu.Unlock()
  1191  
  1192  	if ctr.hcsContainer == nil {
  1193  		return errors.WithStack(errdefs.NotFound(fmt.Errorf("container %q not found", ctr.id)))
  1194  	}
  1195  
  1196  	// Check that there is no task currently running.
  1197  	if ctr.task != nil {
  1198  		select {
  1199  		case <-ctr.task.waitCh:
  1200  		default:
  1201  			return errors.WithStack(errdefs.Conflict(errors.New("container is not stopped")))
  1202  		}
  1203  	}
  1204  
  1205  	var (
  1206  		logger = ctr.client.logger.WithFields(log.Fields{
  1207  			"container": ctr.id,
  1208  		})
  1209  		thisErr error
  1210  	)
  1211  
  1212  	if err := ctr.shutdownContainer(); err != nil {
  1213  		logger.WithError(err).Warn("failed to shutdown container")
  1214  		thisErr = errors.Wrap(err, "failed to shutdown container")
  1215  	} else {
  1216  		logger.Debug("completed container shutdown")
  1217  	}
  1218  
  1219  	if err := ctr.hcsContainer.Close(); err != nil {
  1220  		logger.WithError(err).Error("failed to clean hcs container resources")
  1221  		thisErr = errors.Wrap(err, "failed to terminate container")
  1222  	}
  1223  
  1224  	ctr.hcsContainer = nil
  1225  	return thisErr
  1226  }