1179860b2SJed Brown#!/usr/bin/env python 2179860b2SJed Brown'''A remote dictionary server 3179860b2SJed Brown 4179860b2SJed Brown RDict is a typed, hierarchical, persistent dictionary intended to manage 5179860b2SJed Brown all arguments or options for a program. The interface remains exactly the 6179860b2SJed Brown same as dict, but the storage is more complicated. 7179860b2SJed Brown 8179860b2SJed Brown Argument typing is handled by wrapping all values stored in the dictionary 9179860b2SJed Brown with nargs.Arg or a subclass. A user can call setType() to set the type of 10179860b2SJed Brown an argument without any value being present. Whenever __getitem__() or 11179860b2SJed Brown __setitem__() is called, values are extracted or replaced in the wrapper. 12179860b2SJed Brown These wrappers can be accessed directly using getType(), setType(), and 13179860b2SJed Brown types(). 14179860b2SJed Brown 15179860b2SJed Brown Hierarchy is allowed using a single "parent" dictionary. All operations 16179860b2SJed Brown cascade to the parent. For instance, the length of the dictionary is the 17179860b2SJed Brown number of local keys plus the number of keys in the parent, and its 18179860b2SJed Brown parent, etc. Also, a dictionary need not have a parent. If a key does not 19179860b2SJed Brown appear in the local dicitonary, the call if passed to the parent. However, 20179860b2SJed Brown in this case we see that local keys can shadow those in a parent. 21179860b2SJed Brown Communication with the parent is handled using sockets, with the parent 22179860b2SJed Brown being a server and the interactive dictionary a client. 23179860b2SJed Brown 24179860b2SJed Brown The default persistence mechanism is a pickle file, RDict.db, written 25179860b2SJed Brown whenever an argument is changed locally. A timer thread is created after 26179860b2SJed Brown an initial change, so that many rapid changes do not cause many writes. 27179860b2SJed Brown Each dictionary only saves its local entries, so all parents also 28179860b2SJed Brown separately save data in different RDict.db files. Each time a dictionary 29179860b2SJed Brown is created, the current directory is searched for an RDict.db file, and 30179860b2SJed Brown if found the contents are loaded into the dictionary. 31179860b2SJed Brown 32179860b2SJed Brown This script also provides some default actions: 33179860b2SJed Brown 34179860b2SJed Brown - server [parent] 35179860b2SJed Brown Starts a server in the current directory with an optional parent. This 36179860b2SJed Brown server will accept socket connections from other dictionaries and act 37179860b2SJed Brown as a parent. 38179860b2SJed Brown 39179860b2SJed Brown - client [parent] 40179860b2SJed Brown Creates a dictionary in the current directory with an optional parent 41179860b2SJed Brown and lists the contents. Notice that the contents may come from either 42179860b2SJed Brown an RDict.db file in the current directory, or from the parent. 43179860b2SJed Brown 44179860b2SJed Brown - clear [parent] 45179860b2SJed Brown Creates a dictionary in the current directory with an optional parent 46179860b2SJed Brown and clears the contents. Notice that this will also clear the parent. 47179860b2SJed Brown 48179860b2SJed Brown - insert <parent> <key> <value> 49179860b2SJed Brown Creates a dictionary in the current directory with a parent, and inserts 50179860b2SJed Brown the key-value pair. If "parent" is "None", no parent is assigned. 51179860b2SJed Brown 52179860b2SJed Brown - remove <parent> <key> 53179860b2SJed Brown Creates a dictionary in the current directory with a parent, and removes 54179860b2SJed Brown the given key. If "parent" is "None", no parent is assigned. 55179860b2SJed Brown''' 56*5b6bfdb9SJed Brownfrom __future__ import print_function 57*5b6bfdb9SJed Brownfrom __future__ import absolute_import 58179860b2SJed Browntry: 59179860b2SJed Brown import project # This is necessary for us to create Project objects on load 60179860b2SJed Brown import build.buildGraph # This is necessary for us to create BuildGraph objects on load 61179860b2SJed Brownexcept ImportError: 62179860b2SJed Brown pass 63179860b2SJed Brownimport nargs 64179860b2SJed Brown 65179860b2SJed Brownimport cPickle 66179860b2SJed Brownimport os 67179860b2SJed Brownimport sys 68179860b2SJed BrownuseThreads = nargs.Arg.findArgument('useThreads', sys.argv[1:]) 69179860b2SJed Brownif useThreads is None: 7092626d4aSBarry Smith useThreads = 0 # workaround issue with parallel configure 7192626d4aSBarry Smithelif useThreads == 'no' or useThreads == '0': 7292626d4aSBarry Smith useThreads = 0 7392626d4aSBarry Smithelif useThreads == 'yes' or useThreads == '1': 7431de54b5SMatthew G. Knepley useThreads = 1 75179860b2SJed Brownelse: 7692626d4aSBarry Smith raise RuntimeError('Unknown option value for --useThreads ',useThreads) 77179860b2SJed Brown 78179860b2SJed Brownclass RDict(dict): 79179860b2SJed Brown '''An RDict is a typed dictionary, which may be hierarchically composed. All elements derive from the 80179860b2SJed BrownArg class, which wraps the usual value.''' 81179860b2SJed Brown # The server will self-shutdown after this many seconds 82179860b2SJed Brown shutdownDelay = 60*60*5 83179860b2SJed Brown 84179860b2SJed Brown def __init__(self, parentAddr = None, parentDirectory = None, load = 1, autoShutdown = 1, readonly = False): 85179860b2SJed Brown import atexit 86179860b2SJed Brown import time 87179860b2SJed Brown import xdrlib 88179860b2SJed Brown 89179860b2SJed Brown self.logFile = None 90179860b2SJed Brown self.setupLogFile() 91179860b2SJed Brown self.target = ['default'] 92179860b2SJed Brown self.parent = None 93179860b2SJed Brown self.saveTimer = None 94179860b2SJed Brown self.shutdownTimer = None 95179860b2SJed Brown self.lastAccess = time.time() 96179860b2SJed Brown self.saveFilename = 'RDict.db' 97179860b2SJed Brown self.addrFilename = 'RDict.loc' 98179860b2SJed Brown self.parentAddr = parentAddr 99179860b2SJed Brown self.isServer = 0 100179860b2SJed Brown self.readonly = readonly 101179860b2SJed Brown self.parentDirectory = parentDirectory 102179860b2SJed Brown self.packer = xdrlib.Packer() 103179860b2SJed Brown self.unpacker = xdrlib.Unpacker('') 104179860b2SJed Brown self.stopCmd = cPickle.dumps(('stop',)) 105179860b2SJed Brown self.writeLogLine('Greetings') 106179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 107179860b2SJed Brown if load: self.load() 108179860b2SJed Brown if autoShutdown and useThreads: 109179860b2SJed Brown atexit.register(self.shutdown) 110179860b2SJed Brown self.writeLogLine('SERVER: Last access '+str(self.lastAccess)) 111179860b2SJed Brown return 112179860b2SJed Brown 113179860b2SJed Brown def __getstate__(self): 114179860b2SJed Brown '''Remove any parent socket object, the XDR translators, and the log file from the dictionary before pickling''' 115179860b2SJed Brown self.writeLogLine('Pickling RDict') 116179860b2SJed Brown d = self.__dict__.copy() 117179860b2SJed Brown if 'parent' in d: del d['parent'] 118179860b2SJed Brown if 'saveTimer' in d: del d['saveTimer'] 119179860b2SJed Brown if '_setCommandLine' in d: del d['_setCommandLine'] 120179860b2SJed Brown del d['packer'] 121179860b2SJed Brown del d['unpacker'] 122179860b2SJed Brown del d['logFile'] 123179860b2SJed Brown return d 124179860b2SJed Brown 125179860b2SJed Brown def __setstate__(self, d): 126179860b2SJed Brown '''Reconnect the parent socket object, recreate the XDR translators and reopen the log file after unpickling''' 127179860b2SJed Brown self.logFile = file('RDict.log', 'a') 128179860b2SJed Brown self.writeLogLine('Unpickling RDict') 129179860b2SJed Brown self.__dict__.update(d) 130179860b2SJed Brown import xdrlib 131179860b2SJed Brown self.packer = xdrlib.Packer() 132179860b2SJed Brown self.unpacker = xdrlib.Unpacker('') 133179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 134179860b2SJed Brown return 135179860b2SJed Brown 136179860b2SJed Brown def setupLogFile(self, filename = 'RDict.log'): 137179860b2SJed Brown if not self.logFile is None: 138179860b2SJed Brown self.logFile.close() 139179860b2SJed Brown if os.path.isfile(filename) and os.stat(filename).st_size > 10*1024*1024: 140179860b2SJed Brown if os.path.isfile(filename+'.bkp'): 141179860b2SJed Brown os.remove(filename+'.bkp') 142179860b2SJed Brown os.rename(filename, filename+'.bkp') 143179860b2SJed Brown self.logFile = file(filename, 'w') 144179860b2SJed Brown else: 145179860b2SJed Brown self.logFile = file(filename, 'a') 146179860b2SJed Brown return 147179860b2SJed Brown 148179860b2SJed Brown def writeLogLine(self, message): 149179860b2SJed Brown '''Writes the message to the log along with the current time''' 150179860b2SJed Brown import time 151179860b2SJed Brown self.logFile.write('('+str(os.getpid())+')('+str(id(self))+')'+message+' ['+time.asctime(time.localtime())+']\n') 152179860b2SJed Brown self.logFile.flush() 153179860b2SJed Brown return 154179860b2SJed Brown 155179860b2SJed Brown def __len__(self): 156179860b2SJed Brown '''Returns the length of both the local and parent dictionaries''' 157179860b2SJed Brown length = dict.__len__(self) 158179860b2SJed Brown if not self.parent is None: 159179860b2SJed Brown length = length + self.send() 160179860b2SJed Brown return length 161179860b2SJed Brown 162179860b2SJed Brown def getType(self, key): 163179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Returns the Arg object or None if not found.''' 164179860b2SJed Brown if dict.has_key(self, key): 165179860b2SJed Brown self.writeLogLine('getType: Getting local type for '+key+' '+str(dict.__getitem__(self, key))) 166179860b2SJed Brown return dict.__getitem__(self, key) 167179860b2SJed Brown elif not self.parent is None: 168179860b2SJed Brown return self.send(key) 169179860b2SJed Brown return None 170179860b2SJed Brown 171179860b2SJed Brown def __getitem__(self, key): 172179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Returns the value of the Arg. 173179860b2SJed Brown - If the value has not been set, the user will be prompted for input''' 174179860b2SJed Brown if dict.has_key(self, key): 175179860b2SJed Brown self.writeLogLine('__getitem__: '+key+' has local type') 176179860b2SJed Brown pass 177179860b2SJed Brown elif not self.parent is None: 178179860b2SJed Brown self.writeLogLine('__getitem__: Checking parent value') 179179860b2SJed Brown if self.send(key, operation = 'has_key'): 180179860b2SJed Brown self.writeLogLine('__getitem__: Parent has value') 181179860b2SJed Brown return self.send(key) 182179860b2SJed Brown else: 183179860b2SJed Brown self.writeLogLine('__getitem__: Checking parent type') 184179860b2SJed Brown arg = self.send(key, operation = 'getType') 185179860b2SJed Brown if not arg: 186179860b2SJed Brown self.writeLogLine('__getitem__: Parent has no type') 187179860b2SJed Brown arg = nargs.Arg(key) 188179860b2SJed Brown try: 189179860b2SJed Brown value = arg.getValue() 190*5b6bfdb9SJed Brown except AttributeError as e: 191179860b2SJed Brown self.writeLogLine('__getitem__: Parent had invalid entry: '+str(e)) 192179860b2SJed Brown arg = nargs.Arg(key) 193179860b2SJed Brown value = arg.getValue() 194179860b2SJed Brown self.writeLogLine('__getitem__: Setting parent value '+str(value)) 195179860b2SJed Brown self.send(key, value, operation = '__setitem__') 196179860b2SJed Brown return value 197179860b2SJed Brown else: 198179860b2SJed Brown self.writeLogLine('__getitem__: Setting local type for '+key) 199179860b2SJed Brown dict.__setitem__(self, key, nargs.Arg(key)) 200179860b2SJed Brown self.save() 201179860b2SJed Brown self.writeLogLine('__getitem__: Setting local value for '+key) 202179860b2SJed Brown return dict.__getitem__(self, key).getValue() 203179860b2SJed Brown 204179860b2SJed Brown def setType(self, key, value, forceLocal = 0): 205179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Sets the type for this key. 206179860b2SJed Brown - If a value for the key already exists, it is converted to the new type''' 207179860b2SJed Brown if not isinstance(value, nargs.Arg): 208179860b2SJed Brown raise TypeError('An argument type must be a subclass of Arg') 209179860b2SJed Brown value.setKey(key) 210179860b2SJed Brown if forceLocal or self.parent is None or dict.has_key(self, key): 211179860b2SJed Brown if dict.has_key(self, key): 212179860b2SJed Brown v = dict.__getitem__(self, key) 213179860b2SJed Brown if v.isValueSet(): 214179860b2SJed Brown try: 215179860b2SJed Brown value.setValue(v.getValue()) 216a5737712SSatish Balay except TypeError: 217*5b6bfdb9SJed Brown print(value.__class__.__name__[3:]) 218*5b6bfdb9SJed Brown print('-----------------------------------------------------------------------') 219*5b6bfdb9SJed Brown print('Warning! Incorrect argument type specified: -'+str(key)+'='+str(v.getValue())+' - expecting type '+value.__class__.__name__[3:]+'.') 220*5b6bfdb9SJed Brown print('-----------------------------------------------------------------------') 221a5737712SSatish Balay pass 222179860b2SJed Brown dict.__setitem__(self, key, value) 223179860b2SJed Brown self.save() 224179860b2SJed Brown else: 225179860b2SJed Brown return self.send(key, value) 226179860b2SJed Brown return 227179860b2SJed Brown 228179860b2SJed Brown def __setitem__(self, key, value): 229179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Sets the value of the Arg.''' 230179860b2SJed Brown if not dict.has_key(self, key): 231179860b2SJed Brown if not self.parent is None: 232179860b2SJed Brown return self.send(key, value) 233179860b2SJed Brown else: 234179860b2SJed Brown dict.__setitem__(self, key, nargs.Arg(key)) 235179860b2SJed Brown dict.__getitem__(self, key).setValue(value) 236179860b2SJed Brown self.writeLogLine('__setitem__: Set value for '+key+' to '+str(dict.__getitem__(self, key))) 237179860b2SJed Brown self.save() 238179860b2SJed Brown return 239179860b2SJed Brown 240179860b2SJed Brown def __delitem__(self, key): 241179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Deletes the Arg completely.''' 242179860b2SJed Brown if dict.has_key(self, key): 243179860b2SJed Brown dict.__delitem__(self, key) 244179860b2SJed Brown self.save() 245179860b2SJed Brown elif not self.parent is None: 246179860b2SJed Brown self.send(key) 247179860b2SJed Brown return 248179860b2SJed Brown 249179860b2SJed Brown def clear(self): 250179860b2SJed Brown '''Clears both the local and parent dictionaries''' 251179860b2SJed Brown if dict.__len__(self): 252179860b2SJed Brown dict.clear(self) 253179860b2SJed Brown self.save() 254179860b2SJed Brown if not self.parent is None: 255179860b2SJed Brown self.send() 256179860b2SJed Brown return 257179860b2SJed Brown 258179860b2SJed Brown def __contains__(self, key): 259179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Then checks whether the value has been set''' 260179860b2SJed Brown if dict.has_key(self, key): 261179860b2SJed Brown if dict.__getitem__(self, key).isValueSet(): 262179860b2SJed Brown self.writeLogLine('has_key: Have value for '+key) 263179860b2SJed Brown else: 264179860b2SJed Brown self.writeLogLine('has_key: Do not have value for '+key) 265179860b2SJed Brown return dict.__getitem__(self, key).isValueSet() 266179860b2SJed Brown elif not self.parent is None: 267179860b2SJed Brown return self.send(key) 268179860b2SJed Brown return 0 269179860b2SJed Brown 270179860b2SJed Brown def get(self, key, default=None): 271*5b6bfdb9SJed Brown if key in self: 272179860b2SJed Brown return self.__getitem__(key) 273179860b2SJed Brown else: 274179860b2SJed Brown return default 275179860b2SJed Brown 276179860b2SJed Brown def hasType(self, key): 277179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Then checks whether the type has been set''' 278179860b2SJed Brown if dict.has_key(self, key): 279179860b2SJed Brown return 1 280179860b2SJed Brown elif not self.parent is None: 281179860b2SJed Brown return self.send(key) 282179860b2SJed Brown return 0 283179860b2SJed Brown 284179860b2SJed Brown def items(self): 285179860b2SJed Brown '''Return a list of all accessible items, as (key, value) pairs.''' 286179860b2SJed Brown l = dict.items(self) 287179860b2SJed Brown if not self.parent is None: 288179860b2SJed Brown l.extend(self.send()) 289179860b2SJed Brown return l 290179860b2SJed Brown 291179860b2SJed Brown def localitems(self): 292179860b2SJed Brown '''Return a list of all the items stored locally, as (key, value) pairs.''' 293179860b2SJed Brown return dict.items(self) 294179860b2SJed Brown 295179860b2SJed Brown def keys(self): 296179860b2SJed Brown '''Returns the list of keys in both the local and parent dictionaries''' 297179860b2SJed Brown keyList = filter(lambda key: dict.__getitem__(self, key).isValueSet(), dict.keys(self)) 298179860b2SJed Brown if not self.parent is None: 299179860b2SJed Brown keyList.extend(self.send()) 300179860b2SJed Brown return keyList 301179860b2SJed Brown 302179860b2SJed Brown def types(self): 303179860b2SJed Brown '''Returns the list of keys for which types are defined in both the local and parent dictionaries''' 304179860b2SJed Brown keyList = dict.keys(self) 305179860b2SJed Brown if not self.parent is None: 306179860b2SJed Brown keyList.extend(self.send()) 307179860b2SJed Brown return keyList 308179860b2SJed Brown 309179860b2SJed Brown def update(self, d): 310179860b2SJed Brown '''Update the dictionary with the contents of d''' 311179860b2SJed Brown for k in d: 312179860b2SJed Brown self[k] = d[k] 313179860b2SJed Brown return 314179860b2SJed Brown 315179860b2SJed Brown def updateTypes(self, d): 316179860b2SJed Brown '''Update types locally, which is equivalent to the dict.update() method''' 317179860b2SJed Brown return dict.update(self, d) 318179860b2SJed Brown 319179860b2SJed Brown def insertArg(self, key, value, arg): 320179860b2SJed Brown '''Insert a (key, value) pair into the dictionary. If key is None, arg is put into the target list.''' 321179860b2SJed Brown if not key is None: 322179860b2SJed Brown self[key] = value 323179860b2SJed Brown else: 324179860b2SJed Brown if not self.target == ['default']: 325179860b2SJed Brown self.target.append(arg) 326179860b2SJed Brown else: 327179860b2SJed Brown self.target = [arg] 328179860b2SJed Brown return 329179860b2SJed Brown 330179860b2SJed Brown def insertArgs(self, args): 331179860b2SJed Brown '''Insert some text arguments into the dictionary (list and dictionaries are recognized)''' 332179860b2SJed Brown import UserDict 333179860b2SJed Brown 334179860b2SJed Brown if isinstance(args, list): 335179860b2SJed Brown for arg in args: 336179860b2SJed Brown (key, value) = nargs.Arg.parseArgument(arg) 337179860b2SJed Brown self.insertArg(key, value, arg) 338179860b2SJed Brown # Necessary since os.environ is a UserDict 339179860b2SJed Brown elif isinstance(args, dict) or isinstance(args, UserDict.UserDict): 340179860b2SJed Brown for key in args.keys(): 341179860b2SJed Brown if isinstance(args[key], str): 342179860b2SJed Brown value = nargs.Arg.parseValue(args[key]) 343179860b2SJed Brown else: 344179860b2SJed Brown value = args[key] 345179860b2SJed Brown self.insertArg(key, value, None) 346179860b2SJed Brown elif isinstance(args, str): 347179860b2SJed Brown (key, value) = nargs.Arg.parseArgument(args) 348179860b2SJed Brown self.insertArg(key, value, args) 349179860b2SJed Brown return 350179860b2SJed Brown 351179860b2SJed Brown def hasParent(self): 352179860b2SJed Brown '''Return True if this RDict has a parent dictionary''' 353179860b2SJed Brown return not self.parent is None 354179860b2SJed Brown 355179860b2SJed Brown def getServerAddr(self, dir): 356179860b2SJed Brown '''Read the server socket address (in pickled form) from a file, usually RDict.loc 357179860b2SJed Brown - If we fail to connect to the server specified in the file, we spawn it using startServer()''' 358179860b2SJed Brown filename = os.path.join(dir, self.addrFilename) 359179860b2SJed Brown if not os.path.exists(filename): 360179860b2SJed Brown self.startServer(filename) 361179860b2SJed Brown if not os.path.exists(filename): 362179860b2SJed Brown raise RuntimeError('Server address file does not exist: '+filename) 363179860b2SJed Brown try: 364179860b2SJed Brown f = open(filename, 'r') 365179860b2SJed Brown addr = cPickle.load(f) 366179860b2SJed Brown f.close() 367179860b2SJed Brown return addr 368*5b6bfdb9SJed Brown except Exception as e: 369179860b2SJed Brown self.writeLogLine('CLIENT: Exception during server address determination: '+str(e.__class__)+': '+str(e)) 370179860b2SJed Brown raise RuntimeError('Could not get server address in '+filename) 371179860b2SJed Brown 372179860b2SJed Brown def writeServerAddr(self, server): 373179860b2SJed Brown '''Write the server socket address (in pickled form) to a file, usually RDict.loc.''' 374179860b2SJed Brown f = file(self.addrFilename, 'w') 375179860b2SJed Brown cPickle.dump(server.server_address, f) 376179860b2SJed Brown f.close() 377179860b2SJed Brown self.writeLogLine('SERVER: Wrote lock file '+os.path.abspath(self.addrFilename)) 378179860b2SJed Brown return 379179860b2SJed Brown 380179860b2SJed Brown def startServer(self, addrFilename): 381179860b2SJed Brown '''Spawn a new RDict server in the parent directory''' 382179860b2SJed Brown import RDict # Need this to locate server script 383179860b2SJed Brown import sys 384179860b2SJed Brown import time 385179860b2SJed Brown import distutils.sysconfig 386179860b2SJed Brown 387179860b2SJed Brown self.writeLogLine('CLIENT: Spawning a new server with lock file '+os.path.abspath(addrFilename)) 388179860b2SJed Brown if os.path.exists(addrFilename): 389179860b2SJed Brown os.remove(addrFilename) 390179860b2SJed Brown oldDir = os.getcwd() 391179860b2SJed Brown source = os.path.join(os.path.dirname(os.path.abspath(sys.modules['RDict'].__file__)), 'RDict.py') 392179860b2SJed Brown interpreter = os.path.join(distutils.sysconfig.get_config_var('BINDIR'), distutils.sysconfig.get_config_var('PYTHON')) 393179860b2SJed Brown if not os.path.isfile(interpreter): 394179860b2SJed Brown interpreter = 'python' 395179860b2SJed Brown os.chdir(os.path.dirname(addrFilename)) 396179860b2SJed Brown self.writeLogLine('CLIENT: Executing '+interpreter+' '+source+' server"') 397179860b2SJed Brown try: 398179860b2SJed Brown os.spawnvp(os.P_NOWAIT, interpreter, [interpreter, source, 'server']) 399179860b2SJed Brown except: 400179860b2SJed Brown self.writeLogLine('CLIENT: os.spawnvp failed.\n \ 401179860b2SJed Brown This is a typical problem on CYGWIN systems. If you are using CYGWIN,\n \ 402179860b2SJed Brown you can fix this problem by running /bin/rebaseall. If you do not have\n \ 403179860b2SJed Brown this program, you can install it with the CYGWIN installer in the package\n \ 404179860b2SJed Brown Rebase, under the category System. You must run /bin/rebaseall after\n \ 405179860b2SJed Brown turning off all cygwin services -- in particular sshd, if any such services\n \ 406179860b2SJed Brown are running. For more information about rebase, go to http://www.cygwin.com') 407*5b6bfdb9SJed Brown print('\n \ 408179860b2SJed Brown This is a typical problem on CYGWIN systems. If you are using CYGWIN,\n \ 409179860b2SJed Brown you can fix this problem by running /bin/rebaseall. If you do not have\n \ 410179860b2SJed Brown this program, you can install it with the CYGWIN installer in the package\n \ 411179860b2SJed Brown Rebase, under the category System. You must run /bin/rebaseall after\n \ 412179860b2SJed Brown turning off all cygwin services -- in particular sshd, if any such services\n \ 413*5b6bfdb9SJed Brown are running. For more information about rebase, go to http://www.cygwin.com\n') 414179860b2SJed Brown raise 415179860b2SJed Brown os.chdir(oldDir) 416179860b2SJed Brown timeout = 1 417179860b2SJed Brown for i in range(10): 418179860b2SJed Brown time.sleep(timeout) 419179860b2SJed Brown timeout *= 2 420179860b2SJed Brown if timeout > 100: timeout = 100 421179860b2SJed Brown if os.path.exists(addrFilename): return 422179860b2SJed Brown self.writeLogLine('CLIENT: Could not start server') 423179860b2SJed Brown return 424179860b2SJed Brown 425179860b2SJed Brown def connectParent(self, addr, dir): 426179860b2SJed Brown '''Try to connect to a parent RDict server 427179860b2SJed Brown - If addr and dir are both None, this operation fails 428179860b2SJed Brown - If addr is None, check for an address file in dir''' 429179860b2SJed Brown if addr is None: 430179860b2SJed Brown if dir is None: return 0 431179860b2SJed Brown addr = self.getServerAddr(dir) 432179860b2SJed Brown 433179860b2SJed Brown import socket 434179860b2SJed Brown import errno 435179860b2SJed Brown connected = 0 436179860b2SJed Brown s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 437179860b2SJed Brown timeout = 1 438179860b2SJed Brown for i in range(10): 439179860b2SJed Brown try: 440179860b2SJed Brown self.writeLogLine('CLIENT: Trying to connect to '+str(addr)) 441179860b2SJed Brown s.connect(addr) 442179860b2SJed Brown connected = 1 443179860b2SJed Brown break 444*5b6bfdb9SJed Brown except socket.error as e: 445179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect: '+str(e)) 446179860b2SJed Brown if e[0] == errno.ECONNREFUSED: 447179860b2SJed Brown try: 448179860b2SJed Brown import time 449179860b2SJed Brown time.sleep(timeout) 450179860b2SJed Brown timeout *= 2 451179860b2SJed Brown if timeout > 100: timeout = 100 452179860b2SJed Brown except KeyboardInterrupt: 453179860b2SJed Brown break 454179860b2SJed Brown # Try to spawn parent 455179860b2SJed Brown if dir: 456179860b2SJed Brown filename = os.path.join(dir, self.addrFilename) 457179860b2SJed Brown if os.path.isfile(filename): 458179860b2SJed Brown os.remove(filename) 459179860b2SJed Brown self.startServer(filename) 460*5b6bfdb9SJed Brown except Exception as e: 461179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect: '+str(e.__class__)+': '+str(e)) 462179860b2SJed Brown if not connected: 463179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect to parent') 464179860b2SJed Brown return 0 465179860b2SJed Brown self.parent = s 466179860b2SJed Brown self.writeLogLine('CLIENT: Connected to '+str(self.parent)) 467179860b2SJed Brown return 1 468179860b2SJed Brown 469179860b2SJed Brown def sendPacket(self, s, packet, source = 'Unknown', isPickled = 0): 470179860b2SJed Brown '''Pickle the input packet. Send first the size of the pickled string in 32-bit integer, and then the string itself''' 471179860b2SJed Brown self.writeLogLine(source+': Sending packet '+str(packet)) 472179860b2SJed Brown if isPickled: 473179860b2SJed Brown p = packet 474179860b2SJed Brown else: 475179860b2SJed Brown p = cPickle.dumps(packet) 476179860b2SJed Brown self.packer.reset() 477179860b2SJed Brown self.packer.pack_uint(len(p)) 478179860b2SJed Brown if hasattr(s, 'write'): 479179860b2SJed Brown s.write(self.packer.get_buffer()) 480179860b2SJed Brown s.write(p) 481179860b2SJed Brown else: 482179860b2SJed Brown s.sendall(self.packer.get_buffer()) 483179860b2SJed Brown s.sendall(p) 484179860b2SJed Brown self.writeLogLine(source+': Sent packet') 485179860b2SJed Brown return 486179860b2SJed Brown 487179860b2SJed Brown def recvPacket(self, s, source = 'Unknown'): 488179860b2SJed Brown '''Receive first the size of the pickled string in a 32-bit integer, and then the string itself. Return the unpickled object''' 489179860b2SJed Brown self.writeLogLine(source+': Receiving packet') 490179860b2SJed Brown if hasattr(s, 'read'): 491179860b2SJed Brown s.read(4) 492179860b2SJed Brown value = cPickle.load(s) 493179860b2SJed Brown else: 494179860b2SJed Brown # I probably need to check that it actually read these 4 bytes 495179860b2SJed Brown self.unpacker.reset(s.recv(4)) 496179860b2SJed Brown length = self.unpacker.unpack_uint() 497179860b2SJed Brown objString = '' 498179860b2SJed Brown while len(objString) < length: 499179860b2SJed Brown objString += s.recv(length - len(objString)) 500179860b2SJed Brown value = cPickle.loads(objString) 501179860b2SJed Brown self.writeLogLine(source+': Received packet '+str(value)) 502179860b2SJed Brown return value 503179860b2SJed Brown 504179860b2SJed Brown def send(self, key = None, value = None, operation = None): 505179860b2SJed Brown '''Send a request to the parent''' 506179860b2SJed Brown import inspect 507179860b2SJed Brown 508179860b2SJed Brown objString = '' 509179860b2SJed Brown for i in range(3): 510179860b2SJed Brown try: 511179860b2SJed Brown packet = [] 512179860b2SJed Brown if operation is None: 513179860b2SJed Brown operation = inspect.stack()[1][3] 514179860b2SJed Brown packet.append(operation) 515179860b2SJed Brown if not key is None: 516179860b2SJed Brown packet.append(key) 517179860b2SJed Brown if not value is None: 518179860b2SJed Brown packet.append(value) 519179860b2SJed Brown self.sendPacket(self.parent, tuple(packet), source = 'CLIENT') 520179860b2SJed Brown response = self.recvPacket(self.parent, source = 'CLIENT') 521179860b2SJed Brown break 522*5b6bfdb9SJed Brown except IOError as e: 523179860b2SJed Brown self.writeLogLine('CLIENT: IOError '+str(e)) 524179860b2SJed Brown if e.errno == 32: 525179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 526*5b6bfdb9SJed Brown except Exception as e: 527179860b2SJed Brown self.writeLogLine('CLIENT: Exception '+str(e)+' '+str(e.__class__)) 528179860b2SJed Brown try: 529179860b2SJed Brown if isinstance(response, Exception): 530179860b2SJed Brown self.writeLogLine('CLIENT: Got an exception '+str(response)) 531179860b2SJed Brown raise response 532179860b2SJed Brown else: 533179860b2SJed Brown self.writeLogLine('CLIENT: Received value '+str(response)+' '+str(type(response))) 534179860b2SJed Brown except UnboundLocalError: 535179860b2SJed Brown self.writeLogLine('CLIENT: Could not unpickle response') 536179860b2SJed Brown response = None 537179860b2SJed Brown return response 538179860b2SJed Brown 539179860b2SJed Brown def serve(self): 540179860b2SJed Brown '''Start a server''' 541179860b2SJed Brown import socket 542179860b2SJed Brown import SocketServer 543179860b2SJed Brown 544179860b2SJed Brown if not useThreads: 545179860b2SJed Brown raise RuntimeError('Cannot run a server if threads are disabled') 546179860b2SJed Brown 547179860b2SJed Brown class ProcessHandler(SocketServer.StreamRequestHandler): 548179860b2SJed Brown def handle(self): 549179860b2SJed Brown import time 550179860b2SJed Brown 551179860b2SJed Brown self.server.rdict.lastAccess = time.time() 552179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Started new handler') 553179860b2SJed Brown while 1: 554179860b2SJed Brown try: 555179860b2SJed Brown value = self.server.rdict.recvPacket(self.rfile, source = 'SERVER') 556*5b6bfdb9SJed Brown except EOFError as e: 557179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: EOFError receiving packet '+str(e)+' '+str(e.__class__)) 558179860b2SJed Brown return 559*5b6bfdb9SJed Brown except Exception as e: 560179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Error receiving packet '+str(e)+' '+str(e.__class__)) 561179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, e, source = 'SERVER') 562179860b2SJed Brown continue 563179860b2SJed Brown if value[0] == 'stop': break 564179860b2SJed Brown try: 565179860b2SJed Brown response = getattr(self.server.rdict, value[0])(*value[1:]) 566*5b6bfdb9SJed Brown except Exception as e: 567179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Error executing operation '+str(e)+' '+str(e.__class__)) 568179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, e, source = 'SERVER') 569179860b2SJed Brown else: 570179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, response, source = 'SERVER') 571179860b2SJed Brown return 572179860b2SJed Brown 573179860b2SJed Brown # check if server is running 574179860b2SJed Brown if os.path.exists(self.addrFilename): 575179860b2SJed Brown rdict = RDict(parentDirectory = '.') 576179860b2SJed Brown hasParent = rdict.hasParent() 577179860b2SJed Brown del rdict 578179860b2SJed Brown if hasParent: 579179860b2SJed Brown self.writeLogLine('SERVER: Another server is already running') 580179860b2SJed Brown raise RuntimeError('Server already running') 581179860b2SJed Brown 582179860b2SJed Brown # Daemonize server 583179860b2SJed Brown self.writeLogLine('SERVER: Daemonizing server') 584179860b2SJed Brown if os.fork(): # Launch child 585179860b2SJed Brown os._exit(0) # Kill off parent, so we are not a process group leader and get a new PID 586179860b2SJed Brown os.setsid() # Set session ID, so that we have no controlling terminal 587179860b2SJed Brown # We choose to leave cwd at RDict.py: os.chdir('/') # Make sure root directory is not on a mounted drive 588*5b6bfdb9SJed Brown os.umask(0o77) # Fix creation mask 589179860b2SJed Brown for i in range(3): # Crappy stopgap for closing descriptors 590179860b2SJed Brown try: 591179860b2SJed Brown os.close(i) 592*5b6bfdb9SJed Brown except OSError as e: 593179860b2SJed Brown if e.errno != errno.EBADF: 594179860b2SJed Brown raise RuntimeError('Could not close default descriptor '+str(i)) 595179860b2SJed Brown 596179860b2SJed Brown # wish there was a better way to get a usable socket 597179860b2SJed Brown self.writeLogLine('SERVER: Establishing socket server') 598179860b2SJed Brown basePort = 8000 599179860b2SJed Brown flag = 'nosocket' 600179860b2SJed Brown p = 1 601179860b2SJed Brown while p < 1000 and flag == 'nosocket': 602179860b2SJed Brown try: 603179860b2SJed Brown server = SocketServer.ThreadingTCPServer((socket.gethostname(), basePort+p), ProcessHandler) 604179860b2SJed Brown flag = 'socket' 605*5b6bfdb9SJed Brown except Exception as e: 606179860b2SJed Brown p = p + 1 607179860b2SJed Brown if flag == 'nosocket': 608179860b2SJed Brown p = 1 609179860b2SJed Brown while p < 1000 and flag == 'nosocket': 610179860b2SJed Brown try: 611179860b2SJed Brown server = SocketServer.ThreadingTCPServer(('localhost', basePort+p), ProcessHandler) 612179860b2SJed Brown flag = 'socket' 613*5b6bfdb9SJed Brown except Exception as e: 614179860b2SJed Brown p = p + 1 615179860b2SJed Brown if flag == 'nosocket': 616179860b2SJed Brown self.writeLogLine('SERVER: Could not established socket server on port '+str(basePort+p)) 617*5b6bfdb9SJed Brown raise RuntimeError('Cannot get available socket') 618179860b2SJed Brown self.writeLogLine('SERVER: Established socket server on port '+str(basePort+p)) 619179860b2SJed Brown 620179860b2SJed Brown self.isServer = 1 621179860b2SJed Brown self.writeServerAddr(server) 622179860b2SJed Brown self.serverShutdown(os.getpid()) 623179860b2SJed Brown 624179860b2SJed Brown server.rdict = self 625179860b2SJed Brown self.writeLogLine('SERVER: Started server') 626179860b2SJed Brown server.serve_forever() 627179860b2SJed Brown return 628179860b2SJed Brown 629179860b2SJed Brown def load(self): 630179860b2SJed Brown '''Load the saved dictionary''' 631179860b2SJed Brown if not self.parentDirectory is None and os.path.samefile(os.getcwd(), self.parentDirectory): 632179860b2SJed Brown return 633179860b2SJed Brown self.saveFilename = os.path.abspath(self.saveFilename) 634179860b2SJed Brown if os.path.exists(self.saveFilename): 635179860b2SJed Brown try: 636179860b2SJed Brown dbFile = file(self.saveFilename) 637179860b2SJed Brown data = cPickle.load(dbFile) 638179860b2SJed Brown self.updateTypes(data) 639179860b2SJed Brown dbFile.close() 640179860b2SJed Brown self.writeLogLine('Loaded dictionary from '+self.saveFilename) 641*5b6bfdb9SJed Brown except Exception as e: 642179860b2SJed Brown self.writeLogLine('Problem loading dictionary from '+self.saveFilename+'\n--> '+str(e)) 643179860b2SJed Brown else: 644179860b2SJed Brown self.writeLogLine('No dictionary to load in this file: '+self.saveFilename) 645179860b2SJed Brown return 646179860b2SJed Brown 647603e7a67SSatish Balay def save(self, force = 1): 648179860b2SJed Brown '''Save the dictionary after 5 seconds, ignoring all subsequent calls until the save 649179860b2SJed Brown - Giving force = True will cause an immediate save''' 650179860b2SJed Brown if self.readonly: return 65108eb64ffSMatthew G. Knepley if force: 652179860b2SJed Brown self.saveTimer = None 653179860b2SJed Brown # This should be a critical section 654179860b2SJed Brown dbFile = file(self.saveFilename, 'w') 655179860b2SJed Brown data = dict(filter(lambda i: not i[1].getTemporary(), self.localitems())) 656179860b2SJed Brown cPickle.dump(data, dbFile) 657179860b2SJed Brown dbFile.close() 658179860b2SJed Brown self.writeLogLine('Saved local dictionary to '+os.path.abspath(self.saveFilename)) 659179860b2SJed Brown elif not self.saveTimer: 660179860b2SJed Brown import threading 661179860b2SJed Brown self.saveTimer = threading.Timer(5, self.save, [], {'force': 1}) 662179860b2SJed Brown self.saveTimer.setDaemon(1) 663179860b2SJed Brown self.saveTimer.start() 664179860b2SJed Brown return 665179860b2SJed Brown 666179860b2SJed Brown def shutdown(self): 667179860b2SJed Brown '''Shutdown the dictionary, writing out changes and notifying parent''' 668179860b2SJed Brown if self.saveTimer: 669179860b2SJed Brown self.saveTimer.cancel() 670179860b2SJed Brown self.save(force = 1) 671179860b2SJed Brown if self.isServer and os.path.isfile(self.addrFilename): 672179860b2SJed Brown os.remove(self.addrFilename) 673179860b2SJed Brown if not self.parent is None: 674179860b2SJed Brown self.sendPacket(self.parent, self.stopCmd, isPickled = 1) 675179860b2SJed Brown self.parent.close() 676179860b2SJed Brown self.parent = None 677179860b2SJed Brown self.writeLogLine('Shutting down') 678179860b2SJed Brown self.logFile.close() 679179860b2SJed Brown return 680179860b2SJed Brown 681179860b2SJed Brown def serverShutdown(self, pid, delay = shutdownDelay): 682179860b2SJed Brown if self.shutdownTimer is None: 683179860b2SJed Brown import threading 684179860b2SJed Brown 685179860b2SJed Brown self.shutdownTimer = threading.Timer(delay, self.serverShutdown, [pid], {'delay': 0}) 686179860b2SJed Brown self.shutdownTimer.setDaemon(1) 687179860b2SJed Brown self.shutdownTimer.start() 688179860b2SJed Brown self.writeLogLine('SERVER: Set shutdown timer for process '+str(pid)+' at '+str(delay)+' seconds') 689179860b2SJed Brown else: 690179860b2SJed Brown try: 691179860b2SJed Brown import signal 692179860b2SJed Brown import time 693179860b2SJed Brown 694179860b2SJed Brown idleTime = time.time() - self.lastAccess 695179860b2SJed Brown self.writeLogLine('SERVER: Last access '+str(self.lastAccess)) 696179860b2SJed Brown self.writeLogLine('SERVER: Idle time '+str(idleTime)) 697179860b2SJed Brown if idleTime < RDict.shutdownDelay: 698179860b2SJed Brown self.writeLogLine('SERVER: Extending shutdown timer for '+str(pid)+' by '+str(RDict.shutdownDelay - idleTime)+' seconds') 699179860b2SJed Brown self.shutdownTimer = None 700179860b2SJed Brown self.serverShutdown(pid, RDict.shutdownDelay - idleTime) 701179860b2SJed Brown else: 702179860b2SJed Brown self.writeLogLine('SERVER: Killing server '+str(pid)) 703179860b2SJed Brown os.kill(pid, signal.SIGTERM) 704*5b6bfdb9SJed Brown except Exception as e: 705179860b2SJed Brown self.writeLogLine('SERVER: Exception killing server: '+str(e)) 706179860b2SJed Brown return 707179860b2SJed Brown 708179860b2SJed Brownif __name__ == '__main__': 709179860b2SJed Brown import sys 710179860b2SJed Brown try: 711179860b2SJed Brown if len(sys.argv) < 2: 712*5b6bfdb9SJed Brown print('RDict.py [server | client | clear | insert | remove] [parent]') 713179860b2SJed Brown else: 714179860b2SJed Brown action = sys.argv[1] 715179860b2SJed Brown parent = None 716179860b2SJed Brown if len(sys.argv) > 2: 717179860b2SJed Brown if not sys.argv[2] == 'None': parent = sys.argv[2] 718179860b2SJed Brown if action == 'server': 719179860b2SJed Brown RDict(parentDirectory = parent).serve() 720179860b2SJed Brown elif action == 'client': 721*5b6bfdb9SJed Brown print('Entries in server dictionary') 722179860b2SJed Brown rdict = RDict(parentDirectory = parent) 723179860b2SJed Brown for key in rdict.types(): 724179860b2SJed Brown if not key.startswith('cacheKey') and not key.startswith('stamp-'): 725*5b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 726179860b2SJed Brown elif action == 'cacheClient': 727*5b6bfdb9SJed Brown print('Cache entries in server dictionary') 728179860b2SJed Brown rdict = RDict(parentDirectory = parent) 729179860b2SJed Brown for key in rdict.types(): 730179860b2SJed Brown if key.startswith('cacheKey'): 731*5b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 732179860b2SJed Brown elif action == 'stampClient': 733*5b6bfdb9SJed Brown print('Stamp entries in server dictionary') 734179860b2SJed Brown rdict = RDict(parentDirectory = parent) 735179860b2SJed Brown for key in rdict.types(): 736179860b2SJed Brown if key.startswith('stamp-'): 737*5b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 738179860b2SJed Brown elif action == 'clear': 739*5b6bfdb9SJed Brown print('Clearing all dictionaries') 740179860b2SJed Brown RDict(parentDirectory = parent).clear() 741179860b2SJed Brown elif action == 'insert': 742179860b2SJed Brown rdict = RDict(parentDirectory = parent) 743179860b2SJed Brown rdict[sys.argv[3]] = sys.argv[4] 744179860b2SJed Brown elif action == 'remove': 745179860b2SJed Brown rdict = RDict(parentDirectory = parent) 746179860b2SJed Brown del rdict[sys.argv[3]] 747179860b2SJed Brown else: 748179860b2SJed Brown sys.exit('Unknown action: '+action) 749*5b6bfdb9SJed Brown except Exception as e: 750179860b2SJed Brown import traceback 751*5b6bfdb9SJed Brown print(traceback.print_tb(sys.exc_info()[2])) 752179860b2SJed Brown sys.exit(str(e)) 753179860b2SJed Brown sys.exit(0) 754