// Package fd provides a simple API to pass file descriptors// between different OS processes.//// It can be useful if you want to inherit network connections// from another process without closing them.//// Example scenario://// 1) Running server receives a "let's upgrade" message// 2) Server opens a Unix domain socket for the "upgrade"// 3) Server starts a new copy of itself and passes Unix domain socket name// 4) New copy starts reading for the socket// 5) Server sends its state over the socket, also sending the number// of network connections to inherit, then it sends those connections// using fd.Put()// 6) New copy reads the state and inherits connections using fd.Get(),// checks that everything is OK and sends the "OK" message to the socket// 7) Server receives "OK" message and kills itselfpackage fdimport ( "net" "os" "syscall")// Get receives file descriptors from a Unix domain socket.//// Num specifies the expected number of file descriptors in one message.// Internal files' names to be assigned are specified via optional filenames// argument.//// You need to close all files in the returned slice. The slice can be// non-empty even if this function returns an error.//// Use net.FileConn() if you're receiving a network connection.func Get(via *net.UnixConn, num int, filenames []string) ([]*os.File, error) { if num < 1 { return nil, nil } // get the underlying socket viaf, err := via.File() if err != nil { return nil, err } socket := int(viaf.Fd()) defer viaf.Close() // recvmsg buf := make([]byte, syscall.CmsgSpace(num*4)) _, _, _, _, err = syscall.Recvmsg(socket, nil, buf, 0) if err != nil { return nil, err } // parse control msgs var msgs []syscall.SocketControlMessage msgs, err = syscall.ParseSocketControlMessage(buf) // convert fds to files res := make([]*os.File, 0, len(msgs)) for i := 0; i < len(msgs) && err == nil; i++ { var fds []int fds, err = syscall.ParseUnixRights(&msgs[i]) for fi, fd := range fds { var filename string if fi < len(filenames) { filename = filenames[fi] } res = append(res, os.NewFile(uintptr(fd), filename)) } } return res, err}// Put sends file descriptors to Unix domain socket.//// Please note that the number of descriptors in one message is limited// and is rather small.// Use conn.File() to get a file if you want to put a network connection.func Put(via *net.UnixConn, files ...*os.File) error { if len(files) == 0 { return nil } viaf, err := via.File() if err != nil { return err } socket := int(viaf.Fd()) defer viaf.Close() fds := make([]int, len(files)) for i := range files { fds[i] = int(files[i].Fd()) } rights := syscall.UnixRights(fds...) return syscall.Sendmsg(socket, nil, rights, nil, 0)}
// Package fd provides a simple API to pass file descriptors
// between different OS processes.
//
// It can be useful if you want to inherit network connections
// from another process without closing them.
//
// Example scenario:
//
// 1) Running server receives a "let's upgrade" message
// 2) Server opens a Unix domain socket for the "upgrade"
// 3) Server starts a new copy of itself and passes Unix domain socket name
// 4) New copy starts reading for the socket
// 5) Server sends its state over the socket, also sending the number
// of network connections to inherit, then it sends those connections
// using fd.Put()
// 6) New copy reads the state and inherits connections using fd.Get(),
// checks that everything is OK and sends the "OK" message to the socket
// 7) Server receives "OK" message and kills itself
package fd
import (
"net"
"os"
"syscall"
)
// Get receives file descriptors from a Unix domain socket.
//
// Num specifies the expected number of file descriptors in one message.
// Internal files' names to be assigned are specified via optional filenames
// argument.
//
// You need to close all files in the returned slice. The slice can be
// non-empty even if this function returns an error.
//
// Use net.FileConn() if you're receiving a network connection.
func Get(via *net.UnixConn, num int, filenames []string) ([]*os.File, error) {
if num < 1 {
return nil, nil
}
// get the underlying socket
viaf, err := via.File()
if err != nil {
return nil, err
}
socket := int(viaf.Fd())
defer viaf.Close()
// recvmsg
buf := make([]byte, syscall.CmsgSpace(num*4))
_, _, _, _, err = syscall.Recvmsg(socket, nil, buf, 0)
if err != nil {
return nil, err
}
// parse control msgs
var msgs []syscall.SocketControlMessage
msgs, err = syscall.ParseSocketControlMessage(buf)
// convert fds to files
res := make([]*os.File, 0, len(msgs))
for i := 0; i < len(msgs) && err == nil; i++ {
var fds []int
fds, err = syscall.ParseUnixRights(&msgs[i])
for fi, fd := range fds {
var filename string
if fi < len(filenames) {
filename = filenames[fi]
}
res = append(res, os.NewFile(uintptr(fd), filename))
}
}
return res, err
}
// Put sends file descriptors to Unix domain socket.
//
// Please note that the number of descriptors in one message is limited
// and is rather small.
// Use conn.File() to get a file if you want to put a network connection.
func Put(via *net.UnixConn, files ...*os.File) error {
if len(files) == 0 {
return nil
}
viaf, err := via.File()
if err != nil {
return err
}
socket := int(viaf.Fd())
defer viaf.Close()
fds := make([]int, len(files))
for i := range files {
fds[i] = int(files[i].Fd())
}
rights := syscall.UnixRights(fds...)
return syscall.Sendmsg(socket, nil, rights, nil, 0)
}