From e77c35248e8ce796bc5b108c10013089a0c65bde Mon Sep 17 00:00:00 2001 From: Csaba Henk Date: Sat, 2 Apr 2011 19:40:48 +0000 Subject: syncdaemon: provide transactional semantics to config file writing So updating the config file from multiple contexts won't mess it up. This prepares the next commit where we'll set options internaly (which lacks the serial nature of user actions). Signed-off-by: Csaba Henk Signed-off-by: Vijay Bellur BUG: 2537 (gsync autorestart) URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=2537 --- .../marker/utils/syncdaemon/configinterface.py | 31 +++++++++++++--------- .../features/marker/utils/syncdaemon/syncdutils.py | 31 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/xlators/features/marker/utils/syncdaemon/configinterface.py b/xlators/features/marker/utils/syncdaemon/configinterface.py index cda7da7ebf3..a1079d80394 100644 --- a/xlators/features/marker/utils/syncdaemon/configinterface.py +++ b/xlators/features/marker/utils/syncdaemon/configinterface.py @@ -109,19 +109,19 @@ class GConffile(object): continue print("%s: %s" % (k, v)) - def write(self): - if not self.config.has_section(SECT_META): - self.config.add_section(SECT_META) - self.config.set(SECT_META, 'version', config_version) - f = None - try: - f = open(self.path, 'wb') + def write(self, trfn, *a, **kw): + def mergeconf(f): + self.config = ConfigParser.RawConfigParser() + self.config.readfp(f) + def updateconf(f): + if not self.config.has_section(SECT_META): + self.config.add_section(SECT_META) + self.config.set(SECT_META, 'version', config_version) + trfn(*a, **kw) self.config.write(f) - finally: - if f: - f.close() + syncdutils.update_file(self.path, updateconf, mergeconf) - def set(self, opt, val, rx=False): + def _set(self, opt, val, rx=False): sect = self.section(rx) if not self.config.has_section(sect): self.config.add_section(sect) @@ -130,11 +130,16 @@ class GConffile(object): self.config.add_section(SECT_ORD) self.config.set(SECT_ORD, sect, len(self.config._sections[SECT_ORD])) self.config.set(sect, opt, val) - self.write() - def delete(self, opt, rx=False): + def set(self, *a, **kw): + self.write(self._set, *a, **kw) + + def _delete(self, opt, rx=False): sect = self.section(rx) if not self.config.has_section(sect): return if self.config.remove_option(sect, opt): self.write() + + def delete(self, *a, **kw): + self.write(self._delete, *a, **kw) diff --git a/xlators/features/marker/utils/syncdaemon/syncdutils.py b/xlators/features/marker/utils/syncdaemon/syncdutils.py index 52dad8c5ff3..723ab8fb5fc 100644 --- a/xlators/features/marker/utils/syncdaemon/syncdutils.py +++ b/xlators/features/marker/utils/syncdaemon/syncdutils.py @@ -1,3 +1,5 @@ +import os +import fcntl try: # py 3 from urllib import parse as urllib @@ -9,3 +11,32 @@ def escape(s): def unescape(s): return urllib.unquote_plus(s) + +def update_file(path, updater, merger = lambda f: True): + """update a file in a transaction-like manner""" + + fr = fw = None + try: + fd = os.open(path, os.O_CREAT|os.O_RDWR) + try: + fr = os.fdopen(fd, 'r+b') + except: + os.close(fd) + raise + fcntl.lockf(fr, fcntl.LOCK_EX) + merger(fr) + + tmpp = path + '.tmp.' + str(os.getpid()) + fd = os.open(tmpp, os.O_CREAT|os.O_EXCL|os.O_WRONLY) + try: + fw = os.fdopen(fd, 'wb', 0) + except: + os.close(fd) + raise + updater(fw) + os.fsync(fd) + os.rename(tmpp, path) + finally: + for fx in (fr, fw): + if fx: + fx.close() -- cgit