
This avoids issues with the various deferred error handlers in the event that `err` is shadowed or named differently, which this function currently avoids but which is an easy trap to fall into. Since named return values are all or nothing we need to name the waitGroup too and adjust the code to suite. Thanks to Aaron Lehmann for the suggestion, see also https://github.com/docker/swarmkit/pull/1965#discussion_r118137410 Signed-off-by: Ian Campbell <ian.campbell@docker.com>
102 lines
2.1 KiB
Go
102 lines
2.1 KiB
Go
// +build !windows
|
|
|
|
package main
|
|
|
|
import (
|
|
gocontext "context"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/containerd/fifo"
|
|
"github.com/pkg/errors"
|
|
"github.com/urfave/cli"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/grpclog"
|
|
)
|
|
|
|
func prepareStdio(stdin, stdout, stderr string, console bool) (wg *sync.WaitGroup, err error) {
|
|
wg = &sync.WaitGroup{}
|
|
ctx := gocontext.Background()
|
|
|
|
f, err := fifo.OpenFifo(ctx, stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(c io.Closer) {
|
|
if err != nil {
|
|
c.Close()
|
|
}
|
|
}(f)
|
|
go func(w io.WriteCloser) {
|
|
io.Copy(w, os.Stdin)
|
|
w.Close()
|
|
}(f)
|
|
|
|
f, err = fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(c io.Closer) {
|
|
if err != nil {
|
|
c.Close()
|
|
}
|
|
}(f)
|
|
wg.Add(1)
|
|
go func(r io.ReadCloser) {
|
|
io.Copy(os.Stdout, r)
|
|
r.Close()
|
|
wg.Done()
|
|
}(f)
|
|
|
|
f, err = fifo.OpenFifo(ctx, stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(c io.Closer) {
|
|
if err != nil {
|
|
c.Close()
|
|
}
|
|
}(f)
|
|
if !console {
|
|
wg.Add(1)
|
|
go func(r io.ReadCloser) {
|
|
io.Copy(os.Stderr, r)
|
|
r.Close()
|
|
wg.Done()
|
|
}(f)
|
|
}
|
|
|
|
return wg, nil
|
|
}
|
|
|
|
func getGRPCConnection(context *cli.Context) (*grpc.ClientConn, error) {
|
|
if grpcConn != nil {
|
|
return grpcConn, nil
|
|
}
|
|
|
|
bindSocket := context.GlobalString("address")
|
|
// reset the logger for grpc to log to dev/null so that it does not mess with our stdio
|
|
grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
|
|
dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)}
|
|
dialOpts = append(dialOpts,
|
|
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
|
return net.DialTimeout("unix", bindSocket, timeout)
|
|
},
|
|
))
|
|
|
|
conn, err := grpc.Dial(fmt.Sprintf("unix://%s", bindSocket), dialOpts...)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to dial %q", bindSocket)
|
|
}
|
|
|
|
grpcConn = conn
|
|
return grpcConn, nil
|
|
}
|