github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/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 Link struct {
    13  	*Writer
    14  
    15  	exitStatus io.Reader
    16  	streaming  *sync.WaitGroup
    17  }
    18  
    19  func Create(socketPath string, stdout io.Writer, stderr io.Writer) (*Link, error) {
    20  	conn, err := net.Dial("unix", socketPath)
    21  	if err != nil {
    22  		return nil, fmt.Errorf("failed to connect to i/o daemon: %s", err)
    23  	}
    24  
    25  	var b [2048]byte
    26  	var oob [2048]byte
    27  
    28  	n, oobn, _, _, err := conn.(*net.UnixConn).ReadMsgUnix(b[:], oob[:])
    29  	if err != nil {
    30  		return nil, fmt.Errorf("failed to read unix msg: %s (read: %d, %d)", err, n, oobn)
    31  	}
    32  
    33  	scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
    34  	if err != nil {
    35  		return nil, fmt.Errorf("failed to parse socket control message: %s", err)
    36  	}
    37  
    38  	if len(scms) < 1 {
    39  		return nil, fmt.Errorf("no socket control messages sent")
    40  	}
    41  
    42  	scm := scms[0]
    43  
    44  	fds, err := syscall.ParseUnixRights(&scm)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("failed to parse unix rights: %s", err)
    47  	}
    48  
    49  	if len(fds) != 3 {
    50  		return nil, fmt.Errorf("invalid number of fds; need 3, got %d", len(fds))
    51  	}
    52  
    53  	lstdout := os.NewFile(uintptr(fds[0]), "stdout")
    54  	lstderr := os.NewFile(uintptr(fds[1]), "stderr")
    55  	lstatus := os.NewFile(uintptr(fds[2]), "status")
    56  
    57  	streaming := &sync.WaitGroup{}
    58  
    59  	linkWriter := NewWriter(conn)
    60  
    61  	streaming.Add(1)
    62  	go func() {
    63  		io.Copy(stdout, lstdout)
    64  		lstdout.Close()
    65  		streaming.Done()
    66  	}()
    67  
    68  	streaming.Add(1)
    69  	go func() {
    70  		io.Copy(stderr, lstderr)
    71  		lstderr.Close()
    72  		streaming.Done()
    73  	}()
    74  
    75  	return &Link{
    76  		Writer: linkWriter,
    77  
    78  		exitStatus: lstatus,
    79  		streaming:  streaming,
    80  	}, nil
    81  }
    82  
    83  func (link *Link) Wait() (int, error) {
    84  	link.streaming.Wait()
    85  
    86  	var exitStatus int
    87  	_, err := fmt.Fscanf(link.exitStatus, "%d\n", &exitStatus)
    88  	if err != nil {
    89  		return -1, fmt.Errorf("could not determine exit status: %s", err)
    90  	}
    91  
    92  	return exitStatus, nil
    93  }