mirror of
https://github.com/golang/go
synced 2024-11-26 07:38:00 -07:00
cbd40daf38
R=r DELTA=894 (887 added, 0 deleted, 7 changed) OCL=35115 CL=35286
205 lines
4.6 KiB
Go
205 lines
4.6 KiB
Go
// 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 arg, checks size for array limits,
|
|
// writes return values to ret, and returns an Errno status code.
|
|
type Handler interface {
|
|
Run(arg, ret []interface{}, size []int) 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.Arg, m.Ret, m.Size);
|
|
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(arg, ret []interface{}, size []int) 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() > size[0] {
|
|
return ErrNoMemory;
|
|
}
|
|
ret[0] = b.Bytes();
|
|
return OK;
|
|
}
|
|
|
|
func init() {
|
|
Add("service_discovery", ":C", serviceDiscovery{});
|
|
}
|
|
|