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