diff options
Diffstat (limited to 'src/pkg/syscall/srpc_nacl.go')
-rw-r--r-- | src/pkg/syscall/srpc_nacl.go | 822 |
1 files changed, 0 insertions, 822 deletions
diff --git a/src/pkg/syscall/srpc_nacl.go b/src/pkg/syscall/srpc_nacl.go deleted file mode 100644 index dd07373d1..000000000 --- a/src/pkg/syscall/srpc_nacl.go +++ /dev/null @@ -1,822 +0,0 @@ -// Copyright 2013 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. - -// Native Client SRPC message passing. -// This code is needed to invoke SecureRandom, the NaCl equivalent of /dev/random. - -package syscall - -import ( - "errors" - "sync" - "unsafe" -) - -// An srpcClient represents the client side of an SRPC connection. -type srpcClient struct { - fd int // to server - r msgReceiver - s msgSender - service map[string]srpcService // services by name - - outMu sync.Mutex // protects writing to connection - - mu sync.Mutex // protects following fields - muxer bool // is someone reading and muxing responses - pending map[uint32]*srpc - idGen uint32 // generator for request IDs -} - -// An srpcService is a single method that the server offers. -type srpcService struct { - num uint32 // method number - fmt string // argument format; see "parsing of RPC messages" below -} - -// An srpc represents a single srpc issued by a client. -type srpc struct { - Ret []interface{} - Done chan *srpc - Err error - c *srpcClient - id uint32 -} - -// newClient allocates a new SRPC client using the file descriptor fd. -func newClient(fd int) (*srpcClient, error) { - c := new(srpcClient) - c.fd = fd - c.r.fd = fd - c.s.fd = fd - c.service = make(map[string]srpcService) - c.pending = make(map[uint32]*srpc) - - // service discovery request - m := &msg{ - isRequest: 1, - template: []interface{}{[]byte(nil)}, - size: []int{4000}, // max size to accept for returned byte slice - } - if err := m.pack(); err != nil { - return nil, errors.New("Native Client SRPC service_discovery: preparing request: " + err.Error()) - } - c.s.send(m) - m, err := c.r.recv() - if err != nil { - return nil, err - } - m.unpack() - if m.status != uint32(srpcOK) { - return nil, errors.New("Native Client SRPC service_discovery: " + srpcErrno(m.status).Error()) - } - list := m.value[0].([]byte) - var n uint32 - for len(list) > 0 { - var line []byte - i := byteIndex(list, '\n') - if i < 0 { - line, list = list, nil - } else { - line, list = list[:i], list[i+1:] - } - i = byteIndex(line, ':') - if i >= 0 { - c.service[string(line)] = srpcService{n, string(line[i+1:])} - } - n++ - } - - return c, nil -} - -func byteIndex(b []byte, c byte) int { - for i, bi := range b { - if bi == c { - return i - } - } - return -1 -} - -var yourTurn srpc - -func (c *srpcClient) wait(r *srpc) { - var rx *srpc - for rx = range r.Done { - if rx != &yourTurn { - break - } - c.input() - } - return -} - -func (c *srpcClient) input() { - // read message - m, err := c.r.recv() - if err != nil { - println("Native Client SRPC receive error:", err.Error()) - return - } - if m.unpack(); m.status != uint32(srpcOK) { - println("Native Client SRPC receive error: invalid message: ", srpcErrno(m.status).Error()) - return - } - - // deliver to intended recipient - c.mu.Lock() - rpc, ok := c.pending[m.id] - if ok { - delete(c.pending, m.id) - } - - // wake a new muxer if there are more RPCs to read - c.muxer = false - for _, rpc := range c.pending { - c.muxer = true - rpc.Done <- &yourTurn - break - } - c.mu.Unlock() - if !ok { - println("Native Client: unexpected response for ID", m.id) - return - } - rpc.Ret = m.value - rpc.Done <- rpc -} - -// Wait blocks until the RPC has finished. -func (r *srpc) Wait() { - r.c.wait(r) -} - -// 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 *srpc) Start(name string, arg []interface{}) { - r.Err = nil - r.c.mu.Lock() - srv, ok := r.c.service[name] - if !ok { - r.c.mu.Unlock() - r.Err = srpcErrBadRPCNumber - r.Done <- r - return - } - r.c.pending[r.id] = r - if !r.c.muxer { - r.c.muxer = true - r.Done <- &yourTurn - } - r.c.mu.Unlock() - - var m msg - m.id = r.id - m.isRequest = 1 - m.rpc = srv.num - m.value = 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++ - } - format := srv.fmt[i+1:] - - // Now the return prototypes. - m.template = make([]interface{}, len(format)) - m.size = make([]int, len(format)) - for i := 0; i < len(format); i++ { - switch format[i] { - default: - println("Native Client SRPC: unexpected service type " + string(format[i])) - r.Err = srpcErrBadRPCNumber - r.Done <- r - return - case 'b': - m.template[i] = false - case 'C': - m.template[i] = []byte(nil) - m.size[i] = 1 << 30 - case 'd': - m.template[i] = float64(0) - case 'D': - m.template[i] = []float64(nil) - m.size[i] = 1 << 30 - case 'h': - m.template[i] = int(-1) - case 'i': - m.template[i] = int32(0) - case 'I': - m.template[i] = []int32(nil) - m.size[i] = 1 << 30 - case 's': - m.template[i] = "" - m.size[i] = 1 << 30 - } - } - - if err := m.pack(); err != nil { - r.Err = errors.New("Native Client RPC Start " + name + ": preparing request: " + err.Error()) - r.Done <- r - return - } - - r.c.outMu.Lock() - r.c.s.send(&m) - r.c.outMu.Unlock() -} - -// Call is a convenience wrapper that starts the RPC request, -// waits for it to finish, and then returns the results. -// Its implementation is: -// -// r.Start(name, arg) -// r.Wait() -// return r.Ret, r.Errno -// -func (c *srpcClient) Call(name string, arg ...interface{}) (ret []interface{}, err error) { - r := c.NewRPC(nil) - r.Start(name, arg) - r.Wait() - return r.Ret, r.Err -} - -// NewRPC creates a new RPC on the client connection. -func (c *srpcClient) NewRPC(done chan *srpc) *srpc { - if done == nil { - done = make(chan *srpc, 1) - } - c.mu.Lock() - id := c.idGen - c.idGen++ - c.mu.Unlock() - return &srpc{Done: done, c: c, id: id} -} - -// The current protocol number. -// Kind of useless, since there have been backwards-incompatible changes -// to the wire protocol that did not update the protocol number. -// At this point it's really just a sanity check. -const protocol = 0xc0da0002 - -// An srpcErrno is an SRPC status code. -type srpcErrno uint32 - -const ( - srpcOK srpcErrno = 256 + iota - srpcErrBreak - srpcErrMessageTruncated - srpcErrNoMemory - srpcErrProtocolMismatch - srpcErrBadRPCNumber - srpcErrBadArgType - srpcErrTooFewArgs - srpcErrTooManyArgs - srpcErrInArgTypeMismatch - srpcErrOutArgTypeMismatch - srpcErrInternalError - srpcErrAppError -) - -var srpcErrstr = [...]string{ - srpcOK - srpcOK: "ok", - srpcErrBreak - srpcOK: "break", - srpcErrMessageTruncated - srpcOK: "message truncated", - srpcErrNoMemory - srpcOK: "out of memory", - srpcErrProtocolMismatch - srpcOK: "protocol mismatch", - srpcErrBadRPCNumber - srpcOK: "invalid RPC method number", - srpcErrBadArgType - srpcOK: "unexpected argument type", - srpcErrTooFewArgs - srpcOK: "too few arguments", - srpcErrTooManyArgs - srpcOK: "too many arguments", - srpcErrInArgTypeMismatch - srpcOK: "input argument type mismatch", - srpcErrOutArgTypeMismatch - srpcOK: "output argument type mismatch", - srpcErrInternalError - srpcOK: "internal error", - srpcErrAppError - srpcOK: "application error", -} - -func (e srpcErrno) Error() string { - if e < srpcOK || int(e-srpcOK) >= len(srpcErrstr) { - return "srpcErrno(" + itoa(int(e)) + ")" - } - return srpcErrstr[e-srpcOK] -} - -// A msgHdr is the data argument to the imc_recvmsg -// and imc_sendmsg system calls. -type msgHdr struct { - iov *iov - niov int32 - desc *int32 - ndesc int32 - flags uint32 -} - -// A single region for I/O. -type iov struct { - base *byte - len int32 -} - -const maxMsgSize = 1<<16 - 4*4 - -// A msgReceiver receives messages from a file descriptor. -type msgReceiver struct { - fd int - data [maxMsgSize]byte - desc [8]int32 - hdr msgHdr - iov iov -} - -func (r *msgReceiver) recv() (*msg, 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(sys_imc_recvmsg, uintptr(r.fd), uintptr(unsafe.Pointer(&r.hdr)), 0) - if e != 0 { - println("Native Client imc_recvmsg: ", e.Error()) - return nil, 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.data = make([]byte, n) - copy(m.data, r.data[0:]) - - // Make a copy of the desc too. - // The system call *did* update r.hdr.ndesc. - if r.hdr.ndesc > 0 { - m.desc = make([]int32, r.hdr.ndesc) - copy(m.desc, r.desc[:]) - } - - 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) error { - if len(m.data) > 0 { - s.iov.base = &m.data[0] - } - s.iov.len = int32(len(m.data)) - s.hdr.iov = &s.iov - s.hdr.niov = 1 - s.hdr.desc = nil - s.hdr.ndesc = 0 - _, _, e := Syscall(sys_imc_sendmsg, uintptr(s.fd), uintptr(unsafe.Pointer(&s.hdr)), 0) - if e != 0 { - println("Native Client imc_sendmsg: ", e.Error()) - return e - } - return nil -} - -// A msg is the Go representation of an SRPC message. -type msg struct { - data []byte // message data - desc []int32 // message file descriptors - - // parsed version of message - id uint32 - isRequest uint32 - rpc uint32 - status uint32 - value []interface{} - template []interface{} - size []int - format string - broken bool -} - -// reading from a msg - -func (m *msg) uint32() uint32 { - if m.broken { - return 0 - } - if len(m.data) < 4 { - m.broken = true - return 0 - } - b := m.data[:4] - x := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 - m.data = m.data[4:] - return x -} - -func (m *msg) uint64() uint64 { - x := uint64(m.uint32()) | uint64(m.uint32())<<32 - if m.broken { - return 0 - } - return x -} - -func (m *msg) bytes(n int) []byte { - if m.broken { - return nil - } - if len(m.data) < n { - m.broken = true - return nil - } - x := m.data[0:n] - m.data = m.data[n:] - return x -} - -// writing to a msg - -func (m *msg) wuint32(x uint32) { - m.data = append(m.data, byte(x), byte(x>>8), byte(x>>16), byte(x>>24)) -} - -func (m *msg) wuint64(x uint64) { - lo := uint32(x) - hi := uint32(x >> 32) - m.data = append(m.data, byte(lo), byte(lo>>8), byte(lo>>16), byte(lo>>24), byte(hi), byte(hi>>8), byte(hi>>16), byte(hi>>24)) -} - -func (m *msg) wbytes(p []byte) { - m.data = append(m.data, p...) -} - -func (m *msg) wstring(s string) { - m.data = append(m.data, s...) -} - -// Parsing of RPC messages. -// -// Each message begins with -// total_size uint32 -// total_descs uint32 -// fragment_size uint32 -// fragment_descs uint32 -// -// If fragment_size < total_size or fragment_descs < total_descs, the actual -// message is broken up in multiple messages; follow-up messages omit -// the "total" fields and begin with the "fragment" fields. -// We do not support putting fragmented messages back together. -// To do this we would need to change the message receiver. -// -// After that size information, the message header follows: -// protocol uint32 -// requestID uint32 -// isRequest uint32 -// rpcNumber uint32 -// status uint32 -// numValue uint32 -// numTemplate uint32 -// -// After the header come numTemplate fixed-size arguments, -// numValue fixed-size arguments, and then the variable-sized -// part of the values. The templates describe the expected results -// and have no associated variable sized data in the request. -// -// Each fixed-size argument has the form: -// tag uint32 // really a char, like 'b' or 'C' -// pad uint32 // unused -// val1 uint32 -// val2 uint32 -// -// The tags are: -// 'b': bool; val1 == 0 or 1 -// 'C': []byte; val1 == len, data in variable-sized section -// 'd': float64; (val1, val2) is data -// 'D': []float64; val1 == len, data in variable-sized section -// 'h': int; val1 == file descriptor -// 'i': int32; descriptor in next entry in m.desc -// 'I': []int; val1 == len, data in variable-sized section -// 's': string; val1 == len, data in variable-sized section -// - -func (m *msg) pack() error { - m.data = m.data[:0] - m.desc = m.desc[:0] - - // sizes, to fill in later - m.wuint32(0) - m.wuint32(0) - m.wuint32(0) - m.wuint32(0) - - // message header - m.wuint32(protocol) - m.wuint32(m.id) - m.wuint32(m.isRequest) - m.wuint32(m.rpc) - m.wuint32(m.status) - m.wuint32(uint32(len(m.value))) - m.wuint32(uint32(len(m.template))) - - // fixed-size templates - for i, x := range m.template { - var tag, val1, val2 uint32 - switch x.(type) { - default: - return errors.New("unexpected template type") - case bool: - tag = 'b' - case []byte: - tag = 'C' - val1 = uint32(m.size[i]) - case float64: - tag = 'd' - case []float64: - tag = 'D' - val1 = uint32(m.size[i]) - case int: - tag = 'h' - case int32: - tag = 'i' - case []int32: - tag = 'I' - val1 = uint32(m.size[i]) - case string: - tag = 's' - val1 = uint32(m.size[i]) - } - m.wuint32(tag) - m.wuint32(0) - m.wuint32(val1) - m.wuint32(val2) - } - - // fixed-size values - for _, x := range m.value { - var tag, val1, val2 uint32 - switch x := x.(type) { - default: - return errors.New("unexpected value type") - case bool: - tag = 'b' - if x { - val1 = 1 - } - case []byte: - tag = 'C' - val1 = uint32(len(x)) - case float64: - tag = 'd' - v := float64bits(x) - val1 = uint32(v) - val2 = uint32(v >> 32) - case []float64: - tag = 'D' - val1 = uint32(len(x)) - case int32: - tag = 'i' - m.desc = append(m.desc, x) - case []int32: - tag = 'I' - val1 = uint32(len(x)) - case string: - tag = 's' - val1 = uint32(len(x) + 1) - } - m.wuint32(tag) - m.wuint32(0) - m.wuint32(val1) - m.wuint32(val2) - } - - // variable-length data for values - for _, x := range m.value { - switch x := x.(type) { - case []byte: - m.wbytes(x) - case []float64: - for _, f := range x { - m.wuint64(float64bits(f)) - } - case []int32: - for _, j := range x { - m.wuint32(uint32(j)) - } - case string: - m.wstring(x) - m.wstring("\x00") - } - } - - // fill in sizes - data := m.data - m.data = m.data[:0] - m.wuint32(uint32(len(data))) - m.wuint32(uint32(len(m.desc))) - m.wuint32(uint32(len(data))) - m.wuint32(uint32(len(m.desc))) - m.data = data - - return nil -} - -func (m *msg) unpack() error { - totalSize := m.uint32() - totalDesc := m.uint32() - fragSize := m.uint32() - fragDesc := m.uint32() - if totalSize != fragSize || totalDesc != fragDesc { - return errors.New("Native Client: fragmented RPC messages not supported") - } - if m.uint32() != protocol { - return errors.New("Native Client: RPC protocol mismatch") - } - - // message header - m.id = m.uint32() - m.isRequest = m.uint32() - m.rpc = m.uint32() - m.status = m.uint32() - m.value = make([]interface{}, m.uint32()) - m.template = make([]interface{}, m.uint32()) - m.size = make([]int, len(m.template)) - if m.broken { - return errors.New("Native Client: malformed message") - } - - // fixed-size templates - for i := range m.template { - tag := m.uint32() - m.uint32() // padding - val1 := m.uint32() - m.uint32() // val2 - switch tag { - default: - return errors.New("Native Client: unexpected template type " + string(rune(tag))) - case 'b': - m.template[i] = false - case 'C': - m.template[i] = []byte(nil) - m.size[i] = int(val1) - case 'd': - m.template[i] = float64(0) - case 'D': - m.template[i] = []float64(nil) - m.size[i] = int(val1) - case 'i': - m.template[i] = int32(0) - case 'I': - m.template[i] = []int32(nil) - m.size[i] = int(val1) - case 'h': - m.template[i] = int(0) - case 's': - m.template[i] = "" - m.size[i] = int(val1) - } - } - - // fixed-size values - var ( - strsize []uint32 - d int - ) - for i := range m.value { - tag := m.uint32() - m.uint32() // padding - val1 := m.uint32() - val2 := m.uint32() - switch tag { - default: - return errors.New("Native Client: unexpected value type " + string(rune(tag))) - case 'b': - m.value[i] = val1 > 0 - case 'C': - m.value[i] = []byte(nil) - strsize = append(strsize, val1) - case 'd': - m.value[i] = float64frombits(uint64(val1) | uint64(val2)<<32) - case 'D': - m.value[i] = make([]float64, val1) - case 'i': - m.value[i] = int32(val1) - case 'I': - m.value[i] = make([]int32, val1) - case 'h': - m.value[i] = int(m.desc[d]) - d++ - case 's': - m.value[i] = "" - strsize = append(strsize, val1) - } - } - - // variable-sized parts of values - for i, x := range m.value { - switch x := x.(type) { - case []byte: - m.value[i] = m.bytes(int(strsize[0])) - strsize = strsize[1:] - case []float64: - for i := range x { - x[i] = float64frombits(m.uint64()) - } - case []int32: - for i := range x { - x[i] = int32(m.uint32()) - } - case string: - m.value[i] = string(m.bytes(int(strsize[0]))) - strsize = strsize[1:] - } - } - - if len(m.data) > 0 { - return errors.New("Native Client: junk at end of message") - } - return nil -} - -func float64bits(x float64) uint64 { - return *(*uint64)(unsafe.Pointer(&x)) -} - -func float64frombits(x uint64) float64 { - return *(*float64)(unsafe.Pointer(&x)) -} - -// At startup, connect to the name service. -var nsClient = nsConnect() - -func nsConnect() *srpcClient { - var ns int32 = -1 - _, _, errno := Syscall(sys_nameservice, uintptr(unsafe.Pointer(&ns)), 0, 0) - if errno != 0 { - println("Native Client nameservice:", errno.Error()) - return nil - } - - sock, _, errno := Syscall(sys_imc_connect, uintptr(ns), 0, 0) - if errno != 0 { - println("Native Client nameservice connect:", errno.Error()) - return nil - } - - c, err := newClient(int(sock)) - if err != nil { - println("Native Client nameservice init:", err.Error()) - return nil - } - - return c -} - -const ( - nsSuccess = 0 - nsNameNotFound = 1 - nsDuplicateName = 2 - nsInsufficientResources = 3 - nsPermissionDenied = 4 - nsInvalidArgument = 5 -) - -func openNamedService(name string, mode int32) (fd int, err error) { - if nsClient == nil { - return 0, errors.New("no name service") - } - ret, err := nsClient.Call("lookup:si:ih", name, int32(mode)) - if err != nil { - return 0, err - } - status := ret[0].(int32) - fd = ret[1].(int) - switch status { - case nsSuccess: - // ok - case nsNameNotFound: - return -1, ENOENT - case nsDuplicateName: - return -1, EEXIST - case nsInsufficientResources: - return -1, EWOULDBLOCK - case nsPermissionDenied: - return -1, EPERM - case nsInvalidArgument: - return -1, EINVAL - default: - return -1, EINVAL - } - return fd, nil -} |