I recently needed a scheme to pass a file descriptor between two (or more) golang processes. This was in the context of
wanting to share some memfd buffers as a scheme for sharing
a large chunk of in memory data between those processes.
On Unix platforms this can be done by via SocketControlMessage over a Unix domain socket.
b, err := memfd.Create("memexample", 0)
if err != nil {
panic(err)
}
defer b.Close()
c, err := net.Dial("unix", "/tmp/memexample.sock")
if err != nil {
panic(err)
}
defer c.Close()
// send the fd that contains our mapped memory buffer to the server process
err = unixrights.SendFD(c.(*net.UnixConn), b.Fd())
if err != nil {
panic(err)
}
And here is the associated Send and Get code:
package unixrights
import (
"fmt"
"net"
"syscall"
)
// SendFD sends a file descriptor via a Unix domain socket connection using ancillary data, this transfers/shares a
// file descriptor from one process
// It takes a UnixConn object and the file descriptor to send as parameters.
// Returns an error if sending the file descriptor fails.
func SendFD(c *net.UnixConn, fd uintptr) error {
rights := syscall.UnixRights(int(fd))
sockFile, err := c.File()
if err != nil {
return err
}
err = syscall.Sendmsg(int(sockFile.Fd()), nil, rights, nil, 0)
if err != nil {
return err
}
return nil
}
// GetFD extracts a file descriptor sent over a Unix domain socket connection.
// It returns the received file descriptor as uintptr or an error if the operation fails.
func GetFD(c *net.UnixConn) (uintptr, error) {
sockFile, err := c.File()
if err != nil {
return 0, err
}
buf := make([]byte, syscall.CmsgSpace(4))
_, _, _, _, err = syscall.Recvmsg(int(sockFile.Fd()), nil, buf, 0)
if err != nil {
return 0, err
}
// parse control msgs
var msgs []syscall.SocketControlMessage
msgs, err = syscall.ParseSocketControlMessage(buf)
// convert fds to files
for i := 0; i < len(msgs) && err == nil; i++ {
var fds []int
fds, err = syscall.ParseUnixRights(&msgs[i])
for _, fd := range fds {
fmt.Printf("fd: %d\n", fd)
}
return uintptr(fds[0]), nil
}
return 0, err
}