From: Russ Cox Date: Tue, 29 Sep 2009 23:00:28 +0000 (-0700) Subject: Native Client SRPC (simple RPC), both server and client. X-Git-Tag: weekly.2009-11-06~456 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=8f8b735295702ee1dd8632c44aed96c087b5b085;p=gostls13.git Native Client SRPC (simple RPC), both server and client. R=r DELTA=958 (958 added, 0 deleted, 0 changed) OCL=35096 CL=35106 --- diff --git a/usr/rsc/nacl/srpc/Makefile b/usr/rsc/nacl/srpc/Makefile new file mode 100644 index 0000000000..9014d2c3a8 --- /dev/null +++ b/usr/rsc/nacl/srpc/Makefile @@ -0,0 +1,13 @@ +# 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. + +include $(GOROOT)/src/Make.$(GOARCH) + +TARG=nacl/srpc +GOFILES=\ + client.go\ + msg.go\ + server.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/usr/rsc/nacl/srpc/client.go b/usr/rsc/nacl/srpc/client.go new file mode 100644 index 0000000000..4c375fe2ae --- /dev/null +++ b/usr/rsc/nacl/srpc/client.go @@ -0,0 +1,210 @@ +// 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. + +// This package implements Native Client's simple RPC (SRPC). +package srpc + +import ( + "bytes"; + "log"; + "os"; + "sync"; +) + +// A Client represents the client side of an SRPC connection. +type Client struct { + fd int; // fd to server + r msgReceiver; + s msgSender; + service map[string]srv; // services by name + out chan *msg; // send to out to write to connection + + mu sync.Mutex; // protects pending, idGen + pending map[uint64]*RPC; + idGen uint64; // generator for request IDs +} + +// A srv is a single method that the server offers. +type srv struct { + num uint32; // method number + fmt string; // argument format +} + +// An RPC represents a single RPC issued by a client. +type RPC struct { + Ret []interface{}; // Return values + Done chan *RPC; // Channel where notification of done arrives + Errno Errno; // Status code + c *Client; + id uint64; // request id +} + +// NewClient allocates a new client using the file descriptor fd. +func NewClient(fd int) (c *Client, err os.Error) { + c = new(Client); + c.fd = fd; + c.r.fd = fd; + c.s.fd = fd; + c.service = make(map[string]srv); + c.pending = make(map[uint64]*RPC); + + // service discovery request + m := &msg{ + protocol: protocol, + isReq: true, + Ret: []interface{}{ []byte(nil) }, + Size: []int{ 4000 }, + }; + m.packRequest(); + c.s.send(m); + m, err = c.r.recv(); + if err != nil { + return nil, err; + } + m.unpackResponse(); + if m.status != OK { + log.Stderrf("NewClient service_discovery: %s", m.status); + return nil, m.status; + } + for n, line := range bytes.Split(m.Ret[0].([]byte), []byte{'\n'}, 0) { + i := bytes.Index(line, []byte{':'}); + if i < 0 { + continue; + } + c.service[string(line[0:i])] = srv{uint32(n), string(line[i+1:len(line)])}; + } + + c.out = make(chan *msg); + go c.input(); + go c.output(); + return c, nil; +} + +func (c *Client) input() { + for { + m, err := c.r.recv(); + if err != nil { + log.Exitf("client recv: %s", err); + } + if m.unpackResponse(); m.status != OK { + log.Stderrf("invalid message: %s", m.status); + continue; + } + c.mu.Lock(); + rpc, ok := c.pending[m.requestId]; + if ok { + c.pending[m.requestId] = nil, false; + } + c.mu.Unlock(); + if !ok { + log.Stderrf("unexpected response"); + continue; + } + rpc.Ret = m.Ret; + rpc.Done <- rpc; + } +} + +func (c *Client) output() { + for m := range c.out { + c.s.send(m); + } +} + +// NewRPC creates a new RPC on the client connection. +func (c *Client) NewRPC(done chan *RPC) *RPC { + if done == nil { + done = make(chan *RPC); + } + c.mu.Lock(); + id := c.idGen; + c.idGen++; + c.mu.Unlock(); + return &RPC{nil, done, OK, c, id}; +} + +// Start issues an RPC request for method name with the given arguments. +// The RPC r must not be in use for another pending request. +// To wait for the RPC to finish, receive from r.Done and then +// inspect r.Ret and r.Errno. +func (r *RPC) Start(name string, arg []interface{}) { + var m msg; + + r.Errno = OK; + r.c.mu.Lock(); + srv, ok := r.c.service[name]; + if !ok { + r.c.mu.Unlock(); + r.Errno = ErrBadRPCNumber; + r.Done <- r; + return; + } + r.c.pending[r.id] = r; + r.c.mu.Unlock(); + + m.protocol = protocol; + m.requestId = r.id; + m.isReq = true; + m.rpcNumber = srv.num; + m.Arg = arg; + + // Fill in the return values and sizes to generate + // the right type chars. We'll take most any size. + + // Skip over input arguments. + // We could check them against arg, but the server + // will do that anyway. + i := 0; + for srv.fmt[i] != ':' { + i++; + } + fmt := srv.fmt[i+1:len(srv.fmt)]; + + // Now the return prototypes. + m.Ret = make([]interface{}, len(fmt) - i); + m.Size = make([]int, len(fmt) - i); + for i := 0; i < len(fmt); i++ { + switch fmt[i] { + default: + log.Exitf("unexpected service type %c", fmt[i]); + case 'b': + m.Ret[i] = false; + case 'C': + m.Ret[i] = []byte(nil); + m.Size[i] = 1<<30; + case 'd': + m.Ret[i] = float64(0); + case 'D': + m.Ret[i] = []float64(nil); + m.Size[i] = 1<<30; + case 'h': + m.Ret[i] = int(-1); + case 'i': + m.Ret[i] = int32(0); + case 'I': + m.Ret[i] = []int32(nil); + m.Size[i] = 1<<30; + case 's': + m.Ret[i] = ""; + m.Size[i] = 1<<30; + } + } + + m.packRequest(); + r.c.out <- &m; +} + +// Call is a convenient wrapper that starts the RPC request, +// waits for it to finish, and then returns the results. +// Its implementation is: +// +// r.Start(name, arg); +// <-r.Done; +// return r.Ret, r.Errno; +// +func (r *RPC) Call(name string, arg []interface{}) (ret []interface{}, err Errno) { + r.Start(name, arg); + <-r.Done; + return r.Ret, r.Errno; +} diff --git a/usr/rsc/nacl/srpc/msg.go b/usr/rsc/nacl/srpc/msg.go new file mode 100644 index 0000000000..27fe7212f8 --- /dev/null +++ b/usr/rsc/nacl/srpc/msg.go @@ -0,0 +1,532 @@ +// 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); +} + diff --git a/usr/rsc/nacl/srpc/server.go b/usr/rsc/nacl/srpc/server.go new file mode 100644 index 0000000000..4fd778d635 --- /dev/null +++ b/usr/rsc/nacl/srpc/server.go @@ -0,0 +1,204 @@ +// 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 server + +package srpc + +import ( + "bytes"; + "log"; + "os"; + "syscall"; +) + +// TODO(rsc): I'd prefer to make this +// type Handler func(m *msg) Errno +// but NaCl can't use closures. +// The explicit interface is a way to attach state. + +// A Handler is a handler for an SRPC method. +// It reads arguments from m.Arg, checks m.Size for array limits, +// writes return values to m.Ret, and returns an Errno status code. +type Handler interface { + Run(m *msg) Errno +} + +type method struct { + name string; + fmt string; + handler Handler; +} + +var rpcMethod []method + +// BUG(rsc): Add's format string should be replaced by analyzing the +// type of an arbitrary func passed in an interface{} using reflection. + +// Add registers a handler for the named method. +// Fmt is a Native Client format string, a sequence of +// alphabetic characters representing the types of the parameter values, +// a colon, and then a sequence of alphabetic characters +// representing the types of the returned values. +// The format characters and corresponding dynamic types are: +// +// b bool +// C []byte +// d float64 +// D []float64 +// h int // a file descriptor (aka handle) +// i int32 +// I []int32 +// s string +// +func Add(name, fmt string, handler Handler) { + n := len(rpcMethod); + if n >= cap(rpcMethod) { + a := make([]method, n, (n+4)*2); + for i := range a { + a[i] = rpcMethod[i]; + } + rpcMethod = a; + } + rpcMethod = rpcMethod[0:n+1]; + rpcMethod[n] = method{name, fmt, handler}; +} + +// Serve accepts new SRPC connections from the file descriptor fd +// and answers RPCs issued on those connections. +// It closes fd and returns an error if the imc_accept system call fails. +func Serve(fd int) os.Error { + defer syscall.Close(fd); + + for { + cfd, _, e := syscall.Syscall(syscall.SYS_IMC_ACCEPT, uintptr(fd), 0, 0); + if e != 0 { + return os.NewSyscallError("imc_accept", int(e)); + } + go serveLoop(int(cfd)); + } + panic("unreachable"); +} + +func serveLoop(fd int) { + c := make(chan *msg); + go sendLoop(fd, c); + + var r msgReceiver; + r.fd = fd; + for { + m, err := r.recv(); + if err != nil { + break; + } + m.unpackRequest(); + if !m.gotHeader { + log.Stderrf("cannot unpack header: %s", m.status); + continue; + } + // log.Stdoutf("<- %#v", m); + m.isReq = false; // set up for response + go serveMsg(m, c); + } + close(c); +} + +func sendLoop(fd int, c <-chan *msg) { + var s msgSender; + s.fd = fd; + for m := range c { + // log.Stdoutf("-> %#v", m); + m.packResponse(); + s.send(m); + } + syscall.Close(fd); +} + +func serveMsg(m *msg, c chan<- *msg) { + if m.status != OK { + c <- m; + return; + } + if m.rpcNumber >= uint32(len(rpcMethod)) { + m.status = ErrBadRPCNumber; + c <- m; + return; + } + + meth := &rpcMethod[m.rpcNumber]; + if meth.fmt != m.fmt { + switch { + case len(m.fmt) < len(meth.fmt): + m.status = ErrTooFewArgs; + case len(m.fmt) > len(meth.fmt): + m.status = ErrTooManyArgs; + default: + // There's a type mismatch. + // It's an in-arg mismatch if the mismatch happens + // before the colon; otherwise it's an out-arg mismatch. + m.status = ErrInArgTypeMismatch; + for i := 0; i < len(m.fmt) && m.fmt[i] == meth.fmt[i]; i++ { + if m.fmt[i] == ':' { + m.status = ErrOutArgTypeMismatch; + break; + } + } + } + c <- m; + return; + } + + m.status = meth.handler.Run(m); + c <- m; +} + +// ServeRuntime serves RPCs issued by the Native Client embedded runtime. +// This should be called by main once all methods have been registered using Add. +func ServeRuntime() os.Error { + // Call getFd to check that we are running embedded. + if _, err := getFd(); err != nil { + return err; + } + + // We are running embedded. + // The fd returned by getFd is a red herring. + // Accept connections on magic fd 3. + return Serve(3); +} + +// getFd runs the srpc_get_fd system call. +func getFd() (fd int, err os.Error) { + r1, _, e := syscall.Syscall(syscall.SYS_SRPC_GET_FD, 0, 0, 0); + return int(r1), os.NewSyscallError("srpc_get_fd", int(e)); +} + +// Enabled returns true if SRPC is enabled in the Native Client runtime. +func Enabled() bool { + _, err:= getFd(); + return err == nil; +} + +// Service #0, service_discovery, returns a list of the other services +// and their argument formats. +type serviceDiscovery struct{} + +func (serviceDiscovery) Run(m *msg) Errno { + var b bytes.Buffer; + for _, m := range rpcMethod { + b.WriteString(m.name); + b.WriteByte(':'); + b.WriteString(m.fmt); + b.WriteByte('\n'); + } + if b.Len() > m.Size[0] { + return ErrNoMemory; + } + m.Ret[0] = b.Bytes(); + return OK; +} + +func init() { + Add("service_discovery", ":C", serviceDiscovery{}); +} +