// Signal handles `docker stop` on Windows. While Linux has support for // the full range of signals, signals aren't really implemented on Windows. // We fake supporting regular stop and -9 to force kill. func (clnt *client) Signal(containerID string, sig int) error { var ( cont *container err error ) // Get the container as we need it to get the container handle. clnt.lock(containerID) defer clnt.unlock(containerID) if cont, err = clnt.getContainer(containerID); err != nil { return err } cont.manualStopRequested = true logrus.Debugf("libcontainerd: Signal() containerID=%s sig=%d pid=%d", containerID, sig, cont.systemPid) if syscall.Signal(sig) == syscall.SIGKILL { // Terminate the compute system if err := cont.hcsContainer.Terminate(); err != nil { if !hcsshim.IsPending(err) { logrus.Errorf("libcontainerd: failed to terminate %s - %q", containerID, err) } } } else { // Terminate Process if err := cont.hcsProcess.Kill(); err != nil && !hcsshim.IsAlreadyStopped(err) { // ignore errors logrus.Warnf("libcontainerd: failed to terminate pid %d in %s: %q", cont.systemPid, containerID, err) } } return nil }
// terminate terminates the container in HCS // Caller needs to lock container ID before calling this method. func (ctr *container) terminate() error { const terminateTimeout = time.Minute * 5 err := ctr.hcsContainer.Terminate() if hcsshim.IsPending(err) { err = ctr.hcsContainer.WaitTimeout(terminateTimeout) } else if hcsshim.IsAlreadyStopped(err) { err = nil } if err != nil { logrus.Debugf("libcontainerd: error terminating container %s %v", ctr.containerID, err) return err } return nil }
// shutdown shuts down the container in HCS // Caller needs to lock container ID before calling this method. func (ctr *container) shutdown() error { const shutdownTimeout = time.Minute * 5 err := ctr.hcsContainer.Shutdown() if hcsshim.IsPending(err) { // Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely. err = ctr.hcsContainer.WaitTimeout(shutdownTimeout) } else if hcsshim.IsAlreadyStopped(err) { err = nil } if err != nil { logrus.Debugf("libcontainerd: error shutting down container %s %v calling terminate", ctr.containerID, err) if err := ctr.terminate(); err != nil { return err } return err } return nil }
// Remove unmounts and removes the dir information. func (d *Driver) Remove(id string) error { rID, err := d.resolveID(id) if err != nil { return err } // This retry loop is due to a bug in Windows (Internal bug #9432268) // if GetContainers fails with ErrVmcomputeOperationInvalidState // it is a transient error. Retry until it succeeds. var computeSystems []hcsshim.ContainerProperties retryCount := 0 for { // Get and terminate any template VMs that are currently using the layer computeSystems, err = hcsshim.GetContainers(hcsshim.ComputeSystemQuery{}) if err != nil { if err == hcsshim.ErrVmcomputeOperationInvalidState { if retryCount >= 5 { // If we are unable to get the list of containers // go ahead and attempt to delete the layer anyway // as it will most likely work. break } retryCount++ time.Sleep(2 * time.Second) continue } return err } break } for _, computeSystem := range computeSystems { if strings.Contains(computeSystem.RuntimeImagePath, id) && computeSystem.IsRuntimeTemplate { container, err := hcsshim.OpenContainer(computeSystem.ID) if err != nil { return err } defer container.Close() err = container.Terminate() if hcsshim.IsPending(err) { err = container.Wait() } else if hcsshim.IsAlreadyStopped(err) { err = nil } if err != nil { return err } } } layerPath := filepath.Join(d.info.HomeDir, rID) tmpID := fmt.Sprintf("%s-removing", rID) tmpLayerPath := filepath.Join(d.info.HomeDir, tmpID) if err := os.Rename(layerPath, tmpLayerPath); err != nil && !os.IsNotExist(err) { return err } if err := hcsshim.DestroyLayer(d.info, tmpID); err != nil { logrus.Errorf("Failed to DestroyLayer %s: %s", id, err) } return nil }