From da15c397b3dbbba07d159b3af767aa13d443cfd6 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Thu, 20 Jun 2019 18:52:30 +0200 Subject: Add statsclient - pure Go implementation for stats API Change-Id: Ia5bab652c6089378697459f477f9060dc7a53e90 Signed-off-by: Ondrej Fabry --- vendor/github.com/ftrvxmtrx/fd/fd.go | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 vendor/github.com/ftrvxmtrx/fd/fd.go (limited to 'vendor/github.com/ftrvxmtrx/fd/fd.go') 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) +} -- cgit 1.2.3-korg