--- /dev/null
+// 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;
+}
--- /dev/null
+// 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);
+}
+
--- /dev/null
+// 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{});
+}
+