Skip to contentSkip to conversation

Fix (Python script): renaming of device

dm-crypt Arno Wagner 1 participant 1 message Jan 22, 2010

lore
Share & tools
lore
#0 lore raw

I just wrote a Python wrapper for "cryptsetup status", that not only lists all devices for a (major, minor) combo, but also the pair (major, minor). In addition, it can display status for all crypto devices (just give no argument) and displays the mount points. May not be perfect and may have bugs, but as far as I am concerned solves the issue.

The script is below (I think the list does not support attachments). Feedback, bug-reports, etc. welcome.

Arno

Sample output:
---
root /home/wagner/exp/python/cryptsetup>./cryptstat.py

/dev/mapper/c1 is active:
cipher: aes-cbc-essiv:sha256
keysize: 256 bits
devices: (8, 48) /dev/scsi/sdh6-0c0i0l0 /dev/sdd

  offset:  0 sectors
  size:    1953525168 sectors
  mode:    read/write

mounted: /mnt

---

#!/usr/bin/python

# cryptstat.py

# Displays all matching devices in /dev/ for a cryptsetup target

# Calls "cryptsetup status <target>" to get the initial info,

# then determines major/minor of the reported device and does
# a traversal of /dev/ to find all matches.
# Reports an augemented result of "cryptsetup status", also
# including mount points.

# Arguments are any number of cryptsetup targets. Does a validity

# check and refuses targets not found in /dev/mapper/
# Without argument, displays info on all targets found in /dev/mapper/

# (c) Arno Wagner 2010, distributed under the modified BSD license. # Version 0.01

import sys, os, stat, subprocess, re

dev_path = '/dev'
mapper_path = '/dev/mapper'
cryptsetup_path = '/usr/sbin/cryptsetup'

# Utility functions

def normalize_path(path):
"""Replaces any number of occurences of '/' with a single '/'""" # Note: INefficient, but should not matter
while path.find('//') > -1:
path = path.replace('//','/')
return(path)

def dir_walk(dir):
"""Returns a list of all filenames with full paths
(including dirs, specials, etc.) below the argument. "." and ".." are omitted. Does not follow symblinks to avoid loops, but there is no real loop prevention."""
names = []
dir = normalize_path(dir)
# process current directory
current = os.listdir(dir)
for d in current:
d = dir + '/' + d
# Test whether d is a directory. If so, recurse. s = os.lstat(d)
if stat.S_ISDIR( s[stat.ST_MODE] ):
names.extend(dir_walk(d))
else:
names.append(d)
return(names)

def dir_walk_block(dir):
"""Returns the names of all block devices in the subtree at dir""" names = dir_walk(dir)
block_dev = []
for n in names:
if stat.S_ISBLK(os.lstat(n)[stat.ST_MODE]):
block_dev.append(n)
return(block_dev)

def special_dev_major_minor(n):
"""Returns (major,minor) for a device special file name""" s = os.lstat(n)
if not stat.S_ISBLK(s[stat.ST_MODE]) and \
not stat.S_ISCHR(s[stat.ST_MODE]) :
return None
else:
maj = os.major(s.st_rdev)
min = os.minor(s.st_rdev)
return (maj, min)

def build_block_major_minor_tables(dir):
"""Returns
1) a table of filenames that are all block special files
2) a dict mapping each name to (major,minor)
3) a dict mapping each (major,minor) pair to a table of filenames"""
names = []
name2pair = {}
pair2name = {}

names = dir_walk_block(dir)
for n in names:
maj, min = special_dev_major_minor(n)
name2pair[n] = (maj, min)
if (maj, min) not in pair2name:
pair2name[(maj, min)] = [n]
else:
pair2name[(maj, min)].append(n)

return (names, name2pair, pair2name)

# Start of main action

# determine list of targets to be processed
targets = []
# first get all in mapper_path except 'control'
mapper_targets = []
mapper_entries = os.listdir(mapper_path)
tmp = []
for e in mapper_entries:
    if e != 'control':
        tmp.append(e)
mapper_entries = tmp
# if argument list empty, copy mapper_entries
if len(sys.argv) == 1:
    targets = mapper_entries
    targets.sort()
else:
    targets = sys.argv[1:]

# Make sure all targets are in mapper_entries
tbl = {}
for e in mapper_entries:
tbl[e] = None
for e in targets:
if e not in tbl:
print '\nError: Command argument "'+e+'" is not present in '+\
'mapper dir '+mapper_path+'!\nAborting\n'
sys.exit(1)

# print targets

# Create mappings (major,minor) <-> name
names, name2pair, pair2names = build_block_major_minor_tables(dev_path)

print

for t in targets:

    # do call to "cryptsetup status"
    cmd = cryptsetup_path + ' status ' + t
#    print 'calling "' + cmd + '"'
    p = subprocess.Popen(cmd, shell=True, bufsize=0,
             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
             stderr=subprocess.PIPE, close_fds=True)
    (out, err) = p.communicate()
    out_lines = out.splitlines()
    if (p.returncode != 0): raise IOError(p.returncode, err)  
#    print('command output = [' + out + ']')
    # check output line 6 for '  device:  <device name>'
    if not re.match('\ \ device\:\ \ \S+', out_lines[3]):
        print '\nError: Line 6 of cryptsetup output has unexpected form'
        print 'Expected: "  device:  <device name>'
        print 'found: [' + out_lines[3]+ ']'
        print 'complete cryptsetup output:\m'
        print out
        print '\nAborting!\n'
        sys.exit(1)
    device = out_lines[3][11:]
#    print 'device found: [' + device + ']'
    (maj, min) = name2pair[device]
#    print '(maj, min) = ', (maj, min)
    devices = pair2names[(maj, min)]
    dev_str = ''
    for d in devices: dev_str = dev_str + d + '  '
#    print 'devices: ', dev_str
    out_lines[3] = '  devices: '+str((maj, min))+'  '+dev_str
    for l in out_lines: print l
    # determine mounts and print them
    mounts = '  mounted:'
    cmd = 'mount | grep '+mapper_path+'/'+t
#    print 'calling "' + cmd + '"'
    p = subprocess.Popen(cmd, shell=True, bufsize=0,
             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
             stderr=subprocess.PIPE, close_fds=True)
    (out, err) = p.communicate()
    out_lines = out.splitlines()
    if (p.returncode != 0): raise IOError(p.returncode, err)  
#    print('command output = [' + out + ']')
    if len(out_lines) == 0:
        mounts = mounts + ' not mounted'
    else:
        for l in out_lines:       
           match = re.search(' on (\S+) type', l)
           mounts = mounts + ' ' + match.group(1)
    print mounts
    print
Signature · 7 lines
Arno Wagner, Dr. sc. techn., Dipl. Inform., CISSP -- Email: [email protected] 
GnuPG:  ID: 1E25338F  FP: 0C30 5782 9D93 F785 E79C  0296 797F 6B50 1E25 338F
----
Cuddly UI's are the manifestation of wishful thinking. -- Dylan Evans

If it's in the news, don't worry about it.  The very definition of 
"news" is "something that hardly ever happens." -- Bruce Schneier