Move all core plugins over to framework/plugins.

This should have no impact functionality-wise.
The statistics plugin was left out a) as an example and b) because it
was committed by mistake in the first place and was never meant to be a
real plugin.
This commit is contained in:
Mike Auty
2018-12-16 13:40:15 +00:00
parent 9824538bd9
commit 35ad2325a8
38 changed files with 9 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
import datetime
import struct
from typing import List
from volatility.framework import constants, renderers, symbols, interfaces
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.layers import scanners
from volatility.framework.objects import utility
from volatility.framework.symbols.linux.bash import BashIntermedSymbols
from volatility.plugins import timeliner
from volatility.plugins.linux import pslist
class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
"""Recovers bash command history from memory"""
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
def _generator(self, tasks):
is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["vmlinux"])
if is_32bit:
pack_format = "I"
bash_json_file = "bash32"
else:
pack_format = "Q"
bash_json_file = "bash64"
bash_table_name = BashIntermedSymbols.create(self.context,
self.config_path,
"linux",
bash_json_file)
ts_offset = self.context.symbol_space.get_type(
bash_table_name + constants.BANG + "hist_entry").relative_child_offset("timestamp")
for task in tasks:
task_name = utility.array_to_string(task.comm)
if task_name not in ["bash", "sh", "dash"]:
continue
proc_layer_name = task.add_process_layer()
if not proc_layer_name:
continue
proc_layer = self.context.memory[proc_layer_name]
bang_addrs = []
# find '#' values on the heap
for address in proc_layer.scan(self.context,
scanners.BytesScanner(b"#"),
sections = task.get_process_memory_sections(heap_only = True)):
bang_addrs.append(struct.pack(pack_format, address))
history_entries = []
for address, _ in proc_layer.scan(self.context,
scanners.MultiStringScanner(bang_addrs),
sections = task.get_process_memory_sections(heap_only = True)):
hist = self.context.object(bash_table_name + constants.BANG + "hist_entry",
offset = address - ts_offset,
layer_name = proc_layer_name)
if hist.is_valid():
history_entries.append(hist)
for hist in sorted(history_entries, key = lambda x: x.get_time_as_integer()):
yield (0, (task.pid, task_name, hist.get_time_object(), hist.get_command()))
def run(self):
filt = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
("CommandTime", datetime.datetime),
("Command", str)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filt)))
def generate_timeline(self):
filt = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
for row in self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filt)):
_depth, row_data = row
description = "{} ({}): \"{}\"".format(row_data[0], row_data[1], row_data[3])
yield (description, timeliner.TimeLinerType.CREATED, row_data[2])
@@ -0,0 +1,96 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
import logging
from typing import List
from volatility.framework import exceptions, interfaces
from volatility.framework import renderers
from volatility.framework.automagic import linux
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.renderers import format_hints
vollog = logging.getLogger(__name__)
class Check_afinfo(plugins.PluginInterface):
"""Verifies the operation function pointers of network protocols"""
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
# returns whether the symbol is found within the kernel (system.map) or not
def _is_known_address(self, handler_addr):
symbols = list(self.context.symbol_space.get_symbols_by_location(handler_addr))
return len(symbols) > 0
def _check_members(self, var_ops, var_name, members):
for check in members:
# redhat-specific garbage
if check.startswith("__UNIQUE_ID_rh_kabi_hide"):
continue
if check == "write":
addr = var_ops.member(attr = 'write')
else:
addr = getattr(var_ops, check)
if addr and addr != 0 and not self._is_known_address(addr):
yield check, addr
def _check_afinfo(self, var_name, var, op_members, seq_members):
for hooked_member, hook_address in self._check_members(var.seq_fops, var_name, op_members):
yield var_name, hooked_member, hook_address
# newer kernels
if var.has_member("seq_ops"):
for hooked_member, hook_address in self._check_members(var.seq_ops, var_name, seq_members):
yield var_name, hooked_member, hook_address
# this is the most commonly hooked member by rootkits, so a force a check on it
elif not self._is_known_address(var.seq_show):
yield var_name, "show", var.seq_show
def _generator(self):
_, aslr_shift = linux.LinuxUtilities.find_aslr(self.context, self.config['vmlinux'], self.config['primary'])
vmlinux = self.context.module(self.config['vmlinux'], self.config['primary'], aslr_shift)
linux.LinuxUtilities.aslr_mask_symbol_table(self.context,
self.config['primary'],
self.config['vmlinux'],
aslr_shift)
op_members = vmlinux.get_type('file_operations').members
seq_members = vmlinux.get_type('seq_operations').members
tcp = ("tcp_seq_afinfo", ["tcp6_seq_afinfo", "tcp4_seq_afinfo"])
udp = ("udp_seq_afinfo", ["udplite6_seq_afinfo", "udp6_seq_afinfo", "udplite4_seq_afinfo", "udp4_seq_afinfo"])
protocols = [tcp, udp]
for (struct_type, global_vars) in protocols:
for global_var_name in global_vars:
# this will lookup fail for the IPv6 protocols on kernels without IPv6 support
try:
global_var = vmlinux.get_symbol(global_var_name)
except exceptions.SymbolError:
continue
global_var = vmlinux.object(type_name = struct_type, offset = global_var.address)
for name, member, address in self._check_afinfo(global_var_name, global_var, op_members, seq_members):
yield 0, (name, member, format_hints.Hex(address))
def run(self):
return renderers.TreeGrid(
[("Symbol Name", str),
("Member", str),
("Handler Address", format_hints.Hex)],
self._generator())
@@ -0,0 +1,187 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
import logging
from typing import List
from volatility.framework import exceptions, interfaces
from volatility.framework import renderers, constants
from volatility.framework.automagic import linux
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.renderers import format_hints
vollog = logging.getLogger(__name__)
try:
import capstone
has_capstone = True
except ImportError:
has_capstone = False
class Check_syscall(plugins.PluginInterface):
"""Check system call table for hooks"""
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
def _get_table_size_next_symbol(self, table_addr, ptr_sz, vmlinux):
"""
Returns the size of the table based on the next symbol
"""
ret = 0
sym_table = self.context.symbol_space[vmlinux.name]
sorted_symbols = sorted([(sym_table.get_symbol(sn).address, sn) for sn in sym_table.symbols])
sym_address = 0
for tmp_sym_address, sym_name in sorted_symbols:
if tmp_sym_address > table_addr:
sym_address = tmp_sym_address
break
if sym_address > 0:
ret = int((sym_address - table_addr) / ptr_sz)
return ret
def _get_table_size_meta(self, vmlinux):
"""
returns the number of symbols that start with __syscall_meta__
this is a fast way to determine the number of system calls, but not the most accurate
"""
return len(
[sym for sym in self.context.symbol_space[vmlinux.name].symbols if sym.startswith("__syscall_meta__")])
def _get_table_info_other(self, table_addr, ptr_sz, vmlinux):
table_size_meta = self._get_table_size_meta(vmlinux)
table_size_syms = self._get_table_size_next_symbol(table_addr, ptr_sz, vmlinux)
sizes = [size for size in [table_size_meta, table_size_syms] if size > 0]
table_size = min(sizes)
return table_size
def _get_table_info_disassembly(self, ptr_sz, vmlinux):
"""
Find the size of the system call table by disassembling functions
that immediately reference it in their first isntruction
This is in the form 'cmp reg,NR_syscalls'
"""
table_size = 0
if not has_capstone:
return table_size
if ptr_sz == 4:
syscall_entry_func = "sysenter_do_call"
mode = capstone.CS_MODE_32
else:
syscall_entry_func = "system_call_fastpath"
mode = capstone.CS_MODE_64
md = capstone.Cs(capstone.CS_ARCH_X86, mode)
try:
func_addr = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + syscall_entry_func).address
except exceptions.SymbolError as e:
# if we can't find the disassemble function then bail and rely on a different method
return 0
data = self.context.memory.read(self.config['primary'], func_addr, 6)
for (address, size, mnemonic, op_str) in md.disasm_lite(data, func_addr):
if mnemonic == 'CMP':
table_size = int(op_str.split(",")[1].strip()) & 0xffff
break
return table_size
def _get_table_info(self, vmlinux, table_name, ptr_sz):
table_sym = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + table_name)
table_size = self._get_table_info_disassembly(ptr_sz, vmlinux)
if table_size == 0:
table_size = self._get_table_info_other(table_sym.address, ptr_sz, vmlinux)
if table_size == 0:
vollog.error("Unable to get system call table size")
return 0, 0
return table_sym.address, table_size
# TODO - add finding and parsing unistd.h once cached file enumeration is added
def _generator(self):
_, aslr_shift = linux.LinuxUtilities.find_aslr(self.context, self.config['vmlinux'], self.config['primary'])
vmlinux = self.context.module(self.config['vmlinux'], self.config['primary'], aslr_shift)
linux.LinuxUtilities.aslr_mask_symbol_table(self.context,
self.config['vmlinux'],
self.config['primary'],
aslr_shift)
ptr_sz = vmlinux.get_type("pointer").size
if ptr_sz == 4:
table_name = "32bit"
else:
table_name = "64bit"
try:
table_info = self._get_table_info(vmlinux, "sys_call_table", ptr_sz)
except exceptions.SymbolError:
vollog.error("Unable to find the system call table. Exiting.")
return
tables = [(table_name, table_info)]
# this table is only present on 64 bit systems with 32 bit emulation
# enabled in order to support 32 bit programs and libraries
# if the symbol isn't there then the support isn't in the kernel and so we skip it
try:
ia32_symbol = self.context.symbol_space.get_symbol(vmlinux.name + constants.BANG + "ia32_sys_call_table")
except exceptions.SymbolError:
ia32_symbol = None
if ia32_symbol != None:
ia32_info = self._get_table_info(vmlinux, "ia32_sys_call_table", ptr_sz)
tables.append(("32bit", ia32_info))
for (table_name, (tableaddr, tblsz)) in tables:
table = vmlinux.object(type_name = "array", subtype = vmlinux.get_type("pointer"),
offset = tableaddr, count = tblsz)
for (i, call_addr) in enumerate(table):
if not call_addr:
continue
symbols = list(self.context.symbol_space.get_symbols_by_location(call_addr))
if len(symbols) > 0:
sym_name = str(symbols[0].split(constants.BANG)[1]) if constants.BANG in symbols[0] else \
str(symbols[0])
else:
sym_name = "UNKNOWN"
yield (0, (format_hints.Hex(tableaddr), table_name, i, format_hints.Hex(call_addr), sym_name))
def run(self):
return renderers.TreeGrid(
[("Table Address", format_hints.Hex),
("Table Name", str),
("Index", int),
("Handler Address", format_hints.Hex),
("Handler Symbol", str)],
self._generator())
@@ -0,0 +1,66 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
from typing import List
from volatility.framework import renderers, interfaces
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.plugins.linux import pslist
class Elfs(plugins.PluginInterface):
"""Lists all memory mapped ELF files for all processes"""
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
def _generator(self, tasks):
for task in tasks:
proc_layer_name = task.add_process_layer()
if not proc_layer_name:
continue
proc_layer = self.context.memory[proc_layer_name]
name = utility.array_to_string(task.comm)
for vma in task.mm.mmap_iter:
hdr = proc_layer.read(vma.vm_start, 4, pad = True)
if not (hdr[0] == 0x7f and hdr[1] == 0x45 and hdr[2] == 0x4c and hdr[3] == 0x46):
continue
path = vma.get_name(task)
yield (
0,
(task.pid,
name,
format_hints.Hex(vma.vm_start),
format_hints.Hex(vma.vm_end),
path
))
def run(self):
filt = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
("Start", format_hints.Hex),
("End", format_hints.Hex),
("File Path", str)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filt)))
@@ -0,0 +1,59 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
from volatility.framework import renderers, constants, interfaces
from volatility.framework.automagic import linux
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
class Lsmod(plugins.PluginInterface):
"""Lists loaded kernel modules"""
@classmethod
def get_requirements(cls):
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
@classmethod
def list_modules(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
vmlinux_symbols: str):
"""Lists all the modules in the primary layer"""
_, aslr_shift = linux.LinuxUtilities.find_aslr(context, vmlinux_symbols, layer_name)
vmlinux = context.module(vmlinux_symbols, layer_name, aslr_shift)
module_head_addr = vmlinux.get_symbol("modules").address
modules = vmlinux.object(type_name = "list_head", offset = module_head_addr)
table_name = modules.vol.type_name.split(constants.BANG)[0]
for module in modules.to_list("{}{}module".format(table_name, constants.BANG), "list"):
yield module
def _generator(self):
for module in self.list_modules(self.context,
self.config['primary'],
self.config['vmlinux']):
mod_size = module.get_init_size() + module.get_core_size()
mod_name = utility.array_to_string(module.name)
yield 0, (format_hints.Hex(module.vol.offset), mod_name, mod_size)
def run(self):
return renderers.TreeGrid(
[("Offset", format_hints.Hex),
("Name", str),
("Size", int)],
self._generator())
@@ -0,0 +1,51 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
import logging
from volatility.framework import renderers
from volatility.framework.automagic import linux
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.plugins.linux import pslist
vollog = logging.getLogger(__name__)
class Lsof(plugins.PluginInterface):
"""Lists all memory maps for all processes"""
@classmethod
def get_requirements(cls):
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
def _generator(self, tasks):
for task in tasks:
name = utility.array_to_string(task.comm)
pid = int(task.pid)
for fd_num, _, full_path in linux.LinuxUtilities.files_descriptors_for_process(self.config, self.context,
task):
yield (0, (pid, name, fd_num, full_path))
def run(self):
linux.LinuxUtilities.aslr_mask_symbol_table(self.context, self.config['vmlinux'], self.config['primary'])
filter = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
("FD", int),
("Path", str)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filter)))
@@ -0,0 +1,81 @@
from typing import List
import volatility.framework.interfaces.plugins as interfaces_plugins
import volatility.framework.interfaces.renderers as interfaces_renderers
import volatility.plugins.linux.pslist as pslist
from volatility.framework import constants, interfaces
from volatility.framework import renderers
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
class Malfind(interfaces_plugins.PluginInterface):
"""Lists process memory ranges that potentially contain injected code"""
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
def list_injections(self, task):
"""Generate memory regions for a process that may contain
injected code.
"""
proc_layer_name = task.add_process_layer()
if not proc_layer_name:
return
proc_layer = self.context.memory[proc_layer_name]
for vma in task.mm.mmap_iter:
if vma.is_suspicious() and vma.get_name(task) != "[vdso]":
data = proc_layer.read(vma.vm_start, 64, pad = True)
yield vma, data
def _generator(self, tasks):
# determine if we're on a 32 or 64 bit kernel
if self.context.symbol_space.get_type(self.config["vmlinux"] + constants.BANG + "pointer").size == 4:
is_32bit_arch = True
else:
is_32bit_arch = False
for task in tasks:
process_name = utility.array_to_string(task.comm)
for vma, data in self.list_injections(task):
if is_32bit_arch:
architecture = "intel"
else:
architecture = "intel64"
disasm = interfaces_renderers.Disassembly(data, vma.vm_start, architecture)
yield (0, (task.pid,
process_name,
format_hints.Hex(vma.vm_start),
format_hints.Hex(vma.vm_end),
vma.get_protection(),
format_hints.HexBytes(data),
disasm))
def run(self):
filt = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid([("PID", int),
("Process", str),
("Start", format_hints.Hex),
("End", format_hints.Hex),
("Protection", str),
("Hexdump", format_hints.HexBytes),
("Disasm", interfaces_renderers.Disassembly)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filt)))
@@ -0,0 +1,82 @@
"""A module containing a collection of plugins that produce data
typically found in Linux's /proc file system.
"""
from volatility.framework import renderers
from volatility.framework.configuration import requirements
from volatility.framework.interfaces import plugins
from volatility.framework.objects import utility
from volatility.framework.renderers import format_hints
from volatility.plugins.linux import pslist
class Maps(plugins.PluginInterface):
"""Lists all memory maps for all processes"""
@classmethod
def get_requirements(cls):
# Since we're calling the plugin, make sure we have the plugin's requirements
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
def _generator(self, tasks):
for task in tasks:
if not task.mm:
continue
name = utility.array_to_string(task.comm)
for vma in task.mm.mmap_iter:
flags = vma.get_protection()
page_offset = vma.get_page_offset()
major = 0
minor = 0
inode = 0
if vma.vm_file != 0:
dentry = vma.vm_file.get_dentry()
if dentry != 0:
inode_object = dentry.d_inode
major = inode_object.i_sb.major
minor = inode_object.i_sb.minor
inode = inode_object.i_ino
path = vma.get_name(task)
yield (
0,
(task.pid,
name,
format_hints.Hex(vma.vm_start),
format_hints.Hex(vma.vm_end),
flags,
format_hints.Hex(page_offset),
major,
minor,
inode,
path
))
def run(self):
filter = pslist.PsList.create_filter([self.config.get('pid', None)])
plugin = pslist.PsList.list_tasks
return renderers.TreeGrid(
[("PID", int),
("Process", str),
("Start", format_hints.Hex),
("End", format_hints.Hex),
("Flags", str),
("PgOff", format_hints.Hex),
("Major", int),
("Minor", int),
("Inode", int),
("File Path", str)],
self._generator(plugin(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = filter)))
@@ -0,0 +1,67 @@
from typing import Callable, Iterable, List
import volatility.framework.interfaces.plugins as interfaces_plugins
from volatility.framework import renderers, interfaces
from volatility.framework.automagic import linux
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
class PsList(interfaces_plugins.PluginInterface):
"""Lists the processes present in a particular linux memory image"""
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [requirements.TranslationLayerRequirement(name = 'primary',
description = 'Kernel Address Space',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolRequirement(name = "vmlinux",
description = "Linux Kernel")]
@classmethod
def create_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]:
# FIXME: mypy #4973 or #2608
pid_list = pid_list or []
filter_list = [x for x in pid_list if x is not None]
if filter_list:
def filter_func(x):
return x not in filter_list
return filter_func
else:
return lambda _: False
def _generator(self):
for task in self.list_tasks(self.context,
self.config['primary'],
self.config['vmlinux'],
filter = self.create_filter([self.config.get('pid', None)])):
pid = task.pid
ppid = 0
if task.parent:
ppid = task.parent.pid
name = utility.array_to_string(task.comm)
yield (0, (pid, ppid, name))
@classmethod
def list_tasks(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
vmlinux_symbols: str,
filter: Callable[[int], bool] = lambda _: False) -> Iterable[interfaces.objects.ObjectInterface]:
"""Lists all the tasks in the primary layer"""
_, aslr_shift = linux.LinuxUtilities.find_aslr(context, vmlinux_symbols, layer_name)
vmlinux = context.module(vmlinux_symbols, layer_name, aslr_shift)
init_task = vmlinux.object(symbol_name = "init_task")
for task in init_task.tasks:
if not filter(task):
yield task
def run(self):
return renderers.TreeGrid([("PID", int),
("PPID", int),
("COMM", str)],
self._generator())
@@ -0,0 +1,51 @@
from volatility.framework.objects import utility
from volatility.plugins.linux import pslist
class PsTree(pslist.PsList):
"""Plugin for listing processes in a tree based on their parent process ID """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._processes = {}
self._levels = {}
self._children = {}
def find_level(self, pid):
"""Finds how deep the pid is in the processes list"""
seen = set([])
seen.add(pid)
level = 0
proc = self._processes.get(pid, None)
while proc is not None and proc.parent != 0 and proc.parent.pid not in seen:
ppid = int(proc.parent.pid)
child_list = self._children.get(ppid, set([]))
child_list.add(proc.pid)
self._children[ppid] = child_list
proc = self._processes.get(ppid, None)
level += 1
self._levels[pid] = level
def _generator(self):
"""Generates the """
for proc in self.list_tasks(self.context, self.config['primary'], self.config['vmlinux']):
self._processes[proc.pid] = proc
# Build the child/level maps
for pid in self._processes:
self.find_level(pid)
def yield_processes(pid):
proc = self._processes[pid]
row = (proc.pid,
proc.parent.pid,
utility.array_to_string(proc.comm))
yield (self._levels[pid] - 1, row)
for child_pid in self._children.get(pid, []):
yield from yield_processes(child_pid)
for pid in self._levels:
if self._levels[pid] == 1:
yield from yield_processes(pid)