[dm-crypt] How to determine if a LUKS device is opened or closed

Arno Wagner arno at wagner.name
Fri Aug 19 21:45:48 CEST 2016


Hi Tom,

I wrote the Python-script "cstat" for this purpose a while
ago. Attached for your convenience.

Regards,
Arno

On Fri, Aug 19, 2016 at 17:42:41 CEST, Tom Hartman wrote:
>    Is there a way through either cryptsetup or some other tool to
>    determine if a luks device is opened or closed? It looks like the
>    status command is only meant for mapped volumes.
>    Something like:
>    Â Â Â Â Â Â Â Â Â Â
>    Â Â Â Â Â Â Â Â Â Â Â  cryptsetup isLuksOpen /dev/sda1
>    Thanks,
>    Tom

> _______________________________________________
> dm-crypt mailing list
> dm-crypt at saout.de
> http://www.saout.de/mailman/listinfo/dm-crypt


-- 
Arno Wagner,     Dr. sc. techn., Dipl. Inform.,    Email: arno at wagner.name
GnuPG: ID: CB5D9718  FP: 12D6 C03B 1B30 33BB 13CF  B774 E35C 5FA1 CB5D 9718
----
A good decision is based on knowledge and not on numbers. -- Plato

If it's in the news, don't worry about it.  The very definition of 
"news" is "something that hardly ever happens." -- Bruce Schneier
-------------- next part --------------
#!/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.03 
# Version history
# 0.03 Adjustments for cryptsrtup 1.2.0
# 0.02 fixed error on unmounted but mapped devices

import sys, os, stat, subprocess, re

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

# IEC function
def str_iec(v):

  n = float(v)
  if n < 0 :
    raise ValueError, 'Argument of str_iec() must not be negative'

  prefix = '  '     # Actually the postfix of the output, but the
                    # prefix of the unit, if one is attached.

  if n < 1024:
    pass
  elif n < 1024 * 1024:
    n = n / 1024
    prefix = 'ki'
  elif n < 1024 * 1024 * 1024:
    n = n / ( 1024 * 1024 )
    prefix = 'Mi'
  elif n < 1024 * 1024 * 1024 * 1024:
    n = n / ( 1024 * 1024 * 1024 )
    prefix = 'Gi'
  elif n < 1024 * 1024 * 1024 * 1024 * 1024:
    n = n / ( 1024 * 1024 * 1024 * 1024 )
    prefix = 'Ti'
  elif n < 1024 * 1024 * 1024 * 1024 * 1024 * 1024:
    n = n / ( 1024 * 1024 * 1024 * 1024 * 1024 )
    prefix = 'Ei'
  else:
    raise ValueError, 'Argument of str_iec() larger than (2^50)-1'

  s = "%(n)5.1f " % {'n': n}
  s = s + prefix
  return s

# 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 7 for '  device:  <device name>'
    if not re.match('\ \ device\:\ \ \S+', out_lines[4]):
        print '\nError: Line 7 of cryptsetup output has unexpected form'
        print 'Expected: "  device:  <device name>'
        print 'found: [' + out_lines[4]+ ']'
        print 'complete cryptsetup output:\m'
        print out
        print '\nAborting!\n'
        sys.exit(1)
    device = out_lines[4][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[4] = '  devices: '+str((maj, min))+'  '+dev_str
    for l in out_lines: 
      if re.match('\s+size:\s+(\d+)\ssectors.*', l):
        m = re.match('\s+size:\s+(\d+)\ssectors.*', l)
        s = int(m.group(1))
        l = l.rstrip('\n')
        print l + '   ( '+ str_iec(s * 512) + ' Bytes )'
      else:
        print l
    # determine mounts and print them
    mounts = '  mounted:'
    # ';echo' in next command is so that we have at least one line
    # of output to avoid an error from subprocess
    cmd = 'mount | grep '+mapper_path+'/'+t+'; echo'
#    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()
#    print('command output = [' + out + ']')
    if (p.returncode != 0): raise IOError(p.returncode, err)  
    if len(out_lines) == 1:
        mounts = mounts + ' not mounted'
    else:
        for l in out_lines:       
#           print 'l = ', l
           match = re.search(' on (\S+) type', l)
           if match != None:
               mounts = mounts + ' ' + match.group(1)
    print mounts
    print














More information about the dm-crypt mailing list