2014-03-28 23:57:48 +00:00
|
|
|
package beam
|
|
|
|
|
|
|
|
import (
|
2014-04-02 23:38:36 +00:00
|
|
|
"fmt"
|
2014-03-28 23:57:48 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Sender interface {
|
|
|
|
Send([]byte, *os.File) error
|
|
|
|
}
|
|
|
|
|
|
|
|
type Receiver interface {
|
|
|
|
Receive() ([]byte, *os.File, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ReceiveCloser interface {
|
|
|
|
Receiver
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
|
|
|
type SendCloser interface {
|
|
|
|
Sender
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:58:47 +00:00
|
|
|
type ReceiveSender interface {
|
|
|
|
Receiver
|
|
|
|
Sender
|
|
|
|
}
|
|
|
|
|
2014-05-09 22:39:55 +00:00
|
|
|
const (
|
2014-05-11 13:12:48 +00:00
|
|
|
R = iota
|
2014-05-09 22:39:55 +00:00
|
|
|
W
|
|
|
|
)
|
|
|
|
|
|
|
|
func sendPipe(dst Sender, data []byte, mode int) (*os.File, error) {
|
2014-03-28 23:57:48 +00:00
|
|
|
r, w, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-05-09 22:39:55 +00:00
|
|
|
var (
|
|
|
|
remote *os.File
|
|
|
|
local *os.File
|
|
|
|
)
|
|
|
|
if mode == R {
|
|
|
|
remote = r
|
|
|
|
local = w
|
|
|
|
} else if mode == W {
|
|
|
|
remote = w
|
|
|
|
local = r
|
|
|
|
}
|
|
|
|
if err := dst.Send(data, remote); err != nil {
|
|
|
|
local.Close()
|
|
|
|
remote.Close()
|
2014-03-28 23:57:48 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-05-09 22:39:55 +00:00
|
|
|
return local, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendRPipe create a pipe and sends its *read* end attached in a beam message
|
|
|
|
// to `dst`, with `data` as the message payload.
|
|
|
|
// It returns the *write* end of the pipe, or an error.
|
|
|
|
func SendRPipe(dst Sender, data []byte) (*os.File, error) {
|
|
|
|
return sendPipe(dst, data, R)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendWPipe create a pipe and sends its *read* end attached in a beam message
|
|
|
|
// to `dst`, with `data` as the message payload.
|
|
|
|
// It returns the *write* end of the pipe, or an error.
|
|
|
|
func SendWPipe(dst Sender, data []byte) (*os.File, error) {
|
|
|
|
return sendPipe(dst, data, W)
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
|
2014-03-31 18:58:29 +00:00
|
|
|
func SendConn(dst Sender, data []byte) (conn *UnixConn, err error) {
|
2014-03-28 23:57:48 +00:00
|
|
|
local, remote, err := SocketPair()
|
|
|
|
if err != nil {
|
2014-03-31 18:58:29 +00:00
|
|
|
return nil, err
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
local.Close()
|
|
|
|
remote.Close()
|
|
|
|
}
|
|
|
|
}()
|
2014-03-31 18:58:29 +00:00
|
|
|
conn, err = FileConn(local)
|
2014-03-28 23:57:48 +00:00
|
|
|
if err != nil {
|
2014-03-31 18:58:29 +00:00
|
|
|
return nil, err
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
local.Close()
|
|
|
|
if err := dst.Send(data, remote); err != nil {
|
2014-03-31 18:58:29 +00:00
|
|
|
return nil, err
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
2014-03-31 18:58:29 +00:00
|
|
|
return conn, nil
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
|
2014-03-31 18:58:29 +00:00
|
|
|
func ReceiveConn(src Receiver) ([]byte, *UnixConn, error) {
|
2014-03-28 23:57:48 +00:00
|
|
|
for {
|
|
|
|
data, f, err := src.Receive()
|
|
|
|
if err != nil {
|
2014-03-31 18:58:29 +00:00
|
|
|
return nil, nil, err
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
if f == nil {
|
|
|
|
// Skip empty attachments
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
conn, err := FileConn(f)
|
|
|
|
if err != nil {
|
|
|
|
// Skip beam attachments which are not connections
|
|
|
|
// (for example might be a regular file, directory etc)
|
|
|
|
continue
|
|
|
|
}
|
2014-03-31 18:58:29 +00:00
|
|
|
return data, conn, nil
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
panic("impossibru!")
|
2014-03-31 18:58:29 +00:00
|
|
|
return nil, nil, nil
|
2014-03-28 23:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func Copy(dst Sender, src Receiver) (int, error) {
|
|
|
|
var n int
|
|
|
|
for {
|
|
|
|
payload, attachment, err := src.Receive()
|
|
|
|
if err == io.EOF {
|
|
|
|
return n, nil
|
|
|
|
} else if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
if err := dst.Send(payload, attachment); err != nil {
|
|
|
|
if attachment != nil {
|
|
|
|
attachment.Close()
|
|
|
|
}
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
panic("impossibru!")
|
|
|
|
return n, nil
|
|
|
|
}
|
2014-04-02 23:38:36 +00:00
|
|
|
|
|
|
|
// MsgDesc returns a human readable description of a beam message, usually
|
|
|
|
// for debugging purposes.
|
|
|
|
func MsgDesc(payload []byte, attachment *os.File) string {
|
|
|
|
var filedesc string = "<nil>"
|
|
|
|
if attachment != nil {
|
|
|
|
filedesc = fmt.Sprintf("%d", attachment.Fd())
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("'%s'[%s]", payload, filedesc)
|
|
|
|
}
|
2014-04-03 04:58:47 +00:00
|
|
|
|
|
|
|
type devnull struct{}
|
|
|
|
|
|
|
|
func Devnull() ReceiveSender {
|
|
|
|
return devnull{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d devnull) Send(p []byte, a *os.File) error {
|
|
|
|
if a != nil {
|
|
|
|
a.Close()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d devnull) Receive() ([]byte, *os.File, error) {
|
|
|
|
return nil, nil, io.EOF
|
|
|
|
}
|