You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
buildx/monitor/dap/io.go

144 lines
4.1 KiB
Go

//go:build linux
package dap
// Ported from https://github.com/ktock/buildg/blob/v0.4.1/pkg/dap/dap.go
// Copyright The buildg Authors.
// Licensed under the Apache License, Version 2.0
import (
"context"
"fmt"
"io"
"path/filepath"
"syscall"
"time"
"github.com/containerd/console"
"github.com/containerd/fifo"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
func AttachContainerIO(root string, stdin io.Reader, stdout, stderr io.Writer, setTtyRaw bool) error {
if root == "" {
return errors.Errorf("root needs to be specified")
}
type ioSet struct {
stdin io.WriteCloser
stdout io.ReadCloser
stderr io.ReadCloser
}
ioSetCh := make(chan ioSet)
errCh := make(chan error)
go func() {
stdin, stdout, stderr, err := openFifosClient(context.TODO(), root)
if err != nil {
errCh <- err
return
}
ioSetCh <- ioSet{stdin, stdout, stderr}
}()
var (
pStdin io.WriteCloser
pStdout io.ReadCloser
pStderr io.ReadCloser
)
select {
case ioSet := <-ioSetCh:
pStdin, pStdout, pStderr = ioSet.stdin, ioSet.stdout, ioSet.stderr
case err := <-errCh:
return err
case <-time.After(3 * time.Second):
return errors.Errorf("i/o timeout; check server is up and running")
}
defer func() { pStdin.Close(); pStdout.Close(); pStderr.Close() }()
if setTtyRaw {
con := console.Current()
if err := con.SetRaw(); err != nil {
return errors.Errorf("failed to configure terminal: %v", err)
}
defer con.Reset()
}
go io.Copy(pStdin, stdin)
eg, _ := errgroup.WithContext(context.TODO())
eg.Go(func() error { _, err := io.Copy(stdout, pStdout); return err })
eg.Go(func() error { _, err := io.Copy(stderr, pStderr); return err })
if err := eg.Wait(); err != nil {
return err
}
fmt.Fprintf(stderr, "exec finished\n")
return nil
}
func serveContainerIO(ctx context.Context, root string) (io.ReadCloser, io.WriteCloser, io.WriteCloser, func(), error) {
stdin, stdout, stderr, err := openFifosServer(ctx, root)
if err != nil {
return nil, nil, nil, nil, err
}
return stdin, stdout, stderr, func() {
stdin.Close()
stdout.Close()
stderr.Close()
}, nil
}
func openFifosClient(ctx context.Context, fifosDir string) (stdin io.WriteCloser, stdout, stderr io.ReadCloser, retErr error) {
if stdin, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdin"), syscall.O_WRONLY, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdin fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdin != nil {
stdin.Close()
}
}()
if stdout, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdout"), syscall.O_RDONLY, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdout fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdout != nil {
stdout.Close()
}
}()
if stderr, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stderr"), syscall.O_RDONLY, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stderr fifo: %v", retErr)
}
defer func() {
if retErr != nil && stderr != nil {
stderr.Close()
}
}()
return stdin, stdout, stderr, nil
}
func openFifosServer(ctx context.Context, fifosDir string) (stdin io.ReadCloser, stdout, stderr io.WriteCloser, retErr error) {
if stdin, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdin"), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdin fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdin != nil {
stdin.Close()
}
}()
if stdout, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stdout"), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stdout fifo: %v", retErr)
}
defer func() {
if retErr != nil && stdout != nil {
stdout.Close()
}
}()
if stderr, retErr = fifo.OpenFifo(ctx, filepath.Join(fifosDir, "stderr"), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil {
return nil, nil, nil, errors.Errorf("failed to open stderr fifo: %v", retErr)
}
defer func() {
if retErr != nil && stderr != nil {
stderr.Close()
}
}()
return stdin, stdout, stderr, nil
}