Home | History | Annotate | Download | only in pdist
      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)
    158