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

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package cio
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"net"
    24  
    25  	winio "github.com/Microsoft/go-winio"
    26  	"github.com/containerd/containerd/log"
    27  	"github.com/pkg/errors"
    28  )
    29  
    30  const pipeRoot = `\\.\pipe`
    31  
    32  // NewFIFOSetInDir returns a new set of fifos for the task
    33  func NewFIFOSetInDir(_, id string, terminal bool) (*FIFOSet, error) {
    34  	stderrPipe := ""
    35  	if !terminal {
    36  		stderrPipe = fmt.Sprintf(`%s\ctr-%s-stderr`, pipeRoot, id)
    37  	}
    38  	return NewFIFOSet(Config{
    39  		Terminal: terminal,
    40  		Stdin:    fmt.Sprintf(`%s\ctr-%s-stdin`, pipeRoot, id),
    41  		Stdout:   fmt.Sprintf(`%s\ctr-%s-stdout`, pipeRoot, id),
    42  		Stderr:   stderrPipe,
    43  	}, nil), nil
    44  }
    45  
    46  func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
    47  	var (
    48  		set []io.Closer
    49  	)
    50  
    51  	if fifos.Stdin != "" {
    52  		l, err := winio.ListenPipe(fifos.Stdin, nil)
    53  		if err != nil {
    54  			return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Stdin)
    55  		}
    56  		defer func(l net.Listener) {
    57  			if err != nil {
    58  				l.Close()
    59  			}
    60  		}(l)
    61  		set = append(set, l)
    62  
    63  		go func() {
    64  			c, err := l.Accept()
    65  			if err != nil {
    66  				log.L.WithError(err).Errorf("failed to accept stdin connection on %s", fifos.Stdin)
    67  				return
    68  			}
    69  
    70  			p := bufPool.Get().(*[]byte)
    71  			defer bufPool.Put(p)
    72  
    73  			io.CopyBuffer(c, ioset.Stdin, *p)
    74  			c.Close()
    75  			l.Close()
    76  		}()
    77  	}
    78  
    79  	if fifos.Stdout != "" {
    80  		l, err := winio.ListenPipe(fifos.Stdout, nil)
    81  		if err != nil {
    82  			return nil, errors.Wrapf(err, "failed to create stdout pipe %s", fifos.Stdout)
    83  		}
    84  		defer func(l net.Listener) {
    85  			if err != nil {
    86  				l.Close()
    87  			}
    88  		}(l)
    89  		set = append(set, l)
    90  
    91  		go func() {
    92  			c, err := l.Accept()
    93  			if err != nil {
    94  				log.L.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Stdout)
    95  				return
    96  			}
    97  
    98  			p := bufPool.Get().(*[]byte)
    99  			defer bufPool.Put(p)
   100  
   101  			io.CopyBuffer(ioset.Stdout, c, *p)
   102  			c.Close()
   103  			l.Close()
   104  		}()
   105  	}
   106  
   107  	if fifos.Stderr != "" {
   108  		l, err := winio.ListenPipe(fifos.Stderr, nil)
   109  		if err != nil {
   110  			return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr)
   111  		}
   112  		defer func(l net.Listener) {
   113  			if err != nil {
   114  				l.Close()
   115  			}
   116  		}(l)
   117  		set = append(set, l)
   118  
   119  		go func() {
   120  			c, err := l.Accept()
   121  			if err != nil {
   122  				log.L.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Stderr)
   123  				return
   124  			}
   125  
   126  			p := bufPool.Get().(*[]byte)
   127  			defer bufPool.Put(p)
   128  
   129  			io.CopyBuffer(ioset.Stderr, c, *p)
   130  			c.Close()
   131  			l.Close()
   132  		}()
   133  	}
   134  
   135  	return &cio{config: fifos.Config, closers: set}, nil
   136  }
   137  
   138  // NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser
   139  // and io.WriteCloser.
   140  func NewDirectIO(stdin io.WriteCloser, stdout, stderr io.ReadCloser, terminal bool) *DirectIO {
   141  	return &DirectIO{
   142  		pipes: pipes{
   143  			Stdin:  stdin,
   144  			Stdout: stdout,
   145  			Stderr: stderr,
   146  		},
   147  		cio: cio{
   148  			config: Config{Terminal: terminal},
   149  		},
   150  	}
   151  }
   152  
   153  // NewDirectIOFromFIFOSet returns an IO implementation that exposes the IO streams as io.ReadCloser
   154  // and io.WriteCloser.
   155  func NewDirectIOFromFIFOSet(ctx context.Context, stdin io.WriteCloser, stdout, stderr io.ReadCloser, fifos *FIFOSet) *DirectIO {
   156  	_, cancel := context.WithCancel(ctx)
   157  	pipes := pipes{
   158  		Stdin:  stdin,
   159  		Stdout: stdout,
   160  		Stderr: stderr,
   161  	}
   162  	return &DirectIO{
   163  		pipes: pipes,
   164  		cio: cio{
   165  			config:  fifos.Config,
   166  			closers: append(pipes.closers(), fifos),
   167  			cancel:  cancel,
   168  		},
   169  	}
   170  }