2014-03-11 19:45:58 -04:00
|
|
|
package beam
|
|
|
|
|
|
|
|
import (
|
2014-04-22 19:09:42 -04:00
|
|
|
"bufio"
|
2014-03-11 19:45:58 -04:00
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
2014-03-11 20:15:26 -04:00
|
|
|
"syscall"
|
2014-03-11 19:45:58 -04:00
|
|
|
)
|
|
|
|
|
2014-03-23 23:41:28 -04:00
|
|
|
func debugCheckpoint(msg string, args ...interface{}) {
|
2014-03-25 13:00:23 -04:00
|
|
|
if os.Getenv("DEBUG") == "" {
|
|
|
|
return
|
|
|
|
}
|
2014-03-23 23:41:28 -04:00
|
|
|
os.Stdout.Sync()
|
2014-04-22 19:09:42 -04:00
|
|
|
tty, _ := os.OpenFile("/dev/tty", os.O_RDWR, 0700)
|
2014-03-23 23:41:28 -04:00
|
|
|
fmt.Fprintf(tty, msg, args...)
|
|
|
|
bufio.NewScanner(tty).Scan()
|
|
|
|
tty.Close()
|
|
|
|
}
|
|
|
|
|
2014-03-28 19:57:48 -04:00
|
|
|
type UnixConn struct {
|
|
|
|
*net.UnixConn
|
2014-03-31 05:06:39 -04:00
|
|
|
fds []*os.File
|
|
|
|
}
|
|
|
|
|
|
|
|
// Framing:
|
|
|
|
// In order to handle framing in Send/Recieve, as these give frame
|
|
|
|
// boundaries we use a very simple 4 bytes header. It is a big endiand
|
|
|
|
// uint32 where the high bit is set if the message includes a file
|
|
|
|
// descriptor. The rest of the uint32 is the length of the next frame.
|
|
|
|
// We need the bit in order to be able to assign recieved fds to
|
|
|
|
// the right message, as multiple messages may be coalesced into
|
|
|
|
// a single recieve operation.
|
|
|
|
func makeHeader(data []byte, fds []int) ([]byte, error) {
|
|
|
|
header := make([]byte, 4)
|
|
|
|
|
|
|
|
length := uint32(len(data))
|
|
|
|
|
|
|
|
if length > 0x7fffffff {
|
|
|
|
return nil, fmt.Errorf("Data to large")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(fds) != 0 {
|
|
|
|
length = length | 0x80000000
|
|
|
|
}
|
|
|
|
header[0] = byte((length >> 24) & 0xff)
|
|
|
|
header[1] = byte((length >> 16) & 0xff)
|
|
|
|
header[2] = byte((length >> 8) & 0xff)
|
|
|
|
header[3] = byte((length >> 0) & 0xff)
|
|
|
|
|
|
|
|
return header, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseHeader(header []byte) (uint32, bool) {
|
|
|
|
length := uint32(header[0])<<24 | uint32(header[1])<<16 | uint32(header[2])<<8 | uint32(header[3])
|
|
|
|
hasFd := length&0x80000000 != 0
|
|
|
|
length = length & ^uint32(0x80000000)
|
|
|
|
|
|
|
|
return length, hasFd
|
2014-03-28 19:57:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func FileConn(f *os.File) (*UnixConn, error) {
|
|
|
|
conn, err := net.FileConn(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
uconn, ok := conn.(*net.UnixConn)
|
|
|
|
if !ok {
|
|
|
|
conn.Close()
|
|
|
|
return nil, fmt.Errorf("%d: not a unix connection", f.Fd())
|
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
return &UnixConn{UnixConn: uconn}, nil
|
2014-03-28 19:57:48 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-03-11 19:45:58 -04:00
|
|
|
// Send sends a new message on conn with data and f as payload and
|
|
|
|
// attachment, respectively.
|
2014-03-25 08:32:44 -04:00
|
|
|
// On success, f is closed
|
2014-03-28 19:57:48 -04:00
|
|
|
func (conn *UnixConn) Send(data []byte, f *os.File) error {
|
2014-03-23 23:41:28 -04:00
|
|
|
{
|
|
|
|
var fd int = -1
|
|
|
|
if f != nil {
|
|
|
|
fd = int(f.Fd())
|
|
|
|
}
|
|
|
|
debugCheckpoint("===DEBUG=== about to send '%s'[%d]. Hit enter to confirm: ", data, fd)
|
|
|
|
}
|
2014-03-18 20:05:44 -04:00
|
|
|
var fds []int
|
|
|
|
if f != nil {
|
|
|
|
fds = append(fds, int(f.Fd()))
|
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
if err := conn.sendUnix(data, fds...); err != nil {
|
2014-03-25 08:32:44 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if f != nil {
|
|
|
|
f.Close()
|
|
|
|
}
|
|
|
|
return nil
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Receive waits for a new message on conn, and receives its payload
|
|
|
|
// and attachment, or an error if any.
|
|
|
|
//
|
|
|
|
// If more than 1 file descriptor is sent in the message, they are all
|
|
|
|
// closed except for the first, which is the attachment.
|
|
|
|
// It is legal for a message to have no attachment or an empty payload.
|
2014-03-28 19:57:48 -04:00
|
|
|
func (conn *UnixConn) Receive() (rdata []byte, rf *os.File, rerr error) {
|
2014-03-23 23:41:28 -04:00
|
|
|
defer func() {
|
|
|
|
var fd int = -1
|
|
|
|
if rf != nil {
|
|
|
|
fd = int(rf.Fd())
|
|
|
|
}
|
|
|
|
debugCheckpoint("===DEBUG=== Receive() -> '%s'[%d]. Hit enter to continue.\n", rdata, fd)
|
|
|
|
}()
|
2014-03-31 05:06:39 -04:00
|
|
|
|
|
|
|
// Read header
|
|
|
|
header := make([]byte, 4)
|
|
|
|
nRead := uint32(0)
|
|
|
|
|
|
|
|
for nRead < 4 {
|
|
|
|
n, err := conn.receiveUnix(header[nRead:])
|
2014-03-11 19:45:58 -04:00
|
|
|
if err != nil {
|
2014-03-21 22:20:26 -04:00
|
|
|
return nil, nil, err
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
nRead = nRead + uint32(n)
|
|
|
|
}
|
|
|
|
|
|
|
|
length, hasFd := parseHeader(header)
|
|
|
|
|
|
|
|
if hasFd {
|
|
|
|
if len(conn.fds) == 0 {
|
|
|
|
return nil, nil, fmt.Errorf("No expected file descriptor in message")
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
|
|
|
|
rf = conn.fds[0]
|
|
|
|
conn.fds = conn.fds[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
rdata = make([]byte, length)
|
|
|
|
|
|
|
|
nRead = 0
|
|
|
|
for nRead < length {
|
|
|
|
n, err := conn.receiveUnix(rdata[nRead:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
2014-03-18 20:05:44 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
nRead = nRead + uint32(n)
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
|
|
|
|
return
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
|
|
|
|
2014-03-31 05:06:39 -04:00
|
|
|
func (conn *UnixConn) receiveUnix(buf []byte) (int, error) {
|
|
|
|
oob := make([]byte, syscall.CmsgSpace(4))
|
2014-03-11 19:45:58 -04:00
|
|
|
bufn, oobn, _, _, err := conn.ReadMsgUnix(buf, oob)
|
|
|
|
if err != nil {
|
2014-03-31 05:06:39 -04:00
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
fd := extractFd(oob[:oobn])
|
|
|
|
if fd != -1 {
|
|
|
|
f := os.NewFile(uintptr(fd), "")
|
|
|
|
conn.fds = append(conn.fds, f)
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
|
|
|
|
return bufn, nil
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
|
|
|
|
2014-03-31 05:06:39 -04:00
|
|
|
func (conn *UnixConn) sendUnix(data []byte, fds ...int) error {
|
|
|
|
header, err := makeHeader(data, fds)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// There is a bug in conn.WriteMsgUnix where it doesn't correctly return
|
|
|
|
// the number of bytes writte (http://code.google.com/p/go/issues/detail?id=7645)
|
|
|
|
// So, we can't rely on the return value from it. However, we must use it to
|
|
|
|
// send the fds. In order to handle this we only write one byte using WriteMsgUnix
|
|
|
|
// (when we have to), as that can only ever block or fully suceed. We then write
|
|
|
|
// the rest with conn.Write()
|
|
|
|
// The reader side should not rely on this though, as hopefully this gets fixed
|
|
|
|
// in go later.
|
|
|
|
written := 0
|
|
|
|
if len(fds) != 0 {
|
|
|
|
oob := syscall.UnixRights(fds...)
|
|
|
|
wrote, _, err := conn.WriteMsgUnix(header[0:1], oob, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
written = written + wrote
|
|
|
|
}
|
|
|
|
|
|
|
|
for written < len(header) {
|
|
|
|
wrote, err := conn.Write(header[written:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
written = written + wrote
|
|
|
|
}
|
|
|
|
|
|
|
|
written = 0
|
|
|
|
for written < len(data) {
|
|
|
|
wrote, err := conn.Write(data[written:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
written = written + wrote
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
|
|
|
|
2014-03-31 05:06:39 -04:00
|
|
|
func extractFd(oob []byte) int {
|
2014-03-24 10:33:22 -04:00
|
|
|
// Grab forklock to make sure no forks accidentally inherit the new
|
|
|
|
// fds before they are made CLOEXEC
|
|
|
|
// There is a slight race condition between ReadMsgUnix returns and
|
|
|
|
// when we grap the lock, so this is not perfect. Unfortunately
|
|
|
|
// There is no way to pass MSG_CMSG_CLOEXEC to recvmsg() nor any
|
|
|
|
// way to implement non-blocking i/o in go, so this is hard to fix.
|
|
|
|
syscall.ForkLock.Lock()
|
|
|
|
defer syscall.ForkLock.Unlock()
|
2014-03-11 19:45:58 -04:00
|
|
|
scms, err := syscall.ParseSocketControlMessage(oob)
|
|
|
|
if err != nil {
|
2014-03-31 05:06:39 -04:00
|
|
|
return -1
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
|
|
|
|
foundFd := -1
|
2014-03-11 19:45:58 -04:00
|
|
|
for _, scm := range scms {
|
2014-03-31 05:06:39 -04:00
|
|
|
fds, err := syscall.ParseUnixRights(&scm)
|
2014-03-11 19:45:58 -04:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2014-03-24 10:33:22 -04:00
|
|
|
|
|
|
|
for _, fd := range fds {
|
2014-03-31 05:06:39 -04:00
|
|
|
if foundFd == -1 {
|
|
|
|
syscall.CloseOnExec(fd)
|
|
|
|
foundFd = fd
|
|
|
|
} else {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
2014-03-24 10:33:22 -04:00
|
|
|
}
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
2014-03-31 05:06:39 -04:00
|
|
|
|
|
|
|
return foundFd
|
2014-03-11 19:45:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func socketpair() ([2]int, error) {
|
|
|
|
return syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.FD_CLOEXEC, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SocketPair is a convenience wrapper around the socketpair(2) syscall.
|
|
|
|
// It returns a unix socket of type SOCK_STREAM in the form of 2 file descriptors
|
|
|
|
// not bound to the underlying filesystem.
|
|
|
|
// Messages sent on one end are received on the other, and vice-versa.
|
|
|
|
// It is the caller's responsibility to close both ends.
|
2014-03-25 13:00:23 -04:00
|
|
|
func SocketPair() (a *os.File, b *os.File, err error) {
|
|
|
|
defer func() {
|
|
|
|
var (
|
|
|
|
fdA int = -1
|
|
|
|
fdB int = -1
|
|
|
|
)
|
|
|
|
if a != nil {
|
|
|
|
fdA = int(a.Fd())
|
|
|
|
}
|
|
|
|
if b != nil {
|
|
|
|
fdB = int(b.Fd())
|
|
|
|
}
|
|
|
|
debugCheckpoint("===DEBUG=== SocketPair() = [%d-%d]. Hit enter to confirm: ", fdA, fdB)
|
|
|
|
}()
|
2014-03-11 19:45:58 -04:00
|
|
|
pair, err := socketpair()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return os.NewFile(uintptr(pair[0]), ""), os.NewFile(uintptr(pair[1]), ""), nil
|
|
|
|
}
|
|
|
|
|
2014-03-28 19:57:48 -04:00
|
|
|
func USocketPair() (*UnixConn, *UnixConn, error) {
|
2014-03-25 13:00:23 -04:00
|
|
|
debugCheckpoint("===DEBUG=== USocketPair(). Hit enter to confirm: ")
|
2014-04-22 19:09:42 -04:00
|
|
|
defer debugCheckpoint("===DEBUG=== USocketPair() returned. Hit enter to confirm ")
|
2014-03-18 20:11:03 -04:00
|
|
|
a, b, err := SocketPair()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2014-03-25 13:00:51 -04:00
|
|
|
defer a.Close()
|
|
|
|
defer b.Close()
|
2014-03-28 19:57:48 -04:00
|
|
|
uA, err := FileConn(a)
|
2014-03-18 20:11:03 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2014-03-28 19:57:48 -04:00
|
|
|
uB, err := FileConn(b)
|
2014-03-18 20:11:03 -04:00
|
|
|
if err != nil {
|
2014-03-25 13:00:51 -04:00
|
|
|
uA.Close()
|
2014-03-18 20:11:03 -04:00
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return uA, uB, nil
|
|
|
|
}
|
|
|
|
|
2014-03-11 19:45:58 -04:00
|
|
|
// FdConn wraps a file descriptor in a standard *net.UnixConn object, or
|
|
|
|
// returns an error if the file descriptor does not point to a unix socket.
|
2014-03-23 23:45:44 -04:00
|
|
|
// This creates a duplicate file descriptor. It's the caller's responsibility
|
|
|
|
// to close both.
|
2014-04-22 19:09:42 -04:00
|
|
|
func FdConn(fd int) (n *net.UnixConn, err error) {
|
2014-03-25 13:00:23 -04:00
|
|
|
{
|
|
|
|
debugCheckpoint("===DEBUG=== FdConn([%d]) = (unknown fd). Hit enter to confirm: ", fd)
|
|
|
|
}
|
2014-03-11 19:45:58 -04:00
|
|
|
f := os.NewFile(uintptr(fd), fmt.Sprintf("%d", fd))
|
|
|
|
conn, err := net.FileConn(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
uconn, ok := conn.(*net.UnixConn)
|
|
|
|
if !ok {
|
2014-03-18 20:09:09 -04:00
|
|
|
conn.Close()
|
2014-03-11 19:45:58 -04:00
|
|
|
return nil, fmt.Errorf("%d: not a unix connection", fd)
|
|
|
|
}
|
|
|
|
return uconn, nil
|
|
|
|
}
|