# # ipachangeconf - configuration filke manipulation classes and functions # partially based on authconfig code # Copyright (c) 1999-2007 Red Hat, Inc. # Author: Simo Sorce # # This is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import fcntl import os import string import time def openLocked(filename, perms): fd = -1 try: fd = os.open(filename, os.O_RDWR | os.O_CREAT, perms) fcntl.lockf(fd, fcntl.LOCK_EX) except OSError, (errno, strerr): if fd != -1: try: os.close(fd) except OSError: pass raise IOError(errno, strerr) return os.fdopen(fd, "r+") class IPAChangeConf: def __init__(self, name): self.progname = name self.optpre = (" ",) self.doptpre = self.optpre[0] self.assign = ("=",) self.dassign = self.assign[0] self.comment = ("#",) self.dcomment = self.comment[0] self.eol = ("\n",) self.deol = self.eol[0] #self.sectdel = ("[","]") self.sectdel = () def setProgName(self, name): self.progname = name def setOptionPrefix(self, prefix): self.optpre = prefix self.doptpre = self.optpre[0] def setOptionAssignment(self, assign): self.assign = assign self.dassign = self.assign[0] def setCommentPrefix(self, comment): self.comment = comment self.dcomment = self.comment[0] def setEndLine(self, eol): self.eol = eol self.deol = self.eol[0] def setSectionDelimiters(self, delims): self.sectdel = delims def confDump(self, options): output = "" #pre conf options delimiter output += self.deol output += self.dcomment+"["+self.progname+"]--start-line--"+self.deol output += self.deol output += self.dcomment+" Generated by authconfig on " + time.strftime("%Y/%m/%d %H:%M:%S") + self.deol output += self.dcomment+" DO NOT EDIT THIS SECTION (delimited by --start-line--/--end-line--)"+self.deol output += self.dcomment+" Any modification may be deleted or altered by authconfig in future"+self.deol output += self.deol #set options for opt in options: if opt['action'] == "set": output += self.doptpre+opt['name']+" "+self.dassign+" "+opt['value']+self.deol #post conf options delimiter output += self.deol output += self.dcomment+"["+self.progname+"]--end-line--"+self.deol output += self.deol return output def matchAutoEnd(self, line): if line.endswith("]--end-line--"): return True return False def matchAutoStart(self, line): if line.endswith("]--start-line--"): return True return False def matchComment(self, line): for v in self.comment: if line.strip().startswith(v): return True return False def matchLineOption(self, line, opt): parts = line.split(self.dassign, 1) if len(parts) < 2: return False elif parts[0].split() == opt['name'].split(): return True else: return False def matchSection(self, line): cl = "".join(line.strip().split()).lower() if len(self.sectdel) != 2: return False if not cl.startswith(self.sectdel[0]): return False if not cl.endswith(self.sectdel[1]): return False return cl[len(self.sectdel[0]):-len(self.sectdel[1])] def getSectionLine(self, section): if len(self.sectdel) != 2: return section return self.sectdel[0]+section+self.sectdel[1]+self.deol def checkLineOption(self, line, options): output = "" # Check if this is a setting we care about. for opt in options: if self.matchLineOption(line.strip(), opt): output = self.dcomment break output += line return output; # Write settings to configuration file # file is a path # options is a set of dictionaries in the form: # [{'name': 'foo', 'value': 'bar', 'action': 'set/comment'}] # section is a section name like 'global' def changeConf(self, file, options, section=None): autosection = False savedsection = None done = False output = "" f = None try: f = openLocked(file, 0644) # Read in the old file. for line in f: if autosection: if self.matchAutoEnd(line): autosection = False #skip all previous auto-generated lines continue if self.matchAutoStart(line): autosection = True continue # If it's a comment, just pass it through. if self.matchComment(line): output += line continue # If it's a section start, note the section name. value = self.matchSection(line) if value: # Here we are at start if a new section, if the previous one matches # the specified section, dump here before starting the new one # the comparison with section == None is intentional and matches the # request to dump the options at the end of an implicit initial # unmarked section before any section is started if savedsection == section: output += self.confDump(options) done = True savedsection = value output += line continue # Comment out options we care about. if savedsection == section: output += self.checkLineOption(line, options) continue # Copy anything else as is. output += line if not done: if section: output += getSectionLine(section) output += self.confDump(options) # Write it out and close it. f.seek(0) f.truncate(0) f.write(output) finally: try: if f: f.close() except IOError: pass return True