github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/iodaemon/link/link.go (about)

     1  package link
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"sync"
     9  	"syscall"
    10  )
    11  
    12  type SignalMsg struct {
    13  	Signal syscall.Signal `json:"signal"`
    14  }
    15  
    16  type Link struct {
    17  	*Writer
    18  
    19  	exitStatus io.ReadCloser
    20  	done       <-chan struct{}
    21  }
    22  
    23  func Create(socketPath string, stdout io.Writer, stderr io.Writer) (*Link, error) {
    24  	conn, err := net.Dial("unix", socketPath)
    25  	if err != nil {
    26  		return nil, fmt.Errorf("failed to connect to i/o daemon: %s", err)
    27  	}
    28  
    29  	var b [2048]byte
    30  	var oob [2048]byte
    31  
    32  	n, oobn, _, _, err := conn.(*net.UnixConn).ReadMsgUnix(b[:], oob[:])
    33  	if err != nil {
    34  		return nil, fmt.Errorf("failed to read unix msg: %s (read: %d, %d)", err, n, oobn)
    35  	}
    36  
    37  	scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
    38  	if err != nil {
    39  		return nil, fmt.Errorf("failed to parse socket control message: %s", err)
    40  	}
    41  
    42  	if len(scms) < 1 {
    43  		return nil, fmt.Errorf("no socket control messages sent")
    44  	}
    45  
    46  	scm := scms[0]
    47  
    48  	fds, err := syscall.ParseUnixRights(&scm)
    49  	if err != nil {
    50  		return nil, fmt.Errorf("failed to parse unix rights: %s", err)
    51  	}
    52  
    53  	if len(fds) != 3 {
    54  		return nil, fmt.Errorf("invalid number of fds; need 3, got %d", len(fds))
    55  	}
    56  
    57  	for _, fd := range fds {
    58  		syscall.CloseOnExec(fd)
    59  	}
    60  
    61  	lstdout := os.NewFile(uintptr(fds[0]), "stdout")
    62  	lstderr := os.NewFile(uintptr(fds[1]), "stderr")
    63  	lstatus := os.NewFile(uintptr(fds[2]), "status")
    64  
    65  	streaming := &sync.WaitGroup{}
    66  
    67  	linkWriter := NewWriter(conn)
    68  
    69  	streaming.Add(1)
    70  	go func() {
    71  		io.Copy(stdout, lstdout)
    72  		lstdout.Close()
    73  		streaming.Done()
    74  	}()
    75  
    76  	streaming.Add(1)
    77  	go func() {
    78  		io.Copy(stderr, lstderr)
    79  		lstderr.Close()
    80  		streaming.Done()
    81  	}()
    82  
    83  	done := make(chan struct{})
    84  	go func() {
    85  		streaming.Wait()
    86  		close(done)
    87  		conn.Close()
    88  	}()
    89  
    90  	return &Link{
    91  		Writer: linkWriter,
    92  
    93  		exitStatus: lstatus,
    94  		done:       done,
    95  	}, nil
    96  }
    97  
    98  func (link *Link) Wait() (int, error) {
    99  	<-link.done
   100  	defer link.exitStatus.Close()
   101  
   102  	var exitStatus int
   103  	_, err := fmt.Fscanf(link.exitStatus, "%d\n", &exitStatus)
   104  	if err != nil {
   105  		return -1, fmt.Errorf("could not determine exit status: %s", err)
   106  	}
   107  
   108  	return exitStatus, nil
   109  }