root/trunk/src/cryptobox/core/settings.py @ 710

Revision 710, 22.1 kB (checked in by lars, 4 years ago)

fixed the 'status' method of the encrypted_webinterface module
added a method 'create_misc_config_file' to cryptobox.core.settings
used this method to create the certificate file
improved the description on how to set up an encrypted webinterface connection
added description of the 'encrypted_webinterface' plugin to README.ssl
moved 'coding_guidelines.txt' to the wiki

  • Property svn:keywords set to Id
Line 
1#
2# Copyright 2006 sense.lab e.V.
3#
4# This file is part of the CryptoBox.
5#
6# The CryptoBox is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# The CryptoBox is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with the CryptoBox; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19#
20
21"""Manage the configuration of a CryptoBox
22"""
23
24__revision__ = "$Id"
25
26from cryptobox.core.exceptions import *
27import logging
28import subprocess
29import os
30import configobj, validate
31import syslog
32
33
34CONF_LOCATIONS = [
35        "./cryptobox.conf",
36        "~/.cryptobox.conf",
37        "/etc/cryptobox-server/cryptobox.conf"]
38
39VOLUMESDB_FILE = "cryptobox_volumes.db"
40PLUGINCONF_FILE = "cryptobox_plugins.conf"
41USERDB_FILE = "cryptobox_users.db"
42
43
44class CryptoBoxSettings:
45        """Manage the various configuration files of the CryptoBox
46        """
47
48        def __init__(self, config_file=None):
49                self.log = logging.getLogger("CryptoBox")
50                config_file = self.__get_config_filename(config_file)
51                self.log.info("loading config file: %s" % config_file)
52                self.prefs = self.__get_preferences(config_file)
53                self.__validate_config()
54                self.__configure_log_handler()
55                self.__check_unknown_preferences()
56                self.prepare_partition()
57                self.volumes_db = self.__get_volumes_database()
58                self.plugin_conf = self.__get_plugin_config()
59                self.user_db = self.__get_user_db()
60                self.misc_files = []
61                self.reload_misc_files()
62       
63
64        def reload_misc_files(self):
65                """Call this method after creating or removing a 'misc' configuration file
66                """
67                self.misc_files = self.__get_misc_files()
68       
69
70        def write(self):
71                """
72                write all local setting files including the content of the "misc" subdirectory
73                """
74                status = True
75                try:
76                        self.volumes_db.write()
77                except IOError:
78                        self.log.warn("Could not save the volume database")
79                        status = False
80                try:
81                        self.plugin_conf.write()
82                except IOError:
83                        self.log.warn("Could not save the plugin configuration")
84                        status = False
85                try:
86                        self.user_db.write()
87                except IOError:
88                        self.log.warn("Could not save the user database")
89                        status = False
90                for misc_file in self.misc_files:
91                        if not misc_file.save():
92                                self.log.warn("Could not save a misc setting file (%s)" % misc_file.filename)
93                                status = False
94                return status
95
96
97        def get_misc_config_filename(self, name):
98                """Return an absolute filename for a given filename 'name'
99
100                'name' should not contain slashes (no directory part!)
101                """
102                return os.path.join(self.prefs["Locations"]["SettingsDir"], "misc", name)
103       
104
105        def create_misc_config_file(self, name, content):
106                """Create a new configuration file in the 'settings' directory
107
108                "name" should be the basename (without a directory)
109                "content" will be directly written to the file
110                this method may throw an IOException
111                """
112                misc_conf_file = self.get_misc_config_filename(name)
113                misc_conf_dir = os.path.dirname(misc_conf_file)
114                if not os.path.isdir(misc_conf_dir):
115                        os.mkdir(misc_conf_dir)
116                cfile = open(misc_conf_file, "w")
117                try:
118                        cfile.write(content)
119                except IOError:
120                        cfile.close()
121                        raise
122                cfile.close()
123                ## reread all misc files automatically - this should be ok
124                self.reload_misc_files()
125
126
127        def requires_partition(self):
128                return bool(self.prefs["Main"]["UseConfigPartition"])
129       
130
131        def get_active_partition(self):
132                """Return the currently active cnfiguration partition.
133                """
134                settings_dir = self.prefs["Locations"]["SettingsDir"]
135                if not os.path.ismount(settings_dir):
136                        return None
137                for line in file("/proc/mounts"):
138                        fields = line.split(" ")
139                        mount_dir = fields[1]
140                        try:
141                                if os.path.samefile(mount_dir, settings_dir):
142                                        return fields[0]
143                        except OSError:
144                                pass
145                ## no matching entry found
146                return None
147
148
149        def mount_partition(self):
150                """Mount a config partition.
151                """
152                self.log.debug("trying to mount configuration partition")
153                if not self.requires_partition():
154                        self.log.warn("mountConfigPartition: configuration partition is "
155                                        + "not required - mounting anyway")
156                if self.get_active_partition():
157                        self.log.warn("mountConfigPartition: configuration partition already "
158                                        + "mounted - not mounting again")
159                        return False
160                conf_partitions = self.get_available_partitions()
161                if not conf_partitions:
162                        self.log.warn("no configuration partition found - you have to create "
163                                        + "it first")
164                        #TODO: mount tmpfs in settings directory
165                        self.log.info("Ramdisk (tmpfs) mounted as config partition ...")
166                        return False
167                partition = conf_partitions[0]
168                proc = subprocess.Popen(
169                        shell = False,
170                        stdout = subprocess.PIPE,
171                        stderr = subprocess.PIPE,
172                        args = [
173                                self.prefs["Programs"]["super"],
174                                self.prefs["Programs"]["CryptoBoxRootActions"],
175                                "mount",
176                                partition,
177                                self.prefs["Locations"]["SettingsDir"]])
178                (stdout, stderr) = proc.communicate()
179                if proc.returncode != 0:
180                        self.log.error("failed to mount the configuration partition: %s" % partition)
181                        self.log.error("output of mount: %s" % (stderr,))
182                        return False
183                self.log.info("configuration partition mounted: %s" % partition)
184                return True
185
186
187        def umount_partition(self):
188                """Umount the currently active configuration partition.
189                """
190                if not self.get_active_partition():
191                        self.log.warn("umountConfigPartition: no configuration partition mounted")
192                        return False
193                self.reload_misc_files()
194                proc = subprocess.Popen(
195                        shell = False,
196                        stdout = subprocess.PIPE,
197                        stderr = subprocess.PIPE,
198                        args = [
199                                self.prefs["Programs"]["super"],
200                                self.prefs["Programs"]["CryptoBoxRootActions"],
201                                "umount",
202                                self.prefs["Locations"]["SettingsDir"]])
203                (stdout, stderr) = proc.communicate()
204                if proc.returncode != 0:
205                        self.log.error("failed to unmount the configuration partition")
206                        self.log.error("output of mount: %s" % (stderr,))
207                        return False
208                self.log.info("configuration partition unmounted")
209                return True
210               
211
212        def get_available_partitions(self):
213                """returns a sequence of found config partitions"""
214                proc = subprocess.Popen(
215                        shell = False,
216                        stdout = subprocess.PIPE,
217                        args = [
218                                self.prefs["Programs"]["blkid"],
219                                "-c", os.path.devnull,
220                                "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ])
221                (output, error) = proc.communicate()
222                if output:
223                        return [e.strip().split(":", 1)[0] for e in output.splitlines()]
224                else:
225                        return []
226
227       
228        def prepare_partition(self):
229                """Mount a config partition if necessary.
230                """
231                if self.requires_partition() and not self.get_active_partition():
232                        self.mount_partition()
233
234
235        def __getitem__(self, key):
236                """redirect all requests to the 'prefs' attribute"""
237                return self.prefs[key]
238
239
240        def __get_preferences(self, config_file):
241                """Load the CryptoBox configuration.
242                """
243                import StringIO
244                config_rules = StringIO.StringIO(self.validation_spec)
245                try:
246                        prefs = configobj.ConfigObj(config_file, configspec=config_rules)
247                        if prefs:
248                                self.log.info("found config: %s" % prefs.items())
249                        else:
250                                raise CBConfigUnavailableError(
251                                                "failed to load the config file: %s" % config_file)
252                except IOError:
253                        raise CBConfigUnavailableError(
254                                        "unable to open the config file: %s" % config_file)
255                return prefs
256
257
258        def __validate_config(self):
259                """Check the configuration settings and cast value types.
260                """
261                result = self.prefs.validate(CryptoBoxSettingsValidator(), preserve_errors=True)
262                error_list = configobj.flatten_errors(self.prefs, result)
263                if not error_list:
264                        return
265                error_msgs = []
266                for sections, key, text in error_list:
267                        section_name = "->".join(sections)
268                        if not text:
269                                error_msg = "undefined configuration value (%s) in section '%s'" % \
270                                                (key, section_name)
271                        else:
272                                error_msg = "invalid configuration value (%s) in section '%s': %s" % \
273                                                (key, section_name, text)
274                        error_msgs.append(error_msg)
275                raise CBConfigError, "\n".join(error_msgs)
276
277
278        def __check_unknown_preferences(self):
279                """Check the configuration file for unknown settings to avoid spelling mistakes.
280                """
281                import StringIO
282                config_rules = configobj.ConfigObj(StringIO.StringIO(self.validation_spec),
283                                list_values=False)
284                self.__recursive_section_check("", self.prefs, config_rules)
285               
286       
287        def __recursive_section_check(self, section_path, section_config, section_rules):
288                """should be called by '__check_unknown_preferences' for every section
289                sends a warning message to the logger for every undefined (see validation_spec)
290                configuration setting
291                """
292                for section in section_config.keys():
293                        element_path = section_path + section
294                        if section in section_rules.keys():
295                                if isinstance(section_config[section], configobj.Section):
296                                        if isinstance(section_rules[section], configobj.Section):
297                                                self.__recursive_section_check(element_path + "->",
298                                                                section_config[section], section_rules[section])
299                                        else:
300                                                self.log.warn("configuration setting should be a value "
301                                                                + "instead of a section name: %s" % element_path)
302                                else:
303                                        if not isinstance(section_rules[section], configobj.Section):
304                                                pass    # good - the setting is valid
305                                        else:
306                                                self.log.warn("configuration setting should be a section "
307                                                                + "name instead of a value: %s" % element_path)
308                        elif element_path.startswith("PluginSettings->"):
309                                ## ignore plugin settings
310                                pass
311                        else:
312                                self.log.warn("unknown configuration setting: %s" % element_path)
313
314
315        def __get_plugin_config(self):
316                """Load the plugin configuration file if it exists.
317                """
318                import StringIO
319                plugin_rules = StringIO.StringIO(self.pluginValidationSpec)
320                try:
321                        try:
322                                plugin_conf_file = os.path.join(
323                                                self.prefs["Locations"]["SettingsDir"], PLUGINCONF_FILE)
324                        except KeyError:
325                                raise CBConfigUndefinedError("Locations", "SettingsDir")
326                except SyntaxError:
327                        raise CBConfigInvalidValueError("Locations", "SettingsDir", plugin_conf_file,
328                                        "failed to interprete the filename of the plugin config file "
329                                        + "correctly (%s)" % plugin_conf_file)
330                ## create plugin_conf_file if necessary
331                if os.path.exists(plugin_conf_file):
332                        plugin_conf = configobj.ConfigObj(plugin_conf_file, configspec=plugin_rules)
333                else:
334                        try:
335                                plugin_conf = configobj.ConfigObj(plugin_conf_file,
336                                                configspec=plugin_rules, create_empty=True)
337                        except IOError:
338                                plugin_conf = configobj.ConfigObj(configspec=plugin_rules)
339                                plugin_conf.filename = plugin_conf_file
340                ## validate and convert values according to the spec
341                plugin_conf.validate(validate.Validator())
342                return plugin_conf
343
344
345        def __get_volumes_database(self):
346                """Load the volume database file if it exists.
347                """
348                #TODO: add configuration specification and validation
349                try:
350                        try:
351                                conf_file = os.path.join(
352                                                self.prefs["Locations"]["SettingsDir"], VOLUMESDB_FILE)
353                        except KeyError:
354                                raise CBConfigUndefinedError("Locations", "SettingsDir")
355                except SyntaxError:
356                        raise CBConfigInvalidValueError("Locations", "SettingsDir", conf_file,
357                                        "failed to interprete the filename of the volume database "
358                                        + "correctly (%s)" % conf_file)
359                ## create conf_file if necessary
360                if os.path.exists(conf_file):
361                        conf = configobj.ConfigObj(conf_file)
362                else:
363                        try:
364                                conf = configobj.ConfigObj(conf_file, create_empty=True)
365                        except IOError:
366                                conf = configobj.ConfigObj()
367                                conf.filename = conf_file
368                return conf
369
370
371        def __get_user_db(self):
372                """Load the user database file if it exists.
373                """
374                import StringIO, sha
375                user_db_rules = StringIO.StringIO(self.userDatabaseSpec)
376                try:
377                        try:
378                                user_db_file = os.path.join(
379                                                self.prefs["Locations"]["SettingsDir"], USERDB_FILE)
380                        except KeyError:
381                                raise CBConfigUndefinedError("Locations", "SettingsDir")
382                except SyntaxError:
383                        raise CBConfigInvalidValueError("Locations", "SettingsDir", user_db_file,
384                                        "failed to interprete the filename of the users database file "
385                                        + "correctly (%s)" % user_db_file)
386                ## create user_db_file if necessary
387                if os.path.exists(user_db_file):
388                        user_db = configobj.ConfigObj(user_db_file, configspec=user_db_rules)
389                else:
390                        try:
391                                user_db = configobj.ConfigObj(user_db_file,
392                                                configspec=user_db_rules, create_empty=True)
393                        except IOError:
394                                user_db = configobj.ConfigObj(configspec=user_db_rules)
395                                user_db.filename = user_db_file
396                ## validate and set default value for "admin" user
397                user_db.validate(validate.Validator())
398                ## define password hash function - never use "sha" directly - SPOT
399                user_db.get_digest = lambda password: sha.new(password).hexdigest()
400                return user_db
401
402
403        def __get_misc_files(self):
404                """Load miscelleanous configuration files.
405
406                e.g.: an ssl certificate, ...
407                """
408                misc_dir = os.path.join(self.prefs["Locations"]["SettingsDir"], "misc")
409                if (not os.path.isdir(misc_dir)) or (not os.access(misc_dir, os.X_OK)):
410                        return []
411                misc_files = []
412                for root, dirs, files in os.walk(misc_dir):
413                        misc_files.extend([os.path.join(root, e) for e in files])
414                return [MiscConfigFile(os.path.join(misc_dir, f), self.log) for f in misc_files]
415
416
417        def __get_config_filename(self, config_file):
418                """Search for the configuration file.
419                """
420                import types
421                if config_file is None:
422                        # no config file was specified - we will look for it in the ususal locations
423                        conf_file_list = [os.path.expanduser(f)
424                                for f in CONF_LOCATIONS
425                                if os.path.exists(os.path.expanduser(f))]
426                        if not conf_file_list:
427                                # no possible config file found in the usual locations
428                                raise CBConfigUnavailableError()
429                        config_file = conf_file_list[0]
430                else:
431                        # a config file was specified (e.g. via command line)
432                        if type(config_file) != types.StringType:
433                                raise CBConfigUnavailableError(
434                                                "invalid config file specified: %s" % config_file)
435                        if not os.path.exists(config_file):
436                                raise CBConfigUnavailableError(
437                                                "could not find the specified configuration file (%s)" % config_file)
438                return config_file
439
440
441        def __configure_log_handler(self):
442                """Configure the log handler of the CryptoBox according to the config.
443                """
444                log_level = self.prefs["Log"]["Level"].upper()
445                log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"]
446                if not log_level in log_level_avail:
447                        raise CBConfigInvalidValueError("Log", "Level", log_level,
448                                        "invalid log level: only %s are allowed" % str(log_level_avail))
449                log_destination = self.prefs["Log"]["Destination"].lower()
450                ## keep this in sync with the spec and the log_destination branches below
451                log_dest_avail = ['file', 'syslog']
452                if not log_destination in log_dest_avail:
453                        raise CBConfigInvalidValueError("Log", "Destination", log_destination,
454                                        "invalid log destination: only %s are allowed" % str(log_dest_avail))
455                if log_destination == 'file':
456                        try:
457                                log_handler = logging.FileHandler(self.prefs["Log"]["Details"])
458                        except IOError:
459                                raise CBEnvironmentError("could not write to log file (%s)" % \
460                                                self.prefs["Log"]["Details"])
461                        log_handler.setFormatter(
462                                        logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
463                elif log_destination == 'syslog':
464                        log_facility = self.prefs["Log"]["Details"].upper()
465                        log_facil_avail = ['KERN', 'USER', 'MAIL', 'DAEMON', 'AUTH', 'SYSLOG',
466                                        'LPR', 'NEWS', 'UUCP', 'CRON', 'AUTHPRIV', 'LOCAL0', 'LOCAL1',
467                                        'LOCAL2', 'LOCAL3', 'LOCAL4', 'LOCAL5', 'LOCAL6', 'LOCAL7']
468                        if not log_facility in log_facil_avail:
469                                raise CBConfigInvalidValueError("Log", "Details", log_facility,
470                                                "invalid log details for 'syslog': only %s are allowed" % \
471                                                str(log_facil_avail))
472                        ## retrive the log priority from the syslog module
473                        log_handler = LocalSysLogHandler("CryptoBox",
474                                        getattr(syslog, 'LOG_%s' % log_facility))
475                        log_handler.setFormatter(
476                                        logging.Formatter('%(asctime)s CryptoBox %(levelname)s: %(message)s'))
477                else:
478                        ## this should never happen - we just have it in case someone forgets
479                        ## to update the spec, the 'log_dest_avail' or the above branches
480                        raise CBConfigInvalidValueError("Log", "Destination", log_destination,
481                                        "invalid log destination: only %s are allowed" % str(log_dest_avail))
482                cbox_log = logging.getLogger("CryptoBox")
483                ## remove previous handlers (from 'basicConfig')
484                cbox_log.handlers = []
485                ## add new one
486                cbox_log.addHandler(log_handler)
487                ## do not call parent's handlers
488                cbox_log.propagate = False
489                ## 'log_level' is a string -> use 'getattr'
490                cbox_log.setLevel(getattr(logging, log_level))
491                ## the logger named "CryptoBox" is configured now
492
493
494        validation_spec = """
495[Main]
496AllowedDevices = list(min=1)
497DefaultVolumePrefix = string(min=1)
498DefaultCipher = string(default="aes-cbc-essiv:sha256")
499ConfigVolumeLabel = string(min=1, default="cbox_config")
500UseConfigPartition = integer(min=0, max=1, default=0)
501DisabledPlugins = list(default=list())
502
503[Locations]
504MountParentDir = directoryExists(default="/var/cache/cryptobox-server/mnt")
505SettingsDir = directoryExists(default="/var/cache/cryptobox-server/settings")
506TemplateDir = directoryExists(default="/usr/share/cryptobox-server/template")
507DocDir = directoryExists(default="/usr/share/doc/cryptobox-server/www-data")
508PluginDir = listOfExistingDirectories(default=list("/usr/share/cryptobox-server/plugins"))
509EventDir = string(default="/etc/cryptobox-server/events.d")
510
511[Log]
512Level = option("debug", "info", "warn", "error", default="warn")
513Destination = option("file", "syslog", default="file")
514Details = string(min=1, default="/var/log/cryptobox-server/cryptobox.log")
515
516[WebSettings]
517Stylesheet = string(min=1)
518Languages = list(min=1,default=list("en"))
519
520[Programs]
521cryptsetup = fileExecutable(default="/sbin/cryptsetup")
522mkfs = fileExecutable(default="/sbin/mkfs")
523nice = fileExecutable(default="/usr/bin/nice")
524blkid = fileExecutable(default="/sbin/blkid")
525blockdev = fileExecutable(default="/sbin/blockdev")
526mount = fileExecutable(default="/bin/mount")
527umount = fileExecutable(default="/bin/umount")
528super = fileExecutable(default="/usr/bin/super")
529# this is the "program" name as defined in /etc/super.tab
530CryptoBoxRootActions = string(min=1)
531
532[PluginSettings]
533[[__many__]]
534                """
535
536        pluginValidationSpec = """
537[__many__]
538visibility = boolean(default=None)
539requestAuth = boolean(default=None)
540rank = integer(default=None)
541                """
542       
543        userDatabaseSpec = """
544[admins]
545admin = string(default=d033e22ae348aeb5660fc2140aec35850c4da997)
546                """
547       
548
549class CryptoBoxSettingsValidator(validate.Validator):
550        """Some custom configuration check functions.
551        """
552
553        def __init__(self):
554                validate.Validator.__init__(self)
555                self.functions["directoryExists"] = self.check_directory_exists
556                self.functions["fileExecutable"] = self.check_file_executable
557                self.functions["fileWriteable"] = self.check_file_writeable
558                self.functions["listOfExistingDirectories"] = self.check_existing_directories
559       
560
561        def check_directory_exists(self, value):
562                """Is the directory accessible?
563                """
564                dir_path = os.path.abspath(value)
565                if not os.path.isdir(dir_path):
566                        raise validate.VdtValueError("%s (not found)" % value)
567                if not os.access(dir_path, os.X_OK):
568                        raise validate.VdtValueError("%s (access denied)" % value)
569                return dir_path
570       
571
572        def check_file_executable(self, value):
573                """Is the file executable?
574                """
575                file_path = os.path.abspath(value)
576                if not os.path.isfile(file_path):
577                        raise validate.VdtValueError("%s (not found)" % value)
578                if not os.access(file_path, os.X_OK):
579                        raise validate.VdtValueError("%s (access denied)" % value)
580                return file_path
581       
582
583        def check_file_writeable(self, value):
584                """Is the file writeable?
585                """
586                file_path = os.path.abspath(value)
587                if os.path.isfile(file_path):
588                        if not os.access(file_path, os.W_OK):
589                                raise validate.VdtValueError("%s (not found)" % value)
590                else:
591                        parent_dir = os.path.dirname(file_path)
592                        if os.path.isdir(parent_dir) and os.access(parent_dir, os.W_OK):
593                                return file_path
594                        raise validate.VdtValueError("%s (directory does not exist)" % value)
595                return file_path
596
597
598        def check_existing_directories(self, value):
599                """Are these directories accessible?
600                """
601                if not value:
602                        raise validate.VdtValueError("no plugin directory specified")
603                if not isinstance(value, list):
604                        value = [value]
605                result = []
606                for one_dir in value:
607                        dir_path = os.path.abspath(one_dir)
608                        if not os.path.isdir(dir_path):
609                                raise validate.VdtValueError(
610                                                "%s (plugin directory not found)" % one_dir)
611                        if not os.access(dir_path, os.X_OK):
612                                raise validate.VdtValueError(
613                                                "%s (access denied for plugin directory)" % one_dir)
614                        result.append(dir_path)
615                return result
616
617
618class MiscConfigFile:
619        """all other config files (e.g. a ssl certificate) to be stored"""
620
621        maxSize = 20480
622
623        def __init__(self, filename, logger):
624                self.filename = filename
625                self.log = logger
626                self.content = None
627                self.load()
628
629       
630        def load(self):
631                """Load a configuration file into memory.
632                """
633                fdesc = open(self.filename, "rb")
634                ## limit the maximum size
635                self.content = fdesc.read(self.maxSize)
636                if fdesc.tell() == self.maxSize:
637                        self.log.warn("file in misc settings directory (" + str(self.filename) \
638                                        + ") is bigger than allowed (" + str(self.maxSize) + ")")
639                fdesc.close()
640
641
642        def save(self):
643                """Save a configuration file to disk.
644                """
645                ## overriding of ro-files is not necessary (e.g. samba-include.conf)
646                if os.path.exists(self.filename) and not os.access(self.filename, os.W_OK):
647                        return True
648                save_dir = os.path.dirname(self.filename)
649                ## create the directory, if necessary
650                if not os.path.isdir(save_dir):
651                        try:
652                                os.mkdir(save_dir)
653                        except IOError:
654                                return False
655                ## save the content of the file
656                try:
657                        fdesc = open(self.filename, "wb")
658                except IOError:
659                        return False
660                try:
661                        fdesc.write(self.content)
662                        fdesc.close()
663                        return True
664                except IOError:
665                        fdesc.close()
666                        return False
667
668
669
670class LocalSysLogHandler(logging.Handler):
671        """Pass logging messages to a local syslog server without unix sockets.
672
673        derived from: logging.SysLogHandler
674        """
675
676        def __init__(self, prepend='CryptoBox', facility=syslog.LOG_USER):
677                logging.Handler.__init__(self)
678                self.formatter = None
679                self.facility = facility
680                syslog.openlog(prepend, 0, facility)
681       
682
683        def close(self):
684                """close the syslog connection
685                """
686                syslog.closelog()
687                logging.Handler.close(self)
688
689
690        def emit(self, record):
691                """format and send the log message
692                """
693                msg = "%s: %s" % (record.levelname, record.getMessage())
694                try:
695                        syslog.syslog(record.levelno, msg)
696                except Exception:
697                        self.handleError(record)
Note: See TracBrowser for help on using the browser.