diff --git a/bin/CryptoBoxRootActions b/bin/CryptoBoxRootActions index e668f3b..6956b0f 100755 --- a/bin/CryptoBoxRootActions +++ b/bin/CryptoBoxRootActions @@ -74,6 +74,7 @@ def checkIfPluginIsValid(plugin): import imp try: x = imp.load_source("cbox_plugin",plugin) + #TODO: no wildcard catches, please! except Exception: return False try: @@ -87,7 +88,7 @@ def checkIfPluginIsValid(plugin): def checkIfEventScriptIsValid(plugin): event_dir = os.path.dirname(plugin) - if os.path.exists(os.path.join(event_dir,EVENT_MARKER)): + if os.path.exists(os.path.join(event_dir, EVENT_MARKER)): return True else: return False @@ -106,7 +107,7 @@ def call_plugin(args): ## check if the plugin is a python program, that is marked as a cryptobox plugin if not checkIfPluginIsValid(plugin): raise Exception, "the plugin (%s) is not a correctly marked python script" % plugin - args.insert(0,plugin) + args.insert(0, plugin) proc = subprocess.Popen( shell = False, args = args) @@ -122,12 +123,12 @@ def call_event(args): if not os.access(event, os.X_OK): raise Exception, "could not find executable event script (%s)" % event ## check if the script is valid (the marker file must be in the same directory) - if not checkIfEventScriptIsValid(plugin): - raise Exception, "the event script (%s) does not reside in a directory with the marker file (%s) - this is not allowed due to abuse prevention" % (plugin,EVENT_MARKER) + if not checkIfEventScriptIsValid(event): + raise Exception, "the event script (%s) does not reside in a directory with the marker file (%s) - this is not allowed due to abuse prevention" % (event, EVENT_MARKER) ## check if the event (and its parents) are only writeable for root if not checkIfFileIsSafe(event): raise Exception, "the event (%s) is not safe - check its (and its parents') permissions" % event - args.insert(0,event) + args.insert(0, event) proc = subprocess.Popen( shell = False, args = args) @@ -201,7 +202,7 @@ def run_cryptsetup(args): cmd_args.append(action) cmd_args.append(device) elif action == "luksDelKey": - if len(cs_args) < 2: raise "WrongArguments", "missing arguments" + if len(args) < 2: raise "WrongArguments", "missing arguments" device = args[0]; del args[0] cmd_args.insert(-1, action) cmd_args.insert(-1, device) @@ -361,7 +362,7 @@ def getUserInfo(user): except TypeError: # if a KeyError is raised again, then the supplied user was invalid userinfo = pwd.getpwnam(user) - u_groups =[one_group.gr_gid + u_groups = [one_group.gr_gid for one_group in grp.getgrall() if userinfo.pw_name in one_group.gr_mem] if not userinfo.pw_gid in u_groups: u_groups.append(userinfo.pw_gid) diff --git a/bin/cryptobox.conf b/bin/cryptobox.conf index be84c22..1ec5435 100644 --- a/bin/cryptobox.conf +++ b/bin/cryptobox.conf @@ -76,11 +76,12 @@ Languages = en, de, sl, fr [Programs] cryptsetup = /sbin/cryptsetup -mkfs-data = /sbin/mkfs.ext3 +mkfs = /sbin/mkfs blkid = /sbin/blkid blockdev = /sbin/blockdev mount = /bin/mount umount = /bin/umount +nice = /usr/bin/nice super = /usr/bin/super # this is the "program" name as defined in /etc/super.tab CryptoBoxRootActions = CryptoBoxRootActions diff --git a/bin/cryptoboxwebserver.conf b/bin/cryptoboxwebserver.conf deleted file mode 100644 index b8e1e6a..0000000 --- a/bin/cryptoboxwebserver.conf +++ /dev/null @@ -1,17 +0,0 @@ -[global] -server.socketPort = 8080 -#server.environment = "production" -server.environment = "development" -server.logToScreen = True -server.log_tracebacks = True -server.threadPool = 1 -server.reverseDNS = False -server.logFile = "cryptoboxwebserver.log" - -[/favicon.ico] -static_filter.on = True -# TODO: use live-cd/live-cd-tree.d/var/www/favicon.ico -static_filter.file = "/usr/share/doc/python-cherrypy/cherrypy/favicon.ico" - -[/test_stream] -stream_response = True diff --git a/conf-examples/cryptobox.conf b/conf-examples/cryptobox.conf index 3f1fd61..800f898 100644 --- a/conf-examples/cryptobox.conf +++ b/conf-examples/cryptobox.conf @@ -75,11 +75,12 @@ Languages = de, en, fr [Programs] cryptsetup = /sbin/cryptsetup -mkfs-data = /sbin/mkfs.ext3 +mkfs = /sbin/mkfs blkid = /sbin/blkid blockdev = /sbin/blockdev mount = /bin/mount umount = /bin/umount +nice = /usr/bin/nice super = /usr/bin/super # this is the "program" name as defined in /etc/super.tab CryptoBoxRootActions = CryptoBoxRootActions diff --git a/debian/changelog b/debian/changelog index 01aaf43..5748b67 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +cryptobox (0.2.52-1) unstable; urgency=low + + * format partitions in background + * mark busy partitions + + -- Lars Kruse Wed, 6 Dec 2006 14:57:43 +0100 + cryptobox (0.2.51-1) unstable; urgency=low * favicon included diff --git a/plugins/partition/partition.py b/plugins/partition/partition.py index 6d7cda6..262ff2d 100644 --- a/plugins/partition/partition.py +++ b/plugins/partition/partition.py @@ -28,6 +28,7 @@ import os import logging import cryptobox.core.tools as cbxTools import cryptobox.plugins.base +from cryptobox.core.exceptions import * PARTTYPES = { @@ -228,7 +229,7 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): ## breaks the flow (hanging process) #self.cbox.reReadContainerList() ## write config data - self.cbox.prefs.mountPartition() + self.cbox.prefs.mount_partition() self.cbox.prefs.write() self.cbox.log.info("settings stored on config partition") ## return the result @@ -456,6 +457,26 @@ class partition(cryptobox.plugins.base.CryptoBoxPlugin): def __format_one_partition(self, dev_name, fs_type): + """Format a single partition + """ + import cryptobox.core.container + ## first: retrieve UUID - it can be removed from the database afterwards + prev_name = [e.get_name() for e in self.cbox.get_container_list() + if e.get_device() == dev_name] + ## call "mkfs" + try: + cont = cryptobox.core.container.CryptoBoxContainer(dev_name, self.cbox) + cont.create(cryptobox.core.container.CONTAINERTYPES["plain"], fs_type=fs_type) + except (CBInvalidType, CBCreateError, CBVolumeIsActive), err_msg: + self.cbox.log.warn(err_msg) + return False + ## remove unused volume entry + if prev_name: + del self.cbox.prefs.volumes_db[prev_name[0]] + return True + + + def __old_format_one_partition(self, dev_name, fs_type): """Format a single partition """ ## first: retrieve UUID - it can be removed from the database afterwards diff --git a/plugins/shutdown/shutdown.py b/plugins/shutdown/shutdown.py index 7a3cebe..b970185 100644 --- a/plugins/shutdown/shutdown.py +++ b/plugins/shutdown/shutdown.py @@ -22,7 +22,7 @@ __revision__ = "$Id" import cryptobox.plugins.base -REDIRECT_DELAY = 180 +REDIRECT_DELAY = 120 class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): @@ -44,7 +44,7 @@ class shutdown(cryptobox.plugins.base.CryptoBoxPlugin): elif type == "reboot": if self.__do_shutdown("reboot"): self.hdf["Data.Success"] = "Plugins.shutdown.Reboot" - self.hdf["Data.Redirect.URL"] = "" + self.hdf["Data.Redirect.URL"] = "/" self.hdf["Data.Redirect.Delay"] = REDIRECT_DELAY return "progress_reboot" else: diff --git a/plugins/volume_format_fs/language.hdf b/plugins/volume_format_fs/language.hdf index 43f6f65..16cb50d 100644 --- a/plugins/volume_format_fs/language.hdf +++ b/plugins/volume_format_fs/language.hdf @@ -31,8 +31,8 @@ AdviceMessage { SuccessMessage { FormatSuccess { - Title = Formatting successful - Text = The selected filesystem was successfully formatted. + Title = Formatting is running + Text = The selected filesystem is being formatted in the background. This may take some time (depending on the size of your disk). } } diff --git a/plugins/volume_format_fs/volume_format_fs.py b/plugins/volume_format_fs/volume_format_fs.py index 4f3ffbd..433ec7b 100644 --- a/plugins/volume_format_fs/volume_format_fs.py +++ b/plugins/volume_format_fs/volume_format_fs.py @@ -40,7 +40,7 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): def do_action(self, store=None, fs_type="windows", container_type="luks", crypto_password=None, crypto_password2=None, confirm=None): if not fs_type in FSTYPES.keys(): - self.cbox.info + self.cbox.log.info("invalid filesystem type choosen: %s" % str(fs_type)) return "format_volume" self.hdf[self.hdf_prefix + "fs_type"] = fs_type self.hdf[self.hdf_prefix + "container_type"] = container_type @@ -54,10 +54,10 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): if container_type == "luks": return "volume_format_luks" elif container_type == "plain": - return self.__format_plain(fs_type) + return self.__format_plain(FSTYPES[fs_type]) elif store == "step2": if container_type == "luks": - return self.__format_luks(fs_type, crypto_password, crypto_password2) + return self.__format_luks(FSTYPES[fs_type], crypto_password, crypto_password2) else: self.cbox.log.info("invalid input value for 'container_type': %s" % container_type) return "volume_format" @@ -72,36 +72,10 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): return "no status" - def __format_plain(self, fsType): + def __format_plain(self, fs_type): try: container = self.cbox.get_container(self.device) - container.create(cbx_container.CONTAINERTYPES["plain"]) - except CBVolumeIsActive: - self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" - self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) - return None - except CBContainerError, errMsg: - self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed" - self.cbox.log.warn("initialization of device '%s' failed" % self.device) - self.cbox.log.warn("reason: %s" % errMsg) - return "volume_format" - else: - self.cbox.log.info("successfully initialized device '%s'" % self.device) - return None - - - def __format_luks(self, fsType, pw, pw2): - if not pw: - self.hdf["Data.Warning"] = "EmptyPassword" - self.cbox.log.warn("no crypto password was supplied for initialization of device '%s'" % self.device) - return "volume_format" - if pw != pw2: - self.hdf["Data.Warning"] = "DifferentPasswords" - self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device) - return "volume_format" - container = self.cbox.get_container(self.device) - try: - container.create(cbx_container.CONTAINERTYPES["luks"], pw) + container.create(cbx_container.CONTAINERTYPES["plain"], fs_type=fs_type) except CBVolumeIsActive: self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) @@ -114,5 +88,33 @@ class volume_format_fs(cryptobox.plugins.base.CryptoBoxPlugin): else: self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" self.cbox.log.info("successfully initialized device '%s'" % self.device) - return None + return { "plugin":"disks", "values":{} } + + + def __format_luks(self, fs_type, pw, pw2): + if not pw: + self.hdf["Data.Warning"] = "EmptyPassword" + self.cbox.log.warn("no crypto password was supplied for initialization of device '%s'" % self.device) + return "volume_format" + if pw != pw2: + self.hdf["Data.Warning"] = "DifferentPasswords" + self.cbox.log.warn("the crypto password was not repeated correctly for initialization of device '%s'" % self.device) + return "volume_format" + container = self.cbox.get_container(self.device) + try: + container.create(cbx_container.CONTAINERTYPES["luks"], + fs_type=fs_type, password=pw) + except CBVolumeIsActive: + self.hdf["Data.Warning"] = "VolumeMayNotBeMounted" + self.cbox.log.info("initialization is not possible as long as the device (%s) is mounted" % self.device) + return None + except CBContainerError, errMsg: + self.hdf["Data.Warning"] = "Plugins.volume_format_fs.FormatFailed" + self.cbox.log.warn("initialization of device '%s' failed" % self.device) + self.cbox.log.warn("reason: %s" % errMsg) + return "volume_format" + else: + self.hdf["Data.Success"] = "Plugins.volume_format_fs.FormatSuccess" + self.cbox.log.info("successfully initialized device '%s'" % self.device) + return { "plugin":"disks", "values":{} } diff --git a/src/cryptobox/core/container.py b/src/cryptobox/core/container.py index c2d4d6b..58cb8d8 100644 --- a/src/cryptobox/core/container.py +++ b/src/cryptobox/core/container.py @@ -26,6 +26,7 @@ __revision__ = "$Id" import subprocess import os import re +import time from cryptobox.core.exceptions import * @@ -36,6 +37,10 @@ CONTAINERTYPES = { "swap":3, } +FSTYPES = { + "plain":["ext3", "ext2", "vfat", "reiserfs"], + "swap":["swap"]} + ## we use this marker to make sure, that we do not remove a non-cryptobox directory ## below the mount directory @@ -46,10 +51,6 @@ class CryptoBoxContainer: """Manage a container of the CryptoBox """ - __fsTypes = { - "plain":["ext3", "ext2", "vfat", "reiserfs"], - "swap":["swap"]} - __dmDir = "/dev/mapper" @@ -101,10 +102,10 @@ class CryptoBoxContainer: raise CBVolumeIsActive("the container must not be active during renaming") if not re.search(r'^[a-zA-Z0-9_\.\- ]+$', new_name): raise CBInvalidName("the supplied new name contains illegal characters") - ## check for another partitions with the same name + ## check for another partition with the same name if self.cbox.get_container_list(filter_name=new_name): raise CBNameIsInUse("the supplied new name is already in use for anonther partition") - ## maybe there a is an entry in the volumes database (but the partition is not active + ## maybe there a is an entry in the volumes database (but the partition is not active) try: ## remove possibly existing inactive database item del self.cbox.prefs.volumes_db[new_name] @@ -182,23 +183,64 @@ class CryptoBoxContainer: self.umount = self.__umount_plain - def create(self, cont_type, password=None): + def create(self, cont_type, password=None, fs_type="ext3"): """Format a container. Also set a password for encrypted container. """ + if not fs_type in FSTYPES["plain"]: + raise CBInvalidType("invalid filesystem type supplied: %s" % str(fs_type)) old_name = self.get_name() if cont_type == CONTAINERTYPES["luks"]: - self.__create_luks(password) + self.__create_luks(password, fs_type) elif cont_type == CONTAINERTYPES["plain"]: - self.__create_plain() + self.__create_plain(fs_type) else: raise CBInvalidType("invalid container type (%d) supplied" % (cont_type, )) ## no exception was raised during creation -> we can continue ## reset the properties (encryption state, ...) of the device self.reset_object() ## restore the old name (must be after reset_object) - self.set_name(old_name) + try: + self.set_name(old_name) + except CBNameIsInUse: + ## failure is okay + pass + + + def set_busy(self, new_state, time_limit=300): + """Set the current busy state. + + The timelimit is specified in seconds. + """ + if new_state: + self.cbox.busy_devices[self.device] = int(time.time() + time_limit) + else: + try: + if self.cbox.busy_devices[self.device]: + del self.cbox.busy_devices[self.device] + except KeyError: + pass + + + def is_busy(self): + """Check the busy state of the container. + """ + if not self.cbox.busy_devices.has_key(self.device): + self.cbox.log.debug("no 'busy' attribute for '%s'" % self.get_name()) + return False + ## invalid value - can happen after saving and loading the database + if not isinstance(self.cbox.busy_devices[self.device], int): + self.cbox.log.debug("invalid 'busy' attribute for '%s'" % self.get_name()) + del db_entry["busy"] + return False + if time.time() >= self.cbox.busy_devices[self.device]: + self.cbox.log.debug("expired 'busy' attribute for '%s'" % self.get_name()) + del db_entry["busy"] + return False + ## lock is still active + self.cbox.log.debug("active 'busy' attribute for '%s'" % self.get_name()) + return True def change_password(self, oldpw, newpw): @@ -362,9 +404,9 @@ class CryptoBoxContainer: if self.__is_luks_partition(): return CONTAINERTYPES["luks"] type_of_partition = self.__get_type_id_of_partition() - if type_of_partition in self.__fsTypes["plain"]: + if type_of_partition in FSTYPES["plain"]: return CONTAINERTYPES["plain"] - if type_of_partition in self.__fsTypes["swap"]: + if type_of_partition in FSTYPES["swap"]: return CONTAINERTYPES["swap"] return CONTAINERTYPES["unused"] @@ -372,29 +414,28 @@ class CryptoBoxContainer: def __get_type_id_of_partition(self): "returns the type of the partition (see 'man blkid')" devnull = None - try: - devnull = open(os.devnull, "w") - except IOError: - self.cbox.log.warn("Could not open %s" % (os.devnull, )) proc = subprocess.Popen( - shell=False, - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - args=[self.cbox.prefs["Programs"]["blkid"], + shell = False, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ self.cbox.prefs["Programs"]["blkid"], "-s", "TYPE", "-o", "value", "-c", os.devnull, "-w", os.devnull, - self.device]) - proc.wait() - output = proc.stdout.read().strip() - if proc.returncode != 0: - self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \ - (proc.stderr.read().strip(), )) + self.device ]) + (stdout, stder) = proc.communicate() + if proc.returncode == 0: + ## we found a uuid + return stdout.strip() + elif proc.returncode == 2: + ## failed to find the attribute - no problem + return None + else: + ## something strange happened + self.cbox.log.warn("retrieving of partition type via 'blkid' failed: %s" % \ + (stderr.strip(), )) return None - devnull.close() - return output def __is_luks_partition(self): @@ -596,50 +637,63 @@ class CryptoBoxContainer: self.cbox.send_event_notification("postumount", self.__get_event_args()) - def __create_plain(self): + def __create_plain(self, fs_type="ext3"): "make a plaintext partition" + import threading if self.is_mounted(): - raise CBVolumeIsActive("deactivate the partition before filesystem initialization") - devnull = None - try: - devnull = open(os.devnull, "w") - except IOError: - self.cbox.log.warn("Could not open %s" % (os.devnull, )) - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = devnull, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["mkfs-data"], - self.device]) - proc.wait() - if proc.returncode != 0: - err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.error(err_msg) - raise CBCreateError(err_msg) - devnull.close() + raise CBVolumeIsActive( + "deactivate the partition before filesystem initialization") + def format(): + import os + old_name = self.get_name() + self.set_busy(True, 600) + self.cbox.log.debug("Turn the busy flag on: %s" % self.device) + ## give the main thread a chance to continue + child_pid = os.fork() + if child_pid == 0: + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["nice"], + self.cbox.prefs["Programs"]["mkfs"], + "-t", fs_type, self.device]) + (stdout, sterr) = proc.communicate() + ## for to allow error detection + if proc.returncode == 0: + time.sleep(5) + ## skip cleanup stuff (as common for sys.exit) + os._exit(0) + else: + os.waitpid(child_pid, 0) + self.set_name(old_name) + self.set_busy(False) + self.cbox.log.debug("Turn the busy flag off: %s" % self.device) + bg_task = threading.Thread(target=format) + bg_task.start() + time.sleep(3) + ## if the thread exited very fast, then it failed + if not bg_task.isAlive(): + raise CBCreateError("Failed to initilize device: %s" % self.device) - def __create_luks(self, password): + def __create_luks(self, password, fs_type="ext3"): """Create a luks partition. """ + import threading if not password: raise CBInvalidPassword("no password supplied for new luks mapping") if self.is_mounted(): raise CBVolumeIsActive("deactivate the partition before filesystem initialization") - devnull = None - try: - devnull = open(os.devnull, "w") - except IOError: - self.cbox.log.warn("Could not open %s" % (os.devnull, )) ## remove any potential open luks mapping self.__umount_luks() ## create the luks header proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, - stdout = devnull, + stdout = subprocess.PIPE, stderr = subprocess.PIPE, args = [ self.cbox.prefs["Programs"]["super"], @@ -660,7 +714,7 @@ class CryptoBoxContainer: proc = subprocess.Popen( shell = False, stdin = subprocess.PIPE, - stdout = devnull, + stdout = subprocess.PIPE, stderr = subprocess.PIPE, args = [ self.cbox.prefs["Programs"]["super"], @@ -676,24 +730,44 @@ class CryptoBoxContainer: err_msg = "Could not open the new luks mapping: %s" % (errout.strip(), ) self.cbox.log.error(err_msg) raise CBCreateError(err_msg) - ## make the filesystem - proc = subprocess.Popen( - shell = False, - stdin = None, - stdout = devnull, - stderr = subprocess.PIPE, - args = [ - self.cbox.prefs["Programs"]["mkfs-data"], - os.path.join(self.__dmDir, self.name)]) - proc.wait() - ## remove the mapping - for every exit status - self.__umount_luks() - if proc.returncode != 0: - err_msg = "Could not create the filesystem: %s" % (proc.stderr.read().strip(), ) - self.cbox.log.error(err_msg) - ## remove the luks mapping - raise CBCreateError(err_msg) - devnull.close() + def format_luks(): + import os + old_name = self.get_name() + self.set_busy(True, 600) + self.cbox.log.debug("Turn the busy flag on: %s" % self.device) + child_pid = os.fork() + if child_pid == 0: + ## make the filesystem + proc = subprocess.Popen( + shell = False, + stdin = None, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + args = [ + self.cbox.prefs["Programs"]["nice"], + self.cbox.prefs["Programs"]["mkfs"], + "-t", fs_type, + os.path.join(self.__dmDir, self.name)]) + (stdou, stderr) = proc.communicate() + ## wait to allow error detection + if proc.returncode == 0: + time.sleep(5) + ## skip cleanup stuff (as common for sys.exit) + os._exit(0) + else: + os.waitpid(child_pid, 0) + self.set_name(old_name) + self.set_busy(False) + self.cbox.log.debug("Turn the busy flag off: %s" % self.device) + ## remove the mapping - for every exit status + self.__umount_luks() + bg_task = threading.Thread(target=format_luks) + bg_task.setDaemon(True) + bg_task.start() + time.sleep(3) + ## if the thread exited very fast, then it failed + if not bg_task.isAlive(): + raise CBCreateError("Failed to initilize device: %s" % self.device) def __clean_mount_dirs(self): diff --git a/src/cryptobox/core/main.py b/src/cryptobox/core/main.py index e0baf2a..c4f5b52 100644 --- a/src/cryptobox/core/main.py +++ b/src/cryptobox/core/main.py @@ -46,6 +46,7 @@ class CryptoBox: self.prefs = cbxSettings.CryptoBoxSettings(config_file) self.__run_tests() self.__containers = [] + self.busy_devices = {} self.reread_container_list() diff --git a/src/cryptobox/core/settings.py b/src/cryptobox/core/settings.py index 762d525..a939c90 100644 --- a/src/cryptobox/core/settings.py +++ b/src/cryptobox/core/settings.py @@ -455,7 +455,8 @@ Languages = list(min=1,default=list("en")) [Programs] cryptsetup = fileExecutable(default="/sbin/cryptsetup") -mkfs-data = fileExecutable(default="/sbin/mkfs.ext3") +mkfs = fileExecutable(default="/sbin/mkfs") +nice = fileExecutable(default="/usr/bin/nice") blkid = fileExecutable(default="/sbin/blkid") blockdev = fileExecutable(default="/sbin/blockdev") mount = fileExecutable(default="/bin/mount") diff --git a/src/cryptobox/plugins/base.py b/src/cryptobox/plugins/base.py index 5d50965..4aa7499 100644 --- a/src/cryptobox/plugins/base.py +++ b/src/cryptobox/plugins/base.py @@ -191,6 +191,14 @@ class CryptoBoxPlugin: return self.plugin_visibility + def reset(self): + """Reinitialize the plugin. + + This function should be called before every run + """ + self.hdf = {} + + def get_test_class(self): """Return the unittest class of the feature. """ diff --git a/src/cryptobox/web/dataset.py b/src/cryptobox/web/dataset.py index c04cf17..2abae77 100644 --- a/src/cryptobox/web/dataset.py +++ b/src/cryptobox/web/dataset.py @@ -103,11 +103,13 @@ class WebInterfaceDataset(dict): is_plain = (container.get_type() == \ cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 is_mounted = container.is_mounted() and 1 or 0 + is_busy = container.is_busy() and 1 or 0 self["Data.CurrentDisk.device"] = container.get_device() self["Data.CurrentDisk.name"] = container.get_name() self["Data.CurrentDisk.encryption"] = is_encrypted self["Data.CurrentDisk.plaintext"] = is_plain self["Data.CurrentDisk.active"] = is_mounted + self["Data.CurrentDisk.busy"] = is_busy self["Data.CurrentDisk.size"] = cbxTools.get_blockdevice_size_humanly( container.get_device()) if is_mounted: @@ -133,10 +135,12 @@ class WebInterfaceDataset(dict): is_plain = (container.get_type() == \ cbxContainer.CONTAINERTYPES["plain"]) and 1 or 0 is_mounted = container.is_mounted() and 1 or 0 + is_busy = container.is_busy() and 1 or 0 self["Data.Disks.%d.device" % avail_counter] = container.get_device() self["Data.Disks.%d.name" % avail_counter] = container.get_name() self["Data.Disks.%d.encryption" % avail_counter] = is_encrypted self["Data.Disks.%d.plaintext" % avail_counter] = is_plain + self["Data.Disks.%d.busy" % avail_counter] = is_busy self["Data.Disks.%d.active" % avail_counter] = is_mounted self["Data.Disks.%d.size" % avail_counter] = \ cbxTools.get_blockdevice_size_humanly(container.get_device()) diff --git a/src/cryptobox/web/sites.py b/src/cryptobox/web/sites.py index f9eb04b..bb41cea 100644 --- a/src/cryptobox/web/sites.py +++ b/src/cryptobox/web/sites.py @@ -252,28 +252,20 @@ class WebInterfaceSites: """ returns a function that is suitable for handling a cherrypy page request """ - def handler(self, **args): + def handler(self, weblang="", device=None, redirect=None, message_keep=None, **args): """this function handles a cherrypy page request """ + plugin.reset() self.__reset_dataset() self.__check_environment() - ## set web interface language - try: - self.__set_web_lang(args["weblang"]) - del args["weblang"] - except KeyError: - self.__set_web_lang("") + self.__set_web_lang(weblang) ## we always read the "device" setting - otherwise volume-plugin ## links would not work easily ## (see "volume_props" linking to "volume_format_fs") ## it will get ignored for non-volume plugins - try: - plugin.device = None - if self.__set_device(args["device"]): - plugin.device = args["device"] - del args["device"] - except KeyError: - plugin.device = None + plugin.device = None + if device and self.__set_device(device): + plugin.device = device ## check the device argument of volume plugins if "volume" in plugin.plugin_capabilities: ## initialize the dataset of the selected device if necessary @@ -285,22 +277,18 @@ class WebInterfaceSites: ## check if there is a "redirect" setting - this will override ## the return value of the do_action function ## (e.g. useful for umount-before-format) - try: - if args["redirect"]: - override_next_template = { "plugin":args["redirect"] } - if "volume" in plugin.plugin_capabilities: - override_next_template["values"] = {"device":plugin.device} - del args["redirect"] - except KeyError: - override_next_template = None + override_next_template = None + if redirect: + override_next_template = { "plugin": redirect } + if "volume" in plugin.plugin_capabilities: + override_next_template["values"] = {"device":plugin.device} ## check for information to be kept after the last call - try: - keep_values = args["message_keep"] - del args["message_keep"] - for key, value in keep_values["dataset"].items(): + if message_keep: + for (key, value) in message_keep["dataset"].items(): self.__dataset[key] = value - except KeyError: - keep_values = None + ## check if the device is busy + if plugin.device and self.cbox.get_container(plugin.device).is_busy(): + return self.__render("volume_busy") ## call the plugin handler next_template = plugin.do_action(**args) ## for 'volume' plugins: reread the dataset of the current disk @@ -330,7 +318,7 @@ class WebInterfaceSites: and self.__plugin_manager.get_plugin(next_template["plugin"]): value_dict = dict(next_template["values"]) ## force the current weblang attribute - otherwise it gets lost - value_dict["weblang"] = self.__dataset["Settings.Language"] + value_dict["weblang"] = self.lang_order[0] ## check for warnings/success messages, that should be kept if "Data.Success" in plugin.hdf.keys() \ or "Data.Warning" in plugin.hdf.keys(): @@ -354,7 +342,6 @@ class WebInterfaceSites: @cherrypy.expose - @__request_auth def test(self, weblang=""): """test authentication - this function may be safely removed """ @@ -509,7 +496,10 @@ class WebInterfaceSites: if key == "LINK": return try: - node.setValue("", translator.ugettext(node.value())) + #TODO: we should use unicode - or not? + #node.setValue("", translator.ugettext(node.value())) + ## quite obscure: ugettext can handle None - gettext breaks instead + node.setValue("", str(translator.gettext(node.value()))) except UnicodeEncodeError, err_msg: self.cbox.log.info( "Failed unicode encoding for gettext: %s - %s" \ diff --git a/templates/language.hdf b/templates/language.hdf index cabcbfb..8389634 100644 --- a/templates/language.hdf +++ b/templates/language.hdf @@ -22,6 +22,17 @@ Button { } +AdviceMessage { + + VolumeIsBusy { + Title = Disk is busy + Text = This disk is currently busy. Please wait for a moment. + Link.Rel = / + Link.Text = Show all disks + } +} + + WarningMessage { AccessDenied { diff --git a/templates/macros.cs b/templates/macros.cs index 37bf2bc..ea355a2 100644 --- a/templates/macros.cs +++ b/templates/macros.cs @@ -119,15 +119,31 @@ def:show_volume_icon(volume) ?>icon: volume + + + + + + + diff --git a/www-data/volume_active_crypto_busy.gif b/www-data/volume_active_crypto_busy.gif new file mode 100644 index 0000000..488a84f Binary files /dev/null and b/www-data/volume_active_crypto_busy.gif differ diff --git a/www-data/volume_active_crypto_busy.png b/www-data/volume_active_crypto_busy.png new file mode 100644 index 0000000..ed45257 Binary files /dev/null and b/www-data/volume_active_crypto_busy.png differ diff --git a/www-data/volume_active_plain_busy.gif b/www-data/volume_active_plain_busy.gif new file mode 100644 index 0000000..1c617fc Binary files /dev/null and b/www-data/volume_active_plain_busy.gif differ diff --git a/www-data/volume_active_plain_busy.png b/www-data/volume_active_plain_busy.png new file mode 100644 index 0000000..92fc87e Binary files /dev/null and b/www-data/volume_active_plain_busy.png differ diff --git a/www-data/volume_passive_crypto_busy.gif b/www-data/volume_passive_crypto_busy.gif new file mode 100644 index 0000000..c1b8e15 Binary files /dev/null and b/www-data/volume_passive_crypto_busy.gif differ diff --git a/www-data/volume_passive_crypto_busy.png b/www-data/volume_passive_crypto_busy.png new file mode 100644 index 0000000..2c5255a Binary files /dev/null and b/www-data/volume_passive_crypto_busy.png differ diff --git a/www-data/volume_passive_plain_busy.gif b/www-data/volume_passive_plain_busy.gif new file mode 100644 index 0000000..6fc4be7 Binary files /dev/null and b/www-data/volume_passive_plain_busy.gif differ diff --git a/www-data/volume_passive_plain_busy.png b/www-data/volume_passive_plain_busy.png new file mode 100644 index 0000000..d251040 Binary files /dev/null and b/www-data/volume_passive_plain_busy.png differ