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  }