| // +build !windows |
| |
| package containerd |
| |
| import ( |
| "context" |
| "io" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "sync" |
| "syscall" |
| |
| "github.com/containerd/fifo" |
| ) |
| |
| // NewFifos returns a new set of fifos for the task |
| func NewFifos(id string) (*FIFOSet, error) { |
| root := filepath.Join(os.TempDir(), "containerd") |
| if err := os.MkdirAll(root, 0700); err != nil { |
| return nil, err |
| } |
| dir, err := ioutil.TempDir(root, "") |
| if err != nil { |
| return nil, err |
| } |
| return &FIFOSet{ |
| Dir: dir, |
| In: filepath.Join(dir, id+"-stdin"), |
| Out: filepath.Join(dir, id+"-stdout"), |
| Err: filepath.Join(dir, id+"-stderr"), |
| }, nil |
| } |
| |
| func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) { |
| var ( |
| f io.ReadWriteCloser |
| set []io.Closer |
| ctx, cancel = context.WithCancel(context.Background()) |
| wg = &sync.WaitGroup{} |
| ) |
| defer func() { |
| if err != nil { |
| for _, f := range set { |
| f.Close() |
| } |
| cancel() |
| } |
| }() |
| |
| if f, err = fifo.OpenFifo(ctx, fifos.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { |
| return nil, err |
| } |
| set = append(set, f) |
| go func(w io.WriteCloser) { |
| io.Copy(w, ioset.in) |
| w.Close() |
| }(f) |
| |
| if f, err = fifo.OpenFifo(ctx, fifos.Out, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { |
| return nil, err |
| } |
| set = append(set, f) |
| wg.Add(1) |
| go func(r io.ReadCloser) { |
| io.Copy(ioset.out, r) |
| r.Close() |
| wg.Done() |
| }(f) |
| |
| if f, err = fifo.OpenFifo(ctx, fifos.Err, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { |
| return nil, err |
| } |
| set = append(set, f) |
| |
| if !tty { |
| wg.Add(1) |
| go func(r io.ReadCloser) { |
| io.Copy(ioset.err, r) |
| r.Close() |
| wg.Done() |
| }(f) |
| } |
| return &wgCloser{ |
| wg: wg, |
| dir: fifos.Dir, |
| set: set, |
| cancel: cancel, |
| }, nil |
| } |
| |
| // NewDirectIO returns an IO implementation that exposes the pipes directly |
| func NewDirectIO(ctx context.Context, terminal bool) (*DirectIO, error) { |
| set, err := NewFifos("") |
| if err != nil { |
| return nil, err |
| } |
| f := &DirectIO{ |
| set: set, |
| terminal: terminal, |
| } |
| defer func() { |
| if err != nil { |
| f.Delete() |
| } |
| }() |
| if f.Stdin, err = fifo.OpenFifo(ctx, set.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { |
| return nil, err |
| } |
| if f.Stdout, err = fifo.OpenFifo(ctx, set.Out, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { |
| f.Stdin.Close() |
| return nil, err |
| } |
| if f.Stderr, err = fifo.OpenFifo(ctx, set.Err, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { |
| f.Stdin.Close() |
| f.Stdout.Close() |
| return nil, err |
| } |
| return f, nil |
| } |
| |
| // DirectIO allows task IO to be handled externally by the caller |
| type DirectIO struct { |
| Stdin io.WriteCloser |
| Stdout io.ReadCloser |
| Stderr io.ReadCloser |
| |
| set *FIFOSet |
| terminal bool |
| } |
| |
| // IOCreate returns IO avaliable for use with task creation |
| func (f *DirectIO) IOCreate(id string) (IO, error) { |
| return f, nil |
| } |
| |
| // IOAttach returns IO avaliable for use with task attachment |
| func (f *DirectIO) IOAttach(set *FIFOSet) (IO, error) { |
| return f, nil |
| } |
| |
| // Config returns the IOConfig |
| func (f *DirectIO) Config() IOConfig { |
| return IOConfig{ |
| Terminal: f.terminal, |
| Stdin: f.set.In, |
| Stdout: f.set.Out, |
| Stderr: f.set.Err, |
| } |
| } |
| |
| // Cancel stops any IO copy operations |
| // |
| // Not applicable for DirectIO |
| func (f *DirectIO) Cancel() { |
| // nothing to cancel as all operations are handled externally |
| } |
| |
| // Wait on any IO copy operations |
| // |
| // Not applicable for DirectIO |
| func (f *DirectIO) Wait() { |
| // nothing to wait on as all operations are handled externally |
| } |
| |
| // Close closes all open fds |
| func (f *DirectIO) Close() error { |
| err := f.Stdin.Close() |
| if err2 := f.Stdout.Close(); err == nil { |
| err = err2 |
| } |
| if err2 := f.Stderr.Close(); err == nil { |
| err = err2 |
| } |
| return err |
| } |
| |
| // Delete removes the underlying directory containing fifos |
| func (f *DirectIO) Delete() error { |
| if f.set.Dir == "" { |
| return nil |
| } |
| return os.RemoveAll(f.set.Dir) |
| } |