1 # Sun RPC version 2 -- RFC1057.
3 # XXX There should be separate exceptions for the various reasons why
4 # XXX an RPC can fail, rather than using RuntimeError for everything
6 # XXX Need to use class based exceptions rather than string exceptions
8 # XXX The UDP version of the protocol resends requests when it does
9 # XXX not receive a timely reply -- use only for idempotent calls!
11 # XXX There is no provision for call timeout on TCP connections
30 SUCCESS
= 0 # RPC executed successfully
31 PROG_UNAVAIL
= 1 # remote hasn't exported program
32 PROG_MISMATCH
= 2 # remote can't support version #
33 PROC_UNAVAIL
= 3 # program can't support procedure
34 GARBAGE_ARGS
= 4 # procedure can't decode params
36 RPC_MISMATCH
= 0 # RPC version number != 2
37 AUTH_ERROR
= 1 # remote can't authenticate caller
39 AUTH_BADCRED
= 1 # bad credentials (seal broken)
40 AUTH_REJECTEDCRED
= 2 # client must begin new session
41 AUTH_BADVERF
= 3 # bad verifier (seal broken)
42 AUTH_REJECTEDVERF
= 4 # verifier expired or replayed
43 AUTH_TOOWEAK
= 5 # rejected for security reasons
46 class Packer(xdr
.Packer
):
48 def pack_auth(self
, auth
):
50 self
.pack_enum(flavor
)
51 self
.pack_opaque(stuff
)
53 def pack_auth_unix(self
, stamp
, machinename
, uid
, gid
, gids
):
55 self
.pack_string(machinename
)
58 self
.pack_uint(len(gids
))
62 def pack_callheader(self
, xid
, prog
, vers
, proc
, cred
, verf
):
65 self
.pack_uint(RPCVERSION
)
71 # Caller must add procedure-specific part of call
73 def pack_replyheader(self
, xid
, verf
):
76 self
.pack_uint(MSG_ACCEPTED
)
78 self
.pack_enum(SUCCESS
)
79 # Caller must add procedure-specific part of reply
83 BadRPCFormat
= 'rpc.BadRPCFormat'
84 BadRPCVersion
= 'rpc.BadRPCVersion'
85 GarbageArgs
= 'rpc.GarbageArgs'
87 class Unpacker(xdr
.Unpacker
):
89 def unpack_auth(self
):
90 flavor
= self
.unpack_enum()
91 stuff
= self
.unpack_opaque()
92 return (flavor
, stuff
)
94 def unpack_callheader(self
):
95 xid
= self
.unpack_uint()
96 temp
= self
.unpack_enum()
98 raise BadRPCFormat
, 'no CALL but %r' % (temp
,)
99 temp
= self
.unpack_uint()
100 if temp
!= RPCVERSION
:
101 raise BadRPCVersion
, 'bad RPC version %r' % (temp
,)
102 prog
= self
.unpack_uint()
103 vers
= self
.unpack_uint()
104 proc
= self
.unpack_uint()
105 cred
= self
.unpack_auth()
106 verf
= self
.unpack_auth()
107 return xid
, prog
, vers
, proc
, cred
, verf
108 # Caller must add procedure-specific part of call
110 def unpack_replyheader(self
):
111 xid
= self
.unpack_uint()
112 mtype
= self
.unpack_enum()
114 raise RuntimeError, 'no REPLY but %r' % (mtype
,)
115 stat
= self
.unpack_enum()
116 if stat
== MSG_DENIED
:
117 stat
= self
.unpack_enum()
118 if stat
== RPC_MISMATCH
:
119 low
= self
.unpack_uint()
120 high
= self
.unpack_uint()
121 raise RuntimeError, \
122 'MSG_DENIED: RPC_MISMATCH: %r' % ((low
, high
),)
123 if stat
== AUTH_ERROR
:
124 stat
= self
.unpack_uint()
125 raise RuntimeError, \
126 'MSG_DENIED: AUTH_ERROR: %r' % (stat
,)
127 raise RuntimeError, 'MSG_DENIED: %r' % (stat
,)
128 if stat
!= MSG_ACCEPTED
:
129 raise RuntimeError, \
130 'Neither MSG_DENIED nor MSG_ACCEPTED: %r' % (stat
,)
131 verf
= self
.unpack_auth()
132 stat
= self
.unpack_enum()
133 if stat
== PROG_UNAVAIL
:
134 raise RuntimeError, 'call failed: PROG_UNAVAIL'
135 if stat
== PROG_MISMATCH
:
136 low
= self
.unpack_uint()
137 high
= self
.unpack_uint()
138 raise RuntimeError, \
139 'call failed: PROG_MISMATCH: %r' % ((low
, high
),)
140 if stat
== PROC_UNAVAIL
:
141 raise RuntimeError, 'call failed: PROC_UNAVAIL'
142 if stat
== GARBAGE_ARGS
:
143 raise RuntimeError, 'call failed: GARBAGE_ARGS'
145 raise RuntimeError, 'call failed: %r' % (stat
,)
147 # Caller must get procedure-specific part of reply
150 # Subroutines to create opaque authentication objects
152 def make_auth_null():
155 def make_auth_unix(seed
, host
, uid
, gid
, groups
):
157 p
.pack_auth_unix(seed
, host
, uid
, gid
, groups
)
160 def make_auth_unix_default():
162 from os
import getuid
, getgid
168 return make_auth_unix(int(time
.time()-unix_epoch()), \
169 socket
.gethostname(), uid
, gid
, [])
173 """Very painful calculation of when the Unix Epoch is.
175 This is defined as the return value of time.time() on Jan 1st,
178 On a Unix system, this should always return 0.0. On a Mac, the
179 calculations are needed -- and hard because of integer overflow
180 and other limitations.
184 if _unix_epoch
>= 0: return _unix_epoch
187 localt
= time
.localtime(now
) # (y, m, d, hh, mm, ss, ..., ..., ...)
188 gmt
= time
.gmtime(now
)
189 offset
= time
.mktime(localt
) - time
.mktime(gmt
)
190 y
, m
, d
, hh
, mm
, ss
= 1970, 1, 1, 0, 0, 0
191 offset
, ss
= divmod(ss
+ offset
, 60)
192 offset
, mm
= divmod(mm
+ offset
, 60)
193 offset
, hh
= divmod(hh
+ offset
, 24)
195 _unix_epoch
= time
.mktime((y
, m
, d
, hh
, mm
, ss
, 0, 0, 0))
196 print "Unix epoch:", time
.ctime(_unix_epoch
)
200 # Common base class for clients
204 def __init__(self
, host
, prog
, vers
, port
):
209 self
.makesocket() # Assigns to self.sock
212 self
.lastxid
= 0 # XXX should be more random?
220 def makesocket(self
):
221 # This MUST be overridden
222 raise RuntimeError, 'makesocket not defined'
224 def connsocket(self
):
225 # Override this if you don't want/need a connection
226 self
.sock
.connect((self
.host
, self
.port
))
228 def bindsocket(self
):
229 # Override this to bind to a different port (e.g. reserved)
230 self
.sock
.bind(('', 0))
232 def addpackers(self
):
233 # Override this to use derived classes from Packer/Unpacker
234 self
.packer
= Packer()
235 self
.unpacker
= Unpacker('')
237 def make_call(self
, proc
, args
, pack_func
, unpack_func
):
238 # Don't normally override this (but see Broadcast)
239 if pack_func
is None and args
is not None:
240 raise TypeError, 'non-null args with null pack_func'
241 self
.start_call(proc
)
246 result
= unpack_func()
252 def start_call(self
, proc
):
253 # Don't override this
254 self
.lastxid
= xid
= self
.lastxid
+ 1
259 p
.pack_callheader(xid
, self
.prog
, self
.vers
, proc
, cred
, verf
)
262 # This MUST be overridden
263 raise RuntimeError, 'do_call not defined'
266 # Override this to use more powerful credentials
267 if self
.cred
== None:
268 self
.cred
= (AUTH_NULL
, make_auth_null())
272 # Override this to use a more powerful verifier
273 if self
.verf
== None:
274 self
.verf
= (AUTH_NULL
, make_auth_null())
277 def call_0(self
): # Procedure 0 is always like this
278 return self
.make_call(0, None, None, None)
281 # Record-Marking standard support
283 def sendfrag(sock
, last
, frag
):
285 if last
: x
= x |
0x80000000L
286 header
= (chr(int(x
>>24 & 0xff)) + chr(int(x
>>16 & 0xff)) + \
287 chr(int(x
>>8 & 0xff)) + chr(int(x
& 0xff)))
288 sock
.send(header
+ frag
)
290 def sendrecord(sock
, record
):
291 sendfrag(sock
, 1, record
)
294 header
= sock
.recv(4)
297 x
= long(ord(header
[0]))<<24 |
ord(header
[1])<<16 | \
298 ord(header
[2])<<8 |
ord(header
[3])
299 last
= ((x
& 0x80000000) != 0)
300 n
= int(x
& 0x7fffffff)
304 if not buf
: raise EOFError
309 def recvrecord(sock
):
313 last
, frag
= recvfrag(sock
)
314 record
= record
+ frag
318 # Try to bind to a reserved port (must be root)
320 last_resv_port_tried
= None
321 def bindresvport(sock
, host
):
322 global last_resv_port_tried
323 FIRST
, LAST
= 600, 1024 # Range of ports to try
324 if last_resv_port_tried
== None:
326 last_resv_port_tried
= FIRST
+ os
.getpid() % (LAST
-FIRST
)
327 for i
in range(last_resv_port_tried
, LAST
) + \
328 range(FIRST
, last_resv_port_tried
):
329 last_resv_port_tried
= i
332 return last_resv_port_tried
333 except socket
.error
, (errno
, msg
):
335 raise socket
.error
, (errno
, msg
)
336 raise RuntimeError, 'can\'t assign reserved port'
339 # Client using TCP to a specific port
341 class RawTCPClient(Client
):
343 def makesocket(self
):
344 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
347 call
= self
.packer
.get_buf()
348 sendrecord(self
.sock
, call
)
349 reply
= recvrecord(self
.sock
)
352 xid
, verf
= u
.unpack_replyheader()
353 if xid
!= self
.lastxid
:
354 # Can't really happen since this is TCP...
355 raise RuntimeError, 'wrong xid in reply %r instead of %r' % (
359 # Client using UDP to a specific port
361 class RawUDPClient(Client
):
363 def makesocket(self
):
364 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
367 call
= self
.packer
.get_buf()
370 from select
import select
372 print 'WARNING: select not found, RPC may hang'
374 BUFSIZE
= 8192 # Max UDP buffer size
378 r
, w
, x
= [self
.sock
], [], []
380 r
, w
, x
= select(r
, w
, x
, timeout
)
381 if self
.sock
not in r
:
383 if count
< 0: raise RuntimeError, 'timeout'
384 if timeout
< 25: timeout
= timeout
*2
385 ## print 'RESEND', timeout, count
388 reply
= self
.sock
.recv(BUFSIZE
)
391 xid
, verf
= u
.unpack_replyheader()
392 if xid
!= self
.lastxid
:
398 # Client using UDP broadcast to a specific port
400 class RawBroadcastUDPClient(RawUDPClient
):
402 def __init__(self
, bcastaddr
, prog
, vers
, port
):
403 RawUDPClient
.__init
__(self
, bcastaddr
, prog
, vers
, port
)
404 self
.reply_handler
= None
407 def connsocket(self
):
408 # Don't connect -- use sendto
409 self
.sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_BROADCAST
, 1)
411 def set_reply_handler(self
, reply_handler
):
412 self
.reply_handler
= reply_handler
414 def set_timeout(self
, timeout
):
415 self
.timeout
= timeout
# Use None for infinite timeout
417 def make_call(self
, proc
, args
, pack_func
, unpack_func
):
418 if pack_func
is None and args
is not None:
419 raise TypeError, 'non-null args with null pack_func'
420 self
.start_call(proc
)
423 call
= self
.packer
.get_buf()
424 self
.sock
.sendto(call
, (self
.host
, self
.port
))
426 from select
import select
428 print 'WARNING: select not found, broadcast will hang'
430 BUFSIZE
= 8192 # Max UDP buffer size (for reply)
432 if unpack_func
is None:
436 r
, w
, x
= [self
.sock
], [], []
438 if self
.timeout
is None:
439 r
, w
, x
= select(r
, w
, x
)
441 r
, w
, x
= select(r
, w
, x
, self
.timeout
)
442 if self
.sock
not in r
:
444 reply
, fromaddr
= self
.sock
.recvfrom(BUFSIZE
)
447 xid
, verf
= u
.unpack_replyheader()
448 if xid
!= self
.lastxid
:
451 reply
= unpack_func()
453 replies
.append((reply
, fromaddr
))
454 if self
.reply_handler
:
455 self
.reply_handler(reply
, fromaddr
)
459 # Port mapper interface
461 # Program number, version and (fixed!) port number
467 PMAPPROC_NULL
= 0 # (void) -> void
468 PMAPPROC_SET
= 1 # (mapping) -> bool
469 PMAPPROC_UNSET
= 2 # (mapping) -> bool
470 PMAPPROC_GETPORT
= 3 # (mapping) -> unsigned int
471 PMAPPROC_DUMP
= 4 # (void) -> pmaplist
472 PMAPPROC_CALLIT
= 5 # (call_args) -> call_result
474 # A mapping is (prog, vers, prot, port) and prot is one of:
479 # A pmaplist is a variable-length list of mappings, as follows:
480 # either (1, mapping, pmaplist) or (0).
482 # A call_args is (prog, vers, proc, args) where args is opaque;
483 # a call_result is (port, res) where res is opaque.
486 class PortMapperPacker(Packer
):
488 def pack_mapping(self
, mapping
):
489 prog
, vers
, prot
, port
= mapping
495 def pack_pmaplist(self
, list):
496 self
.pack_list(list, self
.pack_mapping
)
498 def pack_call_args(self
, ca
):
499 prog
, vers
, proc
, args
= ca
503 self
.pack_opaque(args
)
506 class PortMapperUnpacker(Unpacker
):
508 def unpack_mapping(self
):
509 prog
= self
.unpack_uint()
510 vers
= self
.unpack_uint()
511 prot
= self
.unpack_uint()
512 port
= self
.unpack_uint()
513 return prog
, vers
, prot
, port
515 def unpack_pmaplist(self
):
516 return self
.unpack_list(self
.unpack_mapping
)
518 def unpack_call_result(self
):
519 port
= self
.unpack_uint()
520 res
= self
.unpack_opaque()
524 class PartialPortMapperClient
:
526 def addpackers(self
):
527 self
.packer
= PortMapperPacker()
528 self
.unpacker
= PortMapperUnpacker('')
530 def Set(self
, mapping
):
531 return self
.make_call(PMAPPROC_SET
, mapping
, \
532 self
.packer
.pack_mapping
, \
533 self
.unpacker
.unpack_uint
)
535 def Unset(self
, mapping
):
536 return self
.make_call(PMAPPROC_UNSET
, mapping
, \
537 self
.packer
.pack_mapping
, \
538 self
.unpacker
.unpack_uint
)
540 def Getport(self
, mapping
):
541 return self
.make_call(PMAPPROC_GETPORT
, mapping
, \
542 self
.packer
.pack_mapping
, \
543 self
.unpacker
.unpack_uint
)
546 return self
.make_call(PMAPPROC_DUMP
, None, \
548 self
.unpacker
.unpack_pmaplist
)
550 def Callit(self
, ca
):
551 return self
.make_call(PMAPPROC_CALLIT
, ca
, \
552 self
.packer
.pack_call_args
, \
553 self
.unpacker
.unpack_call_result
)
556 class TCPPortMapperClient(PartialPortMapperClient
, RawTCPClient
):
558 def __init__(self
, host
):
559 RawTCPClient
.__init
__(self
, \
560 host
, PMAP_PROG
, PMAP_VERS
, PMAP_PORT
)
563 class UDPPortMapperClient(PartialPortMapperClient
, RawUDPClient
):
565 def __init__(self
, host
):
566 RawUDPClient
.__init
__(self
, \
567 host
, PMAP_PROG
, PMAP_VERS
, PMAP_PORT
)
570 class BroadcastUDPPortMapperClient(PartialPortMapperClient
, \
571 RawBroadcastUDPClient
):
573 def __init__(self
, bcastaddr
):
574 RawBroadcastUDPClient
.__init
__(self
, \
575 bcastaddr
, PMAP_PROG
, PMAP_VERS
, PMAP_PORT
)
578 # Generic clients that find their server through the Port mapper
580 class TCPClient(RawTCPClient
):
582 def __init__(self
, host
, prog
, vers
):
583 pmap
= TCPPortMapperClient(host
)
584 port
= pmap
.Getport((prog
, vers
, IPPROTO_TCP
, 0))
587 raise RuntimeError, 'program not registered'
588 RawTCPClient
.__init
__(self
, host
, prog
, vers
, port
)
591 class UDPClient(RawUDPClient
):
593 def __init__(self
, host
, prog
, vers
):
594 pmap
= UDPPortMapperClient(host
)
595 port
= pmap
.Getport((prog
, vers
, IPPROTO_UDP
, 0))
598 raise RuntimeError, 'program not registered'
599 RawUDPClient
.__init
__(self
, host
, prog
, vers
, port
)
602 class BroadcastUDPClient(Client
):
604 def __init__(self
, bcastaddr
, prog
, vers
):
605 self
.pmap
= BroadcastUDPPortMapperClient(bcastaddr
)
606 self
.pmap
.set_reply_handler(self
.my_reply_handler
)
609 self
.user_reply_handler
= None
615 def set_reply_handler(self
, reply_handler
):
616 self
.user_reply_handler
= reply_handler
618 def set_timeout(self
, timeout
):
619 self
.pmap
.set_timeout(timeout
)
621 def my_reply_handler(self
, reply
, fromaddr
):
623 self
.unpacker
.reset(res
)
624 result
= self
.unpack_func()
626 self
.replies
.append((result
, fromaddr
))
627 if self
.user_reply_handler
is not None:
628 self
.user_reply_handler(result
, fromaddr
)
630 def make_call(self
, proc
, args
, pack_func
, unpack_func
):
634 if unpack_func
is None:
636 self
.unpack_func
= dummy
638 self
.unpack_func
= unpack_func
640 packed_args
= self
.packer
.get_buf()
641 dummy_replies
= self
.pmap
.Callit( \
642 (self
.prog
, self
.vers
, proc
, packed_args
))
648 # These are not symmetric to the Client classes
649 # XXX No attempt is made to provide authorization hooks yet
653 def __init__(self
, host
, prog
, vers
, port
):
654 self
.host
= host
# Should normally be '' for default interface
657 self
.port
= port
# Should normally be 0 for random port
658 self
.makesocket() # Assigns to self.sock and self.prot
660 self
.host
, self
.port
= self
.sock
.getsockname()
664 mapping
= self
.prog
, self
.vers
, self
.prot
, self
.port
665 p
= TCPPortMapperClient(self
.host
)
666 if not p
.Set(mapping
):
667 raise RuntimeError, 'register failed'
669 def unregister(self
):
670 mapping
= self
.prog
, self
.vers
, self
.prot
, self
.port
671 p
= TCPPortMapperClient(self
.host
)
672 if not p
.Unset(mapping
):
673 raise RuntimeError, 'unregister failed'
675 def handle(self
, call
):
676 # Don't use unpack_header but parse the header piecewise
677 # XXX I have no idea if I am using the right error responses!
678 self
.unpacker
.reset(call
)
680 xid
= self
.unpacker
.unpack_uint()
681 self
.packer
.pack_uint(xid
)
682 temp
= self
.unpacker
.unpack_enum()
684 return None # Not worthy of a reply
685 self
.packer
.pack_uint(REPLY
)
686 temp
= self
.unpacker
.unpack_uint()
687 if temp
!= RPCVERSION
:
688 self
.packer
.pack_uint(MSG_DENIED
)
689 self
.packer
.pack_uint(RPC_MISMATCH
)
690 self
.packer
.pack_uint(RPCVERSION
)
691 self
.packer
.pack_uint(RPCVERSION
)
692 return self
.packer
.get_buf()
693 self
.packer
.pack_uint(MSG_ACCEPTED
)
694 self
.packer
.pack_auth((AUTH_NULL
, make_auth_null()))
695 prog
= self
.unpacker
.unpack_uint()
696 if prog
!= self
.prog
:
697 self
.packer
.pack_uint(PROG_UNAVAIL
)
698 return self
.packer
.get_buf()
699 vers
= self
.unpacker
.unpack_uint()
700 if vers
!= self
.vers
:
701 self
.packer
.pack_uint(PROG_MISMATCH
)
702 self
.packer
.pack_uint(self
.vers
)
703 self
.packer
.pack_uint(self
.vers
)
704 return self
.packer
.get_buf()
705 proc
= self
.unpacker
.unpack_uint()
706 methname
= 'handle_' + repr(proc
)
708 meth
= getattr(self
, methname
)
709 except AttributeError:
710 self
.packer
.pack_uint(PROC_UNAVAIL
)
711 return self
.packer
.get_buf()
712 cred
= self
.unpacker
.unpack_auth()
713 verf
= self
.unpacker
.unpack_auth()
715 meth() # Unpack args, call turn_around(), pack reply
716 except (EOFError, GarbageArgs
):
717 # Too few or too many arguments
719 self
.packer
.pack_uint(xid
)
720 self
.packer
.pack_uint(REPLY
)
721 self
.packer
.pack_uint(MSG_ACCEPTED
)
722 self
.packer
.pack_auth((AUTH_NULL
, make_auth_null()))
723 self
.packer
.pack_uint(GARBAGE_ARGS
)
724 return self
.packer
.get_buf()
726 def turn_around(self
):
731 self
.packer
.pack_uint(SUCCESS
)
733 def handle_0(self
): # Handle NULL message
736 def makesocket(self
):
737 # This MUST be overridden
738 raise RuntimeError, 'makesocket not defined'
740 def bindsocket(self
):
741 # Override this to bind to a different port (e.g. reserved)
742 self
.sock
.bind((self
.host
, self
.port
))
744 def addpackers(self
):
745 # Override this to use derived classes from Packer/Unpacker
746 self
.packer
= Packer()
747 self
.unpacker
= Unpacker('')
750 class TCPServer(Server
):
752 def makesocket(self
):
753 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
754 self
.prot
= IPPROTO_TCP
759 self
.session(self
.sock
.accept())
761 def session(self
, connection
):
762 sock
, (host
, port
) = connection
765 call
= recvrecord(sock
)
768 except socket
.error
, msg
:
769 print 'socket error:', msg
771 reply
= self
.handle(call
)
772 if reply
is not None:
773 sendrecord(sock
, reply
)
775 def forkingloop(self
):
776 # Like loop but uses forksession()
779 self
.forksession(self
.sock
.accept())
781 def forksession(self
, connection
):
782 # Like session but forks off a subprocess
784 # Wait for deceased children
787 pid
, sts
= os
.waitpid(0, 1)
794 connection
[0].close()
797 self
.session(connection
)
799 # Make sure we don't fall through in the parent
804 class UDPServer(Server
):
806 def makesocket(self
):
807 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
808 self
.prot
= IPPROTO_UDP
815 call
, host_port
= self
.sock
.recvfrom(8192)
816 reply
= self
.handle(call
)
818 self
.sock
.sendto(reply
, host_port
)
821 # Simple test program -- dump local portmapper status
824 pmap
= UDPPortMapperClient('')
827 for prog
, vers
, prot
, port
in list:
829 if prot
== IPPROTO_TCP
: print 'tcp',
830 elif prot
== IPPROTO_UDP
: print 'udp',
835 # Test program for broadcast operation -- dump everybody's portmapper status
840 bcastaddr
= sys
.argv
[1]
842 bcastaddr
= '<broadcast>'
843 def rh(reply
, fromaddr
):
844 host
, port
= fromaddr
845 print host
+ '\t' + repr(reply
)
846 pmap
= BroadcastUDPPortMapperClient(bcastaddr
)
847 pmap
.set_reply_handler(rh
)
849 replies
= pmap
.Getport((100002, 1, IPPROTO_UDP
, 0))
852 # Test program for server, with corresponding client
853 # On machine A: python -c 'import rpc; rpc.testsvr()'
854 # On machine B: python -c 'import rpc; rpc.testclt()' A
858 # Simple test class -- proc 1 doubles its string argument as reply
861 arg
= self
.unpacker
.unpack_string()
863 print 'RPC function 1 called, arg', repr(arg
)
864 self
.packer
.pack_string(arg
+ arg
)
866 s
= S('', 0x20000000, 1, 0)
869 except RuntimeError, msg
:
870 print 'RuntimeError:', msg
, '(ignored)'
872 print 'Service started...'
877 print 'Service interrupted.'
882 if sys
.argv
[1:]: host
= sys
.argv
[1]
884 # Client for above server
886 def call_1(self
, arg
):
887 return self
.make_call(1, arg
, \
888 self
.packer
.pack_string
, \
889 self
.unpacker
.unpack_string
)
890 c
= C(host
, 0x20000000, 1)
891 print 'making call...'
892 reply
= c
.call_1('hello, world, ')
893 print 'call returned', repr(reply
)