changed the interface of CryptoBoxRootActions: "allowedProgs" are now prefixed with the parameter "program"
added allowedProg "pvdisplay" to CryptoBoxRootActions to allow LVM detection improved blockdevice handling: caching and detection of lvm, luks and raid
This commit is contained in:
parent
53e09ff825
commit
b72310097c
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
# Copyright 2006 sense.lab e.V.
|
# Copyright 2007 sense.lab e.V.
|
||||||
#
|
#
|
||||||
# This file is part of the CryptoBox.
|
# This file is part of the CryptoBox.
|
||||||
#
|
#
|
||||||
|
@ -20,13 +20,23 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
"""module for executing the programs, that need root privileges
|
"""module for executing some programs or scripts that need root privileges
|
||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
- TODO
|
check
|
||||||
|
- return exitcode zero if basic checks succeeded
|
||||||
|
|
||||||
this script will always return with an exitcode 0 (true),
|
program PROGRAM_NAME [ARGS]
|
||||||
if "check" is the only argument
|
- call the program (must be defined in "allowedProgs" below)
|
||||||
|
|
||||||
|
event EVENT_SCRIPT [ARGS]
|
||||||
|
- call an event script
|
||||||
|
|
||||||
|
plugin PLUGIN_NAME [ARGS]
|
||||||
|
- call a root_action script of a plugin
|
||||||
|
|
||||||
|
For more detailed information take a look at the manpage:
|
||||||
|
"man CryptoBoxRootActions"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__revision__ = "$Id"
|
__revision__ = "$Id"
|
||||||
|
@ -44,6 +54,7 @@ allowedProgs = {
|
||||||
"mount": "/bin/mount",
|
"mount": "/bin/mount",
|
||||||
"umount": "/bin/umount",
|
"umount": "/bin/umount",
|
||||||
"blkid": "/sbin/blkid",
|
"blkid": "/sbin/blkid",
|
||||||
|
"pvdisplay": "/sbin/pvdisplay",
|
||||||
}
|
}
|
||||||
|
|
||||||
## this line is necessary for running unittests or playing around with a local
|
## this line is necessary for running unittests or playing around with a local
|
||||||
|
@ -387,6 +398,19 @@ def run_umount(args):
|
||||||
return proc.returncode == 0
|
return proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def run_pvdisplay(args):
|
||||||
|
"""execute pvdisplay to check for physical LVM devices
|
||||||
|
"""
|
||||||
|
if len(args) > 0:
|
||||||
|
raise "WrongArguments", "no arguments may be supplied for 'pvdisplay'"
|
||||||
|
## call pvdisplay with the parameter "--colon"
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
args = [ allowedProgs["pvdisplay"], "--colon" ])
|
||||||
|
proc.wait()
|
||||||
|
return proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def getCallingUserInfo():
|
def getCallingUserInfo():
|
||||||
"""return information about the user that was calling this program via "super"
|
"""return information about the user that was calling this program via "super"
|
||||||
|
|
||||||
|
@ -456,6 +480,7 @@ if __name__ == "__main__":
|
||||||
# exit silently
|
# exit silently
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
## call a plugin root_action script
|
||||||
if args[0].lower() == "plugin":
|
if args[0].lower() == "plugin":
|
||||||
del args[0]
|
del args[0]
|
||||||
try:
|
try:
|
||||||
|
@ -468,6 +493,7 @@ if __name__ == "__main__":
|
||||||
else:
|
else:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
## call an event script
|
||||||
if args[0].lower() == "event":
|
if args[0].lower() == "event":
|
||||||
del args[0]
|
del args[0]
|
||||||
try:
|
try:
|
||||||
|
@ -480,11 +506,13 @@ if __name__ == "__main__":
|
||||||
else:
|
else:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# check parameters count
|
## call one of the allowed programs
|
||||||
if len(args) < 2:
|
if args[0].lower() == "program":
|
||||||
sys.stderr.write("Not enough arguments supplied (%s)!\n" % " ".join(args))
|
del args[0]
|
||||||
sys.exit(100)
|
|
||||||
|
|
||||||
|
if len(args) < 1:
|
||||||
|
sys.stderr.write("No program specified for execution\n")
|
||||||
|
sys.exit(100)
|
||||||
progRequest = args[0]
|
progRequest = args[0]
|
||||||
del args[0]
|
del args[0]
|
||||||
|
|
||||||
|
@ -495,6 +523,7 @@ if __name__ == "__main__":
|
||||||
if progRequest == "cryptsetup": runner = run_cryptsetup
|
if progRequest == "cryptsetup": runner = run_cryptsetup
|
||||||
elif progRequest == "mount": runner = run_mount
|
elif progRequest == "mount": runner = run_mount
|
||||||
elif progRequest == "umount": runner = run_umount
|
elif progRequest == "umount": runner = run_umount
|
||||||
|
elif progRequest == "pvdisplay": runner = run_pvdisplay
|
||||||
else:
|
else:
|
||||||
sys.stderr.write("The interface for this program (%s) is " \
|
sys.stderr.write("The interface for this program (%s) is " \
|
||||||
+ "not yet implemented!\n" % progRequest)
|
+ "not yet implemented!\n" % progRequest)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.TH CryptoBoxRootActions 8 "March 02007" "CryptoBox" "CryptoBox-Server manual"
|
.TH CryptoBoxRootActions 8 "August 02007" "CryptoBox" "CryptoBox-Server manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
CryptoBoxRootActions \- The CryptoBoxWebserver calls this script in order to
|
CryptoBoxRootActions \- The CryptoBoxWebserver calls this script in order to
|
||||||
execute various programs which require root privileges.
|
execute various programs which require root privileges.
|
||||||
|
@ -13,7 +13,7 @@ plugin \fIFEATURE_SCRIPT\fR [\fIARGS\fR]
|
||||||
hook \fIEVENT_SCRIPT\fR [\fIARGS\fR]
|
hook \fIEVENT_SCRIPT\fR [\fIARGS\fR]
|
||||||
.br
|
.br
|
||||||
.B CryptoBoxRootActions
|
.B CryptoBoxRootActions
|
||||||
\fIPROG\fR [\fIARGS\fR]
|
program \fIPROG\fR [\fIARGS\fR]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
CryptoBoxRootActions is a script that is called by the
|
CryptoBoxRootActions is a script that is called by the
|
||||||
\fBCryptoBox\fR-Server to execute programs which require root privileges. You
|
\fBCryptoBox\fR-Server to execute programs which require root privileges. You
|
||||||
|
@ -30,7 +30,8 @@ CryptoBoxRootActions /usr/sbin/CryptoBoxRootActions cryptobox
|
||||||
.PP
|
.PP
|
||||||
We assume that the CryptoBoxRootActions script is located at
|
We assume that the CryptoBoxRootActions script is located at
|
||||||
\fI/usr/sbin/CryptoBoxRootActions\fR. Furthermore the user running the
|
\fI/usr/sbin/CryptoBoxRootActions\fR. Furthermore the user running the
|
||||||
CryptoBox-Server is assumed to be \fIcryptobox\fR.
|
CryptoBox-Server is assumed to be \fIcryptobox\fR. Otherwise you must change the
|
||||||
|
above line accordingly.
|
||||||
.SH CONFIGURATION CHECK
|
.SH CONFIGURATION CHECK
|
||||||
Call the CryptoBoxRootActions script with the argument \fIcheck\fR to test if
|
Call the CryptoBoxRootActions script with the argument \fIcheck\fR to test if
|
||||||
\fBsuper\fR is configured properly. Just type the following:
|
\fBsuper\fR is configured properly. Just type the following:
|
||||||
|
|
|
@ -25,26 +25,44 @@ These classes detect and filter available blockdevices.
|
||||||
__revision__ = "$Id$"
|
__revision__ = "$Id$"
|
||||||
|
|
||||||
|
|
||||||
"""
|
#TODO: use logger to report interesting behaviour
|
||||||
TODO:
|
|
||||||
- implement some caching
|
|
||||||
- find the devnodes for each device (e.g. /dev/hda)
|
|
||||||
- detect luks devices
|
|
||||||
- detect LVM
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import cryptobox.core.settings
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_SYSBLOCK_DIR = '/sys/block'
|
||||||
|
DEFAULT_DEVNODE_DIR = '/dev'
|
||||||
|
MINIMUM_STORAGE_SIZE = 20
|
||||||
|
MAJOR_DEVNUM_RAM = 1
|
||||||
|
MAJOR_DEVNUM_LOOP = 7
|
||||||
|
MAJOR_DEVNUM_MD_RAID = 9
|
||||||
|
|
||||||
|
USE_CACHE = True
|
||||||
|
CACHE_EXPIRE_SECONDS = 60
|
||||||
|
|
||||||
|
#TODO: remove this after profiling
|
||||||
|
IS_VISIBLE = True
|
||||||
|
|
||||||
|
## caching is quite important for the following implementation
|
||||||
|
CACHED_VALUES = {}
|
||||||
|
|
||||||
class Blockdevices:
|
class Blockdevices:
|
||||||
|
"""handle all blockdevices of this system
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, sysblock_dir='/sys/block', devnode_dir='/dev'):
|
def __init__(self,
|
||||||
|
sysblock_dir=DEFAULT_SYSBLOCK_DIR,
|
||||||
|
devnode_dir=DEFAULT_DEVNODE_DIR):
|
||||||
self.sysblock_dir = sysblock_dir
|
self.sysblock_dir = sysblock_dir
|
||||||
self.devnode_dir = devnode_dir
|
self.devnode_dir = devnode_dir
|
||||||
self.devices = []
|
self.devices = []
|
||||||
for devdir in find_blockdevices(self.sysblock_dir):
|
for devdir in find_blockdevices(self.sysblock_dir):
|
||||||
blockdevice = Blockdevice(devdir, self.devnode_dir)
|
blockdevice = get_blockdevice(devdir,
|
||||||
if not blockdevice is None:
|
self.sysblock_dir, self.devnode_dir)
|
||||||
|
if (not blockdevice is None) and blockdevice.is_valid():
|
||||||
self.devices.append(blockdevice)
|
self.devices.append(blockdevice)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,60 +73,197 @@ class Blockdevices:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Blockdevice:
|
class Blockdevice:
|
||||||
|
|
||||||
def __init__(self, dev, devnode_dir='/dev', sysblock_dir='/sys/block'):
|
def __init__(self, dev,
|
||||||
|
sysblock_dir=DEFAULT_SYSBLOCK_DIR,
|
||||||
|
devnode_dir=DEFAULT_DEVNODE_DIR):
|
||||||
|
"""initialize the blockdevice
|
||||||
|
"""
|
||||||
|
self.devdir = dev
|
||||||
self.devnode_dir = devnode_dir
|
self.devnode_dir = devnode_dir
|
||||||
self.sysblock_dir = sysblock_dir
|
self.sysblock_dir = sysblock_dir
|
||||||
if os.path.isabs(dev):
|
|
||||||
self.devdir = dev
|
|
||||||
else:
|
|
||||||
self.devdir = self.__find_relative_device(dev)
|
|
||||||
if self.devdir is None:
|
|
||||||
self = None
|
|
||||||
return
|
|
||||||
self.name = os.path.basename(self.devdir)
|
self.name = os.path.basename(self.devdir)
|
||||||
self.devnum = self.__get_major_minor()
|
self.devnum = self.__get_major_minor()
|
||||||
## check valid devnum
|
|
||||||
try:
|
|
||||||
major, minor = self.devnum
|
|
||||||
except TypeError:
|
|
||||||
self = None
|
|
||||||
return
|
|
||||||
self.size = self.__get_size()
|
self.size = self.__get_size()
|
||||||
self.range = self.__get_device_range()
|
self.range = self.__get_device_range()
|
||||||
self.slaves = self.__get_dev_related("slaves")
|
self.slaves = self.__get_dev_related("slaves")
|
||||||
self.holders = self.__get_dev_related("holders")
|
self.holders = self.__get_dev_related("holders")
|
||||||
self.children = self.__get_children()
|
self.children = self.__get_children()
|
||||||
|
self.devnodes = self.__get_device_nodes()
|
||||||
|
|
||||||
|
|
||||||
def isstorage(self):
|
def is_valid(self):
|
||||||
if self.range > 1:
|
""" check if the device is usable and valid
|
||||||
## partitionable blockdevice
|
"""
|
||||||
|
if not self.devnodes:
|
||||||
return False
|
return False
|
||||||
if self.size < 20:
|
## check valid devnum
|
||||||
## extended partition, unused loop device
|
try:
|
||||||
return False
|
major, minor = self.devnum
|
||||||
if self.devnum[0] == 1:
|
if (major == 0) and (minor == 0):
|
||||||
## ram device
|
return False
|
||||||
return False
|
## ram devices are ignored
|
||||||
if self.children:
|
if major == MAJOR_DEVNUM_RAM:
|
||||||
## a parent blockdevice
|
return False
|
||||||
|
## loop devices are ignored
|
||||||
|
if major == MAJOR_DEVNUM_LOOP:
|
||||||
|
return False
|
||||||
|
except TypeError:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def ispartitionable(self):
|
def is_storage(self):
|
||||||
|
"""return if this device can be used as a storage
|
||||||
|
"""
|
||||||
|
## check the cache first
|
||||||
|
cache_link = ["blockdevice_info", self.name, "is_storage"]
|
||||||
|
cached = _get_cached_value(cache_link)
|
||||||
|
if not cached is None:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
if self.range > 1:
|
||||||
|
## partitionable blockdevice
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
if self.size < MINIMUM_STORAGE_SIZE:
|
||||||
|
## extended partition, unused loop device
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
if self.devnum[0] == MAJOR_DEVNUM_RAM:
|
||||||
|
## ram device
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
## are we the device mapper of a luks device?
|
||||||
|
for slave in self.slaves:
|
||||||
|
if get_blockdevice(slave, self.sysblock_dir,
|
||||||
|
self.devnode_dir).is_luks():
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
## if we are a luks device with exactly one child, then
|
||||||
|
## we are a storage
|
||||||
|
if (len(self.children) == 1) and self.is_luks():
|
||||||
|
_set_cached_value(cache_link, True)
|
||||||
|
return True
|
||||||
|
if self.children:
|
||||||
|
## a parent blockdevice
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
_set_cached_value(cache_link, True)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_partitionable(self):
|
||||||
|
"""is the device partitionable
|
||||||
|
"""
|
||||||
if self.range > 1:
|
if self.range > 1:
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __find_relative_device(self, devname):
|
def is_lvm_pv(self):
|
||||||
for devdir in find_blockdevices(self.sysblock_dir):
|
"""return if the device is a physical volume of a LVM
|
||||||
if os.path.basename(devdir) == devname:
|
"""
|
||||||
return devdir
|
## check the cache first
|
||||||
return None
|
cache_link = ["blockdevice_info", self.name, "is_lvm_pv"]
|
||||||
|
cached = _get_cached_value(cache_link)
|
||||||
|
if not cached is None:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
## is one of the devnodes of the device a physical volume?
|
||||||
|
for one_lvm_pv in find_lvm_pv():
|
||||||
|
if one_lvm_pv in self.devnodes:
|
||||||
|
_set_cached_value(cache_link, True)
|
||||||
|
return True
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_lvm_lv(self):
|
||||||
|
"""return if the device is a logical volume of a LVM
|
||||||
|
"""
|
||||||
|
## check the cache first
|
||||||
|
cache_link = ["blockdevice_info", self.name, "is_lvm_lv"]
|
||||||
|
cached = _get_cached_value(cache_link)
|
||||||
|
if not cached is None:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
## is one of the devnodes of the device a physical volume?
|
||||||
|
## logical LVM volumes always depend on their physical volumes
|
||||||
|
if not self.slaves:
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
## is one of the LVM physical volumes a device node of our slave(s)?
|
||||||
|
for one_lvm_pv in find_lvm_pv():
|
||||||
|
for one_slave in self.slaves:
|
||||||
|
if one_lvm_pv in get_blockdevice(one_slave,
|
||||||
|
self.sysblock_dir, self.devnode_dir).devnodes:
|
||||||
|
_set_cached_value(cache_link, True)
|
||||||
|
return True
|
||||||
|
_set_cached_value(cache_link, False)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_md_raid(self):
|
||||||
|
"""check if the device is the base of a md raid device
|
||||||
|
"""
|
||||||
|
## check the cache first
|
||||||
|
cache_link = ["blockdevice_info", self.name, "is_md_raid"]
|
||||||
|
cached = _get_cached_value(cache_link)
|
||||||
|
if not cached is None:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
if self.range > 1:
|
||||||
|
result = False
|
||||||
|
elif self.size < MINIMUM_STORAGE_SIZE:
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
for hold in self.holders:
|
||||||
|
if get_blockdevice(hold, self.sysblock_dir,
|
||||||
|
self.devnode_dir).devnum[0] == MAJOR_DEVNUM_MD_RAID:
|
||||||
|
result = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
result = False
|
||||||
|
|
||||||
|
## store result and return
|
||||||
|
_set_cached_value(cache_link, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def is_luks(self):
|
||||||
|
"""check if the device is a luks container
|
||||||
|
"""
|
||||||
|
## check the cache first
|
||||||
|
cache_link = ["blockdevice_info", self.name, "is_luks"]
|
||||||
|
cached = _get_cached_value(cache_link)
|
||||||
|
if not cached is None:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
if self.range > 1:
|
||||||
|
result = False
|
||||||
|
elif self.size < MINIMUM_STORAGE_SIZE:
|
||||||
|
result = False
|
||||||
|
elif self.is_lvm_pv():
|
||||||
|
result = False
|
||||||
|
elif self.is_md_raid():
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
## is the device a luks volume?
|
||||||
|
prefs = _load_preferences()
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdout = subprocess.PIPE,
|
||||||
|
stderr = subprocess.PIPE,
|
||||||
|
args = [ prefs["Programs"]["cryptsetup"],
|
||||||
|
"--batch-mode", "isLuks", self.devnodes[0]])
|
||||||
|
proc.wait()
|
||||||
|
result = proc.returncode == 0
|
||||||
|
## store result and return
|
||||||
|
_set_cached_value(cache_link, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def __get_dev_related(self, subdir):
|
def __get_dev_related(self, subdir):
|
||||||
|
@ -121,6 +276,8 @@ class Blockdevice:
|
||||||
|
|
||||||
|
|
||||||
def __get_size(self):
|
def __get_size(self):
|
||||||
|
"""return the size (in kB) of the blockdevice
|
||||||
|
"""
|
||||||
default = 0
|
default = 0
|
||||||
try:
|
try:
|
||||||
return int(file(os.path.join(self.devdir, 'size')).read())
|
return int(file(os.path.join(self.devdir, 'size')).read())
|
||||||
|
@ -132,12 +289,15 @@ class Blockdevice:
|
||||||
|
|
||||||
def __get_major_minor(self):
|
def __get_major_minor(self):
|
||||||
"""return the major and minor of the device"""
|
"""return the major and minor of the device"""
|
||||||
default = (0 ,0)
|
default = (0, 0)
|
||||||
try:
|
try:
|
||||||
content = file(os.path.join(self.devdir, "dev")).read()
|
content = file(os.path.join(self.devdir, "dev")).read()
|
||||||
except IOError:
|
except IOError:
|
||||||
return default
|
return default
|
||||||
major, minor = content.split(":", 2)
|
try:
|
||||||
|
major, minor = content.split(":", 1)
|
||||||
|
except TypeError:
|
||||||
|
return default
|
||||||
try:
|
try:
|
||||||
return int(major), int(minor)
|
return int(major), int(minor)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -165,20 +325,48 @@ class Blockdevice:
|
||||||
|
|
||||||
all holders, subdevices and children of subdevices
|
all holders, subdevices and children of subdevices
|
||||||
"""
|
"""
|
||||||
direct_children = [Blockdevice(child).name
|
direct_children = [
|
||||||
|
get_blockdevice(child, self.sysblock_dir, self.devnode_dir).name
|
||||||
for child in find_blockdevices(self.devdir)]
|
for child in find_blockdevices(self.devdir)]
|
||||||
direct_children.extend(self.holders[:])
|
direct_children.extend(self.holders[:])
|
||||||
children = direct_children[:]
|
children = direct_children[:]
|
||||||
for dchild in direct_children:
|
for dchild in direct_children:
|
||||||
children.extend(Blockdevice(dchild).children)
|
children.extend(get_blockdevice(dchild, self.sysblock_dir,
|
||||||
|
self.devnode_dir).children)
|
||||||
return children
|
return children
|
||||||
|
|
||||||
|
|
||||||
|
def __get_device_nodes(self):
|
||||||
|
"""get all device nodes with the major/minor combination of the device
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
major, minor = self.devnum
|
||||||
|
|
||||||
|
def find_major_minor(arg, dirname, fnames):
|
||||||
|
for fname in fnames:
|
||||||
|
try:
|
||||||
|
stat = os.stat(os.path.join(dirname, fname))
|
||||||
|
## check if it is a blockdevice and compare major/minor
|
||||||
|
if (stat.st_mode & 060000 == 060000) \
|
||||||
|
and (os.major(stat.st_rdev) == major) \
|
||||||
|
and (os.minor(stat.st_rdev) == minor):
|
||||||
|
result.append(os.path.join(dirname, fname))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
os.path.walk(self.devnode_dir, find_major_minor, None)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""display the name of the device
|
||||||
|
"""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
def info(self):
|
def info(self):
|
||||||
|
"""display some information about the device
|
||||||
|
"""
|
||||||
output = "%s:\n" % self.name
|
output = "%s:\n" % self.name
|
||||||
output += "\t%s:\t%s\n" % ("blockdir", self.devdir)
|
output += "\t%s:\t%s\n" % ("blockdir", self.devdir)
|
||||||
output += "\t%s:\t%s\n" % ("major/minor", self.devnum)
|
output += "\t%s:\t%s\n" % ("major/minor", self.devnum)
|
||||||
|
@ -187,12 +375,45 @@ class Blockdevice:
|
||||||
output += "\t%s:\t\t%s\n" % ("slaves", self.slaves)
|
output += "\t%s:\t\t%s\n" % ("slaves", self.slaves)
|
||||||
output += "\t%s:\t%s\n" % ("holders", self.holders)
|
output += "\t%s:\t%s\n" % ("holders", self.holders)
|
||||||
output += "\t%s:\t%s\n" % ("children", self.children)
|
output += "\t%s:\t%s\n" % ("children", self.children)
|
||||||
|
output += "\t%s:\t%s\n" % ("device nodes", self.devnodes)
|
||||||
|
output += "\tflags:\t\t"
|
||||||
|
for funcname in [ "storage", "md_raid", "partitionable", "luks",
|
||||||
|
"lvm_pv", "lvm_lv"]:
|
||||||
|
if getattr(self, "is_%s" % funcname)():
|
||||||
|
output += "%s " % funcname
|
||||||
|
output += "\n"
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def get_blockdevice(dev,
|
||||||
|
sysblock_dir=DEFAULT_SYSBLOCK_DIR,
|
||||||
|
devnode_dir=DEFAULT_DEVNODE_DIR):
|
||||||
|
if os.path.isabs(dev):
|
||||||
|
if os.path.isfile(os.path.join(dev, "dev")):
|
||||||
|
devdir = dev
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
for one_devdir in find_blockdevices(sysblock_dir):
|
||||||
|
if os.path.basename(one_devdir) == dev:
|
||||||
|
devdir = one_devdir
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
devname = os.path.basename(devdir)
|
||||||
|
dev = _get_cached_value(["blockdevices", devname])
|
||||||
|
if dev is None:
|
||||||
|
dev = Blockdevice(devdir, sysblock_dir, devnode_dir)
|
||||||
|
_set_cached_value(["blockdevices", devname], dev)
|
||||||
|
return dev
|
||||||
|
|
||||||
|
|
||||||
def find_blockdevices(top_dir):
|
def find_blockdevices(top_dir):
|
||||||
|
|
||||||
|
cached = _get_cached_value(["blockdevice_dirs", top_dir])
|
||||||
|
if not cached is None:
|
||||||
|
return cached[:]
|
||||||
|
|
||||||
dev_dirs = []
|
dev_dirs = []
|
||||||
|
|
||||||
def look4dev_dirs(arg, dirname, fnames):
|
def look4dev_dirs(arg, dirname, fnames):
|
||||||
|
@ -210,21 +431,141 @@ def find_blockdevices(top_dir):
|
||||||
fnames.remove(fname)
|
fnames.remove(fname)
|
||||||
|
|
||||||
os.path.walk(top_dir, look4dev_dirs, 'dev')
|
os.path.walk(top_dir, look4dev_dirs, 'dev')
|
||||||
return dev_dirs
|
_set_cached_value(["blockdevice_dirs", top_dir], dev_dirs)
|
||||||
|
return dev_dirs[:]
|
||||||
|
|
||||||
|
|
||||||
|
def find_lvm_pv():
|
||||||
|
"""return the blockdevice names of all physical LVM volumes
|
||||||
|
"""
|
||||||
|
cached = _get_cached_value(["lvm", "pv"])
|
||||||
|
if not cached is None:
|
||||||
|
return cached[:]
|
||||||
|
|
||||||
|
#TODO: should we check, if LVM is supported at all?
|
||||||
|
# e.g. by checking the existence of pvdisplay?
|
||||||
|
prefs = _load_preferences()
|
||||||
|
result = None
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
shell = False,
|
||||||
|
stdout = subprocess.PIPE,
|
||||||
|
args = [ prefs["Programs"]["super"],
|
||||||
|
prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
|
"program", "pvdisplay" ])
|
||||||
|
proc.wait()
|
||||||
|
except OSError, err_msg:
|
||||||
|
# TODO: add a logging warning
|
||||||
|
result = []
|
||||||
|
if proc.returncode != 0:
|
||||||
|
# TODO: add a logging warning
|
||||||
|
result = []
|
||||||
|
if result is None:
|
||||||
|
result = []
|
||||||
|
for line in proc.stdout.readlines():
|
||||||
|
result.append(line.split(":", 1)[0].strip())
|
||||||
|
_set_cached_value(["lvm", "pv"], result)
|
||||||
|
return result[:]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_cached_value(link):
|
||||||
|
"""return a cached value
|
||||||
|
|
||||||
|
"link" is an array of the hierachie of the accessed item
|
||||||
|
e.g. link = ["blockdevices", "hda"]
|
||||||
|
return None if the value is not in the cache or if USE_CACHE is False
|
||||||
|
"""
|
||||||
|
if not USE_CACHE:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "expires" in CACHED_VALUES:
|
||||||
|
if CACHED_VALUES["expires"] < int(time.time()):
|
||||||
|
reset_cache()
|
||||||
|
else:
|
||||||
|
__reset_cache_timer()
|
||||||
|
|
||||||
|
ref = CACHED_VALUES
|
||||||
|
for element in link:
|
||||||
|
if element in ref:
|
||||||
|
ref = ref[element]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return ref
|
||||||
|
|
||||||
|
|
||||||
|
def reset_cache():
|
||||||
|
## refresh the cache
|
||||||
|
for item in CACHED_VALUES:
|
||||||
|
CACHED_VALUES[item] = {}
|
||||||
|
__reset_cache_timer()
|
||||||
|
|
||||||
|
|
||||||
|
def __reset_cache_timer():
|
||||||
|
CACHED_VALUES["expires"] = int(time.time()) + CACHE_EXPIRE_SECONDS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _set_cached_value(link, item):
|
||||||
|
"""store an item in the cache
|
||||||
|
|
||||||
|
"link" is an array of the hierachie of the accessed item
|
||||||
|
e.g. link = ["blockdevices", "hda"]
|
||||||
|
"""
|
||||||
|
if not USE_CACHE:
|
||||||
|
return
|
||||||
|
ref = CACHED_VALUES
|
||||||
|
for element in link[:-1]:
|
||||||
|
if not element in ref:
|
||||||
|
## create a non-existing sub element
|
||||||
|
ref[element] = {}
|
||||||
|
ref = ref[element]
|
||||||
|
## store the item
|
||||||
|
ref[link[-1]] = item
|
||||||
|
|
||||||
|
|
||||||
|
def _load_preferences():
|
||||||
|
prefs = cryptobox.core.settings.get_current_settings()
|
||||||
|
if not prefs is None:
|
||||||
|
## now the preferences are loaded
|
||||||
|
return prefs
|
||||||
|
## we have to load an emergency fallback for proper function
|
||||||
|
## this is mainly useful for local testing
|
||||||
|
root_dir = os.path.realpath(os.path.join(globals()["cryptobox"].__path__[0],
|
||||||
|
os.path.pardir, os.path.pardir))
|
||||||
|
config_file = os.path.join(root_dir, "bin", "cryptobox.conf")
|
||||||
|
## we have to chdir to the 'bin' directory - otherwise the paths in
|
||||||
|
## cryptobox.conf do not work
|
||||||
|
os.chdir(os.path.dirname(config_file))
|
||||||
|
return cryptobox.core.settings.CryptoBoxSettings(config_file)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
blocks = Blockdevices()
|
## list the properties of all available devices
|
||||||
for dev in blocks.get_devices():
|
## this is just for testing purposes
|
||||||
print dev.info()
|
blocks = Blockdevices().get_devices()
|
||||||
print
|
|
||||||
print "Usable storage devices:"
|
## do we want to show the result?
|
||||||
for dev in blocks.get_devices():
|
def show(text=""):
|
||||||
if dev.isstorage():
|
if IS_VISIBLE:
|
||||||
print dev
|
print text
|
||||||
print
|
|
||||||
print "Partitionable devices:"
|
if len(blocks) > 0:
|
||||||
for dev in blocks.get_devices():
|
## show all devices and their properties
|
||||||
if dev.ispartitionable():
|
show("Properties of all devices:")
|
||||||
print dev
|
for device in blocks:
|
||||||
|
show(device.info())
|
||||||
|
|
||||||
|
## discover all self-check methods
|
||||||
|
example = blocks[0]
|
||||||
|
flag_checker = [ method for method in dir(example)
|
||||||
|
if callable(getattr(example, method))
|
||||||
|
and method.startswith("is_")]
|
||||||
|
## list all checks and the respective devices
|
||||||
|
for check in flag_checker:
|
||||||
|
show("List of '%s' devices:" % check[3:])
|
||||||
|
for device in blocks:
|
||||||
|
if getattr(device, check)():
|
||||||
|
show("\t%s" % device)
|
||||||
|
show()
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"cryptsetup",
|
"program", "cryptsetup",
|
||||||
"luksAddKey",
|
"luksAddKey",
|
||||||
self.device,
|
self.device,
|
||||||
"--batch-mode"])
|
"--batch-mode"])
|
||||||
|
@ -523,7 +523,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"cryptsetup",
|
"program", "cryptsetup",
|
||||||
"luksOpen",
|
"luksOpen",
|
||||||
self.device,
|
self.device,
|
||||||
self.name,
|
self.name,
|
||||||
|
@ -542,7 +542,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"mount",
|
"program", "mount",
|
||||||
os.path.join(self.__dmDir, self.name),
|
os.path.join(self.__dmDir, self.name),
|
||||||
self.__get_mount_point()])
|
self.__get_mount_point()])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
@ -572,7 +572,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"umount",
|
"program", "umount",
|
||||||
self.__get_mount_point()])
|
self.__get_mount_point()])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
|
@ -588,7 +588,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"cryptsetup",
|
"program", "cryptsetup",
|
||||||
"luksClose",
|
"luksClose",
|
||||||
self.name,
|
self.name,
|
||||||
"--batch-mode"])
|
"--batch-mode"])
|
||||||
|
@ -620,7 +620,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"mount",
|
"program", "mount",
|
||||||
self.device,
|
self.device,
|
||||||
self.__get_mount_point()])
|
self.__get_mount_point()])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
@ -653,7 +653,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"umount",
|
"program", "umount",
|
||||||
self.__get_mount_point()])
|
self.__get_mount_point()])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
|
@ -734,7 +734,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"cryptsetup",
|
"program", "cryptsetup",
|
||||||
"luksFormat",
|
"luksFormat",
|
||||||
self.device,
|
self.device,
|
||||||
"--batch-mode",
|
"--batch-mode",
|
||||||
|
@ -755,7 +755,7 @@ class CryptoBoxContainer:
|
||||||
args = [
|
args = [
|
||||||
self.cbox.prefs["Programs"]["super"],
|
self.cbox.prefs["Programs"]["super"],
|
||||||
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
self.cbox.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"cryptsetup",
|
"program", "cryptsetup",
|
||||||
"luksOpen",
|
"luksOpen",
|
||||||
self.device,
|
self.device,
|
||||||
self.name,
|
self.name,
|
||||||
|
|
|
@ -40,6 +40,19 @@ VOLUMESDB_FILE = "cryptobox_volumes.db"
|
||||||
PLUGINCONF_FILE = "cryptobox_plugins.conf"
|
PLUGINCONF_FILE = "cryptobox_plugins.conf"
|
||||||
USERDB_FILE = "cryptobox_users.db"
|
USERDB_FILE = "cryptobox_users.db"
|
||||||
|
|
||||||
|
## allow to retrieve the most recently created setting object
|
||||||
|
CURRENT_SETTING = []
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_settings():
|
||||||
|
"""return the most recently created setting object
|
||||||
|
"""
|
||||||
|
if not CURRENT_SETTING:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return CURRENT_SETTING[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CryptoBoxSettings:
|
class CryptoBoxSettings:
|
||||||
"""Manage the various configuration files of the CryptoBox
|
"""Manage the various configuration files of the CryptoBox
|
||||||
|
@ -63,6 +76,7 @@ class CryptoBoxSettings:
|
||||||
self.misc_files = []
|
self.misc_files = []
|
||||||
self.reload_misc_files()
|
self.reload_misc_files()
|
||||||
self.__is_initialized = True
|
self.__is_initialized = True
|
||||||
|
CURRENT_SETTING.insert(0, self)
|
||||||
|
|
||||||
|
|
||||||
def reload_misc_files(self):
|
def reload_misc_files(self):
|
||||||
|
@ -190,7 +204,7 @@ class CryptoBoxSettings:
|
||||||
args = [
|
args = [
|
||||||
self.prefs["Programs"]["super"],
|
self.prefs["Programs"]["super"],
|
||||||
self.prefs["Programs"]["CryptoBoxRootActions"],
|
self.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"mount",
|
"program", "mount",
|
||||||
"_tmpfs_",
|
"_tmpfs_",
|
||||||
mount_dir ])
|
mount_dir ])
|
||||||
(stdout, stderr) = proc.communicate()
|
(stdout, stderr) = proc.communicate()
|
||||||
|
@ -211,7 +225,7 @@ class CryptoBoxSettings:
|
||||||
args = [
|
args = [
|
||||||
self.prefs["Programs"]["super"],
|
self.prefs["Programs"]["super"],
|
||||||
self.prefs["Programs"]["CryptoBoxRootActions"],
|
self.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"mount",
|
"program", "mount",
|
||||||
partition,
|
partition,
|
||||||
mount_dir ])
|
mount_dir ])
|
||||||
(stdout, stderr) = proc.communicate()
|
(stdout, stderr) = proc.communicate()
|
||||||
|
@ -241,7 +255,7 @@ class CryptoBoxSettings:
|
||||||
args = [
|
args = [
|
||||||
self.prefs["Programs"]["super"],
|
self.prefs["Programs"]["super"],
|
||||||
self.prefs["Programs"]["CryptoBoxRootActions"],
|
self.prefs["Programs"]["CryptoBoxRootActions"],
|
||||||
"umount",
|
"program", "umount",
|
||||||
mount_dir ])
|
mount_dir ])
|
||||||
(stdout, stderr) = proc.communicate()
|
(stdout, stderr) = proc.communicate()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
|
|
Loading…
Reference in a new issue