# $Id$ # # Copyright (c) 2009 NLNet Labs. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # """ This class keeps track of engine configuration options There is an example config file in /signer_engine/engine.conf """ # todo: allow for spaces in dir? import os import re import Util from Ft.Xml.XPath import Evaluate from xml.dom import minidom from xml.parsers.expat import ExpatError COMMENT_LINE = re.compile("\s*([#;].*)?$") PKCS_LINE = re.compile(\ "pkcs11_token: (?P\w+)\s+(?P\S+)\s*(?P\d+)?\s*$") class EngineConfigurationError(Exception): """This exception is thrown when the engine configuration file cannot be parsed, or contains another error.""" pass # def __init__(self, value): # self.parameter = value # def __str__(self): # return repr(self.parameter) class EngineConfiguration: """Engine Configuration options""" def __init__(self, config_file_name): if not config_file_name: config_file_name = "@OPENDNSSEC_CONFIG_FILE@" self.config_file_name = config_file_name self.signer_pidfile = "@OPENDNSSEC_SIGNER_PIDFILE@" self.zonefetch_pidfile = "@OPENDNSSEC_FETCH_PIDFILE@" self.zonefetch_file = None self.zonelist_file = None self.zone_tmp_dir = None self.tools_dir = None self.notify_command = None self.config_file_name = config_file_name self.command_socket_file = "@OPENDNSSEC_SIGNER_SOCKET@" self.bindir = "@OPENDNSSEC_BIN_DIR@" self.sysconfdir = "@OPENDNSSEC_SYSCONF_DIR@" #self.privs_chroot = None self.privs_username = None self.privs_groupname = None if config_file_name: self.read_config_file(config_file_name) def read_config_file(self, input_file): """Read a configuration file""" try: conff = open(input_file, "r") xstr = conff.read() conff.close() xmlb = minidom.parseString(xstr) self.from_xml(xmlb) xmlb.unlink() except ExpatError, exe: print "syntax error in configuration file " + input_file raise EngineConfigurationError(str(exe)) except IOError, ioe: raise EngineConfigurationError(str(ioe)) def from_xml(self, xml_blob): """Searches the xml blob for the configuration values""" syslog_facility_string = \ Util.get_xml_data("Configuration/Common/Logging/Syslog/Facility", xml_blob, False) if not syslog_facility_string: syslog_facility_string = "LOG_DAEMON" self.syslog_facility_string = syslog_facility_string self.syslog_facility = \ Util.syslog_facility_int(syslog_facility_string) self.zonelist_file = \ Util.get_xml_data("Configuration/Common/ZoneListFile", xml_blob, True) self.zonefetch_file = \ Util.get_xml_data("Configuration/Common/ZoneFetchFile", xml_blob, True) self.zone_tmp_dir = \ Util.get_xml_data("Configuration/Signer/WorkingDirectory", xml_blob, True) if not self.zone_tmp_dir: self.zone_tmp_dir = "@OPENDNSSEC_SIGNER_WORKINGDIR@" self.tools_dir = \ Util.get_xml_data("Configuration/Signer/ToolsDirectory", xml_blob, True) if not self.tools_dir: self.tools_dir = "@OPENDNSSEC_LIBEXEC_DIR@" self.notify_command = \ Util.get_xml_data("Configuration/Signer/NotifyCommand", xml_blob, True) worker_thread_config = Util.get_xml_data( "Configuration/Signer/WorkerThreads", xml_blob, True) if worker_thread_config: try: self.worker_threads = int(worker_thread_config) if self.worker_threads <= 0: raise EngineConfigurationError("Configuration Error: WorkerThreads must be 1 or higher") except ValueError: raise EngineConfigurationError("Configuration Error: WorkerThreads must be an integer") else: # default to 4 self.worker_threads = 4 # chroot and privileges self.privs_username = \ Util.get_xml_data("Configuration/Signer/Privileges/User", xml_blob, True) self.privs_groupname = \ Util.get_xml_data("Configuration/Signer/Privileges/Group", xml_blob, True) self.privs_chroot = \ Util.get_xml_data("Configuration/Signer/Privileges/Directory", xml_blob, True) def check_config(self): """Verifies whether the configuration is correct for the signer. Raises an EngineConfigurationError when there seems to be a problem""" # do we need to check the zonelist file too? # there is the possibility that the kasp hasn't created it # yet if self.zone_tmp_dir is None: raise EngineConfigurationError(\ "WorkingDirectory missing (not configured)") if self.tools_dir is None: raise EngineConfigurationError(\ "Tools directory missing (not configured)") if not os.path.exists(self.zone_tmp_dir): raise EngineConfigurationError(\ "WorkingDirectory " + self.zone_tmp_dir + " does not exist") if not os.path.exists(self.tools_dir): raise EngineConfigurationError(\ "Tools directory " + self.tools_dir + " does not exist") if not os.path.exists(self.tools_dir + os.sep + "signer"): raise EngineConfigurationError(\ "signer tools appear missing")