class cmd: """
Execute a command on local or remote host.
Use bkg() instead to run a command in the background. """ def __init__(self, comm, shell=True, fail=True, ns=None, background=False,
host=None, timeout=5, ksft_wait=None): if ns:
comm = f'ip netns exec {ns} ' + comm
self.comm = comm if host:
self.proc = host.cmd(comm) else: # ksft_wait lets us wait for the background process to fully start, # we pass an FD to the child process, and wait for it to write back. # Similarly term_fd tells child it's time to exit.
pass_fds = ()
env = os.environ.copy() if ksft_wait isnotNone:
rfd, ready_fd = os.pipe()
wait_fd, self.ksft_term_fd = os.pipe()
pass_fds = (ready_fd, wait_fd, )
env["KSFT_READY_FD"] = str(ready_fd)
env["KSFT_WAIT_FD"] = str(wait_fd)
if self.proc.returncode != 0 and fail: if len(stderr) > 0 and stderr[-1] == "\n":
stderr = stderr[:-1] raise CmdExitFailure("Command failed: %s\nSTDOUT: %s\nSTDERR: %s" %
(self.proc.args, stdout, stderr), self)
class bkg(cmd): """
Run a command in the background.
Examples usage:
Run a command on remote host, and wait for it to finish.
This is usually paired with wait_port_listen() to make sure
the command has initialized:
with bkg("socat ...", exit_wait=True, host=cfg.remote) as nc:
...
Run a command and expect it to let us know that it's ready
by writing to a special file descriptor passed via KSFT_READY_FD.
Command will be terminated when we exit the context manager:
def bpftrace(expr, json=None, ns=None, host=None, timeout=None): """
Run bpftrace andreturn map data (if json=True).
The output of bpftrace is inconvenient, so the helper converts
to a dict indexed by map name, e.g.:
{ "@": { ... }, "@map2": { ... },
} """
cmd_arr = ['bpftrace'] # Throw in --quiet if json, otherwise the output has two objects if json:
cmd_arr += ['-f', 'json', '-q'] if timeout:
expr += ' interval:s:' + str(timeout) + ' { exit(); }'
cmd_arr += ['-e', expr]
cmd_obj = cmd(cmd_arr, ns=ns, host=host, shell=False) if json: # bpftrace prints objects as lines
ret = {} for l in cmd_obj.stdout.split('\n'): ifnot l.strip(): continue
one = _json.loads(l) if one.get('type') != 'map': continue for k, v in one["data"].items(): if k.startswith('@'):
k = k.lstrip('@')
ret[k] = v return ret return cmd_obj
def rand_port(type=socket.SOCK_STREAM): """
Get a random unprivileged port. """ with socket.socket(socket.AF_INET6, type) as s:
s.bind(("", 0)) return s.getsockname()[1]
pattern = f":{port:04X} .* " if proto == "tcp": # for tcp protocol additionally check the socket state
pattern += "0A"
pattern = re.compile(pattern)
whileTrue:
data = cmd(f'cat /proc/net/{proto}*', ns=ns, host=host, shell=True).stdout for row in data.split("\n"): if pattern.search(row): return if time.monotonic() > end: raise Exception("Waiting for port listen timed out")
time.sleep(sleep)
def wait_file(fname, test_fn, sleep=0.005, deadline=5, encoding='utf-8'): """
Wait for file contents on the local system to satisfy a condition.
test_fn() should take one argument (file contents) andreturn whether
condition is met. """
end = time.monotonic() + deadline
with open(fname, "r", encoding=encoding) as fp: whileTrue: if test_fn(fp.read()): break
fp.seek(0) if time.monotonic() > end: raise TimeoutError("Wait for file contents failed", fname)
time.sleep(sleep)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.