1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# September 30 2021, Christian Hopps <chopps@labn.net>
#
# Copyright 2021, LabN Consulting, L.L.C.
#
"""Provides functionality to cleanup processes on posix systems."""
import glob
import logging
import os
import signal
def get_pids_with_env(has_var, has_val=None):
result = {}
for pidenv in glob.iglob("/proc/*/environ"):
pid = pidenv.split("/")[2]
try:
with open(pidenv, "rb") as rfb:
envlist = [
x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0")
]
envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist]
envdict = dict(envlist)
if has_var not in envdict:
continue
if has_val is None:
result[pid] = envdict
elif envdict[has_var] == str(has_val):
result[pid] = envdict
except Exception:
# E.g., process exited and files are gone
pass
return result
def _kill_piddict(pids_by_upid, sig):
ourpid = str(os.getpid())
for upid, pids in pids_by_upid:
logging.info("Sending %s to (%s) of munet pid %s", sig, ", ".join(pids), upid)
for pid in pids:
try:
if pid != ourpid:
cmdline = open(f"/proc/{pid}/cmdline", "r", encoding="ascii").read()
cmdline = cmdline.replace("\x00", " ")
logging.info("killing proc %s (%s)", pid, cmdline)
os.kill(int(pid), sig)
except Exception:
pass
def _get_our_pids():
ourpid = str(os.getpid())
piddict = get_pids_with_env("MUNET_PID", ourpid)
pids = [x for x in piddict if x != ourpid]
if pids:
return {ourpid: pids}
return {}
def _get_other_pids(rundir):
if rundir:
# get only munet pids using the given rundir
piddict = get_pids_with_env("MUNET_RUNDIR", str(rundir))
else:
# Get all munet pids
piddict = get_pids_with_env("MUNET_PID")
unet_pids = {d["MUNET_PID"] for d in piddict.values() if "MUNET_PID" in d}
pids_by_upid = {p: set() for p in unet_pids}
for pid, envdict in piddict.items():
if "MUNET_PID" not in envdict:
continue
unet_pid = envdict["MUNET_PID"]
pids_by_upid[unet_pid].add(pid)
# Filter out any child pid sets whos munet pid is still running
return {x: y for x, y in pids_by_upid.items() if x not in y}
def _get_pids_by_upid(ours, rundir):
if ours:
assert rundir is None
return _get_our_pids()
return _get_other_pids(rundir)
def _cleanup_pids(ours, rundir):
pids_by_upid = _get_pids_by_upid(ours, rundir).items()
if not pids_by_upid:
return
t = "current" if ours else "previous"
logging.info("Reaping %s munet processes", t)
# _kill_piddict(pids_by_upid, signal.SIGTERM)
# # Give them 5 second to exit cleanly
# logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids")
# for _ in range(0, 5):
# pids_by_upid = _get_pids_by_upid(ours).items()
# if not pids_by_upid:
# return
# time.sleep(1)
pids_by_upid = _get_pids_by_upid(ours, rundir).items()
_kill_piddict(pids_by_upid, signal.SIGKILL)
def cleanup_current():
"""Attempt to cleanup preview runs.
Currently this only scans for old processes.
"""
_cleanup_pids(True, None)
def cleanup_previous(rundir=None):
"""Attempt to cleanup preview runs.
Currently this only scans for old processes.
"""
_cleanup_pids(False, rundir)
def is_running_in_rundir(rundir):
return bool(get_pids_with_env("MUNET_RUNDIR", str(rundir)))
|