blob: 52f84b884f096ee4bc8ef8964154cb0d4c3e0c24 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// SRPC constants, data structures, and parsing.
package srpc
import (
"math"
"os"
"strconv"
"syscall"
"unsafe"
)
// An Errno is an SRPC status code.
type Errno uint32
const (
OK Errno = 256 + iota
ErrBreak
ErrMessageTruncated
ErrNoMemory
ErrProtocolMismatch
ErrBadRPCNumber
ErrBadArgType
ErrTooFewArgs
ErrTooManyArgs
ErrInArgTypeMismatch
ErrOutArgTypeMismatch
ErrInternalError
ErrAppError
)
var errstr = [...]string{
OK - OK: "ok",
ErrBreak - OK: "break",
ErrMessageTruncated - OK: "message truncated",
ErrNoMemory - OK: "out of memory",
ErrProtocolMismatch - OK: "protocol mismatch",
ErrBadRPCNumber - OK: "invalid RPC method number",
ErrBadArgType - OK: "unexpected argument type",
ErrTooFewArgs - OK: "too few arguments",
ErrTooManyArgs - OK: "too many arguments",
ErrInArgTypeMismatch - OK: "input argument type mismatch",
ErrOutArgTypeMismatch - OK: "output argument type mismatch",
ErrInternalError - OK: "internal error",
ErrAppError - OK: "application error",
}
func (e Errno) String() string {
if e < OK || int(e-OK) >= len(errstr) {
return "Errno(" + strconv.Itoa64(int64(e)) + ")"
}
return errstr[e-OK]
}
// A *msgHdr is the data argument to the imc_recvmsg
// and imc_sendmsg system calls. Because it contains unchecked
// counts trusted by the system calls, the data structure is unsafe
// to expose to package clients.
type msgHdr struct {
iov *iov
niov int32
desc *int32
ndesc int32
flags uint32
}
// A single region for I/O. Just as unsafe as msgHdr.
type iov struct {
base *byte
len int32
}
// A msg is the Go representation of a message.
type msg struct {
rdata []byte // data being consumed during message parsing
rdesc []int32 // file descriptors being consumed during message parsing
wdata []byte // data being generated when replying
// parsed version of message
protocol uint32
requestId uint64
isReq bool
rpcNumber uint32
gotHeader bool
status Errno // error code sent in response
Arg []interface{} // method arguments
Ret []interface{} // method results
Size []int // max sizes for arrays in method results
fmt string // accumulated format string of arg+":"+ret
}
// A msgReceiver receives messages from a file descriptor.
type msgReceiver struct {
fd int
data [128 * 1024]byte
desc [8]int32
hdr msgHdr
iov iov
}
func (r *msgReceiver) recv() (*msg, os.Error) {
// Init pointers to buffers where syscall recvmsg can write.
r.iov.base = &r.data[0]
r.iov.len = int32(len(r.data))
r.hdr.iov = &r.iov
r.hdr.niov = 1
r.hdr.desc = &r.desc[0]
r.hdr.ndesc = int32(len(r.desc))
n, _, e := syscall.Syscall(syscall.SYS_IMC_RECVMSG, uintptr(r.fd), uintptr(unsafe.Pointer(&r.hdr)), 0)
if e != 0 {
return nil, os.NewSyscallError("imc_recvmsg", int(e))
}
// Make a copy of the data so that the next recvmsg doesn't
// smash it. The system call did not update r.iov.len. Instead it
// returned the total byte count as n.
m := new(msg)
m.rdata = make([]byte, n)
copy(m.rdata, &r.data)
// Make a copy of the desc too.
// The system call *did* update r.hdr.ndesc.
if r.hdr.ndesc > 0 {
m.rdesc = make([]int32, r.hdr.ndesc)
for i := range m.rdesc {
m.rdesc[i] = r.desc[i]
}
}
return m, nil
}
// A msgSender sends messages on a file descriptor.
type msgSender struct {
fd int
hdr msgHdr
iov iov
}
func (s *msgSender) send(m *msg) os.Error {
if len(m.wdata) > 0 {
s.iov.base = &m.wdata[0]
}
s.iov.len = int32(len(m.wdata))
s.hdr.iov = &s.iov
s.hdr.niov = 1
s.hdr.desc = nil
s.hdr.ndesc = 0
_, _, e := syscall.Syscall(syscall.SYS_IMC_SENDMSG, uintptr(s.fd), uintptr(unsafe.Pointer(&s.hdr)), 0)
if e != 0 {
return os.NewSyscallError("imc_sendmsg", int(e))
}
return nil
}
// Reading from msg.rdata.
func (m *msg) uint8() uint8 {
if m.status != OK {
return 0
}
if len(m.rdata) < 1 {
m.status = ErrMessageTruncated
return 0
}
x := m.rdata[0]
m.rdata = m.rdata[1:]
return x
}
func (m *msg) uint32() uint32 {
if m.status != OK {
return 0
}
if len(m.rdata) < 4 {
m.status = ErrMessageTruncated
return 0
}
b := m.rdata[0:4]
x := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
m.rdata = m.rdata[4:]
return x
}
func (m *msg) uint64() uint64 {
if m.status != OK {
return 0
}
if len(m.rdata) < 8 {
m.status = ErrMessageTruncated
return 0
}
b := m.rdata[0:8]
x := uint64(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24)
x |= uint64(uint32(b[4])|uint32(b[5])<<8|uint32(b[6])<<16|uint32(b[7])<<24) << 32
m.rdata = m.rdata[8:]
return x
}
func (m *msg) bytes(n int) []byte {
if m.status != OK {
return nil
}
if len(m.rdata) < n {
m.status = ErrMessageTruncated
return nil
}
x := m.rdata[0:n]
m.rdata = m.rdata[n:]
return x
}
// Writing to msg.wdata.
func (m *msg) grow(n int) []byte {
i := len(m.wdata)
if i+n > cap(m.wdata) {
a := make([]byte, i, (i+n)*2)
copy(a, m.wdata)
m.wdata = a
}
m.wdata = m.wdata[0 : i+n]
return m.wdata[i : i+n]
}
func (m *msg) wuint8(x uint8) { m.grow(1)[0] = x }
func (m *msg) wuint32(x uint32) {
b := m.grow(4)
b[0] = byte(x)
b[1] = byte(x >> 8)
b[2] = byte(x >> 16)
b[3] = byte(x >> 24)
}
func (m *msg) wuint64(x uint64) {
b := m.grow(8)
lo := uint32(x)
b[0] = byte(lo)
b[1] = byte(lo >> 8)
b[2] = byte(lo >> 16)
b[3] = byte(lo >> 24)
hi := uint32(x >> 32)
b[4] = byte(hi)
b[5] = byte(hi >> 8)
b[6] = byte(hi >> 16)
b[7] = byte(hi >> 24)
}
func (m *msg) wbytes(p []byte) { copy(m.grow(len(p)), p) }
func (m *msg) wstring(s string) {
b := m.grow(len(s))
for i := range b {
b[i] = s[i]
}
}
// Parsing of RPC header and arguments.
//
// The header format is:
// protocol uint32;
// requestId uint64;
// isReq bool;
// rpcNumber uint32;
// status uint32; // only for response
//
// Then a sequence of values follow, preceded by the length:
// nvalue uint32;
//
// Each value begins with a one-byte type followed by
// type-specific data.
//
// type uint8;
// 'b': x bool;
// 'C': len uint32; x [len]byte;
// 'd': x float64;
// 'D': len uint32; x [len]float64;
// 'h': x int; // handle aka file descriptor
// 'i': x int32;
// 'I': len uint32; x [len]int32;
// 's': len uint32; x [len]byte;
//
// If this is a request, a sequence of pseudo-values follows,
// preceded by its length (nvalue uint32).
//
// Each pseudo-value is a one-byte type as above,
// followed by a maximum length (len uint32)
// for the 'C', 'D', 'I', and 's' types.
//
// In the Go msg, we represent each argument by
// an empty interface containing the type of x in the
// corresponding case.
// The current protocol number.
const protocol = 0xc0da0002
func (m *msg) unpackHeader() {
m.protocol = m.uint32()
m.requestId = m.uint64()
m.isReq = m.uint8() != 0
m.rpcNumber = m.uint32()
m.gotHeader = m.status == OK // signal that header parsed successfully
if m.gotHeader && !m.isReq {
status := Errno(m.uint32())
m.gotHeader = m.status == OK // still ok?
if m.gotHeader {
m.status = status
}
}
}
func (m *msg) packHeader() {
m.wuint32(m.protocol)
m.wuint64(m.requestId)
if m.isReq {
m.wuint8(1)
} else {
m.wuint8(0)
}
m.wuint32(m.rpcNumber)
if !m.isReq {
m.wuint32(uint32(m.status))
}
}
func (m *msg) unpackValues(v []interface{}) {
for i := range v {
t := m.uint8()
m.fmt += string(t)
switch t {
default:
if m.status == OK {
m.status = ErrBadArgType
}
return
case 'b': // bool[1]
v[i] = m.uint8() > 0
case 'C': // char array
v[i] = m.bytes(int(m.uint32()))
case 'd': // double
v[i] = math.Float64frombits(m.uint64())
case 'D': // double array
a := make([]float64, int(m.uint32()))
for j := range a {
a[j] = math.Float64frombits(m.uint64())
}
v[i] = a
case 'h': // file descriptor (handle)
if len(m.rdesc) == 0 {
if m.status == OK {
m.status = ErrBadArgType
}
return
}
v[i] = int(m.rdesc[0])
m.rdesc = m.rdesc[1:]
case 'i': // int
v[i] = int32(m.uint32())
case 'I': // int array
a := make([]int32, int(m.uint32()))
for j := range a {
a[j] = int32(m.uint32())
}
v[i] = a
case 's': // string
v[i] = string(m.bytes(int(m.uint32())))
}
}
}
func (m *msg) packValues(v []interface{}) {
for i := range v {
switch x := v[i].(type) {
default:
if m.status == OK {
m.status = ErrInternalError
}
return
case bool:
m.wuint8('b')
if x {
m.wuint8(1)
} else {
m.wuint8(0)
}
case []byte:
m.wuint8('C')
m.wuint32(uint32(len(x)))
m.wbytes(x)
case float64:
m.wuint8('d')
m.wuint64(math.Float64bits(x))
case []float64:
m.wuint8('D')
m.wuint32(uint32(len(x)))
for _, f := range x {
m.wuint64(math.Float64bits(f))
}
case int32:
m.wuint8('i')
m.wuint32(uint32(x))
case []int32:
m.wuint8('I')
m.wuint32(uint32(len(x)))
for _, i := range x {
m.wuint32(uint32(i))
}
case string:
m.wuint8('s')
m.wuint32(uint32(len(x)))
m.wstring(x)
}
}
}
func (m *msg) unpackRequest() {
m.status = OK
if m.unpackHeader(); m.status != OK {
return
}
if m.protocol != protocol || !m.isReq {
m.status = ErrProtocolMismatch
return
}
// type-tagged argument values
m.Arg = make([]interface{}, m.uint32())
m.unpackValues(m.Arg)
if m.status != OK {
return
}
// type-tagged expected return sizes.
// fill in zero values for each return value
// and save sizes.
m.fmt += ":"
m.Ret = make([]interface{}, m.uint32())
m.Size = make([]int, len(m.Ret))
for i := range m.Ret {
t := m.uint8()
m.fmt += string(t)
switch t {
default:
if m.status == OK {
m.status = ErrBadArgType
}
return
case 'b': // bool[1]
m.Ret[i] = false
case 'C': // char array
m.Size[i] = int(m.uint32())
m.Ret[i] = []byte(nil)
case 'd': // double
m.Ret[i] = float64(0)
case 'D': // double array
m.Size[i] = int(m.uint32())
m.Ret[i] = []float64(nil)
case 'h': // file descriptor (handle)
m.Ret[i] = int(-1)
case 'i': // int
m.Ret[i] = int32(0)
case 'I': // int array
m.Size[i] = int(m.uint32())
m.Ret[i] = []int32(nil)
case 's': // string
m.Size[i] = int(m.uint32())
m.Ret[i] = ""
}
}
}
func (m *msg) packRequest() {
m.packHeader()
m.wuint32(uint32(len(m.Arg)))
m.packValues(m.Arg)
m.wuint32(uint32(len(m.Ret)))
for i, v := range m.Ret {
switch x := v.(type) {
case bool:
m.wuint8('b')
case []byte:
m.wuint8('C')
m.wuint32(uint32(m.Size[i]))
case float64:
m.wuint8('d')
case []float64:
m.wuint8('D')
m.wuint32(uint32(m.Size[i]))
case int:
m.wuint8('h')
case int32:
m.wuint8('i')
case []int32:
m.wuint8('I')
m.wuint32(uint32(m.Size[i]))
case string:
m.wuint8('s')
m.wuint32(uint32(m.Size[i]))
}
}
}
func (m *msg) unpackResponse() {
m.status = OK
if m.unpackHeader(); m.status != OK {
return
}
if m.protocol != protocol || m.isReq {
m.status = ErrProtocolMismatch
return
}
// type-tagged return values
m.fmt = ""
m.Ret = make([]interface{}, m.uint32())
m.unpackValues(m.Ret)
}
func (m *msg) packResponse() {
m.packHeader()
m.wuint32(uint32(len(m.Ret)))
m.packValues(m.Ret)
}