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