github.com/containerd/nerdctl@v1.7.7/pkg/cioutil/container_io_unix.go (about) 1 //go:build !windows 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package cioutil 20 21 import ( 22 "context" 23 "fmt" 24 "io" 25 "os/exec" 26 "sync" 27 "syscall" 28 29 "github.com/containerd/containerd/cio" 30 "github.com/containerd/fifo" 31 ) 32 33 type pipes struct { 34 Stdin io.WriteCloser 35 Stdout io.ReadCloser 36 Stderr io.ReadCloser 37 } 38 39 func (p *pipes) closers() []io.Closer { 40 return []io.Closer{p.Stdin, p.Stdout, p.Stderr} 41 } 42 43 // copyIO is from https://github.com/containerd/containerd/blob/148d21b1ae0718b75718a09ecb307bb874270f59/cio/io_unix.go#L55 44 func copyIO(cmd *exec.Cmd, fifos *cio.FIFOSet, ioset *cio.Streams) (*ncio, error) { 45 var ctx, cancel = context.WithCancel(context.Background()) 46 pipes, err := openFifos(ctx, fifos) 47 if err != nil { 48 cancel() 49 return nil, err 50 } 51 52 if fifos.Stdin != "" { 53 go func() { 54 p := bufPool.Get().(*[]byte) 55 defer bufPool.Put(p) 56 57 io.CopyBuffer(pipes.Stdin, ioset.Stdin, *p) 58 pipes.Stdin.Close() 59 }() 60 } 61 62 var wg = &sync.WaitGroup{} 63 if fifos.Stdout != "" { 64 wg.Add(1) 65 go func() { 66 p := bufPool.Get().(*[]byte) 67 defer bufPool.Put(p) 68 69 io.CopyBuffer(ioset.Stdout, pipes.Stdout, *p) 70 pipes.Stdout.Close() 71 wg.Done() 72 }() 73 } 74 75 if !fifos.Terminal && fifos.Stderr != "" { 76 wg.Add(1) 77 go func() { 78 p := bufPool.Get().(*[]byte) 79 defer bufPool.Put(p) 80 81 io.CopyBuffer(ioset.Stderr, pipes.Stderr, *p) 82 pipes.Stderr.Close() 83 wg.Done() 84 }() 85 } 86 87 return &ncio{ 88 cmd: cmd, 89 config: fifos.Config, 90 wg: wg, 91 closers: append(pipes.closers(), fifos), 92 cancel: func() { 93 cancel() 94 for _, c := range pipes.closers() { 95 if c != nil { 96 c.Close() 97 } 98 } 99 }, 100 }, nil 101 } 102 103 func openFifos(ctx context.Context, fifos *cio.FIFOSet) (f pipes, retErr error) { 104 defer func() { 105 if retErr != nil { 106 fifos.Close() 107 } 108 }() 109 110 if fifos.Stdin != "" { 111 if f.Stdin, retErr = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil { 112 return f, fmt.Errorf("failed to open stdin fifo: %w", retErr) 113 } 114 defer func() { 115 if retErr != nil && f.Stdin != nil { 116 f.Stdin.Close() 117 } 118 }() 119 } 120 if fifos.Stdout != "" { 121 if f.Stdout, retErr = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil { 122 return f, fmt.Errorf("failed to open stdout fifo: %w", retErr) 123 } 124 defer func() { 125 if retErr != nil && f.Stdout != nil { 126 f.Stdout.Close() 127 } 128 }() 129 } 130 if !fifos.Terminal && fifos.Stderr != "" { 131 if f.Stderr, retErr = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil { 132 return f, fmt.Errorf("failed to open stderr fifo: %w", retErr) 133 } 134 } 135 return f, nil 136 }