mirror of
https://github.com/rwinkhart/sys.git
synced 2026-08-28 04:46:44 -04:00
Semi-automatic migration from package syscall to package {plan9,windows,unix}.
No builds attempted yet, but this gets a lot of noise behind us so subsequent
CLs will be more concise and easier to follow.
Subsequent CLs will have semantic content.
LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/121520043
823 lines
18 KiB
Go
823 lines
18 KiB
Go
// 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 unix
|
|
|
|
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
|
|
}
|