singleinstance.py
1 """ 2 This is based upon the singleton class from 3 `tendo <https://github.com/pycontribs/tendo>`_ 4 which is under the Python Software Foundation License version 2 5 """ 6 7 import atexit 8 import os 9 import sys 10 11 from . import state 12 13 try: 14 import fcntl # @UnresolvedImport 15 except ImportError: 16 pass 17 18 19 class singleinstance(object): 20 """ 21 Implements a single instance application by creating a lock file 22 at appdata. 23 """ 24 def __init__(self, flavor_id="", daemon=False): 25 self.initialized = False 26 self.counter = 0 27 self.daemon = daemon 28 self.lockPid = None 29 self.lockfile = os.path.normpath( 30 os.path.join(state.appdata, 'singleton%s.lock' % flavor_id)) 31 32 if state.enableGUI and not self.daemon and not state.curses: 33 # Tells the already running (if any) application to get focus. 34 from . import bitmessageqt 35 bitmessageqt.init() 36 37 self.lock() 38 39 self.initialized = True 40 atexit.register(self.cleanup) 41 42 def lock(self): 43 """Obtain single instance lock""" 44 if self.lockPid is None: 45 self.lockPid = os.getpid() 46 if sys.platform == 'win32': 47 try: 48 # file already exists, we try to remove 49 # (in case previous execution was interrupted) 50 if os.path.exists(self.lockfile): 51 os.unlink(self.lockfile) 52 self.fd = os.open( 53 self.lockfile, 54 os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_TRUNC 55 ) 56 except OSError as e: 57 if e.errno == 13: 58 sys.exit( 59 'Another instance of this application is' 60 ' already running') 61 raise 62 else: 63 pidLine = "%i\n" % self.lockPid 64 os.write(self.fd, pidLine) 65 else: # non Windows 66 self.fp = open(self.lockfile, 'a+') 67 try: 68 if self.daemon and self.lockPid != os.getpid(): 69 # wait for parent to finish 70 fcntl.lockf(self.fp, fcntl.LOCK_EX) 71 else: 72 fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB) 73 self.lockPid = os.getpid() 74 except IOError: 75 sys.exit( 76 'Another instance of this application is' 77 ' already running') 78 else: 79 pidLine = "%i\n" % self.lockPid 80 self.fp.truncate(0) 81 self.fp.write(pidLine) 82 self.fp.flush() 83 84 def cleanup(self): 85 """Release single instance lock""" 86 if not self.initialized: 87 return 88 if self.daemon and self.lockPid == os.getpid(): 89 # these are the two initial forks while daemonizing 90 try: 91 if sys.platform == 'win32': 92 if hasattr(self, 'fd'): 93 os.close(self.fd) 94 else: 95 fcntl.lockf(self.fp, fcntl.LOCK_UN) 96 except (IOError, OSError): 97 pass 98 99 return 100 101 try: 102 if sys.platform == 'win32': 103 if hasattr(self, 'fd'): 104 os.close(self.fd) 105 os.unlink(self.lockfile) 106 else: 107 fcntl.lockf(self.fp, fcntl.LOCK_UN) 108 if os.path.isfile(self.lockfile): 109 os.unlink(self.lockfile) 110 except (IOError, OSError): 111 pass