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  }