aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/ftrvxmtrx/fd/fd.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ftrvxmtrx/fd/fd.go')
-rw-r--r--vendor/github.com/ftrvxmtrx/fd/fd.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/vendor/github.com/ftrvxmtrx/fd/fd.go b/vendor/github.com/ftrvxmtrx/fd/fd.go
new file mode 100644
index 0000000..a5a4d48
--- /dev/null
+++ b/vendor/github.com/ftrvxmtrx/fd/fd.go
@@ -0,0 +1,104 @@
+// 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)
+}