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 }