From 1de2ebe176f9a0906b1093c02a0a32a656cc3d19 Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 19 Feb 2007 01:22:31 +0000 Subject: [PATCH] make blkid more verbose fix permission problem during bootup (Closes: #139) add support for partitioning of blockdevices ending in a digit (e.g. raid (md) devices) added fieldset to partition plugin --- bin/CryptoBoxWebserver | 78 +++++++++++------------------ bin/cryptobox.conf | 2 +- debian/changelog | 7 +++ plugins/partition/partition.py | 23 +++++++-- plugins/partition/select_device.cs | 9 +++- plugins/partition/set_partitions.cs | 9 +++- src/cryptobox/__init__.py | 2 +- src/cryptobox/core/settings.py | 18 +++++-- src/cryptobox/core/tools.py | 6 ++- 9 files changed, 92 insertions(+), 62 deletions(-) diff --git a/bin/CryptoBoxWebserver b/bin/CryptoBoxWebserver index f5637ae..aecd495 100755 --- a/bin/CryptoBoxWebserver +++ b/bin/CryptoBoxWebserver @@ -30,6 +30,7 @@ REMOVE_ENV_SETTINGS = [ "LANG", "LC", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_NUMERIC", "BASH_ENV", "SHELLOPTS" ] import os, sys +import signal, atexit import cryptobox.web.sites from cryptobox.core.exceptions import * from optparse import OptionParser @@ -66,13 +67,14 @@ except: sys.exit(1) -# TODO: change this for the release version [development|production] SERVER_ENVIRONMENT = "production" class CryptoBoxWebserver: '''this class starts the cherrypy webserver and serves the single sites''' def __init__(self, opts): + """Configure cherrypy and check the location of the configuration file + """ self.opts = opts ## check conffile if not os.access(opts.conffile, os.R_OK) or not os.path.isfile(opts.conffile): @@ -98,18 +100,16 @@ class CryptoBoxWebserver: "staticFilter.on" : True, "staticFilter.file": os.path.realpath(os.path.join(opts.datadir, 'favicon.ico'))} }) - ## drop privileges to run the cryptobox without root permissions - self.drop_privileges_temporarily() + + + def bootup_cryptobox(self): ## initialize site class try: cherrypy.root = cryptobox.web.sites.WebInterfaceSites(self.conffile) self.website = cherrypy.root except (CBConfigError,CBEnvironmentError), err_msg: sys.stderr.write("Error: the CryptoBox is misconfigured - please fix it!\n") - sys.stderr.write("%s\n" % str(err_msg)) - sys.exit(1) - ## restore privileges, as we need them to connect to a low socket (<1024) - self.restore_privileges() + raise def get_user_info(self): @@ -132,31 +132,6 @@ class CryptoBoxWebserver: return (pw_uid, pw_gid, additional_groups) - def drop_privileges_temporarily(self): - """Temporarily drop privileges. - """ - if self.opts.user is None: - return - (pw_uid, pw_gid, additional_groups) = self.get_user_info() - try: - os.setegid(pw_gid) - os.seteuid(pw_uid) - except OSError, err_msg: - sys.stderr.write("Failed to drop privileges temporarily: %s\n" % err_msg) - - - def restore_privileges(self): - """Restore previously temporarily dropped privileges. - """ - if self.opts.user is None: - return - try: - os.setegid(os.getgid()) - os.seteuid(os.getuid()) - except OSError, err_msg: - sys.stderr.write("Failed to restore privileges: %s\n" % err_msg) - - def change_groups(self): """Change the groups of the current process to the ones of the given user @@ -191,10 +166,13 @@ class CryptoBoxWebserver: def start(self): try: ## first: change the groups (cherrypy.server.start stores the - ## current setting for creating new threads later + ## current setting for creating new threads later) self.change_groups() cherrypy.server.start(initOnly=True) self.drop_privileges_permanently() + ## this must be done with dropped privileges - otherwise there is + ## at least a problem with 'blkid' - see bug #139 + self.bootup_cryptobox() cherrypy.server.wait_for_http_ready() except cherrypy._cperror.NotReady, err_msg: sys.stderr.write("Failed to start CryptoBox: %s\n" % err_msg) @@ -357,13 +335,29 @@ if __name__ == "__main__": options = parseOptions() ## set umask to 022 (aka 755) - octal value os.umask(022) - ## initialize the webserver class (before forking to get some error messages) + ## initialize the webserver class cbw = CryptoBoxWebserver(options) ## remove some environment settings clean_environment(REMOVE_ENV_SETTINGS) ## fork to background before cbw.start() - otherwise we lose the socket if options.background: fork_to_background() + ## define the default exit handler + def exit_handler(signum, sigframe): + if hasattr(cbw, "website"): + ## are we already up? + cbw.website.cbox.log.info("Shutting down ...") + cbw.website.cleanup() + cherrypy.server.stop() + try: + os.remove(options.pidfile) + except OSError: + pass + os._exit(0) + ## the signal handler gets called by a kill signal (usually in background mode) + signal.signal(signal.SIGTERM, exit_handler) + ## this exit handler gets called by KeyboardInterrupt and similar ones (foreground) + atexit.register(exit_handler, None, None) ## start the webserver try: if options.profile_file: @@ -375,6 +369,7 @@ if __name__ == "__main__": sys.stderr.write("Failed to start the CryptoBox webserver!\n") sys.stderr.write("%s\n" % str(err_msg)) sys.stderr.write("Check the log file for details.\n") + cherrypy.server.stop() sys.exit(1) ## redirect stderr to the webserver's logfile if options.background: @@ -384,22 +379,7 @@ if __name__ == "__main__": os.close(2) os.open(options.logfile, os.O_APPEND) ## startup went fine - fork is done - now we may write the pid file - ## write pid file write_pid_file(options.pidfile) - def exit_handler(signum, sigframe): - cbw.website.cbox.log.info("Shutting down ...") - cbw.website.cleanup() - try: - os.remove(options.pidfile) - except OSError: - pass - os._exit(0) - ## the signal handler gets called by a kill signal (usually in background mode) - import signal - signal.signal(signal.SIGTERM, exit_handler) - ## this exit handler gets called by KeyboardInterrupt and similar ones (foreground) - import atexit - atexit.register(exit_handler, None, None) ## this will never exit - one of the above exit handlers will get triggered cherrypy.server.block() diff --git a/bin/cryptobox.conf b/bin/cryptobox.conf index 59ad492..0262b23 100644 --- a/bin/cryptobox.conf +++ b/bin/cryptobox.conf @@ -2,7 +2,7 @@ # comma separated list of possible prefixes for accesible devices # beware: .e.g "/dev/hd" grants access to _all_ harddisks -AllowedDevices = /dev/loop, /dev/ubdb +AllowedDevices = /dev/loop, /dev/ubdb, /dev/md_d127 # use separate config partition? (1=yes / 0=no) UseConfigPartition = 1 diff --git a/debian/changelog b/debian/changelog index 0afd03d..6f9246a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +cryptobox (0.3.4-1) unstable; urgency=low + + * fixed uid handling during bootup (Closes: '139) + * added support for partitioning of raid devices + + -- Lars Kruse Sat, 17 Feb 2007 11:03:41 +0100 + cryptobox (0.3.3-1) unstable; urgency=low * new upstream release diff --git a/plugins/partition/partition.py b/plugins/partition/partition.py index 7e762e4..6cf3712 100644 --- a/plugins/partition/partition.py +++ b/plugins/partition/partition.py @@ -25,6 +25,7 @@ __revision__ = "$Id" import subprocess import os +import re import logging import cryptobox.core.tools as cbox_tools import cryptobox.plugins.base @@ -53,7 +54,6 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): def do_action(self, **args): """Show the partitioning form and execute the requested action. """ - import re ## load default hdf values self.__prepare_dataset() ## retrieve some values from 'args' - defaults are empty @@ -455,14 +455,14 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): part_num = 1 ## maybe a config partition? if self.with_config_partition: - dev_name = self.blockdevice + str(part_num) + dev_name = self.__get_partition_name(self.blockdevice, part_num) self.cbox.log.info("formatting config partition (%s)" % dev_name) if self.__format_one_partition(dev_name, CONFIGPARTITION["fs"]): self.__set_label_of_partition(dev_name, self.cbox.prefs["Main"]["ConfigVolumeLabel"]) part_num += 1 ## the first data partition - dev_name = self.blockdevice + str(part_num) + dev_name = self.__get_partition_name(self.blockdevice, part_num) part_type = PARTTYPES[parts[0]["type"]][1] self.cbox.log.info("formatting partition (%s) as '%s'" % (dev_name, part_type)) yield self.__format_one_partition(dev_name, part_type) @@ -470,7 +470,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): ## other data partitions part_num = 5 while parts: - dev_name = self.blockdevice + str(part_num) + dev_name = self.__get_partition_name(self.blockdevice, part_num) part_type = PARTTYPES[parts[0]["type"]][1] self.cbox.log.info("formatting partition (%s) as '%s'" % \ (dev_name, part_type)) @@ -478,6 +478,21 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): part_num += 1 del parts[0] return + + + def __get_partition_name(self, blockdev, number): + """Return the name of a specific partition of a device + + """ + ## do we need to put a "p" between name and number? + ## TODO: should we check for existence? + if re.search("[0-9]$", blockdev): + ## blockdev endswith a digit - we need a 'p' + return "%sp%d" % (blockdev, number) + else: + ## no 'p' necessary + return "%s%d" % (blockdev, number) + def __format_one_partition(self, dev_name, fs_type): diff --git a/plugins/partition/select_device.cs b/plugins/partition/select_device.cs index 6dc4946..dcda799 100644 --- a/plugins/partition/select_device.cs +++ b/plugins/partition/select_device.cs @@ -1,8 +1,13 @@ - +
+ + + + + 0 ?> @@ -38,5 +43,7 @@ +
+ diff --git a/plugins/partition/set_partitions.cs b/plugins/partition/set_partitions.cs index fceab2d..26b4628 100644 --- a/plugins/partition/set_partitions.cs +++ b/plugins/partition/set_partitions.cs @@ -1,8 +1,13 @@ - +
+ + + + + 0) || (subcount(Data.Plugins.partition.Parts) > 0) ?> @@ -94,3 +99,5 @@ +
+ diff --git a/src/cryptobox/__init__.py b/src/cryptobox/__init__.py index f3fe337..5a88a59 100644 --- a/src/cryptobox/__init__.py +++ b/src/cryptobox/__init__.py @@ -10,5 +10,5 @@ __all__ = ['core', 'web', 'plugins', 'tests'] __revision__ = "$Id$" -__version__ = "0.3.3" +__version__ = "0.3.4" diff --git a/src/cryptobox/core/settings.py b/src/cryptobox/core/settings.py index f4fff91..a0c6bd1 100644 --- a/src/cryptobox/core/settings.py +++ b/src/cryptobox/core/settings.py @@ -253,18 +253,30 @@ class CryptoBoxSettings: def get_available_partitions(self): """returns a sequence of found config partitions""" + self.log.debug("Retrieving available configuration partitions ...") proc = subprocess.Popen( shell = False, stdout = subprocess.PIPE, + stderr = subprocess.PIPE, args = [ self.prefs["Programs"]["blkid"], "-c", os.path.devnull, "-t", "LABEL=%s" % self.prefs["Main"]["ConfigVolumeLabel"] ]) (output, error) = proc.communicate() - if output: - return [e.strip().split(":", 1)[0] for e in output.splitlines()] - else: + if proc.returncode == 2: + self.log.info("No configuration partitions found") return [] + elif proc.returncode == 4: + self.log.warn("Failed to call 'blkid' for unknown reasons.") + return [] + elif proc.returncode == 0: + if output: + return [e.strip().split(":", 1)[0] for e in output.splitlines()] + else: + return [] + else: + self.log.warn("Unknown exit code of 'blkid': %d - %s" \ + % (proc.returncode, error)) def prepare_partition(self): diff --git a/src/cryptobox/core/tools.py b/src/cryptobox/core/tools.py index 0a28f6a..f1500d2 100644 --- a/src/cryptobox/core/tools.py +++ b/src/cryptobox/core/tools.py @@ -46,10 +46,12 @@ def get_available_partitions(): ## ignore lines with: invalid minor/major or extend partitions (size=1) if re.search('^[0-9]*$', p_major) and \ re.search('^[0-9]*$', p_minor) and (p_size != "1"): - p_parent = re.sub('[1-9]?[0-9]$', '', p_device) + ## for some parent devices we have to remove a 'p' (partition) + ## an example are partitionable mdadm raid devices (e.g. md1p1) + p_parent = re.sub('p?[1-9]?[0-9]$', '', p_device) if p_parent == p_device: if [e for e in ret_list - if re.search('^' + p_parent + '[1-9]?[0-9]$', e)]: + if re.search('^' + p_parent + 'p?[1-9]?[0-9]$', e)]: ## major partition - its children are already in the list pass else: