104 lines · 2.8 KB · Go
RawDownload
// 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 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)}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t