// 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); }