blob: f2e5a93fe1d1686b06b1e38a1403449e60025d5a [file] [log] [blame]
package libcontainerd
import (
"context"
"io"
"net"
"sync"
winio "github.com/Microsoft/go-winio"
"github.com/containerd/containerd/cio"
"github.com/pkg/errors"
)
type winpipe struct {
sync.Mutex
ctx context.Context
listener net.Listener
readyCh chan struct{}
readyErr error
client net.Conn
}
func newWinpipe(ctx context.Context, pipe string) (*winpipe, error) {
l, err := winio.ListenPipe(pipe, nil)
if err != nil {
return nil, errors.Wrapf(err, "%q pipe creation failed", pipe)
}
wp := &winpipe{
ctx: ctx,
listener: l,
readyCh: make(chan struct{}),
}
go func() {
go func() {
defer close(wp.readyCh)
defer wp.listener.Close()
c, err := wp.listener.Accept()
if err != nil {
wp.Lock()
if wp.readyErr == nil {
wp.readyErr = err
}
wp.Unlock()
return
}
wp.client = c
}()
select {
case <-wp.readyCh:
case <-ctx.Done():
wp.Lock()
if wp.readyErr == nil {
wp.listener.Close()
wp.readyErr = ctx.Err()
}
wp.Unlock()
}
}()
return wp, nil
}
func (wp *winpipe) Read(b []byte) (int, error) {
select {
case <-wp.ctx.Done():
return 0, wp.ctx.Err()
case <-wp.readyCh:
return wp.client.Read(b)
}
}
func (wp *winpipe) Write(b []byte) (int, error) {
select {
case <-wp.ctx.Done():
return 0, wp.ctx.Err()
case <-wp.readyCh:
return wp.client.Write(b)
}
}
func (wp *winpipe) Close() error {
select {
case <-wp.readyCh:
return wp.client.Close()
default:
return nil
}
}
func newIOPipe(fifos *cio.FIFOSet) (*IOPipe, error) {
var (
err error
ctx, cancel = context.WithCancel(context.Background())
p io.ReadWriteCloser
iop = &IOPipe{
Terminal: fifos.Terminal,
cancel: cancel,
config: cio.Config{
Terminal: fifos.Terminal,
Stdin: fifos.In,
Stdout: fifos.Out,
Stderr: fifos.Err,
},
}
)
defer func() {
if err != nil {
cancel()
iop.Close()
}
}()
if fifos.In != "" {
if p, err = newWinpipe(ctx, fifos.In); err != nil {
return nil, err
}
iop.Stdin = p
}
if fifos.Out != "" {
if p, err = newWinpipe(ctx, fifos.Out); err != nil {
return nil, err
}
iop.Stdout = p
}
if fifos.Err != "" {
if p, err = newWinpipe(ctx, fifos.Err); err != nil {
return nil, err
}
iop.Stderr = p
}
return iop, nil
}