Fix unit tests
[govpp.git] / vendor / github.com / ftrvxmtrx / fd / fd.go
1 // Package fd provides a simple API to pass file descriptors
2 // between different OS processes.
3 //
4 // It can be useful if you want to inherit network connections
5 // from another process without closing them.
6 //
7 // Example scenario:
8 //
9 //   1) Running server receives a "let's upgrade" message
10 //   2) Server opens a Unix domain socket for the "upgrade"
11 //   3) Server starts a new copy of itself and passes Unix domain socket name
12 //   4) New copy starts reading for the socket
13 //   5) Server sends its state over the socket, also sending the number
14 //      of network connections to inherit, then it sends those connections
15 //      using fd.Put()
16 //   6) New copy reads the state and inherits connections using fd.Get(),
17 //      checks that everything is OK and sends the "OK" message to the socket
18 //   7) Server receives "OK" message and kills itself
19 package fd
20
21 import (
22         "net"
23         "os"
24         "syscall"
25 )
26
27 // Get receives file descriptors from a Unix domain socket.
28 //
29 // Num specifies the expected number of file descriptors in one message.
30 // Internal files' names to be assigned are specified via optional filenames
31 // argument.
32 //
33 // You need to close all files in the returned slice. The slice can be
34 // non-empty even if this function returns an error.
35 //
36 // Use net.FileConn() if you're receiving a network connection.
37 func Get(via *net.UnixConn, num int, filenames []string) ([]*os.File, error) {
38         if num < 1 {
39                 return nil, nil
40         }
41
42         // get the underlying socket
43         viaf, err := via.File()
44         if err != nil {
45                 return nil, err
46         }
47         socket := int(viaf.Fd())
48         defer viaf.Close()
49
50         // recvmsg
51         buf := make([]byte, syscall.CmsgSpace(num*4))
52         _, _, _, _, err = syscall.Recvmsg(socket, nil, buf, 0)
53         if err != nil {
54                 return nil, err
55         }
56
57         // parse control msgs
58         var msgs []syscall.SocketControlMessage
59         msgs, err = syscall.ParseSocketControlMessage(buf)
60
61         // convert fds to files
62         res := make([]*os.File, 0, len(msgs))
63         for i := 0; i < len(msgs) && err == nil; i++ {
64                 var fds []int
65                 fds, err = syscall.ParseUnixRights(&msgs[i])
66
67                 for fi, fd := range fds {
68                         var filename string
69                         if fi < len(filenames) {
70                                 filename = filenames[fi]
71                         }
72
73                         res = append(res, os.NewFile(uintptr(fd), filename))
74                 }
75         }
76
77         return res, err
78 }
79
80 // Put sends file descriptors to Unix domain socket.
81 //
82 // Please note that the number of descriptors in one message is limited
83 // and is rather small.
84 // Use conn.File() to get a file if you want to put a network connection.
85 func Put(via *net.UnixConn, files ...*os.File) error {
86         if len(files) == 0 {
87                 return nil
88         }
89
90         viaf, err := via.File()
91         if err != nil {
92                 return err
93         }
94         socket := int(viaf.Fd())
95         defer viaf.Close()
96
97         fds := make([]int, len(files))
98         for i := range files {
99                 fds[i] = int(files[i].Fd())
100         }
101
102         rights := syscall.UnixRights(fds...)
103         return syscall.Sendmsg(socket, nil, rights, nil, 0)
104 }