NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

ObjectCallGPCS.py (30019B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\script\net\ObjectCallGPCS.py
      2 import copy
      3 import sys
      4 import types
      5 import weakref
      6 import blue
      7 import carbon.common.script.net.machobase as macho
      8 import localstorage
      9 import log
     10 import monolithconfig
     11 import stackless
     12 import uthread
     13 from carbon.common.lib import const
     14 from carbon.common.script.net.machoNetExceptions import UnMachoDestination
     15 from carbon.common.script.net.moniker import Moniker
     16 from carbon.common.script.sys.sessions import GetSessions
     17 from carbon.common.script.util.timerstuff import ClockThisWithoutTheStars
     18 from carbon.common.script.net import objectCaching
     19 from carbon.common.script.sys import basesession, service
     20 from carbon.common.script.net.machoNetPacket import CallReq
     21 from carbon.common.script.net.machoNetPacket import Notification
     22 from carbon.common.script.net.machoNetAddress import MachoAddress
     23 from carbon.common.script.net.cachedObject import CachedObject as utilCachedObject
     24 from cluster import MACHONETMSG_TYPE_AUTHENTICATION_REQ
     25 from eveprefs import prefs, boot
     26 from machonet_tracing import EgressTracer, IngressTracer
     27 from stringutil import strx
     28 
     29 class ObjectCall():
     30     __guid__ = 'gpcs.ObjectCall'
     31 
     32     def __init__(self):
     33         if macho.mode == 'client':
     34             self.identifier = 'C=0'
     35         else:
     36             self.identifier = 'N=%d' % sm.services['machoNet'].nodeID
     37         self.releasedObjects = {}
     38         self.logClientCall = prefs.GetValue('logClientCall', 0) or prefs.GetValue('clusterMode', '') == 'LOCAL'
     39         uthread.new(self.__ReleaseObjectHelper).context = 'machoNet::ObjectCall.ReleaseObjectHelper'
     40 
     41     def __ReleaseObjectHelper(self):
     42         while not self.machoNet.stop:
     43             blue.pyos.synchro.SleepWallclock(30000)
     44             if self.releasedObjects:
     45                 tmp = self.releasedObjects
     46                 self.releasedObjects = {}
     47                 chrto = {}
     48                 for objectID, v in tmp.iteritems():
     49                     for refID, sess, destination in v:
     50                         if hasattr(destination, 'nodeID'):
     51                             try:
     52                                 if objectID not in sess.machoObjectConnectionsByObjectID:
     53                                     daKey = (sess.sid, destination.nodeID)
     54                                     if daKey in chrto:
     55                                         chrto[daKey][2].append((objectID, refID))
     56                                     else:
     57                                         chrto[daKey] = [sess, destination, [(objectID, refID)]]
     58                             except ReferenceError:
     59                                 sys.exc_clear()
     60 
     61                 for v in chrto.itervalues():
     62                     oidsminus = {}
     63                     for each in v[2]:
     64                         if each[0] in oidsminus:
     65                             oidsminus[each[0]] = max(each[1], oidsminus[each[0]])
     66                         else:
     67                             oidsminus[each[0]] = each[1]
     68 
     69                     oob = {'OID-': oidsminus}
     70                     self.ObjectCall(oob, 'ClientHasReleasedTheseObjects', -1, v[0], v[1], v[2], 'ClientHasReleasedTheseObjects')
     71 
     72     def CallUp(self, packet):
     73         if packet.command == MACHONETMSG_TYPE_AUTHENTICATION_REQ:
     74             retval = self.ForwardCallUp(packet)
     75             self.__StringifyPayloadForLogging(retval, retval.payload)
     76             retval.payload = self.ToPickle(retval.payload)
     77             if self.added_objects:
     78                 retval.oob['OID+'] = self.added_objects
     79             retval.compressedPart = self.compressedPart
     80             return retval
     81         elif packet.payload[0]:
     82             tup = self.FromPickle(packet.payload[1], packet.source)
     83             objectID, method, args = tup[0], tup[1], tup[2]
     84             if objectID[0] == 'C' and macho.mode == 'client':
     85                 cid, oid = objectID.split(':')
     86                 objectID = '%s:%s' % (self.identifier, oid)
     87             if objectID not in session.machoObjectsByID:
     88                 session.DumpSession('OBJECTNOTMEMBER', 'The specified object (%s) is not a member of this session' % objectID)
     89                 raise UnMachoDestination('The specified object (%s) is not a member of this session' % objectID)
     90             object = session.machoObjectsByID[objectID][1]
     91             if isinstance(object, basesession.ObjectConnection):
     92                 objclass = object.GetObjectConnectionLogClass()
     93             else:
     94                 objclass = macho.AssignLogName(object)
     95             IngressTracer.start_trace(packet, objclass, method)
     96             callTimer = basesession.CallTimer('%s::%s (ObjectCall\\Server)' % (objclass, method))
     97             if self.logClientCall and packet.source.addressType == const.ADDRESS_TYPE_CLIENT:
     98                 kwargs = tup[3] if len(tup) >= 4 else None
     99                 self.machoNet.LogClientCall(session, objclass, method, args, kwargs)
    100             try:
    101                 func = getattr(object, method)
    102                 if method in object.__dict__.get('__restrictedcalls__', {}) or method.startswith('_'):
    103                     raise RuntimeError('This call can only be the result of a hack attack', method)
    104                 if not isinstance(object, basesession.ObjectConnection):
    105                     if 0 == session.role & service.ROLE_SERVICE and hasattr(object, '__exportedcalls__') and method not in object.__exportedcalls__:
    106                         raise RuntimeError('%s call is not exported from this pass-by-reference object.' % method)
    107                     if not session.role & service.ROLE_SERVICE:
    108                         paramList = object.__exportedcalls__.get(method, None)
    109                         paramDict = None
    110                         if paramList is not None:
    111                             if type(paramList) is list:
    112                                 if len(paramList):
    113                                     tmp = {}
    114                                     tmp['role'] = paramList[0]
    115                                     tmp['preargs'] = paramList[1:]
    116                                     paramDict = tmp
    117                             elif type(paramList) is dict:
    118                                 paramDict = paramList
    119                         if paramDict is not None:
    120                             role = paramDict.get('role', service.ROLE_SERVICE)
    121                             if not role & session.role:
    122                                 session.LogSessionError("Called %s::%s, which requires role 0x%x, which the user doesn't have. User has role 0x%x" % (object.__class__.__name__,
    123                                  method,
    124                                  role,
    125                                  session.role))
    126                                 raise RuntimeError('RoleNotAssigned', "%s::%s requires role 0x%x, which the user doesn't have. User has role 0x%x. Calling session: %s" % (object.__class__.__name__,
    127                                  method,
    128                                  role,
    129                                  session.role,
    130                                  str(session)))
    131                             preargs = paramDict.get('preargs', [])
    132                             args2 = len(preargs) * [None] + list(args)
    133                             for i in range(len(preargs)):
    134                                 args2[i] = getattr(session, preargs[i])
    135 
    136                             args = tuple(args2)
    137                     if len(tup) == 4 and 'machoVersion' in tup[3]:
    138                         if len(tup[3]) == 1:
    139                             tup = (tup[0], tup[1], tup[2])
    140                         else:
    141                             del tup[3]['machoVersion']
    142                 with IngressTracer.record_process_call_ms():
    143                     if len(tup) == 3:
    144                         retval = ClockThisWithoutTheStars('Remote Object Call::%s::%s' % (objclass, method), func, args)
    145                     else:
    146                         retval = ClockThisWithoutTheStars('Remote Object Call::%s::%s' % (objclass, method), func, args, tup[3])
    147                 retpickle = self.ToPickle(retval)
    148                 retval = packet.Response(payload=retpickle)
    149                 if self.added_objects:
    150                     retval.oob['OID+'] = self.added_objects
    151                 self.__StringifyPayloadForLogging(retval, retpickle)
    152                 retval.compressedPart = self.compressedPart
    153             finally:
    154                 callTimer.Done()
    155 
    156             return retval
    157         else:
    158             packet.payload = self.FromPickle(packet.payload[1], packet.source)
    159             retval = self.ForwardCallUp(packet)
    160             self.__StringifyPayloadForLogging(retval, retval.payload)
    161             retval.payload = self.ToPickle(retval.payload)
    162             if self.added_objects:
    163                 retval.oob['OID+'] = self.added_objects
    164             retval.compressedPart = self.compressedPart
    165             return retval
    166 
    167     def CallDown(self, packet):
    168         if packet.command == MACHONETMSG_TYPE_AUTHENTICATION_REQ:
    169             retval = self.ForwardCallDown(packet)
    170             retval.payload = self.FromPickle(retval.payload, packet.destination)
    171             return retval
    172         else:
    173             trace = False
    174             if packet.oob.get('traceMe', False) is True:
    175                 log.general.Log('ObjectCall::CallDown: ForwardCallDown ist: %s' % str(self.ForwardCallDown), log.LGNOTICE)
    176             self.__StringifyPayloadForLogging(packet, packet.payload)
    177             packet.payload = (0, self.ToPickle(packet.payload, 0))
    178             if self.added_objects:
    179                 packet.oob['OID+'] = self.added_objects
    180             packet.compressedPart = self.compressedPart
    181             retval = self.ForwardCallDown(packet)
    182             retval.payload = self.FromPickle(retval.payload, packet.destination)
    183             return retval
    184 
    185     def NotifyUp(self, packet):
    186         if packet.payload[0] == 1:
    187             tup = self.FromPickle(packet.payload[1], packet.source)
    188             objectID, method, args = tup[0], tup[1], tup[2]
    189             if method == 'ClientHasReleasedTheseObjects':
    190                 oidsandrefs = objectID
    191                 for each in oidsandrefs:
    192                     objectID, refID = each
    193                     if objectID in session.machoObjectsByID:
    194                         object = session.machoObjectsByID[objectID][1]
    195                         objectType, guts = self.__GetObjectTypeAndGuts(object)
    196                         session.UnregisterMachoObject(objectID, refID)
    197 
    198                 return
    199             else:
    200                 if objectID[0] == 'C' and macho.mode == 'client':
    201                     cid, oid = objectID.split(':')
    202                     objectID = '%s:%s' % (self.identifier, oid)
    203                 if objectID not in session.machoObjectsByID:
    204                     raise UnMachoDestination('The specified object (%s) is not a member of this session' % objectID)
    205                 object = session.machoObjectsByID[objectID][1]
    206                 if isinstance(object, basesession.ObjectConnection):
    207                     objclass = object.GetObjectConnectionLogClass()
    208                 else:
    209                     objclass = macho.AssignLogName(object)
    210                 callTimer = basesession.CallTimer('%s::%s (ObjectNotify\\Server)' % (objclass, method))
    211                 try:
    212                     func = getattr(object, method)
    213                     if method in object.__dict__.get('__restrictedcalls__', {}) or method.startswith('_'):
    214                         raise RuntimeError('This call can only be the result of a hack attack', method)
    215                     if not isinstance(object, basesession.ObjectConnection) and len(tup) == 4 and 'machoVersion' in tup[3]:
    216                         if len(tup[3]) == 1:
    217                             tup = (tup[0], tup[1], tup[2])
    218                         else:
    219                             del tup[3]['machoVersion']
    220                     if len(tup) == 3:
    221                         ClockThisWithoutTheStars('Remote Object Notify::%s::%s' % (objclass, method), func, args)
    222                     else:
    223                         ClockThisWithoutTheStars('Remote Object Notify::%s::%s' % (objclass, method), func, args, tup[3])
    224                 finally:
    225                     callTimer.Done()
    226 
    227                 return
    228         else:
    229             packet.payload = self.FromPickle(packet.payload[1], packet.source)
    230         self.ForwardNotifyUp(packet)
    231 
    232     def NotifyDown(self, packet):
    233         self.__StringifyPayloadForLogging(packet, packet.payload)
    234         packet.payload = (0, self.ToPickle(packet.payload))
    235         if self.added_objects:
    236             packet.oob['OID+'] = self.added_objects
    237         packet.compressedPart = self.compressedPart
    238         self.ForwardNotifyDown(packet)
    239 
    240     def __GetObjectTypeAndGuts(self, object):
    241         ob = object
    242         if isinstance(object, basesession.ObjectConnection):
    243             ob = object.__dict__['__object__']
    244             objclass = object.GetObjectConnectionLogClass()
    245         else:
    246             objclass = macho.AssignLogName(object)
    247         return (objclass, ob)
    248 
    249     def GetObjectID(self, object):
    250         if type(object) == types.InstanceType:
    251             if isinstance(object, stackless.tasklet):
    252                 raise RuntimeError('Thou shall not send stackless.tasklet across the wire')
    253             if not getattr(object, '__passbyvalue__', 0):
    254                 objectID = basesession.GetObjectUUID(object)
    255                 if objectID in session.machoObjectsByID:
    256                     session.machoObjectsByID[objectID][0] = blue.os.GetWallclockTime()
    257                 else:
    258                     objectType, guts = self.__GetObjectTypeAndGuts(object)
    259                     session.LogSessionHistory('%s object %s added to session' % (objectType, objectID), strx(guts))
    260                     if objectType is None:
    261                         raise RuntimeError('NoneType object added by reference, undoubtedly ObjectConnection to None')
    262                 if objectID not in self.added_objects:
    263                     refID = blue.os.GetWallclockTime()
    264                     self.added_objects[objectID] = refID
    265                     session.RegisterMachoObject(objectID, object, refID)
    266                 else:
    267                     refID = self.added_objects[objectID]
    268                 pa = {}
    269                 for each in getattr(object, '__publicattributes__', []):
    270                     if hasattr(object, each):
    271                         pa[each] = getattr(object, each)
    272 
    273                 if pa:
    274                     return blue.marshal.Save((objectID, pa, refID), self.GetObjectID)
    275                 else:
    276                     return blue.marshal.Save((objectID, refID), self.GetObjectID)
    277             elif isinstance(object, utilCachedObject):
    278                 sm.services['objectCaching'].CacheObject(object)
    279             elif isinstance(object, objectCaching.CachedObject):
    280                 self.compressedPart += object.CompressedPart()
    281             elif isinstance(object, Moniker):
    282                 object.QuickResolve()
    283 
    284     def ParseObjectID(self, objectID):
    285         if session is None:
    286             self.machoNet.LogError('Session is none during ParseObjectID!!!')
    287             log.LogTraceback()
    288         t = blue.marshal.Load(objectID, self.ParseObjectID)
    289         if len(t) == 2:
    290             objectID, refID = t
    291             pa = {}
    292         else:
    293             objectID, pa, refID = t
    294         if objectID not in session.machoObjectsByID:
    295             nid, oid = objectID.split(':')
    296             if nid != self.identifier:
    297                 if nid[0] == 'C':
    298                     if self.otherDudesAddress.addressType == const.ADDRESS_TYPE_CLIENT:
    299                         objectID = 'C=%d:%s' % (self.otherDudesAddress.clientID, oid)
    300                 return MachoObjectConnection(session.GetActualSession(), self, objectID, pa, refID)
    301             raise RuntimeError('The specified object (%s) does not exist within this session (%s)' % (strx(objectID), strx(session)))
    302         return weakref.proxy(session.machoObjectsByID[objectID][1])
    303 
    304     def OnObjectPublicAttributesUpdated(self, uuid, pa, args, keywords):
    305         for sess in GetSessions():
    306             try:
    307                 if sess.machoObjectConnectionsByObjectID.get(uuid, 0):
    308                     for conn in sess.machoObjectConnectionsByObjectID[uuid][1].itervalues():
    309                         k = keywords.get('partial', [])
    310                         if k:
    311                             old = {}
    312                             for each in k:
    313                                 old[each] = conn.__publicattributes__[each]
    314                                 conn.__publicattributes__[each] = pa[each]
    315 
    316                         else:
    317                             old = conn.__publicattributes__
    318                             conn.__publicattributes__ = pa
    319                         for each in conn.objectChangeHandlers.iterkeys():
    320                             try:
    321                                 func = getattr(each, 'OnObjectChanged')
    322                             except Exception:
    323                                 log.LogException()
    324                                 sys.exc_clear()
    325                                 continue
    326 
    327                             theArgs = [conn, old, conn.__publicattributes__] + list(args)
    328                             uthread.worker('machoNet::OnObjectChanged', func, *theArgs, **keywords)
    329 
    330             except Exception:
    331                 log.LogException('Exception during OnObjectPublicAttributesUpdated')
    332                 sys.exc_clear()
    333 
    334     def ToPickle(self, value, nonblocking = 1):
    335         try:
    336             self.compressedPart = 0
    337             self.added_objects = {}
    338             ret = blue.marshal.Save(value, self.GetObjectID)
    339             if nonblocking and len(ret) > self.machoNet.largeResponseWarningThreshold:
    340                 ctk = localstorage.GetLocalStorage().get('calltimer.key', None)
    341                 if len(ret) > self.machoNet.largeResponseErrorThreshold:
    342                     if ctk is not None:
    343                         self.machoNet.LogError('Unacceptably large response from ', ctk, ', ', len(ret), ' bytes')
    344                     else:
    345                         try:
    346                             self.machoNet.LogError('Unacceptably large response from unknown source, ', len(ret), ' bytes - ', repr(value)[:1024])
    347                         except Exception:
    348                             self.machoNet.LogError('Unacceptably large response from unknown source 2, ', len(ret), ' bytes - ', ret[:256])
    349 
    350                     log.LogTraceback()
    351                 elif ctk is not None:
    352                     self.machoNet.LogWarn('Unacceptably large response from ', ctk, ', ', len(ret), ' bytes')
    353                 else:
    354                     try:
    355                         self.machoNet.LogWarn('Unacceptably large response from unknown source, ', len(ret), ' bytes - ', repr(value)[:1024])
    356                     except Exception:
    357                         self.machoNet.LogWarn('Unacceptably large response from unknown source 2, ', len(ret), ' bytes - ', ret[:256])
    358 
    359             return ret
    360         except Exception:
    361             self.machoNet.LogError("That was NOT a nice thing to do!  Keep yo' unpicklables to yourself.")
    362             log.LogTraceback()
    363             raise
    364 
    365     def __StringifyPayloadForLogging(self, packet, payload):
    366         if self.machoNet.logChannel.IsOpen(1):
    367             packet.strayload = strx(payload)
    368 
    369     def FromPickle(self, thePickle, otherDudesAddress):
    370         try:
    371             self.otherDudesAddress = otherDudesAddress
    372             payload = blue.marshal.Load(thePickle, self.ParseObjectID)
    373         except Exception:
    374             self.machoNet.LogError('Failed unpickling buffer of length %d' % len(thePickle))
    375             self.machoNet.LogError('First 100 bytes: %s' % strx(thePickle[:100]))
    376             log.LogTraceback()
    377             raise
    378 
    379         return payload
    380 
    381     def ObjectCall(self, oob, persistantObjectID, blocking, sess, destination, objectID, method, *args, **keywords):
    382         return self.ObjectCallWithoutTheStars(oob, persistantObjectID, blocking, sess, destination, objectID, method, args, keywords)
    383 
    384     def ObjectCallWithoutTheStars(self, oob, persistantObjectID, blocking, sess, destination, objectID, method, args, keywords):
    385         cachable, versionCheck, throwOK, cachedResultRecord, cacheKey, cacheDetails = sm.GetService('objectCaching').PerformCachedMethodCall(None, persistantObjectID, method, args)
    386         if cachable:
    387             if not versionCheck:
    388                 if throwOK:
    389                     sm.GetService('objectCaching').LogInfo('Local cache hit with throw for remote object ', persistantObjectID, '::', method, '(', args, ')')
    390                     raise objectCaching.CacheOK()
    391                 elif cachedResultRecord:
    392                     sm.GetService('objectCaching').LogInfo('Local cache hit for remote object ', persistantObjectID, '::', method, '(', args, ')')
    393                     return cachedResultRecord['lret']
    394         if keywords:
    395             kw2 = copy.copy(keywords)
    396         else:
    397             kw2 = {}
    398         if kw2.get('machoVersion', None) is None:
    399             if cachedResultRecord is not None and cachedResultRecord['version'] is not None:
    400                 kw2['machoVersion'] = cachedResultRecord['version']
    401             else:
    402                 kw2['machoVersion'] = 1
    403         if currentcall:
    404             machoTimeout = kw2.get('machoTimeout', currentcall.packet.oob.get('machoTimeout', None))
    405         else:
    406             machoTimeout = kw2.get('machoTimeout', None)
    407         if 'machoTimeout' in kw2:
    408             del kw2['machoTimeout']
    409         payload = (1, self.ToPickle((objectID,
    410           method,
    411           args,
    412           kw2)))
    413         if blocking == 1:
    414             packet = CallReq(destination=destination, payload=payload)
    415             service_name = self.get_brokering_service_name(persistantObjectID)
    416             with EgressTracer(packet, service_name, method) as trace:
    417                 bind_params = self.get_object_bind_params(persistantObjectID)
    418                 trace.set_bind_parameters(bind_params)
    419                 if oob:
    420                     packet.oob = oob
    421                 if machoTimeout is not None:
    422                     packet.oob['machoTimeout'] = machoTimeout
    423                 if self.added_objects:
    424                     if 'OID+' in packet.oob:
    425                         packet.oob['OID+'].update(self.added_objects)
    426                     else:
    427                         packet.oob['OID+'] = self.added_objects
    428                 try:
    429                     callTimer = basesession.CallTimer('Remote Object Call::%s (ObjectCall\\Client)' % (method,))
    430                     try:
    431                         response = self.ForwardCallDown(packet)
    432                     finally:
    433                         callTimer.Done()
    434 
    435                     retval = self.FromPickle(response.payload, packet.destination)
    436                 except objectCaching.CacheOK:
    437                     if cachedResultRecord is None:
    438                         raise
    439                     sys.exc_clear()
    440                     sm.GetService('objectCaching').LogInfo('Version matches for remote object ', persistantObjectID, '::', method, '(', args, ')')
    441                     sm.GetService('objectCaching').UpdateVersionCheckPeriod(cacheKey)
    442                     return cachedResultRecord['lret']
    443 
    444                 if isinstance(retval, objectCaching.CachedMethodCallResult):
    445                     sm.GetService('objectCaching').CacheMethodCall(persistantObjectID, method, args, retval)
    446                     retval = retval.GetResult()
    447                 return retval
    448         else:
    449             packet = Notification(destination=destination, payload=payload)
    450             if oob:
    451                 packet.oob = oob
    452             if machoTimeout is not None:
    453                 packet.oob['machoTimeout'] = machoTimeout
    454             if self.added_objects:
    455                 if 'OID+' in packet.oob:
    456                     packet.oob['OID+'].update(self.added_objects)
    457                 else:
    458                     packet.oob['OID+'] = self.added_objects
    459             self.ForwardNotifyDown(packet)
    460             return MachoBoobyTrap()
    461 
    462     def get_brokering_service_name(self, persistantObjectID):
    463         if isinstance(persistantObjectID, tuple) and len(persistantObjectID) > 1:
    464             return persistantObjectID[0]
    465         return 'unknown object call type'
    466 
    467     def get_object_bind_params(self, persistantObjectID):
    468         if isinstance(persistantObjectID, tuple) and len(persistantObjectID) > 1:
    469             return persistantObjectID[1]
    470 
    471 
    472 class MachoBoobyTrap():
    473     __guid__ = 'gpcs.MachoBoobyTrap'
    474 
    475     def __str__(self):
    476         return 'MachoBoobyTrap'
    477 
    478     def __getattr__(self, attribute):
    479         try:
    480             log.LogTraceback()
    481         except Exception:
    482             sys.exc_clear()
    483 
    484         raise RuntimeError("Attempted to access %s from a non-blocking call's result" % attribute)
    485 
    486 
    487 class MachoObjectConnection():
    488 
    489     def __init__(self, sess, gpcs, objectID, pa, refID):
    490         try:
    491             self.__dict__['session'] = weakref.proxy(sess)
    492         except TypeError:
    493             self.__dict__['session'] = sess
    494             sys.exc_clear()
    495 
    496         self.__dict__['objectID'] = objectID
    497         self.__dict__['sessionCheck'] = None
    498         self.__dict__['persistantObjectID'] = objectID
    499         self.__dict__['gpcs'] = weakref.proxy(gpcs)
    500         self.__dict__['__publicattributes__'] = pa
    501         self.__dict__['objectChangeHandlers'] = weakref.WeakKeyDictionary({})
    502         mask = sess.Masquerade()
    503         try:
    504             self.__dict__['clisid'] = gpcs.machoNet.GetClientSessionID()
    505         finally:
    506             mask.UnMask()
    507 
    508         if objectID[0] == 'C':
    509             self.__dict__['blocking'] = 0
    510             cid, oid = objectID.split(':')
    511             cid = cid.split('=')[1]
    512             self.__dict__['destination'] = MachoAddress(clientID=long(cid))
    513         else:
    514             self.__dict__['blocking'] = 1
    515             nid, oid = objectID.split(':')
    516             nid = nid.split('=')[1]
    517             self.__dict__['destination'] = MachoAddress(nodeID=long(nid))
    518         self.__dict__['deleted'] = 0
    519         sess.RegisterMachoObjectConnection(objectID, self, refID)
    520 
    521     def SetSessionCheck(self, sessionCheck):
    522         self.sessionCheck = sessionCheck
    523 
    524     def PerformSessionCheck(self):
    525         if not self.sessionCheck:
    526             return
    527         for attribute, expected in self.sessionCheck.iteritems():
    528             current = None
    529             if self.session:
    530                 current = getattr(self.session, attribute, None)
    531             if current != expected or not expected:
    532                 log.LogTraceback()
    533                 raise RuntimeError('RemoteObjectSessionCheckFailure', {'attribute': attribute,
    534                  'expected': expected,
    535                  'current': current,
    536                  'objectID': self.objectID,
    537                  'persistantObjectID': self.persistantObjectID})
    538 
    539     def __del__(self):
    540         if not self.deleted:
    541             self.deleted = 1
    542             releaseRefID = self.session.UnregisterMachoObjectConnection(self.objectID, self)
    543             if releaseRefID:
    544                 try:
    545                     if self.objectID not in self.gpcs.releasedObjects:
    546                         self.gpcs.releasedObjects[self.objectID] = []
    547                     self.gpcs.releasedObjects[self.objectID].append((releaseRefID, self.session, self.destination))
    548                 except ReferenceError:
    549                     pass
    550 
    551     def __str__(self):
    552         return '<RemoteObject:' + strx(self.__dict__['objectID']) + '>'
    553 
    554     def __nonzero__(self):
    555         return 1
    556 
    557     def __repr__(self):
    558         return '<RemoteObject:' + strx(self.__dict__['objectID']) + '>'
    559 
    560     def RegisterObjectChangeHandler(self, callback):
    561         self.objectChangeHandlers[callback] = None
    562 
    563     def UnRegisterObjectChangeHandler(self, callback):
    564         try:
    565             del self.objectChangeHandlers[callback]
    566         except ReferenceError as e:
    567             sys.exc_clear()
    568         except KeyError as e:
    569             sys.exc_clear()
    570 
    571     def __getattr__(self, attribute):
    572         if getattr(self, 'deleted', 0):
    573             raise AttributeError(attribute, 'This object is being deleted, so the attribute is no longer available')
    574         if not attribute[0].isupper():
    575             pa = self.__publicattributes__
    576             if attribute not in pa:
    577                 raise AttributeError(attribute)
    578             return pa[attribute]
    579         self.PerformSessionCheck()
    580         return MachoObjectCallWrapper(self.__dict__['persistantObjectID'], self.__dict__['clisid'], self.__dict__['blocking'], self.__dict__['session'], self.__dict__['gpcs'], self.__dict__['destination'], self.__dict__['objectID'], attribute)
    581 
    582 
    583 class MachoObjectCallWrapper():
    584 
    585     def __init__(self, persistantObjectID, clisid, blocking, sess, gpcs, destination, objectID, method):
    586         self.persistantObjectID = persistantObjectID
    587         self.clisid = clisid
    588         self.blocking = blocking
    589         self.session = sess
    590         self.gpcs = gpcs
    591         self.destination = destination
    592         self.objectID = objectID
    593         self.method = method
    594 
    595     def __call__(self, *args, **kwargs):
    596         mask = self.session.Masquerade()
    597         try:
    598             if self.gpcs.machoNet.GetClientSessionID() != self.clisid:
    599                 raise RuntimeError('This object was acquired by a different session')
    600             if boot.role == 'client':
    601                 if kwargs.get('noCallThrottling', None) is None:
    602                     key = (self.objectID,
    603                      self.method,
    604                      str(args),
    605                      str(kwargs))
    606                     return macho.ThrottledCall(key, self.gpcs.ObjectCallWithoutTheStars, {}, self.persistantObjectID, self.blocking, self.session, self.destination, self.objectID, self.method, args, kwargs)
    607                 del kwargs['noCallThrottling']
    608             return self.gpcs.ObjectCallWithoutTheStars({}, self.persistantObjectID, self.blocking, self.session, self.destination, self.objectID, self.method, args, kwargs)
    609         finally:
    610             mask.UnMask()
    611             self.__dict__.clear()