Golang and Unix rights

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
}