1 // Package fd provides a simple API to pass file descriptors
2 // between different OS processes.
4 // It can be useful if you want to inherit network connections
5 // from another process without closing them.
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
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
27 // Get receives file descriptors from a Unix domain socket.
29 // Num specifies the expected number of file descriptors in one message.
30 // Internal files' names to be assigned are specified via optional filenames
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.
36 // Use net.FileConn() if you're receiving a network connection.
37 func Get(via *net.UnixConn, num int, filenames []string) ([]*os.File, error) {
42 // get the underlying socket
43 viaf, err := via.File()
47 socket := int(viaf.Fd())
51 buf := make([]byte, syscall.CmsgSpace(num*4))
52 _, _, _, _, err = syscall.Recvmsg(socket, nil, buf, 0)
58 var msgs []syscall.SocketControlMessage
59 msgs, err = syscall.ParseSocketControlMessage(buf)
61 // convert fds to files
62 res := make([]*os.File, 0, len(msgs))
63 for i := 0; i < len(msgs) && err == nil; i++ {
65 fds, err = syscall.ParseUnixRights(&msgs[i])
67 for fi, fd := range fds {
69 if fi < len(filenames) {
70 filename = filenames[fi]
73 res = append(res, os.NewFile(uintptr(fd), filename))
80 // Put sends file descriptors to Unix domain socket.
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 {
90 viaf, err := via.File()
94 socket := int(viaf.Fd())
97 fds := make([]int, len(files))
98 for i := range files {
99 fds[i] = int(files[i].Fd())
102 rights := syscall.UnixRights(fds...)
103 return syscall.Sendmsg(socket, nil, rights, nil, 0)