00001 """ 00002 Configuration file parsing utilities, layered on top of stock Python 00003 ConfigParser module. 00004 00005 $Id: config.py 3107 2010-03-16 23:13:08Z sra $ 00006 00007 Copyright (C) 2009-2010 Internet Systems Consortium ("ISC") 00008 00009 Permission to use, copy, modify, and distribute this software for any 00010 purpose with or without fee is hereby granted, provided that the above 00011 copyright notice and this permission notice appear in all copies. 00012 00013 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 00014 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00015 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 00016 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00017 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00018 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00019 PERFORMANCE OF THIS SOFTWARE. 00020 00021 Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN") 00022 00023 Permission to use, copy, modify, and distribute this software for any 00024 purpose with or without fee is hereby granted, provided that the above 00025 copyright notice and this permission notice appear in all copies. 00026 00027 THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH 00028 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00029 AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, 00030 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00031 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00032 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00033 PERFORMANCE OF THIS SOFTWARE. 00034 """ 00035 00036 import ConfigParser, os, re 00037 00038 class parser(object): 00039 """ 00040 Extensions to stock Python ConfigParser: 00041 00042 Read config file and set default section while initializing parser object. 00043 00044 Support for OpenSSL-style subscripted options and a limited form of 00045 OpenSSL-style indirect variable references (${section::option}). 00046 00047 get-methods with default values and default section name. 00048 """ 00049 00050 def __init__(self, filename, section = None, allow_missing = False): 00051 """ 00052 Initialize this parser. 00053 """ 00054 00055 self.filename = filename 00056 self.cfg = ConfigParser.RawConfigParser() 00057 try: 00058 self.cfg.readfp(open(filename), filename) 00059 except IOError: 00060 if not allow_missing: 00061 raise 00062 self.default_section = section 00063 00064 def has_section(self, section): 00065 """ 00066 Test whether a section exists. 00067 """ 00068 00069 return self.cfg.has_section(section) 00070 00071 def has_option(self, option, section = None): 00072 """ 00073 Test whether an option exists. 00074 """ 00075 00076 if section is None: 00077 section = self.default_section 00078 return self.cfg.has_option(section, option) 00079 00080 def multiget(self, option, section = None): 00081 """ 00082 Parse OpenSSL-style foo.0, foo.1, ... subscripted options. 00083 00084 Returns a list of values matching the specified option name. 00085 """ 00086 00087 matches = [] 00088 if section is None: 00089 section = self.default_section 00090 if self.cfg.has_option(section, option): 00091 matches.append((-1, self.get(option, section = section))) 00092 for key, value in self.cfg.items(section): 00093 s = key.rsplit(".", 1) 00094 if len(s) == 2 and s[0] == option and s[1].isdigit(): 00095 matches.append((int(s[1]), self.get(option, section = section))) 00096 matches.sort() 00097 return [match[1] for match in matches] 00098 00099 _regexp = re.compile("\\${(.*?)::(.*?)}") 00100 00101 def _repl(self, m): 00102 """ 00103 Replacement function for indirect variable substitution. 00104 This is intended for use with re.subn(). 00105 """ 00106 section, option = m.group(1, 2) 00107 if section == "ENV": 00108 return os.getenv(option, "") 00109 else: 00110 return self.cfg.get(section, option) 00111 00112 def get(self, option, default = None, section = None): 00113 """ 00114 Get an option, perhaps with a default value. 00115 """ 00116 if section is None: 00117 section = self.default_section 00118 if default is not None and not self.cfg.has_option(section, option): 00119 return default 00120 val = self.cfg.get(section, option) 00121 while True: 00122 val, modified = self._regexp.subn(self._repl, val, 1) 00123 if not modified: 00124 return val 00125 00126 def getboolean(self, option, default = None, section = None): 00127 """ 00128 Get a boolean option, perhaps with a default value. 00129 """ 00130 v = self.get(option, default, section) 00131 if isinstance(v, str): 00132 v = v.lower() 00133 if v not in self.cfg._boolean_states: 00134 raise ValueError, "Not a boolean: %s" % v 00135 v = self.cfg._boolean_states[v] 00136 return v 00137 00138 def getint(self, option, default = None, section = None): 00139 """ 00140 Get an integer option, perhaps with a default value. 00141 """ 00142 return int(self.get(option, default, section)) 00143 00144 def getlong(self, option, default = None, section = None): 00145 """ 00146 Get a long integer option, perhaps with a default value. 00147 """ 00148 return long(self.get(option, default, section)) 00149 00150 def set_global_flags(self): 00151 """ 00152 Consolidated control for all the little global control flags 00153 scattered through the libraries. This isn't a particularly good 00154 place for this function to live, but it has to live somewhere and 00155 making it a method of the config parser from which it gets all of 00156 its data is less silly than the available alternatives. 00157 """ 00158 00159 import rpki.https, rpki.x509, rpki.sql, rpki.async 00160 00161 try: 00162 rpki.https.debug_http = self.getboolean("debug_http") 00163 except ConfigParser.NoOptionError: 00164 pass 00165 00166 try: 00167 rpki.https.debug_tls_certs = self.getboolean("debug_tls_certs") 00168 except ConfigParser.NoOptionError: 00169 pass 00170 00171 try: 00172 rpki.https.want_persistent_client = self.getboolean("want_persistent_client") 00173 except ConfigParser.NoOptionError: 00174 pass 00175 00176 try: 00177 rpki.https.want_persistent_server = self.getboolean("want_persistent_server") 00178 except ConfigParser.NoOptionError: 00179 pass 00180 00181 try: 00182 rpki.x509.CMS_object.debug_cms_certs = self.getboolean("debug_cms_certs") 00183 except ConfigParser.NoOptionError: 00184 pass 00185 00186 try: 00187 rpki.sql.sql_persistent.sql_debug = self.getboolean("sql_debug") 00188 except ConfigParser.NoOptionError: 00189 pass 00190 00191 try: 00192 rpki.async.timer.gc_debug = self.getboolean("gc_debug") 00193 except ConfigParser.NoOptionError: 00194 pass 00195 00196 try: 00197 rpki.async.timer.run_debug = self.getboolean("timer_debug") 00198 except ConfigParser.NoOptionError: 00199 pass 00200 00201 try: 00202 rpki.x509.XML_CMS_object.dump_outbound_cms = rpki.x509.DeadDrop(self.get("dump_outbound_cms")) 00203 except ConfigParser.NoOptionError: 00204 pass 00205 00206 try: 00207 rpki.x509.XML_CMS_object.dump_inbound_cms = rpki.x509.DeadDrop(self.get("dump_inbound_cms")) 00208 except ConfigParser.NoOptionError: 00209 pass 00210 00211 try: 00212 rpki.async.gc_summary(self.getint("gc_summary"), self.getint("gc_summary_threshold", 0)) 00213 except ConfigParser.NoOptionError: 00214 pass