summaryrefslogtreecommitdiff
path: root/src/syscall/srpc_nacl.go
blob: dd07373d1a68059b77e4d8acbd7e8bbef74a2375 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
// 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
}