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''' 565b6bfdb9SJed Brownfrom __future__ import print_function 575b6bfdb9SJed 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 65492432c8SJed Brownimport pickle 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('') 104492432c8SJed Brown self.stopCmd = pickle.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''' 127c6ef1b5bSJed Brown self.logFile = open('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') 143c6ef1b5bSJed Brown self.logFile = open(filename, 'w') 144179860b2SJed Brown else: 145c6ef1b5bSJed Brown self.logFile = open(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.''' 1643b049ba2SJed Brown try: 1653b049ba2SJed Brown value = dict.__getitem__(self, key) 1663b049ba2SJed Brown self.writeLogLine('getType: Getting local type for '+key+' '+str(value)) 1673b049ba2SJed Brown return value 1683b049ba2SJed Brown except KeyError: 1693b049ba2SJed Brown pass 1703b049ba2SJed Brown if self.parent: 171179860b2SJed Brown return self.send(key) 172179860b2SJed Brown return None 173179860b2SJed Brown 1743b049ba2SJed Brown def dict_has_key(self, key): 1753b049ba2SJed Brown """Utility to check whether the key is present in the dictionary without RDict side-effects.""" 1763b049ba2SJed Brown return key in dict(self) 1773b049ba2SJed Brown 178179860b2SJed Brown def __getitem__(self, key): 179179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Returns the value of the Arg. 180179860b2SJed Brown - If the value has not been set, the user will be prompted for input''' 1813b049ba2SJed Brown if self.dict_has_key(key): 182179860b2SJed Brown self.writeLogLine('__getitem__: '+key+' has local type') 183179860b2SJed Brown pass 184179860b2SJed Brown elif not self.parent is None: 185179860b2SJed Brown self.writeLogLine('__getitem__: Checking parent value') 186179860b2SJed Brown if self.send(key, operation = 'has_key'): 187179860b2SJed Brown self.writeLogLine('__getitem__: Parent has value') 188179860b2SJed Brown return self.send(key) 189179860b2SJed Brown else: 190179860b2SJed Brown self.writeLogLine('__getitem__: Checking parent type') 191179860b2SJed Brown arg = self.send(key, operation = 'getType') 192179860b2SJed Brown if not arg: 193179860b2SJed Brown self.writeLogLine('__getitem__: Parent has no type') 194179860b2SJed Brown arg = nargs.Arg(key) 195179860b2SJed Brown try: 196179860b2SJed Brown value = arg.getValue() 1975b6bfdb9SJed Brown except AttributeError as e: 198179860b2SJed Brown self.writeLogLine('__getitem__: Parent had invalid entry: '+str(e)) 199179860b2SJed Brown arg = nargs.Arg(key) 200179860b2SJed Brown value = arg.getValue() 201179860b2SJed Brown self.writeLogLine('__getitem__: Setting parent value '+str(value)) 202179860b2SJed Brown self.send(key, value, operation = '__setitem__') 203179860b2SJed Brown return value 204179860b2SJed Brown else: 205179860b2SJed Brown self.writeLogLine('__getitem__: Setting local type for '+key) 206179860b2SJed Brown dict.__setitem__(self, key, nargs.Arg(key)) 20731f4da61SSatish Balay #self.save() 208179860b2SJed Brown self.writeLogLine('__getitem__: Setting local value for '+key) 209179860b2SJed Brown return dict.__getitem__(self, key).getValue() 210179860b2SJed Brown 211179860b2SJed Brown def setType(self, key, value, forceLocal = 0): 212179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Sets the type for this key. 213179860b2SJed Brown - If a value for the key already exists, it is converted to the new type''' 214179860b2SJed Brown if not isinstance(value, nargs.Arg): 215179860b2SJed Brown raise TypeError('An argument type must be a subclass of Arg') 216179860b2SJed Brown value.setKey(key) 2173b049ba2SJed Brown if forceLocal or self.parent is None or self.dict_has_key(key): 2183b049ba2SJed Brown if self.dict_has_key(key): 219179860b2SJed Brown v = dict.__getitem__(self, key) 220179860b2SJed Brown if v.isValueSet(): 221179860b2SJed Brown try: 222179860b2SJed Brown value.setValue(v.getValue()) 223a5737712SSatish Balay except TypeError: 2245b6bfdb9SJed Brown print(value.__class__.__name__[3:]) 2255b6bfdb9SJed Brown print('-----------------------------------------------------------------------') 2265b6bfdb9SJed Brown print('Warning! Incorrect argument type specified: -'+str(key)+'='+str(v.getValue())+' - expecting type '+value.__class__.__name__[3:]+'.') 2275b6bfdb9SJed Brown print('-----------------------------------------------------------------------') 228a5737712SSatish Balay pass 229179860b2SJed Brown dict.__setitem__(self, key, value) 23031f4da61SSatish Balay #self.save() 231179860b2SJed Brown else: 232179860b2SJed Brown return self.send(key, value) 233179860b2SJed Brown return 234179860b2SJed Brown 235179860b2SJed Brown def __setitem__(self, key, value): 236179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Sets the value of the Arg.''' 2373b049ba2SJed Brown if not self.dict_has_key(key): 238179860b2SJed Brown if not self.parent is None: 239179860b2SJed Brown return self.send(key, value) 240179860b2SJed Brown else: 241179860b2SJed Brown dict.__setitem__(self, key, nargs.Arg(key)) 242179860b2SJed Brown dict.__getitem__(self, key).setValue(value) 243179860b2SJed Brown self.writeLogLine('__setitem__: Set value for '+key+' to '+str(dict.__getitem__(self, key))) 24431f4da61SSatish Balay #self.save() 245179860b2SJed Brown return 246179860b2SJed Brown 247179860b2SJed Brown def __delitem__(self, key): 248179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Deletes the Arg completely.''' 2493b049ba2SJed Brown if self.dict_has_key(key): 250179860b2SJed Brown dict.__delitem__(self, key) 25131f4da61SSatish Balay #self.save() 252179860b2SJed Brown elif not self.parent is None: 253179860b2SJed Brown self.send(key) 254179860b2SJed Brown return 255179860b2SJed Brown 256179860b2SJed Brown def clear(self): 257179860b2SJed Brown '''Clears both the local and parent dictionaries''' 258179860b2SJed Brown if dict.__len__(self): 259179860b2SJed Brown dict.clear(self) 26031f4da61SSatish Balay #self.save() 261179860b2SJed Brown if not self.parent is None: 262179860b2SJed Brown self.send() 263179860b2SJed Brown return 264179860b2SJed Brown 265179860b2SJed Brown def __contains__(self, key): 266179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Then checks whether the value has been set''' 2673b049ba2SJed Brown if self.dict_has_key(key): 268179860b2SJed Brown if dict.__getitem__(self, key).isValueSet(): 269179860b2SJed Brown self.writeLogLine('has_key: Have value for '+key) 270179860b2SJed Brown else: 271179860b2SJed Brown self.writeLogLine('has_key: Do not have value for '+key) 272179860b2SJed Brown return dict.__getitem__(self, key).isValueSet() 273179860b2SJed Brown elif not self.parent is None: 274179860b2SJed Brown return self.send(key) 275179860b2SJed Brown return 0 276179860b2SJed Brown 277179860b2SJed Brown def get(self, key, default=None): 2785b6bfdb9SJed Brown if key in self: 279179860b2SJed Brown return self.__getitem__(key) 280179860b2SJed Brown else: 281179860b2SJed Brown return default 282179860b2SJed Brown 283179860b2SJed Brown def hasType(self, key): 284179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Then checks whether the type has been set''' 2853b049ba2SJed Brown if self.dict_has_key(key): 286179860b2SJed Brown return 1 287179860b2SJed Brown elif not self.parent is None: 288179860b2SJed Brown return self.send(key) 289179860b2SJed Brown return 0 290179860b2SJed Brown 291179860b2SJed Brown def items(self): 292179860b2SJed Brown '''Return a list of all accessible items, as (key, value) pairs.''' 293179860b2SJed Brown l = dict.items(self) 294179860b2SJed Brown if not self.parent is None: 295179860b2SJed Brown l.extend(self.send()) 296179860b2SJed Brown return l 297179860b2SJed Brown 298179860b2SJed Brown def localitems(self): 299179860b2SJed Brown '''Return a list of all the items stored locally, as (key, value) pairs.''' 300179860b2SJed Brown return dict.items(self) 301179860b2SJed Brown 302179860b2SJed Brown def keys(self): 303179860b2SJed Brown '''Returns the list of keys in both the local and parent dictionaries''' 304bb3dd2f6SJed Brown keyList = [key for key in dict.keys(self) if dict.__getitem__(self, key).isValueSet()] 305179860b2SJed Brown if not self.parent is None: 306179860b2SJed Brown keyList.extend(self.send()) 307179860b2SJed Brown return keyList 308179860b2SJed Brown 309179860b2SJed Brown def types(self): 310179860b2SJed Brown '''Returns the list of keys for which types are defined in both the local and parent dictionaries''' 311179860b2SJed Brown keyList = dict.keys(self) 312179860b2SJed Brown if not self.parent is None: 313179860b2SJed Brown keyList.extend(self.send()) 314179860b2SJed Brown return keyList 315179860b2SJed Brown 316179860b2SJed Brown def update(self, d): 317179860b2SJed Brown '''Update the dictionary with the contents of d''' 318179860b2SJed Brown for k in d: 319179860b2SJed Brown self[k] = d[k] 320179860b2SJed Brown return 321179860b2SJed Brown 322179860b2SJed Brown def updateTypes(self, d): 323179860b2SJed Brown '''Update types locally, which is equivalent to the dict.update() method''' 324179860b2SJed Brown return dict.update(self, d) 325179860b2SJed Brown 326179860b2SJed Brown def insertArg(self, key, value, arg): 327179860b2SJed Brown '''Insert a (key, value) pair into the dictionary. If key is None, arg is put into the target list.''' 328179860b2SJed Brown if not key is None: 329179860b2SJed Brown self[key] = value 330179860b2SJed Brown else: 331179860b2SJed Brown if not self.target == ['default']: 332179860b2SJed Brown self.target.append(arg) 333179860b2SJed Brown else: 334179860b2SJed Brown self.target = [arg] 335179860b2SJed Brown return 336179860b2SJed Brown 337179860b2SJed Brown def insertArgs(self, args): 338179860b2SJed Brown '''Insert some text arguments into the dictionary (list and dictionaries are recognized)''' 339179860b2SJed Brown 340179860b2SJed Brown if isinstance(args, list): 341179860b2SJed Brown for arg in args: 342179860b2SJed Brown (key, value) = nargs.Arg.parseArgument(arg) 343179860b2SJed Brown self.insertArg(key, value, arg) 3443b049ba2SJed Brown elif hasattr(args, keys): 345179860b2SJed Brown for key in args.keys(): 346179860b2SJed Brown if isinstance(args[key], str): 347179860b2SJed Brown value = nargs.Arg.parseValue(args[key]) 348179860b2SJed Brown else: 349179860b2SJed Brown value = args[key] 350179860b2SJed Brown self.insertArg(key, value, None) 351179860b2SJed Brown elif isinstance(args, str): 352179860b2SJed Brown (key, value) = nargs.Arg.parseArgument(args) 353179860b2SJed Brown self.insertArg(key, value, args) 354179860b2SJed Brown return 355179860b2SJed Brown 356179860b2SJed Brown def hasParent(self): 357179860b2SJed Brown '''Return True if this RDict has a parent dictionary''' 358179860b2SJed Brown return not self.parent is None 359179860b2SJed Brown 360179860b2SJed Brown def getServerAddr(self, dir): 361179860b2SJed Brown '''Read the server socket address (in pickled form) from a file, usually RDict.loc 362179860b2SJed Brown - If we fail to connect to the server specified in the file, we spawn it using startServer()''' 363179860b2SJed Brown filename = os.path.join(dir, self.addrFilename) 364179860b2SJed Brown if not os.path.exists(filename): 365179860b2SJed Brown self.startServer(filename) 366179860b2SJed Brown if not os.path.exists(filename): 367179860b2SJed Brown raise RuntimeError('Server address file does not exist: '+filename) 368179860b2SJed Brown try: 369179860b2SJed Brown f = open(filename, 'r') 370492432c8SJed Brown addr = pickle.load(f) 371179860b2SJed Brown f.close() 372179860b2SJed Brown return addr 3735b6bfdb9SJed Brown except Exception as e: 374179860b2SJed Brown self.writeLogLine('CLIENT: Exception during server address determination: '+str(e.__class__)+': '+str(e)) 375179860b2SJed Brown raise RuntimeError('Could not get server address in '+filename) 376179860b2SJed Brown 377179860b2SJed Brown def writeServerAddr(self, server): 378179860b2SJed Brown '''Write the server socket address (in pickled form) to a file, usually RDict.loc.''' 379c6ef1b5bSJed Brown f = open(self.addrFilename, 'w') 380492432c8SJed Brown pickle.dump(server.server_address, f) 381179860b2SJed Brown f.close() 382179860b2SJed Brown self.writeLogLine('SERVER: Wrote lock file '+os.path.abspath(self.addrFilename)) 383179860b2SJed Brown return 384179860b2SJed Brown 385179860b2SJed Brown def startServer(self, addrFilename): 386179860b2SJed Brown '''Spawn a new RDict server in the parent directory''' 387179860b2SJed Brown import RDict # Need this to locate server script 388179860b2SJed Brown import sys 389179860b2SJed Brown import time 390179860b2SJed Brown import distutils.sysconfig 391179860b2SJed Brown 392179860b2SJed Brown self.writeLogLine('CLIENT: Spawning a new server with lock file '+os.path.abspath(addrFilename)) 393179860b2SJed Brown if os.path.exists(addrFilename): 394179860b2SJed Brown os.remove(addrFilename) 395179860b2SJed Brown oldDir = os.getcwd() 396179860b2SJed Brown source = os.path.join(os.path.dirname(os.path.abspath(sys.modules['RDict'].__file__)), 'RDict.py') 397179860b2SJed Brown interpreter = os.path.join(distutils.sysconfig.get_config_var('BINDIR'), distutils.sysconfig.get_config_var('PYTHON')) 398179860b2SJed Brown if not os.path.isfile(interpreter): 399179860b2SJed Brown interpreter = 'python' 400179860b2SJed Brown os.chdir(os.path.dirname(addrFilename)) 401179860b2SJed Brown self.writeLogLine('CLIENT: Executing '+interpreter+' '+source+' server"') 402179860b2SJed Brown try: 403179860b2SJed Brown os.spawnvp(os.P_NOWAIT, interpreter, [interpreter, source, 'server']) 404179860b2SJed Brown except: 405179860b2SJed Brown self.writeLogLine('CLIENT: os.spawnvp failed.\n \ 406179860b2SJed Brown This is a typical problem on CYGWIN systems. If you are using CYGWIN,\n \ 407179860b2SJed Brown you can fix this problem by running /bin/rebaseall. If you do not have\n \ 408179860b2SJed Brown this program, you can install it with the CYGWIN installer in the package\n \ 409179860b2SJed Brown Rebase, under the category System. You must run /bin/rebaseall after\n \ 410179860b2SJed Brown turning off all cygwin services -- in particular sshd, if any such services\n \ 411179860b2SJed Brown are running. For more information about rebase, go to http://www.cygwin.com') 4125b6bfdb9SJed Brown print('\n \ 413179860b2SJed Brown This is a typical problem on CYGWIN systems. If you are using CYGWIN,\n \ 414179860b2SJed Brown you can fix this problem by running /bin/rebaseall. If you do not have\n \ 415179860b2SJed Brown this program, you can install it with the CYGWIN installer in the package\n \ 416179860b2SJed Brown Rebase, under the category System. You must run /bin/rebaseall after\n \ 417179860b2SJed Brown turning off all cygwin services -- in particular sshd, if any such services\n \ 4185b6bfdb9SJed Brown are running. For more information about rebase, go to http://www.cygwin.com\n') 419179860b2SJed Brown raise 420179860b2SJed Brown os.chdir(oldDir) 421179860b2SJed Brown timeout = 1 422179860b2SJed Brown for i in range(10): 423179860b2SJed Brown time.sleep(timeout) 424179860b2SJed Brown timeout *= 2 425179860b2SJed Brown if timeout > 100: timeout = 100 426179860b2SJed Brown if os.path.exists(addrFilename): return 427179860b2SJed Brown self.writeLogLine('CLIENT: Could not start server') 428179860b2SJed Brown return 429179860b2SJed Brown 430179860b2SJed Brown def connectParent(self, addr, dir): 431179860b2SJed Brown '''Try to connect to a parent RDict server 432179860b2SJed Brown - If addr and dir are both None, this operation fails 433179860b2SJed Brown - If addr is None, check for an address file in dir''' 434179860b2SJed Brown if addr is None: 435179860b2SJed Brown if dir is None: return 0 436179860b2SJed Brown addr = self.getServerAddr(dir) 437179860b2SJed Brown 438179860b2SJed Brown import socket 439179860b2SJed Brown import errno 440179860b2SJed Brown connected = 0 441179860b2SJed Brown s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 442179860b2SJed Brown timeout = 1 443179860b2SJed Brown for i in range(10): 444179860b2SJed Brown try: 445179860b2SJed Brown self.writeLogLine('CLIENT: Trying to connect to '+str(addr)) 446179860b2SJed Brown s.connect(addr) 447179860b2SJed Brown connected = 1 448179860b2SJed Brown break 4495b6bfdb9SJed Brown except socket.error as e: 450179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect: '+str(e)) 451179860b2SJed Brown if e[0] == errno.ECONNREFUSED: 452179860b2SJed Brown try: 453179860b2SJed Brown import time 454179860b2SJed Brown time.sleep(timeout) 455179860b2SJed Brown timeout *= 2 456179860b2SJed Brown if timeout > 100: timeout = 100 457179860b2SJed Brown except KeyboardInterrupt: 458179860b2SJed Brown break 459179860b2SJed Brown # Try to spawn parent 460179860b2SJed Brown if dir: 461179860b2SJed Brown filename = os.path.join(dir, self.addrFilename) 462179860b2SJed Brown if os.path.isfile(filename): 463179860b2SJed Brown os.remove(filename) 464179860b2SJed Brown self.startServer(filename) 4655b6bfdb9SJed Brown except Exception as e: 466179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect: '+str(e.__class__)+': '+str(e)) 467179860b2SJed Brown if not connected: 468179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect to parent') 469179860b2SJed Brown return 0 470179860b2SJed Brown self.parent = s 471179860b2SJed Brown self.writeLogLine('CLIENT: Connected to '+str(self.parent)) 472179860b2SJed Brown return 1 473179860b2SJed Brown 474179860b2SJed Brown def sendPacket(self, s, packet, source = 'Unknown', isPickled = 0): 475179860b2SJed Brown '''Pickle the input packet. Send first the size of the pickled string in 32-bit integer, and then the string itself''' 476179860b2SJed Brown self.writeLogLine(source+': Sending packet '+str(packet)) 477179860b2SJed Brown if isPickled: 478179860b2SJed Brown p = packet 479179860b2SJed Brown else: 480492432c8SJed Brown p = pickle.dumps(packet) 481179860b2SJed Brown self.packer.reset() 482179860b2SJed Brown self.packer.pack_uint(len(p)) 483179860b2SJed Brown if hasattr(s, 'write'): 484179860b2SJed Brown s.write(self.packer.get_buffer()) 485179860b2SJed Brown s.write(p) 486179860b2SJed Brown else: 487179860b2SJed Brown s.sendall(self.packer.get_buffer()) 488179860b2SJed Brown s.sendall(p) 489179860b2SJed Brown self.writeLogLine(source+': Sent packet') 490179860b2SJed Brown return 491179860b2SJed Brown 492179860b2SJed Brown def recvPacket(self, s, source = 'Unknown'): 493179860b2SJed Brown '''Receive first the size of the pickled string in a 32-bit integer, and then the string itself. Return the unpickled object''' 494179860b2SJed Brown self.writeLogLine(source+': Receiving packet') 495179860b2SJed Brown if hasattr(s, 'read'): 496179860b2SJed Brown s.read(4) 497492432c8SJed Brown value = pickle.load(s) 498179860b2SJed Brown else: 499179860b2SJed Brown # I probably need to check that it actually read these 4 bytes 500179860b2SJed Brown self.unpacker.reset(s.recv(4)) 501179860b2SJed Brown length = self.unpacker.unpack_uint() 502179860b2SJed Brown objString = '' 503179860b2SJed Brown while len(objString) < length: 504179860b2SJed Brown objString += s.recv(length - len(objString)) 505492432c8SJed Brown value = pickle.loads(objString) 506179860b2SJed Brown self.writeLogLine(source+': Received packet '+str(value)) 507179860b2SJed Brown return value 508179860b2SJed Brown 509179860b2SJed Brown def send(self, key = None, value = None, operation = None): 510179860b2SJed Brown '''Send a request to the parent''' 511179860b2SJed Brown import inspect 512179860b2SJed Brown 513179860b2SJed Brown objString = '' 514179860b2SJed Brown for i in range(3): 515179860b2SJed Brown try: 516179860b2SJed Brown packet = [] 517179860b2SJed Brown if operation is None: 518179860b2SJed Brown operation = inspect.stack()[1][3] 519179860b2SJed Brown packet.append(operation) 520179860b2SJed Brown if not key is None: 521179860b2SJed Brown packet.append(key) 522179860b2SJed Brown if not value is None: 523179860b2SJed Brown packet.append(value) 524179860b2SJed Brown self.sendPacket(self.parent, tuple(packet), source = 'CLIENT') 525179860b2SJed Brown response = self.recvPacket(self.parent, source = 'CLIENT') 526179860b2SJed Brown break 5275b6bfdb9SJed Brown except IOError as e: 528179860b2SJed Brown self.writeLogLine('CLIENT: IOError '+str(e)) 529179860b2SJed Brown if e.errno == 32: 530179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 5315b6bfdb9SJed Brown except Exception as e: 532179860b2SJed Brown self.writeLogLine('CLIENT: Exception '+str(e)+' '+str(e.__class__)) 533179860b2SJed Brown try: 534179860b2SJed Brown if isinstance(response, Exception): 535179860b2SJed Brown self.writeLogLine('CLIENT: Got an exception '+str(response)) 536179860b2SJed Brown raise response 537179860b2SJed Brown else: 538179860b2SJed Brown self.writeLogLine('CLIENT: Received value '+str(response)+' '+str(type(response))) 539179860b2SJed Brown except UnboundLocalError: 540179860b2SJed Brown self.writeLogLine('CLIENT: Could not unpickle response') 541179860b2SJed Brown response = None 542179860b2SJed Brown return response 543179860b2SJed Brown 544179860b2SJed Brown def serve(self): 545179860b2SJed Brown '''Start a server''' 546179860b2SJed Brown import socket 547179860b2SJed Brown import SocketServer 548179860b2SJed Brown 549179860b2SJed Brown if not useThreads: 550179860b2SJed Brown raise RuntimeError('Cannot run a server if threads are disabled') 551179860b2SJed Brown 552179860b2SJed Brown class ProcessHandler(SocketServer.StreamRequestHandler): 553179860b2SJed Brown def handle(self): 554179860b2SJed Brown import time 555179860b2SJed Brown 556179860b2SJed Brown self.server.rdict.lastAccess = time.time() 557179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Started new handler') 558179860b2SJed Brown while 1: 559179860b2SJed Brown try: 560179860b2SJed Brown value = self.server.rdict.recvPacket(self.rfile, source = 'SERVER') 5615b6bfdb9SJed Brown except EOFError as e: 562179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: EOFError receiving packet '+str(e)+' '+str(e.__class__)) 563179860b2SJed Brown return 5645b6bfdb9SJed Brown except Exception as e: 565179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Error receiving packet '+str(e)+' '+str(e.__class__)) 566179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, e, source = 'SERVER') 567179860b2SJed Brown continue 568179860b2SJed Brown if value[0] == 'stop': break 569179860b2SJed Brown try: 570179860b2SJed Brown response = getattr(self.server.rdict, value[0])(*value[1:]) 5715b6bfdb9SJed Brown except Exception as e: 572179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Error executing operation '+str(e)+' '+str(e.__class__)) 573179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, e, source = 'SERVER') 574179860b2SJed Brown else: 575179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, response, source = 'SERVER') 576179860b2SJed Brown return 577179860b2SJed Brown 578179860b2SJed Brown # check if server is running 579179860b2SJed Brown if os.path.exists(self.addrFilename): 580179860b2SJed Brown rdict = RDict(parentDirectory = '.') 581179860b2SJed Brown hasParent = rdict.hasParent() 582179860b2SJed Brown del rdict 583179860b2SJed Brown if hasParent: 584179860b2SJed Brown self.writeLogLine('SERVER: Another server is already running') 585179860b2SJed Brown raise RuntimeError('Server already running') 586179860b2SJed Brown 587179860b2SJed Brown # Daemonize server 588179860b2SJed Brown self.writeLogLine('SERVER: Daemonizing server') 589179860b2SJed Brown if os.fork(): # Launch child 590179860b2SJed Brown os._exit(0) # Kill off parent, so we are not a process group leader and get a new PID 591179860b2SJed Brown os.setsid() # Set session ID, so that we have no controlling terminal 592179860b2SJed Brown # We choose to leave cwd at RDict.py: os.chdir('/') # Make sure root directory is not on a mounted drive 5935b6bfdb9SJed Brown os.umask(0o77) # Fix creation mask 594179860b2SJed Brown for i in range(3): # Crappy stopgap for closing descriptors 595179860b2SJed Brown try: 596179860b2SJed Brown os.close(i) 5975b6bfdb9SJed Brown except OSError as e: 598179860b2SJed Brown if e.errno != errno.EBADF: 599179860b2SJed Brown raise RuntimeError('Could not close default descriptor '+str(i)) 600179860b2SJed Brown 601179860b2SJed Brown # wish there was a better way to get a usable socket 602179860b2SJed Brown self.writeLogLine('SERVER: Establishing socket server') 603179860b2SJed Brown basePort = 8000 604179860b2SJed Brown flag = 'nosocket' 605179860b2SJed Brown p = 1 606179860b2SJed Brown while p < 1000 and flag == 'nosocket': 607179860b2SJed Brown try: 608179860b2SJed Brown server = SocketServer.ThreadingTCPServer((socket.gethostname(), basePort+p), ProcessHandler) 609179860b2SJed Brown flag = 'socket' 6105b6bfdb9SJed Brown except Exception as e: 611179860b2SJed Brown p = p + 1 612179860b2SJed Brown if flag == 'nosocket': 613179860b2SJed Brown p = 1 614179860b2SJed Brown while p < 1000 and flag == 'nosocket': 615179860b2SJed Brown try: 616179860b2SJed Brown server = SocketServer.ThreadingTCPServer(('localhost', basePort+p), ProcessHandler) 617179860b2SJed Brown flag = 'socket' 6185b6bfdb9SJed Brown except Exception as e: 619179860b2SJed Brown p = p + 1 620179860b2SJed Brown if flag == 'nosocket': 621179860b2SJed Brown self.writeLogLine('SERVER: Could not established socket server on port '+str(basePort+p)) 6225b6bfdb9SJed Brown raise RuntimeError('Cannot get available socket') 623179860b2SJed Brown self.writeLogLine('SERVER: Established socket server on port '+str(basePort+p)) 624179860b2SJed Brown 625179860b2SJed Brown self.isServer = 1 626179860b2SJed Brown self.writeServerAddr(server) 627179860b2SJed Brown self.serverShutdown(os.getpid()) 628179860b2SJed Brown 629179860b2SJed Brown server.rdict = self 630179860b2SJed Brown self.writeLogLine('SERVER: Started server') 631179860b2SJed Brown server.serve_forever() 632179860b2SJed Brown return 633179860b2SJed Brown 634179860b2SJed Brown def load(self): 635179860b2SJed Brown '''Load the saved dictionary''' 636179860b2SJed Brown if not self.parentDirectory is None and os.path.samefile(os.getcwd(), self.parentDirectory): 637179860b2SJed Brown return 638179860b2SJed Brown self.saveFilename = os.path.abspath(self.saveFilename) 639179860b2SJed Brown if os.path.exists(self.saveFilename): 640179860b2SJed Brown try: 641c6ef1b5bSJed Brown dbFile = open(self.saveFilename) 642492432c8SJed Brown data = pickle.load(dbFile) 643179860b2SJed Brown self.updateTypes(data) 644179860b2SJed Brown dbFile.close() 645179860b2SJed Brown self.writeLogLine('Loaded dictionary from '+self.saveFilename) 6465b6bfdb9SJed Brown except Exception as e: 647179860b2SJed Brown self.writeLogLine('Problem loading dictionary from '+self.saveFilename+'\n--> '+str(e)) 648179860b2SJed Brown else: 649179860b2SJed Brown self.writeLogLine('No dictionary to load in this file: '+self.saveFilename) 650179860b2SJed Brown return 651179860b2SJed Brown 652603e7a67SSatish Balay def save(self, force = 1): 653179860b2SJed Brown '''Save the dictionary after 5 seconds, ignoring all subsequent calls until the save 654179860b2SJed Brown - Giving force = True will cause an immediate save''' 655179860b2SJed Brown if self.readonly: return 65608eb64ffSMatthew G. Knepley if force: 657179860b2SJed Brown self.saveTimer = None 658179860b2SJed Brown # This should be a critical section 659*b8b3d021SJed Brown dbFile = open(self.saveFilename, 'wb') 660bb3dd2f6SJed Brown data = dict([i for i in self.localitems() if not i[1].getTemporary()]) 661492432c8SJed Brown pickle.dump(data, dbFile) 662179860b2SJed Brown dbFile.close() 663179860b2SJed Brown self.writeLogLine('Saved local dictionary to '+os.path.abspath(self.saveFilename)) 664179860b2SJed Brown elif not self.saveTimer: 665179860b2SJed Brown import threading 666179860b2SJed Brown self.saveTimer = threading.Timer(5, self.save, [], {'force': 1}) 667179860b2SJed Brown self.saveTimer.setDaemon(1) 668179860b2SJed Brown self.saveTimer.start() 669179860b2SJed Brown return 670179860b2SJed Brown 671179860b2SJed Brown def shutdown(self): 672179860b2SJed Brown '''Shutdown the dictionary, writing out changes and notifying parent''' 673179860b2SJed Brown if self.saveTimer: 674179860b2SJed Brown self.saveTimer.cancel() 675179860b2SJed Brown self.save(force = 1) 676179860b2SJed Brown if self.isServer and os.path.isfile(self.addrFilename): 677179860b2SJed Brown os.remove(self.addrFilename) 678179860b2SJed Brown if not self.parent is None: 679179860b2SJed Brown self.sendPacket(self.parent, self.stopCmd, isPickled = 1) 680179860b2SJed Brown self.parent.close() 681179860b2SJed Brown self.parent = None 682179860b2SJed Brown self.writeLogLine('Shutting down') 683179860b2SJed Brown self.logFile.close() 684179860b2SJed Brown return 685179860b2SJed Brown 686179860b2SJed Brown def serverShutdown(self, pid, delay = shutdownDelay): 687179860b2SJed Brown if self.shutdownTimer is None: 688179860b2SJed Brown import threading 689179860b2SJed Brown 690179860b2SJed Brown self.shutdownTimer = threading.Timer(delay, self.serverShutdown, [pid], {'delay': 0}) 691179860b2SJed Brown self.shutdownTimer.setDaemon(1) 692179860b2SJed Brown self.shutdownTimer.start() 693179860b2SJed Brown self.writeLogLine('SERVER: Set shutdown timer for process '+str(pid)+' at '+str(delay)+' seconds') 694179860b2SJed Brown else: 695179860b2SJed Brown try: 696179860b2SJed Brown import signal 697179860b2SJed Brown import time 698179860b2SJed Brown 699179860b2SJed Brown idleTime = time.time() - self.lastAccess 700179860b2SJed Brown self.writeLogLine('SERVER: Last access '+str(self.lastAccess)) 701179860b2SJed Brown self.writeLogLine('SERVER: Idle time '+str(idleTime)) 702179860b2SJed Brown if idleTime < RDict.shutdownDelay: 703179860b2SJed Brown self.writeLogLine('SERVER: Extending shutdown timer for '+str(pid)+' by '+str(RDict.shutdownDelay - idleTime)+' seconds') 704179860b2SJed Brown self.shutdownTimer = None 705179860b2SJed Brown self.serverShutdown(pid, RDict.shutdownDelay - idleTime) 706179860b2SJed Brown else: 707179860b2SJed Brown self.writeLogLine('SERVER: Killing server '+str(pid)) 708179860b2SJed Brown os.kill(pid, signal.SIGTERM) 7095b6bfdb9SJed Brown except Exception as e: 710179860b2SJed Brown self.writeLogLine('SERVER: Exception killing server: '+str(e)) 711179860b2SJed Brown return 712179860b2SJed Brown 713179860b2SJed Brownif __name__ == '__main__': 714179860b2SJed Brown import sys 715179860b2SJed Brown try: 716179860b2SJed Brown if len(sys.argv) < 2: 7175b6bfdb9SJed Brown print('RDict.py [server | client | clear | insert | remove] [parent]') 718179860b2SJed Brown else: 719179860b2SJed Brown action = sys.argv[1] 720179860b2SJed Brown parent = None 721179860b2SJed Brown if len(sys.argv) > 2: 722179860b2SJed Brown if not sys.argv[2] == 'None': parent = sys.argv[2] 723179860b2SJed Brown if action == 'server': 724179860b2SJed Brown RDict(parentDirectory = parent).serve() 725179860b2SJed Brown elif action == 'client': 7265b6bfdb9SJed Brown print('Entries in server dictionary') 727179860b2SJed Brown rdict = RDict(parentDirectory = parent) 728179860b2SJed Brown for key in rdict.types(): 729179860b2SJed Brown if not key.startswith('cacheKey') and not key.startswith('stamp-'): 7305b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 731179860b2SJed Brown elif action == 'cacheClient': 7325b6bfdb9SJed Brown print('Cache entries in server dictionary') 733179860b2SJed Brown rdict = RDict(parentDirectory = parent) 734179860b2SJed Brown for key in rdict.types(): 735179860b2SJed Brown if key.startswith('cacheKey'): 7365b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 737179860b2SJed Brown elif action == 'stampClient': 7385b6bfdb9SJed Brown print('Stamp entries in server dictionary') 739179860b2SJed Brown rdict = RDict(parentDirectory = parent) 740179860b2SJed Brown for key in rdict.types(): 741179860b2SJed Brown if key.startswith('stamp-'): 7425b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 743179860b2SJed Brown elif action == 'clear': 7445b6bfdb9SJed Brown print('Clearing all dictionaries') 745179860b2SJed Brown RDict(parentDirectory = parent).clear() 746179860b2SJed Brown elif action == 'insert': 747179860b2SJed Brown rdict = RDict(parentDirectory = parent) 748179860b2SJed Brown rdict[sys.argv[3]] = sys.argv[4] 749179860b2SJed Brown elif action == 'remove': 750179860b2SJed Brown rdict = RDict(parentDirectory = parent) 751179860b2SJed Brown del rdict[sys.argv[3]] 752179860b2SJed Brown else: 753179860b2SJed Brown sys.exit('Unknown action: '+action) 7545b6bfdb9SJed Brown except Exception as e: 755179860b2SJed Brown import traceback 7565b6bfdb9SJed Brown print(traceback.print_tb(sys.exc_info()[2])) 757179860b2SJed Brown sys.exit(str(e)) 758179860b2SJed Brown sys.exit(0) 759