| // 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 ( |
| "bytes"; |
| "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); |
| bytes.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:len(m.rdata)]; |
| 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:len(m.rdata)]; |
| 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:len(m.rdata)]; |
| 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:len(m.rdata)]; |
| 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); |
| bytes.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) { bytes.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:len(m.rdesc)]; |
| 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); |
| } |