1 | """RPC Client module."""
|
---|
2 |
|
---|
3 | import sys
|
---|
4 | import socket
|
---|
5 | import pickle
|
---|
6 | import __builtin__
|
---|
7 | import os
|
---|
8 |
|
---|
9 |
|
---|
10 | # Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
|
---|
11 | VERBOSE = 1
|
---|
12 |
|
---|
13 |
|
---|
14 | class Client:
|
---|
15 |
|
---|
16 | """RPC Client class. No need to derive a class -- it's fully generic."""
|
---|
17 |
|
---|
18 | def __init__(self, address, verbose = VERBOSE):
|
---|
19 | self._pre_init(address, verbose)
|
---|
20 | self._post_init()
|
---|
21 |
|
---|
22 | def _pre_init(self, address, verbose = VERBOSE):
|
---|
23 | if type(address) == type(0):
|
---|
24 | address = ('', address)
|
---|
25 | self._address = address
|
---|
26 | self._verbose = verbose
|
---|
27 | if self._verbose: print "Connecting to %s ..." % repr(address)
|
---|
28 | self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
---|
29 | self._socket.connect(address)
|
---|
30 | if self._verbose: print "Connected."
|
---|
31 | self._lastid = 0 # Last id for which a reply has been received
|
---|
32 | self._nextid = 1 # Id of next request
|
---|
33 | self._replies = {} # Unprocessed replies
|
---|
34 | self._rf = self._socket.makefile('r')
|
---|
35 | self._wf = self._socket.makefile('w')
|
---|
36 |
|
---|
37 | def _post_init(self):
|
---|
38 | self._methods = self._call('.methods')
|
---|
39 |
|
---|
40 | def __del__(self):
|
---|
41 | self._close()
|
---|
42 |
|
---|
43 | def _close(self):
|
---|
44 | if self._rf: self._rf.close()
|
---|
45 | self._rf = None
|
---|
46 | if self._wf: self._wf.close()
|
---|
47 | self._wf = None
|
---|
48 | if self._socket: self._socket.close()
|
---|
49 | self._socket = None
|
---|
50 |
|
---|
51 | def __getattr__(self, name):
|
---|
52 | if name in self._methods:
|
---|
53 | method = _stub(self, name)
|
---|
54 | setattr(self, name, method) # XXX circular reference
|
---|
55 | return method
|
---|
56 | raise AttributeError, name
|
---|
57 |
|
---|
58 | def _setverbose(self, verbose):
|
---|
59 | self._verbose = verbose
|
---|
60 |
|
---|
61 | def _call(self, name, *args):
|
---|
62 | return self._vcall(name, args)
|
---|
63 |
|
---|
64 | def _vcall(self, name, args):
|
---|
65 | return self._recv(self._vsend(name, args))
|
---|
66 |
|
---|
67 | def _send(self, name, *args):
|
---|
68 | return self._vsend(name, args)
|
---|
69 |
|
---|
70 | def _send_noreply(self, name, *args):
|
---|
71 | return self._vsend(name, args, 0)
|
---|
72 |
|
---|
73 | def _vsend_noreply(self, name, args):
|
---|
74 | return self._vsend(name, args, 0)
|
---|
75 |
|
---|
76 | def _vsend(self, name, args, wantreply = 1):
|
---|
77 | id = self._nextid
|
---|
78 | self._nextid = id+1
|
---|
79 | if not wantreply: id = -id
|
---|
80 | request = (name, args, id)
|
---|
81 | if self._verbose > 1: print "sending request: %s" % repr(request)
|
---|
82 | wp = pickle.Pickler(self._wf)
|
---|
83 | wp.dump(request)
|
---|
84 | return id
|
---|
85 |
|
---|
86 | def _recv(self, id):
|
---|
87 | exception, value, rid = self._vrecv(id)
|
---|
88 | if rid != id:
|
---|
89 | raise RuntimeError, "request/reply id mismatch: %d/%d" % (id, rid)
|
---|
90 | if exception is None:
|
---|
91 | return value
|
---|
92 | x = exception
|
---|
93 | if hasattr(__builtin__, exception):
|
---|
94 | x = getattr(__builtin__, exception)
|
---|
95 | elif exception in ('posix.error', 'mac.error'):
|
---|
96 | x = os.error
|
---|
97 | if x == exception:
|
---|
98 | exception = x
|
---|
99 | raise exception, value
|
---|
100 |
|
---|
101 | def _vrecv(self, id):
|
---|
102 | self._flush()
|
---|
103 | if self._replies.has_key(id):
|
---|
104 | if self._verbose > 1: print "retrieving previous reply, id = %d" % id
|
---|
105 | reply = self._replies[id]
|
---|
106 | del self._replies[id]
|
---|
107 | return reply
|
---|
108 | aid = abs(id)
|
---|
109 | while 1:
|
---|
110 | if self._verbose > 1: print "waiting for reply, id = %d" % id
|
---|
111 | rp = pickle.Unpickler(self._rf)
|
---|
112 | reply = rp.load()
|
---|
113 | del rp
|
---|
114 | if self._verbose > 1: print "got reply: %s" % repr(reply)
|
---|
115 | rid = reply[2]
|
---|
116 | arid = abs(rid)
|
---|
117 | if arid == aid:
|
---|
118 | if self._verbose > 1: print "got it"
|
---|
119 | return reply
|
---|
120 | self._replies[rid] = reply
|
---|
121 | if arid > aid:
|
---|
122 | if self._verbose > 1: print "got higher id, assume all ok"
|
---|
123 | return (None, None, id)
|
---|
124 |
|
---|
125 | def _flush(self):
|
---|
126 | self._wf.flush()
|
---|
127 |
|
---|
128 |
|
---|
129 | from security import Security
|
---|
130 |
|
---|
131 |
|
---|
132 | class SecureClient(Client, Security):
|
---|
133 |
|
---|
134 | def __init__(self, *args):
|
---|
135 | import string
|
---|
136 | apply(self._pre_init, args)
|
---|
137 | Security.__init__(self)
|
---|
138 | self._wf.flush()
|
---|
139 | line = self._rf.readline()
|
---|
140 | challenge = string.atoi(string.strip(line))
|
---|
141 | response = self._encode_challenge(challenge)
|
---|
142 | line = repr(long(response))
|
---|
143 | if line[-1] in 'Ll': line = line[:-1]
|
---|
144 | self._wf.write(line + '\n')
|
---|
145 | self._wf.flush()
|
---|
146 | self._post_init()
|
---|
147 |
|
---|
148 | class _stub:
|
---|
149 |
|
---|
150 | """Helper class for Client -- each instance serves as a method of the client."""
|
---|
151 |
|
---|
152 | def __init__(self, client, name):
|
---|
153 | self._client = client
|
---|
154 | self._name = name
|
---|
155 |
|
---|
156 | def __call__(self, *args):
|
---|
157 | return self._client._vcall(self._name, args)
|
---|