This commit is contained in:
Alexandre
2025-03-03 07:51:16 +01:00
committed by GitHub
parent 024502eb95
commit 988fb2c8dd

View File

@@ -21,8 +21,8 @@ import fnmatch
import re import re
from types import GeneratorType from types import GeneratorType
__copyright__ = "(C) 2016-2024 Guido U. Draheim, licensed under the EUPL" __copyright__ = "(C) 2016-2025 Guido U. Draheim, licensed under the EUPL"
__version__ = "1.5.8066" __version__ = "1.5.9063"
# | # |
# | # |
@@ -561,9 +561,8 @@ def shutil_truncate(filename):
filedir = os.path.dirname(filename) filedir = os.path.dirname(filename)
if not os.path.isdir(filedir): if not os.path.isdir(filedir):
os.makedirs(filedir) os.makedirs(filedir)
f = open(filename, "w") with open(filename, "w") as f:
f.write("") f.write("")
f.close()
# http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid # http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid
def pid_exists(pid): def pid_exists(pid):
@@ -615,7 +614,8 @@ def _pid_zombie(pid):
raise ValueError('invalid PID 0') raise ValueError('invalid PID 0')
check = _proc_pid_status.format(**locals()) check = _proc_pid_status.format(**locals())
try: try:
for line in open(check): with open(check) as f:
for line in f:
if line.startswith("State:"): if line.startswith("State:"):
return "Z" in line return "Z" in line
except IOError as e: except IOError as e:
@@ -770,7 +770,8 @@ class SystemctlConfigParser(SystemctlConfData):
name, text = "", "" name, text = "", ""
if os.path.isfile(filename): if os.path.isfile(filename):
self._files.append(filename) self._files.append(filename)
for orig_line in open(filename): with open(filename) as f:
for orig_line in f:
if nextline: if nextline:
text += orig_line text += orig_line
if text.rstrip().endswith("\\") or text.rstrip().endswith("\\\n"): if text.rstrip().endswith("\\") or text.rstrip().endswith("\\\n"):
@@ -810,6 +811,8 @@ class SystemctlConfigParser(SystemctlConfData):
else: else:
# hint: an empty line shall reset the value-list # hint: an empty line shall reset the value-list
self.set(section, name, text and text or None) self.set(section, name, text and text or None)
if nextline:
self.set(section, name, text)
return self return self
def read_sysv(self, filename): def read_sysv(self, filename):
""" an LSB header is scanned and converted to (almost) """ an LSB header is scanned and converted to (almost)
@@ -819,7 +822,8 @@ class SystemctlConfigParser(SystemctlConfData):
section = "GLOBAL" section = "GLOBAL"
if os.path.isfile(filename): if os.path.isfile(filename):
self._files.append(filename) self._files.append(filename)
for orig_line in open(filename): with open(filename) as f:
for orig_line in f:
line = orig_line.strip() line = orig_line.strip()
if line.startswith("#"): if line.startswith("#"):
if " BEGIN INIT INFO" in line: if " BEGIN INIT INFO" in line:
@@ -962,7 +966,8 @@ class PresetFile:
return None return None
def read(self, filename): def read(self, filename):
self._files.append(filename) self._files.append(filename)
for line in open(filename): with open(filename) as f:
for line in f:
self._lines.append(line.strip()) self._lines.append(line.strip())
return self return self
def get_preset(self, unit): def get_preset(self, unit):
@@ -1834,7 +1839,8 @@ class Systemctl:
return default return default
try: try:
# some pid-files from applications contain multiple lines # some pid-files from applications contain multiple lines
for line in open(pid_file): with open(pid_file) as f:
for line in f:
if line.strip(): if line.strip():
pid = to_intN(line.strip()) pid = to_intN(line.strip())
break break
@@ -1953,7 +1959,8 @@ class Systemctl:
return status return status
try: try:
if DEBUG_STATUS: logg.debug("reading %s", status_file) if DEBUG_STATUS: logg.debug("reading %s", status_file)
for line in open(status_file): with open(status_file) as f:
for line in f:
if line.strip(): if line.strip():
m = re.match(r"(\w+)[:=](.*)", line) m = re.match(r"(\w+)[:=](.*)", line)
if m: if m:
@@ -2060,7 +2067,6 @@ class Systemctl:
assert isinstance(line, bytes) assert isinstance(line, bytes)
if line.startswith(b"btime"): if line.startswith(b"btime"):
system_btime = float(line.decode().split()[1]) system_btime = float(line.decode().split()[1])
f.closed
if DEBUG_BOOTTIME: if DEBUG_BOOTTIME:
logg.debug(" BOOT 2. System btime secs: %.3f (%s)", system_btime, system_stat) logg.debug(" BOOT 2. System btime secs: %.3f (%s)", system_btime, system_stat)
@@ -2113,7 +2119,8 @@ class Systemctl:
logg.debug("file does not exist: %s", real_file) logg.debug("file does not exist: %s", real_file)
return return
try: try:
for real_line in open(os_path(self._root, env_file)): with open(os_path(self._root, env_file)) as f:
for real_line in f:
line = real_line.strip() line = real_line.strip()
if not line or line.startswith("#"): if not line or line.startswith("#"):
continue continue
@@ -5293,18 +5300,18 @@ class Systemctl:
logg.error(" %s: %s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", unit, section) logg.error(" %s: %s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", unit, section)
errors += 101 errors += 101
if len(usedExecStart) > 1 and haveType != "oneshot": if len(usedExecStart) > 1 and haveType != "oneshot":
logg.error(" %s: there may be only one %s ExecStart statement (unless for 'oneshot' services)." logg.error(" %s: there may be only one %s ExecStart statement (unless for 'oneshot' services)." +
+ "\n\t\t\tYou can use ExecStartPre / ExecStartPost to add additional commands.", unit, section) "\n\t\t\tYou can use ExecStartPre / ExecStartPost to add additional commands.", unit, section)
errors += 1 errors += 1
if len(usedExecStop) > 1 and haveType != "oneshot": if len(usedExecStop) > 1 and haveType != "oneshot":
logg.info(" %s: there should be only one %s ExecStop statement (unless for 'oneshot' services)." logg.info(" %s: there should be only one %s ExecStop statement (unless for 'oneshot' services)." +
+ "\n\t\t\tYou can use ExecStopPost to add additional commands (also executed on failed Start)", unit, section) "\n\t\t\tYou can use ExecStopPost to add additional commands (also executed on failed Start)", unit, section)
if len(usedExecReload) > 1: if len(usedExecReload) > 1:
logg.info(" %s: there should be only one %s ExecReload statement." logg.info(" %s: there should be only one %s ExecReload statement." +
+ "\n\t\t\tUse ' ; ' for multiple commands (ExecReloadPost or ExedReloadPre do not exist)", unit, section) "\n\t\t\tUse ' ; ' for multiple commands (ExecReloadPost or ExedReloadPre do not exist)", unit, section)
if len(usedExecReload) > 0 and "/bin/kill " in usedExecReload[0]: if len(usedExecReload) > 0 and "/bin/kill " in usedExecReload[0]:
logg.warning(" %s: the use of /bin/kill is not recommended for %s ExecReload as it is asynchronous." logg.warning(" %s: the use of /bin/kill is not recommended for %s ExecReload as it is asynchronous." +
+ "\n\t\t\tThat means all the dependencies will perform the reload simultaneously / out of order.", unit, section) "\n\t\t\tThat means all the dependencies will perform the reload simultaneously / out of order.", unit, section)
if conf.getlist(Service, "ExecRestart", []): # pragma: no cover if conf.getlist(Service, "ExecRestart", []): # pragma: no cover
logg.error(" %s: there no such thing as an %s ExecRestart (ignored)", unit, section) logg.error(" %s: there no such thing as an %s ExecRestart (ignored)", unit, section)
if conf.getlist(Service, "ExecRestartPre", []): # pragma: no cover if conf.getlist(Service, "ExecRestartPre", []): # pragma: no cover
@@ -5961,7 +5968,7 @@ class Systemctl:
interval = conf.get(Service, "StartLimitIntervalSec", strE(defaults)) # 10s interval = conf.get(Service, "StartLimitIntervalSec", strE(defaults)) # 10s
return time_to_seconds(interval, maximum) return time_to_seconds(interval, maximum)
def get_RestartSec(self, conf, maximum = None): def get_RestartSec(self, conf, maximum = None):
maximum = maximum or DefaultStartLimitIntervalSec maximum = maximum or DefaultMaximumTimeout
delay = conf.get(Service, "RestartSec", strE(DefaultRestartSec)) delay = conf.get(Service, "RestartSec", strE(DefaultRestartSec))
return time_to_seconds(delay, maximum) return time_to_seconds(delay, maximum)
def restart_failed_units(self, units, maximum = None): def restart_failed_units(self, units, maximum = None):
@@ -6186,7 +6193,8 @@ class Systemctl:
zombie = False zombie = False
ppid = -1 ppid = -1
try: try:
for line in open(proc_status): with open(proc_status) as f:
for line in f:
m = re.match(r"State:\s*Z.*", line) m = re.match(r"State:\s*Z.*", line)
if m: zombie = True if m: zombie = True
m = re.match(r"PPid:\s*(\d+)", line) m = re.match(r"PPid:\s*(\d+)", line)
@@ -6263,7 +6271,8 @@ class Systemctl:
proc_status = _proc_pid_status.format(**locals()) proc_status = _proc_pid_status.format(**locals())
if os.path.isfile(proc_status): if os.path.isfile(proc_status):
try: try:
for line in open(proc_status): with open(proc_status) as f:
for line in f:
if line.startswith("PPid:"): if line.startswith("PPid:"):
ppid_text = line[len("PPid:"):].strip() ppid_text = line[len("PPid:"):].strip()
try: ppid = int(ppid_text) try: ppid = int(ppid_text)
@@ -6302,7 +6311,8 @@ class Systemctl:
if pid: if pid:
try: try:
cmdline = _proc_pid_cmdline.format(**locals()) cmdline = _proc_pid_cmdline.format(**locals())
cmd = open(cmdline).read().split("\0") with open(cmdline) as f:
cmd = f.read().split("\0")
if DEBUG_KILLALL: logg.debug("cmdline %s", cmd) if DEBUG_KILLALL: logg.debug("cmdline %s", cmd)
found = None found = None
cmd_exe = os.path.basename(cmd[0]) cmd_exe = os.path.basename(cmd[0])
@@ -6333,33 +6343,33 @@ class Systemctl:
logg.debug("checking hosts sysconf for '::1 localhost'") logg.debug("checking hosts sysconf for '::1 localhost'")
lines = [] lines = []
sysconf_hosts = os_path(self._root, _etc_hosts) sysconf_hosts = os_path(self._root, _etc_hosts)
for line in open(sysconf_hosts): with open(sysconf_hosts) as f:
for line in f:
if "::1" in line: if "::1" in line:
newline = re.sub("\\slocalhost\\s", " ", line) newline = re.sub("\\slocalhost\\s", " ", line)
if line != newline: if line != newline:
logg.info("%s: '%s' => '%s'", _etc_hosts, line.rstrip(), newline.rstrip()) logg.info("%s: '%s' => '%s'", _etc_hosts, line.rstrip(), newline.rstrip())
line = newline line = newline
lines.append(line) lines.append(line)
f = open(sysconf_hosts, "w") with open(sysconf_hosts, "w") as f:
for line in lines: for line in lines:
f.write(line) f.write(line)
f.close()
def force_ipv6(self, *args): def force_ipv6(self, *args):
""" only ipv4 localhost in /etc/hosts """ """ only ipv4 localhost in /etc/hosts """
logg.debug("checking hosts sysconf for '127.0.0.1 localhost'") logg.debug("checking hosts sysconf for '127.0.0.1 localhost'")
lines = [] lines = []
sysconf_hosts = os_path(self._root, _etc_hosts) sysconf_hosts = os_path(self._root, _etc_hosts)
for line in open(sysconf_hosts): with open(sysconf_hosts) as f:
for line in f:
if "127.0.0.1" in line: if "127.0.0.1" in line:
newline = re.sub("\\slocalhost\\s", " ", line) newline = re.sub("\\slocalhost\\s", " ", line)
if line != newline: if line != newline:
logg.info("%s: '%s' => '%s'", _etc_hosts, line.rstrip(), newline.rstrip()) logg.info("%s: '%s' => '%s'", _etc_hosts, line.rstrip(), newline.rstrip())
line = newline line = newline
lines.append(line) lines.append(line)
f = open(sysconf_hosts, "w") with open(sysconf_hosts, "w") as f:
for line in lines: for line in lines:
f.write(line) f.write(line)
f.close()
def help_modules(self, *args): def help_modules(self, *args):
"""[command] -- show this help """[command] -- show this help
""" """
@@ -6523,7 +6533,12 @@ def print_str_dict_dict(result):
logg.log(HINT, "EXEC END %i items", shown) logg.log(HINT, "EXEC END %i items", shown)
logg.debug(" END %s", result) logg.debug(" END %s", result)
def run(command, *modules): def runcommand(command, *modules):
systemctl = Systemctl()
if FORCE_IPV4:
systemctl.force_ipv4()
elif FORCE_IPV6:
systemctl.force_ipv6()
exitcode = 0 exitcode = 0
if command in ["help"]: if command in ["help"]:
print_str_list(systemctl.help_modules(*modules)) print_str_list(systemctl.help_modules(*modules))
@@ -6770,6 +6785,8 @@ if __name__ == "__main__":
_only_type = opt.only_type _only_type = opt.only_type
_only_property = opt.only_property _only_property = opt.only_property
_only_what = opt.only_what _only_what = opt.only_what
FORCE_IPV4 = opt.ipv4
FORCE_IPV6 = opt.ipv6
# being PID 1 (or 0) in a container will imply --init # being PID 1 (or 0) in a container will imply --init
_pid = os.getpid() _pid = os.getpid()
_init = opt.init or _pid in [1, 0] _init = opt.init or _pid in [1, 0]
@@ -6829,7 +6846,6 @@ if __name__ == "__main__":
# #
print_begin(sys.argv, args) print_begin(sys.argv, args)
# #
systemctl = Systemctl()
if opt.version: if opt.version:
args = ["version"] args = ["version"]
if not args: if not args:
@@ -6844,8 +6860,4 @@ if __name__ == "__main__":
modules.remove("service") modules.remove("service")
except ValueError: except ValueError:
pass pass
if opt.ipv4: sys.exit(runcommand(command, *modules))
systemctl.force_ipv4()
elif opt.ipv6:
systemctl.force_ipv6()
sys.exit(run(command, *modules))