From 0ae548f89cdfa65a81fc51a7a9e9b3062de44cc1 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Sat, 30 Oct 2021 22:15:43 +0000 Subject: [PATCH] Format code with autopep8 This commit fixes the style issues introduced in e4e4747 according to the output from autopep8. Details: https://deepsource.io/gh/amir9339/volatility-docker/transform/75f601c0-2b75-462d-b87b-254204ffd5f2/ --- plugins/file.py | 120 +++++++++++++++++++++++++--------------------- plugins/mount.py | 113 +++++++++++++++++++++++-------------------- plugins/pslist.py | 39 ++++++++------- 3 files changed, 150 insertions(+), 122 deletions(-) diff --git a/plugins/file.py b/plugins/file.py index 91516fd..3a9a413 100644 --- a/plugins/file.py +++ b/plugins/file.py @@ -11,35 +11,35 @@ # inode types # see https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/stat.h -S_IFMT = 0o170000 # inode type mask -S_IFSOCK = 0o140000 # socket -S_IFLNK = 0o120000 # symbolic link -S_IFREG = 0o100000 # regular file -S_IFBLK = 0o60000 # block device -S_IFDIR = 0o40000 # directory -S_IFCHR = 0o20000 # character device -S_IFIFO = 0o10000 # fifo (pipe) -S_ISUID = 0o4000 -S_ISGID = 0o2000 -S_ISVTX = 0o1000 +S_IFMT = 0o170000 # inode type mask +S_IFSOCK = 0o140000 # socket +S_IFLNK = 0o120000 # symbolic link +S_IFREG = 0o100000 # regular file +S_IFBLK = 0o60000 # block device +S_IFDIR = 0o40000 # directory +S_IFCHR = 0o20000 # character device +S_IFIFO = 0o10000 # fifo (pipe) +S_ISUID = 0o4000 +S_ISGID = 0o2000 +S_ISVTX = 0o1000 # user permissions -S_IRWXU = 0o700 # user permissions mask -S_IRUSR = 0o400 # user read -S_IWUSR = 0o200 # user write -S_IXUSR = 0o100 # user execute +S_IRWXU = 0o700 # user permissions mask +S_IRUSR = 0o400 # user read +S_IWUSR = 0o200 # user write +S_IXUSR = 0o100 # user execute # group permissions -S_IRWXG = 0o070 # group permissions mask -S_IRGRP = 0o040 # group read -S_IWGRP = 0o020 # group write -S_IXGRP = 0o010 # group execute +S_IRWXG = 0o070 # group permissions mask +S_IRGRP = 0o040 # group read +S_IWGRP = 0o020 # group write +S_IXGRP = 0o010 # group execute # other permissions -S_IRWXO = 0o007 # other permissions mask -S_IROTH = 0o004 # other read -S_IWOTH = 0o002 # other write -S_IXOTH = 0o001 # other execute +S_IRWXO = 0o007 # other permissions mask +S_IROTH = 0o004 # other read +S_IWOTH = 0o002 # other write +S_IXOTH = 0o001 # other execute vollog = logging.getLogger(__name__) @@ -83,7 +83,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] description='Sort files by path', optional=True) ] - + @classmethod def create_mount_filter(cls, mnt_list: List[int] = None) -> Callable[[Any], bool]: """Constructs a filter function for mount IDs. @@ -104,7 +104,7 @@ def filter_func(mount): return filter_func else: return lambda _: False - + @classmethod def create_path_filter(cls, path) -> Callable[[Any], bool]: """Constructs a filter function for file paths. @@ -118,9 +118,9 @@ def create_path_filter(cls, path) -> Callable[[Any], bool]: def filter_func(x): return not x.startswith(path) - + return filter_func - + @classmethod def create_uid_filter(cls, uid_list: List[int] = None) -> Callable[[Any], bool]: """Constructs a filter function for owner UIDs. @@ -137,13 +137,13 @@ def create_uid_filter(cls, uid_list: List[int] = None) -> Callable[[Any], bool]: def filter_func(uid): return uid not in filter_list - + return filter_func else: return lambda _: False @classmethod - def _mode_to_str(cls, mode:int) -> str: + def _mode_to_str(cls, mode: int) -> str: """Calculate the mode string (see http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/string/strmode.c?rev=1.16&content-type=text/x-cvsweb-markup)""" string = '' @@ -165,7 +165,7 @@ def _mode_to_str(cls, mode:int) -> str: string += 'p' else: string += '?' - + # get user permissions string += 'r' if mode & S_IRUSR else '-' string += 'w' if mode & S_IWUSR else '-' @@ -178,7 +178,7 @@ def _mode_to_str(cls, mode:int) -> str: string += 'S' elif user_execute == S_IXUSR | S_ISUID: string += 's' - + # get group permissions string += 'r' if mode & S_IRGRP else '-' string += 'w' if mode & S_IWGRP else '-' @@ -219,7 +219,8 @@ def get_file_info(cls, A mount and task are required for path calculation. """ # get file path - path = symbols.linux.LinuxUtilities.prepend_path(dentry, mount, task.fs.root) + path = symbols.linux.LinuxUtilities.prepend_path( + dentry, mount, task.fs.root) if path is None: path = '' @@ -236,7 +237,7 @@ def get_file_info(cls, inode_id = -1 inode_addr = 0 inode = None - + # get file info mode = '' uid = -1 @@ -262,7 +263,7 @@ def get_file_info(cls, accessed = inode.i_atime.tv_sec return mnt_id, inode_id, inode_addr, mode, uid, gid, size, created, modified, accessed, path - + @classmethod def _walk_dentry(cls, context: interfaces.context.ContextInterface, @@ -289,15 +290,16 @@ def _walk_dentry(cls, # walk subdirs linked list for subdir_dentry in dentry.d_subdirs.to_list(symbol_table + constants.BANG + 'dentry', 'd_child'): # walk subdir dentry - cls._walk_dentry(context, vmlinux_module_name, dentry_set, subdir_dentry) - + cls._walk_dentry(context, vmlinux_module_name, + dentry_set, subdir_dentry) + @classmethod def get_dentries(cls, context: interfaces.context.ContextInterface, vmlinux_module_name: str, pid_filter: Callable[[Any], bool] = None, mnt_filter: Callable[[Any], bool] = lambda _: False, - all:bool = False) -> Tuple[symbols.linux.extensions.mount, List[symbols.linux.extensions.dentry]]: + all: bool = False) -> Tuple[symbols.linux.extensions.mount, List[symbols.linux.extensions.dentry]]: """Get a list of all cached dentries in the filesystem that match the given filters.""" # make sure pid_filter and all aren't used together if all and pid_filter is not None: @@ -313,17 +315,21 @@ def get_dentries(cls, pid_filter = pslist.PsList.create_pid_filter([1]) if all: - non_filtered_mounts = mount_plugin.Mount.get_all_mounts(context, vmlinux_module_name) + non_filtered_mounts = mount_plugin.Mount.get_all_mounts( + context, vmlinux_module_name) else: - non_filtered_mounts = mount_plugin.Mount.get_mounts(context, vmlinux_module_name, pid_filter) - + non_filtered_mounts = mount_plugin.Mount.get_mounts( + context, vmlinux_module_name, pid_filter) + # filter out mounts - mounts = [(task, mount) for task, mount in non_filtered_mounts if not mnt_filter(mount)] + mounts = [(task, mount) + for task, mount in non_filtered_mounts if not mnt_filter(mount)] num_mounts = len(mounts) for i, (task, mount) in enumerate(mounts): - vollog.info(f'[{i}/{num_mounts}] listing files for mount ID {mount.mnt_id}') - + vollog.info( + f'[{i}/{num_mounts}] listing files for mount ID {mount.mnt_id}') + # set of dentry addresses for this mount mount_dentries = set() @@ -331,15 +337,17 @@ def get_dentries(cls, root_dentry = mount.get_mnt_root().dereference() # walk root dentry and extract all dentries recursively - cls._walk_dentry(context, vmlinux_module_name, mount_dentries, root_dentry) + cls._walk_dentry(context, vmlinux_module_name, + mount_dentries, root_dentry) # add dentries for this mount to global list for dentry_ptr in mount_dentries: - dentry = vmlinux.object(object_type='dentry', offset=dentry_ptr, absolute=True) + dentry = vmlinux.object( + object_type='dentry', offset=dentry_ptr, absolute=True) dentries.append((task, mount, dentry)) - + return dentries - + def _generator(self): # create path and UID filters path_filter = self.create_path_filter(self.config.get('path', None)) @@ -351,7 +359,7 @@ def _generator(self): pid_filter = pslist.PsList.create_pid_filter(pids) else: pid_filter = None - + # if a mount list was specified but PID list wasn't, extract all mounts to search for the requested mounts if self.config.get('mount') and not pids: # TODO: build a list of all PIDs using pslist, extract mounts using this list as the filter, and use the requested mounts (which come with a task struct) @@ -365,7 +373,8 @@ def _generator(self): dentries = self.get_dentries(context=self.context, vmlinux_module_name=self.config['kernel'], pid_filter=pid_filter, - mnt_filter=self.create_mount_filter(self.config.get('mount', None)), + mnt_filter=self.create_mount_filter( + self.config.get('mount', None)), all=all) num_dentries = len(dentries) @@ -373,7 +382,8 @@ def _generator(self): for i, (task, mount, dentry) in enumerate(dentries): # print info message every 1000 files if i % 1000 == 0: - vollog.info(f'[{i}/{num_dentries}] extracting file info and filtering paths') + vollog.info( + f'[{i}/{num_dentries}] extracting file info and filtering paths') info = self.get_file_info(task, mount, dentry) # info could not be extracted @@ -384,7 +394,7 @@ def _generator(self): # apply path and UID filters if not path_filter(file_path) and not uid_filter(uid): files[file_path] = mnt_id, inode_id, inode_addr, mode, uid, gid, size, created, modified, accessed, file_path - + paths = list(files.keys()) # sort files by path if self.config.get('sort', None): @@ -392,12 +402,14 @@ def _generator(self): paths.sort() vollog.info('done sorting') for path in paths: - mnt_id, inode_id, inode_addr, mode, uid, gid, size, created, modified, accessed, file_path = files[path] + mnt_id, inode_id, inode_addr, mode, uid, gid, size, created, modified, accessed, file_path = files[ + path] yield (0, (mnt_id, inode_id, format_hints.Hex(inode_addr), mode, uid, gid, size, created, modified, accessed, file_path)) - + def run(self): # make sure 'all' and 'pid' aren't used together if self.config.get('all') and self.config.get('pid'): - raise exceptions.PluginRequirementException('"pid" and "all" cannot be used together') + raise exceptions.PluginRequirementException( + '"pid" and "all" cannot be used together') return renderers.TreeGrid([('Mount ID', int), ('Inode ID', int), ('Inode Address', format_hints.Hex), ('Mode', str), ('UID', int), ('GID', int), ('Size', int), ('Created', int), ('Modified', int), ('Accessed', int), ('File Path', str)], self._generator()) diff --git a/plugins/mount.py b/plugins/mount.py index ee9926f..7af7df7 100644 --- a/plugins/mount.py +++ b/plugins/mount.py @@ -13,35 +13,36 @@ # mount flags - see https://elixir.bootlin.com/linux/v5.15-rc5/source/include/linux/mount.h#L26 MNT_FLAGS = { - 0x1 : "MNT_NOSUID", - 0x2 : "MNT_NODEV", - 0x4 : "MNT_NOEXEC", - 0x8 : "MNT_NOATIME", - 0x10 : "MNT_NODIRATIME", - 0x20 : "MNT_RELATIME", - 0x40 : "MNT_READONLY", - 0x80 : "MNT_NOSYMFOLLOW", - 0x100 : "MNT_SHRINKABLE", - 0x200 : "MNT_WRITE_HOLD", - 0x1000 : "MNT_SHARED", - 0x2000 : "MNT_UNBINDABLE", - 0x4000 : "MNT_INTERNAL", - 0x40000 : "MNT_LOCK_ATIME", - 0x80000 : "MNT_LOCK_NOEXEC", - 0x100000 : "MNT_LOCK_NOSUID", - 0x200000 : "MNT_LOCK_NODEV", - 0x400000 : "MNT_LOCK_READONLY", - 0x800000 : "MNT_LOCKED", - 0x1000000 : "MNT_DOOMED", - 0x2000000 : "MNT_SYNC_UMOUNT", - 0x4000000 : "MNT_MARKED", - 0x8000000 : "MNT_UMOUNT", - 0x10000000 : "MNT_CURSOR" + 0x1: "MNT_NOSUID", + 0x2: "MNT_NODEV", + 0x4: "MNT_NOEXEC", + 0x8: "MNT_NOATIME", + 0x10: "MNT_NODIRATIME", + 0x20: "MNT_RELATIME", + 0x40: "MNT_READONLY", + 0x80: "MNT_NOSYMFOLLOW", + 0x100: "MNT_SHRINKABLE", + 0x200: "MNT_WRITE_HOLD", + 0x1000: "MNT_SHARED", + 0x2000: "MNT_UNBINDABLE", + 0x4000: "MNT_INTERNAL", + 0x40000: "MNT_LOCK_ATIME", + 0x80000: "MNT_LOCK_NOEXEC", + 0x100000: "MNT_LOCK_NOSUID", + 0x200000: "MNT_LOCK_NODEV", + 0x400000: "MNT_LOCK_READONLY", + 0x800000: "MNT_LOCKED", + 0x1000000: "MNT_DOOMED", + 0x2000000: "MNT_SYNC_UMOUNT", + 0x4000000: "MNT_MARKED", + 0x8000000: "MNT_UMOUNT", + 0x10000000: "MNT_CURSOR" } # for determining access -MNT_READONLY = 0x40 # https://elixir.bootlin.com/linux/v5.15-rc4/source/include/linux/mount.h#L32 -SB_RDONLY = 0x1 # https://elixir.bootlin.com/linux/v5.15-rc4/source/include/linux/fs.h#L1394 +# https://elixir.bootlin.com/linux/v5.15-rc4/source/include/linux/mount.h#L32 +MNT_READONLY = 0x40 +SB_RDONLY = 0x1 # https://elixir.bootlin.com/linux/v5.15-rc4/source/include/linux/fs.h#L1394 vollog = logging.getLogger(__name__) @@ -73,8 +74,8 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] description='Sort mounts by mount ID', optional=True, default=False) - ] - + ] + @classmethod def get_all_mounts(cls, context: interfaces.context.ContextInterface, @@ -94,33 +95,37 @@ def get_all_mounts(cls, else: mnt_type = 'vfsmount' mount_hashtable_type = 'list_head' - + # in kernel < 3.13.9 mount_hashtable size is predefined if mount_hashtable_type == 'list_head': list_head_size = vmlinux.get_type('list_head').size page_size = layer.page_size - mount_hashtable_entries = 1 << int(math.log(page_size/list_head_size, 2)) - + mount_hashtable_entries = 1 << int( + math.log(page_size/list_head_size, 2)) + # in kernel >= 3.13.9 mount_hashtable size is determined at boot time else: try: # mhash_entries is initialized from boot parameters during setup # and is used to allocate memory for mount_hashtable - mount_hashtable_entries = vmlinux.object_from_symbol('mhash_entries') + mount_hashtable_entries = vmlinux.object_from_symbol( + 'mhash_entries') # sometimes mhash_entries isn't available (it is marked in the linux source # as __init_data which means it may be deallocated at some point) except exceptions.PagedInvalidAddressException: # m_hash_mask is the binary mask of the number of entries - mount_hashtable_entries = vmlinux.object_from_symbol('m_hash_mask') + 1 - + mount_hashtable_entries = vmlinux.object_from_symbol( + 'm_hash_mask') + 1 + vollog.info(f'mount_hashtable entries: {mount_hashtable_entries}') mount_hashtable_ptr = vmlinux.object_from_symbol('mount_hashtable') mount_hashtable = vmlinux.object(object_type='array', - offset=mount_hashtable_ptr, - subtype=vmlinux.get_type(mount_hashtable_type), - count=mount_hashtable_entries, - absolute=True) + offset=mount_hashtable_ptr, + subtype=vmlinux.get_type( + mount_hashtable_type), + count=mount_hashtable_entries, + absolute=True) # iterate through mount_hashtable for hash in mount_hashtable: @@ -166,16 +171,17 @@ def get_mounts(cls, try: mnt_ns = task.get_mnt_ns() except AttributeError as ex: - vollog.error(f'No mount namespace information available: {str(ex)}') + vollog.error( + f'No mount namespace information available: {str(ex)}') return - + # get identifier for mnt_ns try: identifier = mnt_ns.get_inum() # in kernel < 3.8 mnt_namespace has no inum, track address of the mnt_namespace struct instead except AttributeError: identifier = mnt_ns.vol.offset - + # make sure we haven't seen this namespace yet if identifier in seen_mnt_namespaces: continue @@ -191,7 +197,7 @@ def get_mounts(cls, def get_mount_info(cls, context: interfaces.context.ContextInterface, vmlinux_module_name: str, - mount:symbols.linux.extensions.mount, + mount: symbols.linux.extensions.mount, task: symbols.linux.extensions.task_struct) -> Tuple[int, str, str, str, str, str, str]: """Parse a mount and return the following tuple: id, devname, path, absolute_path, fstype, access, flags @@ -210,7 +216,8 @@ def get_mount_info(cls, devname = utility.pointer_to_string(mount.mnt_devname, MAX_STRING) # get path - path = symbols.linux.LinuxUtilities.prepend_path(mount.get_mnt_root().dereference(), mount, task.fs.root) + path = symbols.linux.LinuxUtilities.prepend_path( + mount.get_mnt_root().dereference(), mount, task.fs.root) if path is None: path = '' @@ -220,7 +227,7 @@ def get_mount_info(cls, # when a mount has a master, its absolute path is the master's path if mount.mnt_master != 0: root_mnt = mount.mnt_master.dereference() - + # otherwise, the mount's absolute path is calculated by treating its root as belonging to the absolute fs root mount else: root_mnt = init_task.fs.root.mnt.dereference() @@ -228,13 +235,15 @@ def get_mount_info(cls, dentry = mount.get_mnt_root().dereference() # the absolute path is calculated relative to the fs root of the init task - absolute_path = symbols.linux.LinuxUtilities.prepend_path(dentry, root_mnt, init_task.fs.root) + absolute_path = symbols.linux.LinuxUtilities.prepend_path( + dentry, root_mnt, init_task.fs.root) # if absolute path could not be calculated, the mount is independent from the fs root if absolute_path is None: absolute_path = '-' # get fs type - fs_type = utility.pointer_to_string(mount.get_mnt_sb().dereference().s_type.dereference().name, MAX_STRING) + fs_type = utility.pointer_to_string( + mount.get_mnt_sb().dereference().s_type.dereference().name, MAX_STRING) # get access mnt_flags = mount.get_mnt_flags() @@ -255,7 +264,7 @@ def get_mount_info(cls, flags.append(MNT_FLAGS[flag]) except KeyError: flags.append(f'FLAG_{hex(flag)}') - + return mnt_id, parent_id, devname, path, absolute_path, fs_type, access, ','.join(flags) def _generator(self): @@ -269,8 +278,9 @@ def _generator(self): if not pids: pids = [1] pid_filter = pslist.PsList.create_pid_filter(pids) - mounts = self.get_mounts(self.context, self.config['kernel'], pid_filter) - + mounts = self.get_mounts( + self.context, self.config['kernel'], pid_filter) + # sort mounts by ID if self.config.get('sort', False): mounts_by_id = {mount.mnt_id: mount for mount in mounts} @@ -280,10 +290,11 @@ def _generator(self): for task, mount in mounts: yield (0, self.get_mount_info(self.context, self.config['kernel'], mount, task=task)) - + def run(self): # make sure 'all' and 'pid' aren't used together if self.config.get('all') and self.config.get('pid'): - raise exceptions.PluginRequirementException('"pid" and "all" cannot be used together') + raise exceptions.PluginRequirementException( + '"pid" and "all" cannot be used together') return renderers.TreeGrid([('Mount ID', int), ('Parent ID', int), ('Devname', str), ('Path', str), ('Absolute Path', str), ('FS Type', str), ('Access', str), ('Flags', str)], self._generator()) diff --git a/plugins/pslist.py b/plugins/pslist.py index c610105..153d710 100644 --- a/plugins/pslist.py +++ b/plugins/pslist.py @@ -45,14 +45,16 @@ class TaskInfo: cap_bnd: int = -1 def tuple(self, nsinfo: bool = False, credinfo: bool = False): - time_str = datetime.utcfromtimestamp(self.start_time).isoformat(sep=' ', timespec='milliseconds') + time_str = datetime.utcfromtimestamp( + self.start_time).isoformat(sep=' ', timespec='milliseconds') lst = [self.pid, self.ppid, self.name, time_str] if nsinfo: - lst.extend([self.pid_in_ns, self.uts_ns, self.ipc_ns, self.mnt_ns, self.net_ns, self.pid_ns, self.user_ns]) + lst.extend([self.pid_in_ns, self.uts_ns, self.ipc_ns, + self.mnt_ns, self.net_ns, self.pid_ns, self.user_ns]) if credinfo: lst.extend([self.real_uid, self.real_gid, self.eff_uid, self.eff_gid, format_hints.Hex(self.cap_inh), format_hints.Hex(self.cap_prm), format_hints.Hex(self.cap_eff), format_hints.Hex(self.cap_bnd)]) - + return tuple(lst) @@ -71,7 +73,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] requirements.ListRequirement(name='pid', description='Filter on specific process IDs', element_type=int, - optional = True), + optional=True), requirements.BooleanRequirement(name='nsinfo', description='Display namespace information', optional=True, @@ -126,7 +128,7 @@ def get_task_info(cls, if boot_time is None: boot_time = symbols.linux.LinuxUtilities.get_boot_time(vmlinux) info.start_time = task.get_start_time(boot_time) - + # extract namespace information if nsinfo: # Get namespace IDs. @@ -141,7 +143,7 @@ def get_task_info(cls, info.uts_ns = nsproxy.get_uts_ns().get_inum() except (AttributeError, exceptions.PagedInvalidAddressException): info.uts_ns = -1 - + # get ipc namespace try: info.ipc_ns = nsproxy.get_ipc_ns().get_inum() @@ -153,35 +155,36 @@ def get_task_info(cls, info.mnt_ns = nsproxy.get_mnt_ns().get_inum() except (AttributeError, exceptions.PagedInvalidAddressException): info.mnt_ns = -1 - + # get net namespace try: info.net_ns = nsproxy.get_net_ns().get_inum() except (AttributeError, exceptions.PagedInvalidAddressException): info.net_ns = -1 - + # get pid namespace try: info.pid_ns = task.get_pid_ns().get_inum() except (AttributeError, exceptions.PagedInvalidAddressException): info.pid_ns = -1 - + # get user namespace try: info.user_ns = nsproxy.get_user_ns().get_inum() except (AttributeError, exceptions.PagedInvalidAddressException): info.user_ns = -1 - + # get pid from within the namespace try: info.pid_in_ns = task.get_namespace_pid() except (AttributeError, exceptions.PagedInvalidAddressException): info.pid_in_ns = -1 - + # no task -> nsproxy else: - vollog.error('Unable to extract namespace information (no task -> nsproxy member)') - + vollog.error( + 'Unable to extract namespace information (no task -> nsproxy member)') + # extract credentials and capability information if credinfo: cred = task.cred.dereference() @@ -229,8 +232,9 @@ def _generator(self): for task in self.list_tasks(self.context, self.config['kernel'], - filter_func = self.create_pid_filter(self.config.get('pid', None))): - taskinfo = self.get_task_info(self.context, self.config['kernel'], task, boot_time=boot_time, nsinfo=nsinfo, credinfo=credinfo) + filter_func=self.create_pid_filter(self.config.get('pid', None))): + taskinfo = self.get_task_info( + self.context, self.config['kernel'], task, boot_time=boot_time, nsinfo=nsinfo, credinfo=credinfo) yield (0, taskinfo.tuple(nsinfo=nsinfo, credinfo=credinfo)) @classmethod @@ -250,7 +254,7 @@ def list_tasks( """ vmlinux = context.modules[vmlinux_module_name] - init_task = vmlinux.object_from_symbol(symbol_name = "init_task") + init_task = vmlinux.object_from_symbol(symbol_name="init_task") # Note that the init_task itself is not yielded, since "ps" also never shows it. for task in init_task.tasks: @@ -258,7 +262,8 @@ def list_tasks( yield task def run(self): - columns = [('PID', int), ('PPID', int), ('COMM', str), ('Start Time (UTC)', str)] + columns = [('PID', int), ('PPID', int), + ('COMM', str), ('Start Time (UTC)', str)] if self.config.get('nsinfo', False): columns.extend([('PID in NS', int), ('UTS NS', int), ('IPC NS', int),