github.com/containerd/Containerd@v1.4.13/cio/io_unix.go (about)

     1  // +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 cio
    20  
    21  import (
    22  	"context"
    23  	"io"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"sync"
    28  	"syscall"
    29  
    30  	"github.com/containerd/fifo"
    31  	"github.com/pkg/errors"
    32  )
    33  
    34  // NewFIFOSetInDir returns a new FIFOSet with paths in a temporary directory under root
    35  func NewFIFOSetInDir(root, id string, terminal bool) (*FIFOSet, error) {
    36  	if root != "" {
    37  		if err := os.MkdirAll(root, 0700); err != nil {
    38  			return nil, err
    39  		}
    40  	}
    41  	dir, err := ioutil.TempDir(root, "")
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	closer := func() error {
    46  		return os.RemoveAll(dir)
    47  	}
    48  	return NewFIFOSet(Config{
    49  		Stdin:    filepath.Join(dir, id+"-stdin"),
    50  		Stdout:   filepath.Join(dir, id+"-stdout"),
    51  		Stderr:   filepath.Join(dir, id+"-stderr"),
    52  		Terminal: terminal,
    53  	}, closer), nil
    54  }
    55  
    56  func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
    57  	var ctx, cancel = context.WithCancel(context.Background())
    58  	pipes, err := openFifos(ctx, fifos)
    59  	if err != nil {
    60  		cancel()
    61  		return nil, err
    62  	}
    63  
    64  	if fifos.Stdin != "" {
    65  		go func() {
    66  			p := bufPool.Get().(*[]byte)
    67  			defer bufPool.Put(p)
    68  
    69  			io.CopyBuffer(pipes.Stdin, ioset.Stdin, *p)
    70  			pipes.Stdin.Close()
    71  		}()
    72  	}
    73  
    74  	var wg = &sync.WaitGroup{}
    75  	if fifos.Stdout != "" {
    76  		wg.Add(1)
    77  		go func() {
    78  			p := bufPool.Get().(*[]byte)
    79  			defer bufPool.Put(p)
    80  
    81  			io.CopyBuffer(ioset.Stdout, pipes.Stdout, *p)
    82  			pipes.Stdout.Close()
    83  			wg.Done()
    84  		}()
    85  	}
    86  
    87  	if !fifos.Terminal && fifos.Stderr != "" {
    88  		wg.Add(1)
    89  		go func() {
    90  			p := bufPool.Get().(*[]byte)
    91  			defer bufPool.Put(p)
    92  
    93  			io.CopyBuffer(ioset.Stderr, pipes.Stderr, *p)
    94  			pipes.Stderr.Close()
    95  			wg.Done()
    96  		}()
    97  	}
    98  	return &cio{
    99  		config:  fifos.Config,
   100  		wg:      wg,
   101  		closers: append(pipes.closers(), fifos),
   102  		cancel:  cancel,
   103  	}, nil
   104  }
   105  
   106  func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) {
   107  	var err error
   108  	defer func() {
   109  		if err != nil {
   110  			fifos.Close()
   111  		}
   112  	}()
   113  
   114  	var f pipes
   115  	if fifos.Stdin != "" {
   116  		if f.Stdin, err = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
   117  			return f, errors.Wrapf(err, "failed to open stdin fifo")
   118  		}
   119  		defer func() {
   120  			if err != nil && f.Stdin != nil {
   121  				f.Stdin.Close()
   122  			}
   123  		}()
   124  	}
   125  	if fifos.Stdout != "" {
   126  		if f.Stdout, err = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
   127  			return f, errors.Wrapf(err, "failed to open stdout fifo")
   128  		}
   129  		defer func() {
   130  			if err != nil && f.Stdout != nil {
   131  				f.Stdout.Close()
   132  			}
   133  		}()
   134  	}
   135  	if !fifos.Terminal && fifos.Stderr != "" {
   136  		if f.Stderr, err = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
   137  			return f, errors.Wrapf(err, "failed to open stderr fifo")
   138  		}
   139  	}
   140  	return f, nil
   141  }
   142  
   143  // NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser
   144  // and io.WriteCloser.
   145  func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) {
   146  	ctx, cancel := context.WithCancel(ctx)
   147  	pipes, err := openFifos(ctx, fifos)
   148  	return &DirectIO{
   149  		pipes: pipes,
   150  		cio: cio{
   151  			config:  fifos.Config,
   152  			closers: append(pipes.closers(), fifos),
   153  			cancel:  cancel,
   154  		},
   155  	}, err
   156  }