freeipa/ipa-client/ipaclient/ipachangeconf.py
2007-08-28 10:44:43 -04:00

228 lines
7.0 KiB
Python

#
# ipachangeconf - configuration filke manipulation classes and functions
# partially based on authconfig code
# Copyright (c) 1999-2007 Red Hat, Inc.
# Author: Simo Sorce <ssorce@redhat.com>
#
# 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