[PATCH 11/13] Rework lock/pidfile. Make ui class var of OfflineImap

Sebastian Spaeth Sebastian at SSpaeth.de
Wed Nov 24 09:56:48 UTC 2010


Remove lock and pid files after running offlineimap.
Make the ui a class variable of OfflineImap.

Signed-off-by: Sebastian Spaeth <Sebastian at SSpaeth.de>
---
 offlineimap/init.py |  132 +++++++++++++++++++++++++++++----------------------
 1 files changed, 75 insertions(+), 57 deletions(-)

diff --git a/offlineimap/init.py b/offlineimap/init.py
index 56667ce..d06e766 100644
--- a/offlineimap/init.py
+++ b/offlineimap/init.py
@@ -29,30 +29,46 @@ from optparse import OptionParser
 import signal
 import logging
 
-try:
-    import fcntl
-    hasfcntl = 1
-except:
-    hasfcntl = 0
-
-lockfd = None
 
 class OfflineImap:
+    """Main class that is being run when invoking OfflineImap"""
+    ui = None
+    """class variable, holding the used ui"""
 
     def __init__(self):
+        self._lockfile = None       # lockfile path when locked
+        self._pidfile  = None       # pidfile path when locked
         self.parse_commandline()
 
-    def lock(self, config, ui):
-        global lockfd, hasfcntl
-        if not hasfcntl:
-            return
-        lockfd = open(config.getmetadatadir() + "/lock", "w")
+
+    def lock(self):
+        """Create lock file and exit if not possible"""
         try:
-            fcntl.flock(lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
-        except IOError:
-            ui.locked()
-            ui.terminate(1)
-    
+            lockfd = os.open(self._config.getmetadatadir() + "/lock", os.O_CREAT|os.O_EXCL)
+            os.close(lockfd)
+        except OSError as e:
+            if e.errno == 17:
+                #File exists
+                OfflineImap.ui.locked()
+                OfflineImap.ui.terminate(1)
+        self._lockfile = self._config.getmetadatadir() + "/lock"
+
+    def unlock(self):
+        """Delete lockfile if existing."""
+        if self._lockfile:
+            os.unlink(self._lockfile)
+
+    def write_pidfile(self, pidfile):
+        """Write the current process pid into a pidfile"""
+        pidfd = open(pidfile, "aw")
+        pidfd.write(str(os.getpid()) + "\n")
+        pidfd.close()
+        self._pidfile = pidfile
+
+    def delete_pidfile(self):
+        """Delete the current pid pidfile if existing."""
+        os.unlink(self._pidfile)
+
     def parse_commandline(self):
         """Parse the commandline and invoke everything"""
 
@@ -155,12 +171,12 @@ class OfflineImap:
         #read in configuration file
         configfilename = os.path.expanduser(options.configfile)
     
-        config = ConfigParser()
+        self._config = ConfigParser()
         if not os.path.exists(configfilename):
             logging.error(" *** Config file '%s' does not exist; aborting!" %
                           configfilename)
             sys.exit(1)
-        config.read(configfilename)
+        self._config.read(configfilename)
 
         #profile mode chosen?
         if options.profiledir:
@@ -182,54 +198,58 @@ class OfflineImap:
                     section = secname.replace("_", " ")
                 else:
                     section = "general"
-                config.set(section, key, value)
+                self._config.set(section, key, value)
 
         #init the ui, and set up additional log files
-        ui = offlineimap.ui.detector.findUI(config, options.interface)
-        offlineimap.ui.UIBase.setglobalui(ui)
+        OfflineImap.ui = offlineimap.ui.detector.findUI(
+            self._config,
+            options.interface)
+        offlineimap.ui.UIBase.setglobalui(OfflineImap.ui)
     
         if options.logfile:
-            ui.setlogfd(open(options.logfile, 'wt'))
+            OfflineImap.ui.setlogfd(open(options.logfile, 'wt'))
     
         #welcome blurb
-        ui.init_banner()
+        OfflineImap.ui.init_banner()
 
+        #debugging active?
+        #FIXME: thread debugging currently dies!
         if options.debugtype:
             if options.debugtype.lower() == 'all':
                 options.debugtype = 'imap,maildir,thread'
             for type in options.debugtype.split(','):
                 type = type.strip()
-                ui.add_debug(type)
+                OfflineImap.ui.add_debug(type)
                 if type.lower() == 'imap':
                     imaplib.Debug = 5
                 if type.lower() == 'thread':
                     threading._VERBOSE = 1
 
         if options.runonce:
-            # FIXME: maybe need a better
-            for section in accounts.getaccountlist(config):
-                config.remove_option('Account ' + section, "autorefresh")
+            for section in accounts.getaccountlist(self._config):
+                self._config.remove_option('Account ' + section, "autorefresh")
 
         if options.quick:
-            for section in accounts.getaccountlist(config):
-                config.set('Account ' + section, "quick", '-1')
+            for section in accounts.getaccountlist(self._config):
+                self._config.set('Account ' + section, "quick", '-1')
 
         if options.folders:
             foldernames = options.folders.replace(" ", "").split(",")
             folderfilter = "lambda f: f in %s" % foldernames
             folderincludes = "[]"
-            for accountname in accounts.getaccountlist(config):
+            for accountname in accounts.getaccountlist(self._config):
                 account_section = 'Account ' + accountname
                 remote_repo_section = 'Repository ' + \
-                                      config.get(account_section, 'remoterepository')
+                                      self._config.get(account_section, 'remoterepository')
                 local_repo_section = 'Repository ' + \
-                                     config.get(account_section, 'localrepository')
+                                     self._config.get(account_section, 'localrepository')
                 for section in [remote_repo_section, local_repo_section]:
-                    config.set(section, "folderfilter", folderfilter)
-                    config.set(section, "folderincludes", folderincludes)
-
-        self.lock(config, ui)
+                    self._config.set(section, "folderfilter", folderfilter)
+                    self._config.set(section, "folderincludes", folderincludes)
 
+        self.lock()
+        self.write_pidfile(
+            os.path.join(self._config.getmetadatadir(), "pid"))
     
         def sigterm_handler(self, signum, frame):
             # die immediately
@@ -237,28 +257,21 @@ class OfflineImap:
             ui.terminate(errormsg="terminating...")
 
         signal.signal(signal.SIGTERM,sigterm_handler)
-    
-        try:
-            pidfd = open(config.getmetadatadir() + "/pid", "w")
-            pidfd.write(str(os.getpid()) + "\n")
-            pidfd.close()
-        except:
-            pass
-    
+
         try:
             if options.logfile:
-                sys.stderr = ui.logfile
+                sys.stderr = OfflineImap.ui.logfile
     
-            socktimeout = config.getdefaultint("general", "socktimeout", 0)
+            socktimeout = self._config.getdefaultint("general", "socktimeout", 0)
             if socktimeout > 0:
                 socket.setdefaulttimeout(socktimeout)
     
-            activeaccounts = config.get("general", "accounts")
+            activeaccounts = self._config.get("general", "accounts")
             if options.accounts:
                 activeaccounts = options.accounts
             activeaccounts = activeaccounts.replace(" ", "")
             activeaccounts = activeaccounts.split(",")
-            allaccounts = accounts.AccountHashGenerator(config)
+            allaccounts = accounts.AccountHashGenerator(self._config)
     
             syncaccounts = []
             for account in activeaccounts:
@@ -269,7 +282,7 @@ class OfflineImap:
                         errormsg = 'The account "%s" does not exist.  Valid accounts are:'%account
                         for name in allaccounts.keys():
                             errormsg += '\n%s'%name
-                    ui.terminate(1, errortitle = 'Unknown Account "%s"'%account, errormsg = errormsg)
+                    OfflineImap.ui.terminate(1, errortitle = 'Unknown Account "%s"'%account, errormsg = errormsg)
                 if account not in syncaccounts:
                     syncaccounts.append(account)
     
@@ -281,17 +294,18 @@ class OfflineImap:
                 threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
             else:
                 threadutil.initInstanceLimit("ACCOUNTLIMIT",
-                                             config.getdefaultint("general", "maxsyncaccounts", 1))
+                                             self._config.getdefaultint("general", "maxsyncaccounts", 1))
     
-            for reposname in config.getsectionlist('Repository'):
+            for reposname in self._config.getsectionlist('Repository'):
                 for instancename in ["FOLDER_" + reposname,
                                      "MSGCOPY_" + reposname]:
                     if options.singlethreading:
                         threadutil.initInstanceLimit(instancename, 1)
                     else:
                         threadutil.initInstanceLimit(instancename,
-                                                     config.getdefaultint('Repository ' + reposname, "maxconnections", 1))
+                                                     self._config.getdefaultint('Repository ' + reposname, "maxconnections", 1))
             siglisteners = []
+
             def sig_handler(signum, frame):
                 if signum == signal.SIGUSR1:
                     # tell each account to do a full sync asap
@@ -314,18 +328,22 @@ class OfflineImap:
             t = ExitNotifyThread(target=syncmaster.syncitall,
                                  name='Sync Runner',
                                  kwargs = {'accounts': syncaccounts,
-                                           'config': config,
+                                           'config': self._config,
                                            'siglisteners': siglisteners})
             t.setDaemon(1)
             t.start()
         except:
-            ui.mainException()
+            OfflineImap.ui.mainException()
     
         try:
             threadutil.exitnotifymonitorloop(threadutil.threadexited)
         except SystemExit:
             raise
         except:
-            ui.mainException()  # Also expected to terminate.
-
+            OfflineImap.ui.mainException()  # Also expected to terminate.
+        finally:
+            # remove lockfile
+            # TODO: also remove on SIGTERM and SIGKILL
+            self.unlock()
+            self.delete_pidfile()
         
-- 
1.7.1




More information about the OfflineIMAP-project mailing list