diff --git a/linux/proc/io.go b/linux/proc/io.go index 335b8b89c..96b759cf9 100644 --- a/linux/proc/io.go +++ b/linux/proc/io.go @@ -22,6 +22,7 @@ import ( "context" "fmt" "io" + "os" "sync" "syscall" @@ -37,44 +38,75 @@ var bufPool = sync.Pool{ } func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error { - for name, dest := range map[string]func(wc io.WriteCloser, rc io.Closer){ - stdout: func(wc io.WriteCloser, rc io.Closer) { - wg.Add(1) - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - io.CopyBuffer(wc, rio.Stdout(), *p) - wg.Done() - wc.Close() - rc.Close() - }() - }, - stderr: func(wc io.WriteCloser, rc io.Closer) { - wg.Add(1) - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - - io.CopyBuffer(wc, rio.Stderr(), *p) - wg.Done() - wc.Close() - rc.Close() - }() + var sameFile io.WriteCloser + for _, i := range []struct { + name string + dest func(wc io.WriteCloser, rc io.Closer) + }{ + { + name: stdout, + dest: func(wc io.WriteCloser, rc io.Closer) { + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(wc, rio.Stdout(), *p) + wg.Done() + wc.Close() + if rc != nil { + rc.Close() + } + }() + }, + }, { + name: stderr, + dest: func(wc io.WriteCloser, rc io.Closer) { + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(wc, rio.Stderr(), *p) + wg.Done() + wc.Close() + if rc != nil { + rc.Close() + } + }() + }, }, } { - fw, err := fifo.OpenFifo(ctx, name, syscall.O_WRONLY, 0) + ok, err := isFifo(i.name) if err != nil { - return fmt.Errorf("containerd-shim: opening %s failed: %s", name, err) + return err } - fr, err := fifo.OpenFifo(ctx, name, syscall.O_RDONLY, 0) - if err != nil { - return fmt.Errorf("containerd-shim: opening %s failed: %s", name, err) + var ( + fw io.WriteCloser + fr io.Closer + ) + if ok { + if fw, err = fifo.OpenFifo(ctx, i.name, syscall.O_WRONLY, 0); err != nil { + return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err) + } + if fr, err = fifo.OpenFifo(ctx, i.name, syscall.O_RDONLY, 0); err != nil { + return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err) + } + } else { + if sameFile != nil { + i.dest(sameFile, nil) + continue + } + if fw, err = os.OpenFile(i.name, syscall.O_WRONLY|syscall.O_APPEND, 0); err != nil { + return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err) + } + if stdout == stderr { + sameFile = fw + } } - dest(fw, fr) + i.dest(fw, fr) } if stdin == "" { rio.Stdin().Close() @@ -96,3 +128,19 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w }() return nil } + +// isFifo checks if a file is a fifo +// if the file does not exist then it returns false +func isFifo(path string) (bool, error) { + stat, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + if stat.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { + return true, nil + } + return false, nil +}