From 1a52d1a9b063ca47fe999249178b6d4438a66356 Mon Sep 17 00:00:00 2001
From: Sascha Markus <s.ascha.markus@gmail.com>
Date: Wed, 30 May 2018 13:51:54 +0000
Subject: [PATCH] feature/8514 Replace WhisperBack.mail_appended_info with a
 dictionary Creates a dictionary with the debug info. Each entry has the
 command or file as key and the command output or file content as value. Lines
 of a file are returned as list of strings. The script prints the json
 serialization of the dict. This draft version doesn't support /bin/journalctl
 because deserializing failed.

---
 .../usr/local/sbin/tails-debugging-info            | 134 ++++++++++-----------
 1 file changed, 61 insertions(+), 73 deletions(-)

diff --git a/config/chroot_local-includes/usr/local/sbin/tails-debugging-info b/config/chroot_local-includes/usr/local/sbin/tails-debugging-info
index 883c88a29d..93ce0ee509 100755
--- a/config/chroot_local-includes/usr/local/sbin/tails-debugging-info
+++ b/config/chroot_local-includes/usr/local/sbin/tails-debugging-info
@@ -29,18 +29,14 @@ If any of these rules does not apply, the file cannot be added here
 safely and something is probably quite wrong and should be
 investigated carefully.
 
->>> # run script
->>> import sh
->>> this_command = sh.Command(sys.argv[0])
->>> this_command()
-<BLANKLINE>
-...
 """
 
+import json
 import os
 import sys
-from pwd import getpwuid
 import subprocess
+from collections import OrderedDict
+from pwd import getpwuid
 
 
 # AppArmor Ux rules don't sanitize PATH, which can lead to an
@@ -52,121 +48,113 @@ os.environ['PATH'] = '/usr/local/bin:/usr/bin:/bin'
 
 
 def main():
-    """Print debug information.
+    """Print debug information serialized as json.
 
-    >>> main()
-    <BLANKLINE>
-    ...
     """
 
-    debug_file('root', '/proc/cmdline')
+    debug_info = OrderedDict()
+    debug_info.update(debug_file('root', '/proc/cmdline'))
 
     # General hardware and filesystems information
-    debug_command('/usr/sbin/dmidecode', '-s', 'system-manufacturer')
-    debug_command('/usr/sbin/dmidecode', '-s', 'system-product-name')
-    debug_command('/usr/sbin/dmidecode', '-s', 'system-version')
-    debug_command('/usr/bin/lspci', '-nn')
-    debug_command('/bin/df', '--human-readable', '--print-type')
-    debug_command('/bin/mount', '--show-labels')
-    debug_command('/bin/lsmod')
-    debug_file('root', '/proc/asound/cards')
-    debug_file('root', '/proc/asound/devices')
-    debug_file('root', '/proc/asound/modules')
+    debug_info.update(debug_command('/usr/sbin/dmidecode', '-s', 'system-manufacturer'))
+    debug_info.update(debug_command('/usr/sbin/dmidecode', '-s', 'system-product-name'))
+    debug_info.update(debug_command('/usr/sbin/dmidecode', '-s', 'system-version'))
+    debug_info.update(debug_command('/usr/bin/lspci', '-nn'))
+    debug_info.update(debug_command('/bin/df', '--human-readable', '--print-type'))
+    debug_info.update(debug_command('/bin/mount', '--show-labels'))
+    debug_info.update(debug_command('/bin/lsmod'))
+    debug_info.update(debug_file('root', '/proc/asound/cards'))
+    debug_info.update(debug_file('root', '/proc/asound/devices'))
+    debug_info.update(debug_file('root', '/proc/asound/modules'))
 
     # Miscellaneous configuration and log files
-    debug_file('root', '/etc/X11/xorg.conf')
-    debug_file('Debian-gdm', '/var/log/gdm3/tails-greeter.errors')
-    debug_file('root', '/var/log/live/boot.log')
-    debug_file('root', '/var/log/live/config.log')
-    debug_file('root', '/var/lib/live/config/tails.physical_security')
+    debug_info.update(debug_file('root', '/etc/X11/xorg.conf'))
+    debug_info.update(debug_file('Debian-gdm', '/var/log/gdm3/tails-greeter.errors'))
+    debug_info.update(debug_file('root', '/var/log/live/boot.log'))
+    debug_info.update(debug_file('root', '/var/log/live/config.log'))
+    debug_info.update(debug_file('root', '/var/lib/live/config/tails.physical_security'))
 
     # Persistence
-    debug_file('root', '/var/lib/gdm3/tails.persistence')
-    debug_file('tails-persistence-setup', '/live/persistence/TailsData_unlocked/persistence.conf')
-    debug_file('tails-persistence-setup', '/live/persistence/TailsData_unlocked/live-additional-software.conf')
-    debug_directory('root', '/live/persistence/TailsData_unlocked/apt-sources.list.d')
-    debug_file('root', '/var/log/live-persist')
+    debug_info.update(debug_file('root', '/var/lib/gdm3/tails.persistence'))
+    debug_info.update(debug_file('tails-persistence-setup', '/live/persistence/TailsData_unlocked/persistence.conf'))
+    debug_info.update(debug_file('tails-persistence-setup', '/live/persistence/TailsData_unlocked/live-additional-software.conf'))
+    debug_info.update(debug_directory('root', '/live/persistence/TailsData_unlocked/apt-sources.list.d'))
+    debug_info.update(debug_file('root', '/var/log/live-persist'))
 
     # The Journal
-    debug_command('/bin/journalctl', '--catalog', '--no-pager')
+    # debug_info.update(debug_command('/bin/journalctl', '--catalog', '--no-pager'))
+
+    print(json.dumps(debug_info, indent=4))
 
 
 def debug_command(command, *args):
-    """Print the command and then run it.
+    """Return the command and it's result as dict.
 
     >>> debug_command('echo', 'foo')
-    <BLANKLINE>
-    ===== output of command echo foo =====
-    foo
+    {'echo foo': 'foo'}
     """
-    print()
-    print('===== output of command {} ====='.format(' '.join((command,) + args)))
-    print(subprocess.check_output([command, *args]).decode().strip())
+    return {'{}'.format(' '.join((command,) + args)):
+	    subprocess.check_output([command, *args]).decode().strip()}
 
 
 def debug_file(user, filename):
-    """Print file content.
+    """Return the filename and the file content as dict.
 
     >>> import tempfile, getpass
     >>> with tempfile.NamedTemporaryFile('w') as f:
     ...     _ = f.write("foo\\nbar")
     ...     _ = f.seek(0)
-    ...     debug_file(getpass.getuser(), f.name)
-    <BLANKLINE>
-    ===== content of ... =====
-    foo
-    bar
+    ...     debug_file(getpass.getuser(), f.name) 
+    {...: ['foo', 'bar']}
     """
     if not os.path.isfile(filename):
-        return
+        return {filename: 'Not found'}
 
     # This check is not sufficient, see the comment at the top of the file
     # for the complete requirements required for security
     owner = getpwuid(os.stat(filename).st_uid).pw_name
     if owner != user:
-        print()
-        print('WARNING: not opening file {}, '.format(filename), end='')
-        print('because it is owned by {} instead of {}'.format(owner, user))
-        return
+        return {filename: 'WARNING: not opening file {}, because it is owned by {} instead of {}'.format(filename, owner, user)}
+        
 
-    print()
-    print('===== content of {} ====='.format(filename))
+    file_content = [] 
     with open(filename) as f:
-        print(f.read(), end='')
-
+        for l in f:
+            file_content.append(l.replace('\n', ''))
+    return {filename: file_content}
 
 def debug_directory(user, dir_name):
-    """List directory and print content of all contained files (non-recursively).
+    """Return a dict with the dir_name and dicts with the content of all contained files (non-recursively).
 
-    >>> import tempfile, getpass
-    >>> with tempfile.TemporaryDirectory() as tmpdir:
-    ...     open(os.path.join(tmpdir, 'foo'), 'w').close()
-    ...     debug_directory(getpass.getuser(), tmpdir)
-    <BLANKLINE>
-    ===== listing of ... =====
-    foo
+    >>> import os, getpass
+    >>> tmpdir = '/tmp/mytempdir'
+    >>> os.makedirs(tmpdir)
+    >>> with open(os.path.join(tmpdir, 'foo'), 'w') as f:
+    ...     _ = f.write("foobar\\nbar")
+    ...     _ = f.seek(0)
+    ...     result = debug_directory(getpass.getuser(), tmpdir)
+    >>> os.remove(os.path.join(tmpdir, 'foo'))
+    >>> os.rmdir(tmpdir)
+    >>> result 
+    {'/tmp/mytempdir': [{'/tmp/mytempdir/foo': ['foobar', 'bar']}]}
     """
     if not os.path.isdir(dir_name):
-        return
+        return {dir_name: 'Not found'}
 
-    print()
 
     # This check is not sufficient, see the comment at the top of the file
     # for the complete requirements required for security
     owner = getpwuid(os.stat(dir_name).st_uid).pw_name
     if owner != user:
-        print('WARNING: not opening directory {}, '.format(dir_name), end='')
-        print('because it is owned by {} instead of {}'.format(owner, user))
-        return
-
+       return {dir_name: 'WARNING: not opening directory {}, because it is owned by {} instead of {}'.format(dir_name, owner, user)}
+        
     files = os.listdir(dir_name)
 
-    print('===== listing of {} ====='.format(dir_name))
-    for f in files:
-        print(f)
+    listing = []
 
     for f in files:
-        debug_file(user, f)
+        listing.append(debug_file(user, os.path.join(dir_name, f)))
+    return {dir_name: listing}
 
 
 if __name__ == '__main__':
-- 
2.11.0

