From 4702eb907585556d83b3373bf06705d83ae3ed2d Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Tue, 28 Aug 2007 10:44:43 -0400 Subject: [PATCH] Initial configuration library import --- ipa-client/ipaclient/__init__.py | 5 +- ipa-client/ipaclient/ipachangeconf.py | 227 ++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 ipa-client/ipaclient/ipachangeconf.py diff --git a/ipa-client/ipaclient/__init__.py b/ipa-client/ipaclient/__init__.py index 66a4eb14b..c07a549a5 100644 --- a/ipa-client/ipaclient/__init__.py +++ b/ipa-client/ipaclient/__init__.py @@ -1,6 +1,5 @@ #! /usr/bin/python -E -# Authors: Karl MacMillan -# see inline +# Authors: Simo Sorce # # Copyright (C) 2007 Red Hat # see file 'COPYING' for use and warranty information @@ -19,5 +18,5 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -__all__ = ["ipadiscovery"] +__all__ = ["ipadiscovery", "ipachangeconf", "dnsclient"] diff --git a/ipa-client/ipaclient/ipachangeconf.py b/ipa-client/ipaclient/ipachangeconf.py new file mode 100644 index 000000000..31ba41805 --- /dev/null +++ b/ipa-client/ipaclient/ipachangeconf.py @@ -0,0 +1,227 @@ +# +# 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