113 lines
3.4 KiB
Python
113 lines
3.4 KiB
Python
#
|
|
# A simple example plugin implementing a command "ps.zeek" that shows all Zeek
|
|
# processes currently running on any of the configured nodes, including those
|
|
# not controlled by this zeekctl. The latter are marked with "(-)", while "our"
|
|
# processes get a "(+)".
|
|
|
|
import ZeekControl.cmdresult
|
|
import ZeekControl.plugin
|
|
|
|
|
|
class PsZeek(ZeekControl.plugin.Plugin):
|
|
def __init__(self):
|
|
super().__init__(apiversion=1)
|
|
|
|
def name(self):
|
|
return "ps"
|
|
|
|
def pluginVersion(self):
|
|
return 1
|
|
|
|
def commands(self):
|
|
return [
|
|
("zeek", "[<nodes>]", "Show Zeek processes on nodes' systems"),
|
|
]
|
|
|
|
def cmd_custom(self, cmd, args, cmdout):
|
|
results = ZeekControl.cmdresult.CmdResult()
|
|
|
|
assert cmd == "zeek" # Can't be anything else.
|
|
|
|
# Get the nodes the user wants.
|
|
if args:
|
|
nodes, notnodes = self.parseNodes(args)
|
|
for n in notnodes:
|
|
cmdout.error(f"unknown node '{n}'")
|
|
else:
|
|
nodes = self.nodes()
|
|
|
|
if not nodes:
|
|
cmdout.error("No nodes given.")
|
|
results.ok = False
|
|
return results
|
|
|
|
# Get one node for every host running at least one of the nodes. Also
|
|
# records each hosts Zeek PIDs.
|
|
host_nodes = {}
|
|
pids = {}
|
|
|
|
for n in nodes:
|
|
pid = n.getPID()
|
|
if pid:
|
|
p = pids.setdefault(n.host, set())
|
|
p.add(pid)
|
|
|
|
host_nodes[n.host] = n
|
|
|
|
# Build commands to execute.
|
|
|
|
# The grep command grabs the header line and all lines ending in "zeek"
|
|
# (and ignores "run-zeek" that may appear in the output of ps).
|
|
cmd = "POSIXLY_CORRECT=1 ps axco user,pid,ppid,%cpu,%mem,vsz,rss,tt,state,start,time,command | grep -e PID -e '[^-]zeek$'"
|
|
cmds = [(n, cmd) for n in host_nodes.values()]
|
|
cmds.sort(key=lambda n: n[0].name)
|
|
|
|
# Run them in parallel and print output.
|
|
|
|
first_node = True
|
|
|
|
for n, success, output in self.executeParallel(cmds):
|
|
outlines = output.splitlines()
|
|
# Remove stderr output (if any)
|
|
while outlines and not outlines[0].startswith("USER"):
|
|
outlines = outlines[1:]
|
|
|
|
# Print the header line.
|
|
if first_node and outlines:
|
|
cmdout.info(f" {outlines[0]}")
|
|
|
|
if success:
|
|
cmdout.info(f">>> {n.host}")
|
|
else:
|
|
cmdout.error(f">>> {n.host} failed")
|
|
results.ok = False
|
|
|
|
if not outlines:
|
|
continue
|
|
|
|
for line in outlines[1:]:
|
|
m = line.split()
|
|
try:
|
|
pid, ppid = int(m[1]), int(m[2])
|
|
except IndexError:
|
|
cmdout.error(f"unexpected output from ps command: {line}")
|
|
results.ok = False
|
|
continue
|
|
except ValueError as err:
|
|
cmdout.error(f"{err}")
|
|
results.ok = False
|
|
continue
|
|
try:
|
|
known = pid in pids[n.host] or ppid in pids[n.host]
|
|
except KeyError:
|
|
known = False
|
|
|
|
if known:
|
|
cmdout.info(f" (+) {line.strip()}")
|
|
else:
|
|
cmdout.info(f" (-) {line.strip()}")
|
|
|
|
first_node = False
|
|
|
|
return results
|