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