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 The UDP version of the protocol resends requests when it does
7 # XXX not receive a timely reply -- use only for idempotent calls!
9 # XXX There is no provision for call timeout on TCP connections
28 SUCCESS
= 0 # RPC executed successfully
29 PROG_UNAVAIL
= 1 # remote hasn't exported program
30 PROG_MISMATCH
= 2 # remote can't support version #
31 PROC_UNAVAIL
= 3 # program can't support procedure
32 GARBAGE_ARGS
= 4 # procedure can't decode params
34 RPC_MISMATCH
= 0 # RPC version number != 2
35 AUTH_ERROR
= 1 # remote can't authenticate caller
37 AUTH_BADCRED
= 1 # bad credentials (seal broken)
38 AUTH_REJECTEDCRED
= 2 # client must begin new session
39 AUTH_BADVERF
= 3 # bad verifier (seal broken)
40 AUTH_REJECTEDVERF
= 4 # verifier expired or replayed
41 AUTH_TOOWEAK
= 5 # rejected for security reasons
44 class Packer(xdr
.Packer
):
46 def pack_auth(self
, auth
):
48 self
.pack_enum(flavor
)
49 self
.pack_opaque(stuff
)
51 def pack_auth_unix(self
, stamp
, machinename
, uid
, gid
, gids
):
53 self
.pack_string(machinename
)
56 self
.pack_uint(len(gids
))
60 def pack_callheader(self
, xid
, prog
, vers
, proc
, cred
, verf
):
63 self
.pack_uint(RPCVERSION
)
69 # Caller must add procedure-specific part of call
71 def pack_replyheader(self
, xid
, verf
):
74 self
.pack_uint(MSG_ACCEPTED
)
76 self
.pack_enum(SUCCESS
)
77 # Caller must add procedure-specific part of reply
81 BadRPCFormat
= 'rpc.BadRPCFormat'
82 BadRPCVersion
= 'rpc.BadRPCVersion'
83 GarbageArgs
= 'rpc.GarbageArgs'
85 class Unpacker(xdr
.Unpacker
):
87 def unpack_auth(self
):
88 flavor
= self
.unpack_enum()
89 stuff
= self
.unpack_opaque()
90 return (flavor
, stuff
)
92 def unpack_callheader(self
):
93 xid
= self
.unpack_uint(xid
)
94 temp
= self
.unpack_enum()
96 raise BadRPCFormat
, 'no CALL but ' + `temp`
97 temp
= self
.unpack_uint()
98 if temp
<> RPCVERSION
:
99 raise BadRPCVerspion
, 'bad RPC version ' + `temp`
100 prog
= self
.unpack_uint()
101 vers
= self
.unpack_uint()
102 proc
= self
.unpack_uint()
103 cred
= self
.unpack_auth()
104 verf
= self
.unpack_auth()
105 return xid
, prog
, vers
, proc
, cred
, verf
106 # Caller must add procedure-specific part of call
108 def unpack_replyheader(self
):
109 xid
= self
.unpack_uint()
110 mtype
= self
.unpack_enum()
112 raise RuntimeError, 'no REPLY but ' + `mtype`
113 stat
= self
.unpack_enum()
114 if stat
== MSG_DENIED
:
115 stat
= self
.unpack_enum()
116 if stat
== RPC_MISMATCH
:
117 low
= self
.unpack_uint()
118 high
= self
.unpack_uint()
119 raise RuntimeError, \
120 'MSG_DENIED: RPC_MISMATCH: ' + `low
, high`
121 if stat
== AUTH_ERROR
:
122 stat
= self
.unpack_uint()
123 raise RuntimeError, \
124 'MSG_DENIED: AUTH_ERROR: ' + `stat`
125 raise RuntimeError, 'MSG_DENIED: ' + `stat`
126 if stat
<> MSG_ACCEPTED
:
127 raise RuntimeError, \
128 'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat`
129 verf
= self
.unpack_auth()
130 stat
= self
.unpack_enum()
131 if stat
== PROG_UNAVAIL
:
132 raise RuntimeError, 'call failed: PROG_UNAVAIL'
133 if stat
== PROG_MISMATCH
:
134 low
= self
.unpack_uint()
135 high
= self
.unpack_uint()
136 raise RuntimeError, \
137 'call failed: PROG_MISMATCH: ' + `low
, high`
138 if stat
== PROC_UNAVAIL
:
139 raise RuntimeError, 'call failed: PROC_UNAVAIL'
140 if stat
== GARBAGE_ARGS
:
141 raise RuntimeError, 'call failed: GARBAGE_ARGS'
143 raise RuntimeError, 'call failed: ' + `stat`
145 # Caller must get procedure-specific part of reply
148 # Subroutines to create opaque authentication objects
150 def make_auth_null():
153 def make_auth_unix(seed
, host
, uid
, gid
, groups
):
155 p
.pack_auth_unix(seed
, host
, uid
, gid
, groups
)
158 def make_auth_unix_default():
160 from os
import getuid
, getgid
166 return make_auth_unix(int(time
.time()-unix_epoch()), \
167 socket
.gethostname(), uid
, gid
, [])
171 """Very painful calculation of when the Unix Epoch is.
173 This is defined as the return value of time.time() on Jan 1st,
176 On a Unix system, this should always return 0.0. On a Mac, the
177 calculations are needed -- and hard because of integer overflow
178 and other limitations.
182 if _unix_epoch
>= 0: return _unix_epoch
185 localt
= time
.localtime(now
) # (y, m, d, hh, mm, ss, ..., ..., ...)
186 gmt
= time
.gmtime(now
)
187 offset
= time
.mktime(localt
) - time
.mktime(gmt
)
188 y
, m
, d
, hh
, mm
, ss
= 1970, 1, 1, 0, 0, 0
189 offset
, ss
= divmod(ss
+ offset
, 60)
190 offset
, mm
= divmod(mm
+ offset
, 60)
191 offset
, hh
= divmod(hh
+ offset
, 24)
193 _unix_epoch
= time
.mktime((y
, m
, d
, hh
, mm
, ss
, 0, 0, 0))
194 print "Unix epoch:", time
.ctime(_unix_epoch
)
198 # Common base class for clients
202 def __init__(self
, host
, prog
, vers
, port
):
207 self
.makesocket() # Assigns to self.sock
210 self
.lastxid
= 0 # XXX should be more random?
218 def makesocket(self
):
219 # This MUST be overridden
220 raise RuntimeError, 'makesocket not defined'
222 def connsocket(self
):
223 # Override this if you don't want/need a connection
224 self
.sock
.connect((self
.host
, self
.port
))
226 def bindsocket(self
):
227 # Override this to bind to a different port (e.g. reserved)
228 self
.sock
.bind(('', 0))
230 def addpackers(self
):
231 # Override this to use derived classes from Packer/Unpacker
232 self
.packer
= Packer()
233 self
.unpacker
= Unpacker('')
235 def make_call(self
, proc
, args
, pack_func
, unpack_func
):
236 # Don't normally override this (but see Broadcast)
237 if pack_func
is None and args
is not None:
238 raise TypeError, 'non-null args with null pack_func'
239 self
.start_call(proc
)
244 result
= unpack_func()
250 def start_call(self
, proc
):
251 # Don't override this
252 self
.lastxid
= xid
= self
.lastxid
+ 1
257 p
.pack_callheader(xid
, self
.prog
, self
.vers
, proc
, cred
, verf
)
260 # This MUST be overridden
261 raise RuntimeError, 'do_call not defined'
264 # Override this to use more powerful credentials
265 if self
.cred
== None:
266 self
.cred
= (AUTH_NULL
, make_auth_null())
270 # Override this to use a more powerful verifier
271 if self
.verf
== None:
272 self
.verf
= (AUTH_NULL
, make_auth_null())
275 def call_0(self
): # Procedure 0 is always like this
276 return self
.make_call(0, None, None, None)
279 # Record-Marking standard support
281 def sendfrag(sock
, last
, frag
):
283 if last
: x
= x |
0x80000000L
284 header
= (chr(int(x
>>24 & 0xff)) + chr(int(x
>>16 & 0xff)) + \
285 chr(int(x
>>8 & 0xff)) + chr(int(x
& 0xff)))
286 sock
.send(header
+ frag
)
288 def sendrecord(sock
, record
):
289 sendfrag(sock
, 1, record
)
292 header
= sock
.recv(4)
295 x
= long(ord(header
[0]))<<24 |
ord(header
[1])<<16 | \
296 ord(header
[2])<<8 |
ord(header
[3])
297 last
= ((x
& 0x80000000) != 0)
298 n
= int(x
& 0x7fffffff)
302 if not buf
: raise EOFError
307 def recvrecord(sock
):
311 last
, frag
= recvfrag(sock
)
312 record
= record
+ frag
316 # Try to bind to a reserved port (must be root)
318 last_resv_port_tried
= None
319 def bindresvport(sock
, host
):
320 global last_resv_port_tried
321 FIRST
, LAST
= 600, 1024 # Range of ports to try
322 if last_resv_port_tried
== None:
324 last_resv_port_tried
= FIRST
+ os
.getpid() % (LAST
-FIRST
)
325 for i
in range(last_resv_port_tried
, LAST
) + \
326 range(FIRST
, last_resv_port_tried
):
327 last_resv_port_tried
= i
330 return last_resv_port_tried
331 except socket
.error
, (errno
, msg
):
333 raise socket
.error
, (errno
, msg
)
334 raise RuntimeError, 'can\'t assign reserved port'
337 # Client using TCP to a specific port
339 class RawTCPClient(Client
):
341 def makesocket(self
):
342 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
345 call
= self
.packer
.get_buf()
346 sendrecord(self
.sock
, call
)
347 reply
= recvrecord(self
.sock
)
350 xid
, verf
= u
.unpack_replyheader()
351 if xid
<> self
.lastxid
:
352 # Can't really happen since this is TCP...
353 raise RuntimeError, 'wrong xid in reply ' + `xid`
+ \
354 ' instead of ' + `self
.lastxid`
357 # Client using UDP to a specific port
359 class RawUDPClient(Client
):
361 def makesocket(self
):
362 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
365 call
= self
.packer
.get_buf()
368 from select
import select
370 print 'WARNING: select not found, RPC may hang'
372 BUFSIZE
= 8192 # Max UDP buffer size
376 r
, w
, x
= [self
.sock
], [], []
378 r
, w
, x
= select(r
, w
, x
, timeout
)
379 if self
.sock
not in r
:
381 if count
< 0: raise RuntimeError, 'timeout'
382 if timeout
< 25: timeout
= timeout
*2
383 ## print 'RESEND', timeout, count
386 reply
= self
.sock
.recv(BUFSIZE
)
389 xid
, verf
= u
.unpack_replyheader()
390 if xid
<> self
.lastxid
:
396 # Client using UDP broadcast to a specific port
398 class RawBroadcastUDPClient(RawUDPClient
):
400 def __init__(self
, bcastaddr
, prog
, vers
, port
):
401 RawUDPClient
.__init
__(self
, bcastaddr
, prog
, vers
, port
)
402 self
.reply_handler
= None
405 def connsocket(self
):
406 # Don't connect -- use sendto
407 self
.sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_BROADCAST
, 1)
409 def set_reply_handler(self
, reply_handler
):
410 self
.reply_handler
= reply_handler
412 def set_timeout(self
, timeout
):
413 self
.timeout
= timeout
# Use None for infinite timeout
415 def make_call(self
, proc
, args
, pack_func
, unpack_func
):
416 if pack_func
is None and args
is not None:
417 raise TypeError, 'non-null args with null pack_func'
418 self
.start_call(proc
)
421 call
= self
.packer
.get_buf()
422 self
.sock
.sendto(call
, (self
.host
, self
.port
))
424 from select
import select
426 print 'WARNING: select not found, broadcast will hang'
428 BUFSIZE
= 8192 # Max UDP buffer size (for reply)
430 if unpack_func
is None:
434 r
, w
, x
= [self
.sock
], [], []
436 if self
.timeout
is None:
437 r
, w
, x
= select(r
, w
, x
)
439 r
, w
, x
= select(r
, w
, x
, self
.timeout
)
440 if self
.sock
not in r
:
442 reply
, fromaddr
= self
.sock
.recvfrom(BUFSIZE
)
445 xid
, verf
= u
.unpack_replyheader()
446 if xid
<> self
.lastxid
:
449 reply
= unpack_func()
451 replies
.append((reply
, fromaddr
))
452 if self
.reply_handler
:
453 self
.reply_handler(reply
, fromaddr
)
457 # Port mapper interface
459 # Program number, version and (fixed!) port number
465 PMAPPROC_NULL
= 0 # (void) -> void
466 PMAPPROC_SET
= 1 # (mapping) -> bool
467 PMAPPROC_UNSET
= 2 # (mapping) -> bool
468 PMAPPROC_GETPORT
= 3 # (mapping) -> unsigned int
469 PMAPPROC_DUMP
= 4 # (void) -> pmaplist
470 PMAPPROC_CALLIT
= 5 # (call_args) -> call_result
472 # A mapping is (prog, vers, prot, port) and prot is one of:
477 # A pmaplist is a variable-length list of mappings, as follows:
478 # either (1, mapping, pmaplist) or (0).
480 # A call_args is (prog, vers, proc, args) where args is opaque;
481 # a call_result is (port, res) where res is opaque.
484 class PortMapperPacker(Packer
):
486 def pack_mapping(self
, mapping
):
487 prog
, vers
, prot
, port
= mapping
493 def pack_pmaplist(self
, list):
494 self
.pack_list(list, self
.pack_mapping
)
496 def pack_call_args(self
, ca
):
497 prog
, vers
, proc
, args
= ca
501 self
.pack_opaque(args
)
504 class PortMapperUnpacker(Unpacker
):
506 def unpack_mapping(self
):
507 prog
= self
.unpack_uint()
508 vers
= self
.unpack_uint()
509 prot
= self
.unpack_uint()
510 port
= self
.unpack_uint()
511 return prog
, vers
, prot
, port
513 def unpack_pmaplist(self
):
514 return self
.unpack_list(self
.unpack_mapping
)
516 def unpack_call_result(self
):
517 port
= self
.unpack_uint()
518 res
= self
.unpack_opaque()
522 class PartialPortMapperClient
:
524 def addpackers(self
):
525 self
.packer
= PortMapperPacker()
526 self
.unpacker
= PortMapperUnpacker('')
528 def Set(self
, mapping
):
529 return self
.make_call(PMAPPROC_SET
, mapping
, \
530 self
.packer
.pack_mapping
, \
531 self
.unpacker
.unpack_uint
)
533 def Unset(self
, mapping
):
534 return self
.make_call(PMAPPROC_UNSET
, mapping
, \
535 self
.packer
.pack_mapping
, \
536 self
.unpacker
.unpack_uint
)
538 def Getport(self
, mapping
):
539 return self
.make_call(PMAPPROC_GETPORT
, mapping
, \
540 self
.packer
.pack_mapping
, \
541 self
.unpacker
.unpack_uint
)
544 return self
.make_call(PMAPPROC_DUMP
, None, \
546 self
.unpacker
.unpack_pmaplist
)
548 def Callit(self
, ca
):
549 return self
.make_call(PMAPPROC_CALLIT
, ca
, \
550 self
.packer
.pack_call_args
, \
551 self
.unpacker
.unpack_call_result
)
554 class TCPPortMapperClient(PartialPortMapperClient
, RawTCPClient
):
556 def __init__(self
, host
):
557 RawTCPClient
.__init
__(self
, \
558 host
, PMAP_PROG
, PMAP_VERS
, PMAP_PORT
)
561 class UDPPortMapperClient(PartialPortMapperClient
, RawUDPClient
):
563 def __init__(self
, host
):
564 RawUDPClient
.__init
__(self
, \
565 host
, PMAP_PROG
, PMAP_VERS
, PMAP_PORT
)
568 class BroadcastUDPPortMapperClient(PartialPortMapperClient
, \
569 RawBroadcastUDPClient
):
571 def __init__(self
, bcastaddr
):
572 RawBroadcastUDPClient
.__init
__(self
, \
573 bcastaddr
, PMAP_PROG
, PMAP_VERS
, PMAP_PORT
)
576 # Generic clients that find their server through the Port mapper
578 class TCPClient(RawTCPClient
):
580 def __init__(self
, host
, prog
, vers
):
581 pmap
= TCPPortMapperClient(host
)
582 port
= pmap
.Getport((prog
, vers
, IPPROTO_TCP
, 0))
585 raise RuntimeError, 'program not registered'
586 RawTCPClient
.__init
__(self
, host
, prog
, vers
, port
)
589 class UDPClient(RawUDPClient
):
591 def __init__(self
, host
, prog
, vers
):
592 pmap
= UDPPortMapperClient(host
)
593 port
= pmap
.Getport((prog
, vers
, IPPROTO_UDP
, 0))
596 raise RuntimeError, 'program not registered'
597 RawUDPClient
.__init
__(self
, host
, prog
, vers
, port
)
600 class BroadcastUDPClient(Client
):
602 def __init__(self
, bcastaddr
, prog
, vers
):
603 self
.pmap
= BroadcastUDPPortMapperClient(bcastaddr
)
604 self
.pmap
.set_reply_handler(self
.my_reply_handler
)
607 self
.user_reply_handler
= None
613 def set_reply_handler(self
, reply_handler
):
614 self
.user_reply_handler
= reply_handler
616 def set_timeout(self
, timeout
):
617 self
.pmap
.set_timeout(timeout
)
619 def my_reply_handler(self
, reply
, fromaddr
):
621 self
.unpacker
.reset(res
)
622 result
= self
.unpack_func()
624 self
.replies
.append((result
, fromaddr
))
625 if self
.user_reply_handler
is not None:
626 self
.user_reply_handler(result
, fromaddr
)
628 def make_call(self
, proc
, args
, pack_func
, unpack_func
):
632 if unpack_func
is None:
634 self
.unpack_func
= dummy
636 self
.unpack_func
= unpack_func
638 packed_args
= self
.packer
.get_buf()
639 dummy_replies
= self
.pmap
.Callit( \
640 (self
.prog
, self
.vers
, proc
, packed_args
))
646 # These are not symmetric to the Client classes
647 # XXX No attempt is made to provide authorization hooks yet
651 def __init__(self
, host
, prog
, vers
, port
):
652 self
.host
= host
# Should normally be '' for default interface
655 self
.port
= port
# Should normally be 0 for random port
656 self
.makesocket() # Assigns to self.sock and self.prot
658 self
.host
, self
.port
= self
.sock
.getsockname()
662 mapping
= self
.prog
, self
.vers
, self
.prot
, self
.port
663 p
= TCPPortMapperClient(self
.host
)
664 if not p
.Set(mapping
):
665 raise RuntimeError, 'register failed'
667 def unregister(self
):
668 mapping
= self
.prog
, self
.vers
, self
.prot
, self
.port
669 p
= TCPPortMapperClient(self
.host
)
670 if not p
.Unset(mapping
):
671 raise RuntimeError, 'unregister failed'
673 def handle(self
, call
):
674 # Don't use unpack_header but parse the header piecewise
675 # XXX I have no idea if I am using the right error responses!
676 self
.unpacker
.reset(call
)
678 xid
= self
.unpacker
.unpack_uint()
679 self
.packer
.pack_uint(xid
)
680 temp
= self
.unpacker
.unpack_enum()
682 return None # Not worthy of a reply
683 self
.packer
.pack_uint(REPLY
)
684 temp
= self
.unpacker
.unpack_uint()
685 if temp
<> RPCVERSION
:
686 self
.packer
.pack_uint(MSG_DENIED
)
687 self
.packer
.pack_uint(RPC_MISMATCH
)
688 self
.packer
.pack_uint(RPCVERSION
)
689 self
.packer
.pack_uint(RPCVERSION
)
690 return self
.packer
.get_buf()
691 self
.packer
.pack_uint(MSG_ACCEPTED
)
692 self
.packer
.pack_auth((AUTH_NULL
, make_auth_null()))
693 prog
= self
.unpacker
.unpack_uint()
694 if prog
<> self
.prog
:
695 self
.packer
.pack_uint(PROG_UNAVAIL
)
696 return self
.packer
.get_buf()
697 vers
= self
.unpacker
.unpack_uint()
698 if vers
<> self
.vers
:
699 self
.packer
.pack_uint(PROG_MISMATCH
)
700 self
.packer
.pack_uint(self
.vers
)
701 self
.packer
.pack_uint(self
.vers
)
702 return self
.packer
.get_buf()
703 proc
= self
.unpacker
.unpack_uint()
704 methname
= 'handle_' + `proc`
706 meth
= getattr(self
, methname
)
707 except AttributeError:
708 self
.packer
.pack_uint(PROC_UNAVAIL
)
709 return self
.packer
.get_buf()
710 cred
= self
.unpacker
.unpack_auth()
711 verf
= self
.unpacker
.unpack_auth()
713 meth() # Unpack args, call turn_around(), pack reply
714 except (EOFError, GarbageArgs
):
715 # Too few or too many arguments
717 self
.packer
.pack_uint(xid
)
718 self
.packer
.pack_uint(REPLY
)
719 self
.packer
.pack_uint(MSG_ACCEPTED
)
720 self
.packer
.pack_auth((AUTH_NULL
, make_auth_null()))
721 self
.packer
.pack_uint(GARBAGE_ARGS
)
722 return self
.packer
.get_buf()
724 def turn_around(self
):
729 self
.packer
.pack_uint(SUCCESS
)
731 def handle_0(self
): # Handle NULL message
734 def makesocket(self
):
735 # This MUST be overridden
736 raise RuntimeError, 'makesocket not defined'
738 def bindsocket(self
):
739 # Override this to bind to a different port (e.g. reserved)
740 self
.sock
.bind((self
.host
, self
.port
))
742 def addpackers(self
):
743 # Override this to use derived classes from Packer/Unpacker
744 self
.packer
= Packer()
745 self
.unpacker
= Unpacker('')
748 class TCPServer(Server
):
750 def makesocket(self
):
751 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
752 self
.prot
= IPPROTO_TCP
757 self
.session(self
.sock
.accept())
759 def session(self
, connection
):
760 sock
, (host
, port
) = connection
763 call
= recvrecord(sock
)
766 except socket
.error
, msg
:
767 print 'socket error:', msg
769 reply
= self
.handle(call
)
770 if reply
is not None:
771 sendrecord(sock
, reply
)
773 def forkingloop(self
):
774 # Like loop but uses forksession()
777 self
.forksession(self
.sock
.accept())
779 def forksession(self
, connection
):
780 # Like session but forks off a subprocess
782 # Wait for deceased children
785 pid
, sts
= os
.waitpid(0, 1)
792 connection
[0].close()
795 self
.session(connection
)
797 # Make sure we don't fall through in the parent
802 class UDPServer(Server
):
804 def makesocket(self
):
805 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
806 self
.prot
= IPPROTO_UDP
813 call
, host_port
= self
.sock
.recvfrom(8192)
814 reply
= self
.handle(call
)
816 self
.sock
.sendto(reply
, host_port
)
819 # Simple test program -- dump local portmapper status
822 pmap
= UDPPortMapperClient('')
825 for prog
, vers
, prot
, port
in list:
827 if prot
== IPPROTO_TCP
: print 'tcp',
828 elif prot
== IPPROTO_UDP
: print 'udp',
833 # Test program for broadcast operation -- dump everybody's portmapper status
838 bcastaddr
= sys
.argv
[1]
840 bcastaddr
= '<broadcast>'
841 def rh(reply
, fromaddr
):
842 host
, port
= fromaddr
843 print host
+ '\t' + `reply`
844 pmap
= BroadcastUDPPortMapperClient(bcastaddr
)
845 pmap
.set_reply_handler(rh
)
847 replies
= pmap
.Getport((100002, 1, IPPROTO_UDP
, 0))
850 # Test program for server, with corresponding client
851 # On machine A: python -c 'import rpc; rpc.testsvr()'
852 # On machine B: python -c 'import rpc; rpc.testclt()' A
856 # Simple test class -- proc 1 doubles its string argument as reply
859 arg
= self
.unpacker
.unpack_string()
861 print 'RPC function 1 called, arg', `arg`
862 self
.packer
.pack_string(arg
+ arg
)
864 s
= S('', 0x20000000, 1, 0)
867 except RuntimeError, msg
:
868 print 'RuntimeError:', msg
, '(ignored)'
870 print 'Service started...'
875 print 'Service interrupted.'
880 if sys
.argv
[1:]: host
= sys
.argv
[1]
882 # Client for above server
884 def call_1(self
, arg
):
885 return self
.make_call(1, arg
, \
886 self
.packer
.pack_string
, \
887 self
.unpacker
.unpack_string
)
888 c
= C(host
, 0x20000000, 1)
889 print 'making call...'
890 reply
= c
.call_1('hello, world, ')
891 print 'call returned', `reply`