blob: 9447d4c6f338867e639959e8ddfa503dcdebb33b [file] [log] [blame]
Ryan Brownfda311b2014-08-28 13:24:13 -07001// Package unixfd provides provides support for Dialing and Listening
2// on already connected file descriptors (like those returned by socketpair).
3package unixfd
4
5import (
6 "errors"
7 "fmt"
8 "io"
9 "net"
10 "os"
11 "strconv"
12 "sync"
13 "syscall"
14 "veyron2/ipc/stream"
15)
16
17const Network string = "unixfd"
18
19// singleConnListener implements net.Listener for an already-connected socket.
20// This is different from net.FileListener, which calls syscall.Listen
21// on an unconnected socket.
22type singleConnListener struct {
23 c chan net.Conn
24 addr net.Addr
25 sync.Mutex
26}
27
28func (l *singleConnListener) getChan() chan net.Conn {
29 l.Lock()
30 defer l.Unlock()
31 return l.c
32}
33
34func (l *singleConnListener) Accept() (net.Conn, error) {
35 c := l.getChan()
36 if c == nil {
37 return nil, errors.New("listener closed")
38 }
39 if conn, ok := <-c; ok {
40 return conn, nil
41 }
42 return nil, io.EOF
43}
44
45func (l *singleConnListener) Close() error {
46 l.Lock()
47 defer l.Unlock()
48 lc := l.c
49 if lc == nil {
50 return errors.New("listener already closed")
51 }
52 close(l.c)
53 l.c = nil
54 // If the socket was never Accept'ed we need to close it.
55 if c, ok := <-lc; ok {
56 return c.Close()
57 }
58 return nil
59}
60
61func (l *singleConnListener) Addr() net.Addr {
62 return l.addr
63}
64
65func unixFDConn(address string) (net.Conn, error) {
66 fd, err := strconv.ParseInt(address, 10, 32)
67 if err != nil {
68 return nil, err
69 }
70 file := os.NewFile(uintptr(fd), "tmp")
71 defer file.Close()
72 return net.FileConn(file)
73}
74
75func unixFDListen(address string) (net.Listener, error) {
76 conn, err := unixFDConn(address)
77 if err != nil {
78 return nil, err
79 }
80 c := make(chan net.Conn, 1)
81 c <- conn
82 return &singleConnListener{c, conn.LocalAddr(), sync.Mutex{}}, nil
83}
84
85type addr string
86
87func (a addr) Network() string { return Network }
88func (a addr) String() string { return string(a) }
89
90// Addr returns a net.Addr for the unixfd network for the given file descriptor.
91func Addr(fd uintptr) net.Addr {
92 return addr(fmt.Sprintf("%d", fd))
93}
94
95// Socketpair returns two connected unix domain sockets, or an error.
96func Socketpair() ([]*os.File, error) {
97 fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
98 if err != nil {
99 return nil, err
100 }
101 return []*os.File{os.NewFile(uintptr(fds[0]), "local"), os.NewFile(uintptr(fds[1]), "remote")}, nil
102}
103
104func init() {
105 stream.RegisterProtocol(Network, unixFDConn, unixFDListen)
106}