func (ctr *container) start(checkpoint string, checkpointDir string) error { spec, err := ctr.spec() if err != nil { return nil } createChan := make(chan struct{}) iopipe, err := ctr.openFifos(spec.Process.Terminal) if err != nil { return err } // we need to delay stdin closure after container start or else "stdin close" // event will be rejected by containerd. // stdin closure happens in AttachStreams stdin := iopipe.Stdin iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { go func() { <-createChan stdin.Close() }() return nil }) r := &containerd.CreateContainerRequest{ Id: ctr.containerID, BundlePath: ctr.dir, Stdin: ctr.fifo(syscall.Stdin), Stdout: ctr.fifo(syscall.Stdout), Stderr: ctr.fifo(syscall.Stderr), Checkpoint: checkpoint, CheckpointDir: checkpointDir, // check to see if we are running in ramdisk to disable pivot root NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", Runtime: ctr.runtime, RuntimeArgs: ctr.runtimeArgs, } ctr.client.appendContainer(ctr) if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil { close(createChan) ctr.closeFifos(iopipe) return err } resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) if err != nil { close(createChan) ctr.closeFifos(iopipe) return err } ctr.startedAt = time.Now() ctr.systemPid = systemPid(resp.Container) close(createChan) return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ CommonStateInfo: CommonStateInfo{ State: StateStart, Pid: ctr.systemPid, }}) }
func (bufPool *BufioWriterPool) NewWriteCloserWrapper(buf *bufio.Writer, w io.Writer) io.WriteCloser { return ioutils.NewWriteCloserWrapper(w, func() error { buf.Flush() if writeCloser, ok := w.(io.WriteCloser); ok { return writeCloser.Close() } return nil }) }
func (fm *fileMetadataTransaction) TarSplitWriter() (io.WriteCloser, error) { f, err := os.OpenFile(filepath.Join(fm.root, "tar-split.json.gz"), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nil, err } fz := gzip.NewWriter(f) return ioutils.NewWriteCloserWrapper(fz, func() error { fz.Close() return f.Close() }), nil }
func createStdInCloser(pipe io.WriteCloser, process hcsshim.Process) io.WriteCloser { return ioutils.NewWriteCloserWrapper(pipe, func() error { if err := pipe.Close(); err != nil { return err } err := process.CloseStdin() if err != nil && !hcsshim.IsNotExist(err) && !hcsshim.IsAlreadyClosed(err) { // This error will occur if the compute system is currently shutting down if perr, ok := err.(*hcsshim.ProcessError); ok && perr.Err != hcsshim.ErrVmcomputeOperationInvalidState { return err } } return nil }) }
func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) { f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nil, err } var wc io.WriteCloser if compressInput { wc = gzip.NewWriter(f) } else { wc = f } return ioutils.NewWriteCloserWrapper(wc, func() error { wc.Close() return f.Close() }), nil }
func createStdInCloser(pipe io.WriteCloser, process hcsshim.Process) io.WriteCloser { return ioutils.NewWriteCloserWrapper(pipe, func() error { if err := pipe.Close(); err != nil { return err } // We do not need to lock container ID here, even though // we are calling into hcsshim. This is safe, because the // only place that closes this process handle is this method. err := process.CloseStdin() if err != nil && !hcsshim.IsNotExist(err) { // This error will occur if the compute system is currently shutting down if perr, ok := err.(*hcsshim.ProcessError); ok && perr.Err != hcsshim.ErrVmcomputeOperationInvalidState { return err } } return err }) }
func (p *process) openFifos(terminal bool) (*IOPipe, error) { bundleDir := p.dir if err := os.MkdirAll(bundleDir, 0700); err != nil { return nil, err } for i := 0; i < 3; i++ { f := p.fifo(i) if err := syscall.Mkfifo(f, 0700); err != nil && !os.IsExist(err) { return nil, fmt.Errorf("mkfifo: %s %v", f, err) } } io := &IOPipe{} stdinf, err := os.OpenFile(p.fifo(syscall.Stdin), syscall.O_RDWR, 0) if err != nil { return nil, err } io.Stdout = openReaderFromFifo(p.fifo(syscall.Stdout)) if !terminal { io.Stderr = openReaderFromFifo(p.fifo(syscall.Stderr)) } else { io.Stderr = emptyReader{} } io.Stdin = ioutils.NewWriteCloserWrapper(stdinf, func() error { stdinf.Close() _, err := p.client.remote.apiClient.UpdateProcess(context.Background(), &containerd.UpdateProcessRequest{ Id: p.containerID, Pid: p.friendlyName, CloseStdin: true, }) return err }) return io, nil }
func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) { r, w := io.Pipe() out := io.MultiWriter(in, w) targetChan := make(chan []target) go func() { targets := []target{} scanner := bufio.NewScanner(r) scanner.Split(ansiescape.ScanANSILines) for scanner.Scan() { line := scanner.Bytes() if matches := targetRegexp.FindSubmatch(line); len(matches) == 4 { dgst, err := digest.ParseDigest(string(matches[2])) if err != nil { // Line does match what is expected, continue looking for valid lines logrus.Debugf("Bad digest value %q in matched line, ignoring\n", string(matches[2])) continue } s, err := strconv.ParseInt(string(matches[3]), 10, 64) if err != nil { // Line does match what is expected, continue looking for valid lines logrus.Debugf("Bad size value %q in matched line, ignoring\n", string(matches[3])) continue } targets = append(targets, target{ reference: registry.ParseReference(string(matches[1])), digest: dgst, size: s, }) } } targetChan <- targets }() return ioutils.NewWriteCloserWrapper(out, w.Close), targetChan }
// AddProcess is the handler for adding a process to an already running // container. It's called through docker exec. It returns the system pid of the // exec'd process. func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, specp Process, attachStdio StdioCallback) (int, error) { clnt.lock(containerID) defer clnt.unlock(containerID) container, err := clnt.getContainer(containerID) if err != nil { return -1, err } spec, err := container.spec() if err != nil { return -1, err } sp := spec.Process sp.Args = specp.Args sp.Terminal = specp.Terminal if len(specp.Env) > 0 { sp.Env = specp.Env } if specp.Cwd != nil { sp.Cwd = *specp.Cwd } if specp.User != nil { sp.User = specs.User{ UID: specp.User.UID, GID: specp.User.GID, AdditionalGids: specp.User.AdditionalGids, } } if specp.Capabilities != nil { sp.Capabilities = specp.Capabilities } p := container.newProcess(processFriendlyName) r := &containerd.AddProcessRequest{ Args: sp.Args, Cwd: sp.Cwd, Terminal: sp.Terminal, Id: containerID, Env: sp.Env, User: &containerd.User{ Uid: sp.User.UID, Gid: sp.User.GID, AdditionalGids: sp.User.AdditionalGids, }, Pid: processFriendlyName, Stdin: p.fifo(syscall.Stdin), Stdout: p.fifo(syscall.Stdout), Stderr: p.fifo(syscall.Stderr), Capabilities: sp.Capabilities, ApparmorProfile: sp.ApparmorProfile, SelinuxLabel: sp.SelinuxLabel, NoNewPrivileges: sp.NoNewPrivileges, Rlimits: convertRlimits(sp.Rlimits), } iopipe, err := p.openFifos(sp.Terminal) if err != nil { return -1, err } resp, err := clnt.remote.apiClient.AddProcess(ctx, r) if err != nil { p.closeFifos(iopipe) return -1, err } var stdinOnce sync.Once stdin := iopipe.Stdin iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { var err error stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed err = stdin.Close() if err2 := p.sendCloseStdin(); err == nil { err = err2 } }) return err }) container.processes[processFriendlyName] = p if err := attachStdio(*iopipe); err != nil { p.closeFifos(iopipe) return -1, err } return int(resp.SystemPid), nil }
func (clnt *client) restore(cont *containerd.Container, lastEvent *containerd.Event, attachStdio StdioCallback, options ...CreateOption) (err error) { clnt.lock(cont.Id) defer clnt.unlock(cont.Id) logrus.Debugf("libcontainerd: restore container %s state %s", cont.Id, cont.Status) containerID := cont.Id if _, err := clnt.getContainer(containerID); err == nil { return fmt.Errorf("container %s is already active", containerID) } defer func() { if err != nil { clnt.deleteContainer(cont.Id) } }() container := clnt.newContainer(cont.BundlePath, options...) container.systemPid = systemPid(cont) var terminal bool for _, p := range cont.Processes { if p.Pid == InitFriendlyName { terminal = p.Terminal } } iopipe, err := container.openFifos(terminal) if err != nil { return err } var stdinOnce sync.Once stdin := iopipe.Stdin iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { var err error stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed err = stdin.Close() }) return err }) if err := attachStdio(*iopipe); err != nil { container.closeFifos(iopipe) return err } clnt.appendContainer(container) err = clnt.backend.StateChanged(containerID, StateInfo{ CommonStateInfo: CommonStateInfo{ State: StateRestore, Pid: container.systemPid, }}) if err != nil { container.closeFifos(iopipe) return err } if lastEvent != nil { // This should only be a pause or resume event if lastEvent.Type == StatePause || lastEvent.Type == StateResume { return clnt.backend.StateChanged(containerID, StateInfo{ CommonStateInfo: CommonStateInfo{ State: lastEvent.Type, Pid: container.systemPid, }}) } logrus.Warnf("libcontainerd: unexpected backlog event: %#v", lastEvent) } return nil }
func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio StdioCallback) error { spec, err := ctr.spec() if err != nil { return nil } ctx, cancel := context.WithCancel(context.Background()) defer cancel() ready := make(chan struct{}) iopipe, err := ctr.openFifos(spec.Process.Terminal) if err != nil { return err } var stdinOnce sync.Once // we need to delay stdin closure after container start or else "stdin close" // event will be rejected by containerd. // stdin closure happens in attachStdio stdin := iopipe.Stdin iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { var err error stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed err = stdin.Close() go func() { select { case <-ready: if err := ctr.sendCloseStdin(); err != nil { logrus.Warnf("failed to close stdin: %+v", err) } case <-ctx.Done(): } }() }) return err }) r := &containerd.CreateContainerRequest{ Id: ctr.containerID, BundlePath: ctr.dir, Stdin: ctr.fifo(syscall.Stdin), Stdout: ctr.fifo(syscall.Stdout), Stderr: ctr.fifo(syscall.Stderr), Checkpoint: checkpoint, CheckpointDir: checkpointDir, // check to see if we are running in ramdisk to disable pivot root NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", Runtime: ctr.runtime, RuntimeArgs: ctr.runtimeArgs, } ctr.client.appendContainer(ctr) if err := attachStdio(*iopipe); err != nil { ctr.closeFifos(iopipe) return err } resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) if err != nil { ctr.closeFifos(iopipe) return err } ctr.systemPid = systemPid(resp.Container) close(ready) return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ CommonStateInfo: CommonStateInfo{ State: StateStart, Pid: ctr.systemPid, }}) }