Skip to content

Commit 9a9a9ef

Browse files
committed
Merge branches 'support-permissionsStartOnly', 'add-list-units-filter', 'bug_unmask_if_symlink_not_devnull', 'support-enviroment', 'support-fake-journalctl-since' and 'support-module-names-aliases' into feoize
7 parents eb2a963 + a14d0cd + d919802 + 15137e5 + 3161ac6 + 912b08a + 68cbc0e commit 9a9a9ef

4 files changed

Lines changed: 264 additions & 28 deletions

File tree

files/docker/journalctl3.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
parser.add_argument('--user', default=False, action='store_true', help='Show user units')
1414
parser.add_argument('--root', metavar='path', type=str, help='Use subdirectory path')
1515
parser.add_argument('-x', default=False, action='store_true', help='Switch on verbose mode')
16+
parser.add_argument('--since', metavar='since', type=str, help='Dummy, do nothing')
1617
args = parser.parse_args()
1718

18-
systemctl_py = "systemctl3.py"
19+
systemctl_py = "systemctl"
1920
path = os.path.dirname(sys.argv[0])
2021
systemctl = os.path.join(path, systemctl_py)
2122

files/docker/systemctl.py

Lines changed: 126 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import fnmatch
2121
import re
2222
from types import GeneratorType
23+
import pickle
2324

2425
__copyright__ = "(C) 2016-2025 Guido U. Draheim, licensed under the EUPL"
2526
__version__ = "1.5.9063"
@@ -1351,6 +1352,7 @@ def __init__(self):
13511352
self._loaded_file_sysd = {} # /etc/systemd/system/name.service => config data
13521353
self._file_for_unit_sysv = None # name.service => /etc/init.d/name
13531354
self._file_for_unit_sysd = None # name.service => /etc/systemd/system/name.service
1355+
self._sysd_alias = None #map od aliases of services (aliased => realservice)
13541356
self._preset_file_list = None # /etc/systemd/system-preset/* => file content
13551357
self._default_target = DefaultTarget
13561358
self._sysinit_target = None # stores a UnitConf()
@@ -1435,6 +1437,7 @@ def scan_unit_sysd_files(self, module = None): # -> [ unit-names,... ]
14351437
""" reads all unit files, returns the first filename for the unit given """
14361438
if self._file_for_unit_sysd is None:
14371439
self._file_for_unit_sysd = {}
1440+
self._sysd_alias = {}
14381441
for folder in self.sysd_folders():
14391442
if not folder:
14401443
continue
@@ -1448,6 +1451,12 @@ def scan_unit_sysd_files(self, module = None): # -> [ unit-names,... ]
14481451
service_name = name
14491452
if service_name not in self._file_for_unit_sysd:
14501453
self._file_for_unit_sysd[service_name] = path
1454+
if os.path.islink(path):
1455+
path_target = os.readlink(path)
1456+
if path_target.endswith(".service"):
1457+
service_name_target = os.path.basename(path_target)
1458+
self._sysd_alias[service_name] = service_name_target
1459+
logg.debug("alias found %s => %s", service_name, service_name_target)
14511460
logg.debug("found %s sysd files", len(self._file_for_unit_sysd))
14521461
return list(self._file_for_unit_sysd.keys())
14531462
def scan_unit_sysv_files(self, module = None): # -> [ unit-names,... ]
@@ -1469,10 +1478,18 @@ def scan_unit_sysv_files(self, module = None): # -> [ unit-names,... ]
14691478
self._file_for_unit_sysv[service_name] = path
14701479
logg.debug("found %s sysv files", len(self._file_for_unit_sysv))
14711480
return list(self._file_for_unit_sysv.keys())
1481+
def _getRealModuleName(self, module):
1482+
if module is None:
1483+
return None
1484+
if self._sysd_alias is None:
1485+
self.scan_unit_sysd_files()
1486+
assert self._sysd_alias is not None
1487+
return module if not module in self._sysd_alias else self._sysd_alias[module]
14721488
def unit_sysd_file(self, module = None): # -> filename?
14731489
""" file path for the given module (systemd) """
14741490
self.scan_unit_sysd_files()
14751491
assert self._file_for_unit_sysd is not None
1492+
module = self._getRealModuleName(module)
14761493
if module and module in self._file_for_unit_sysd:
14771494
return self._file_for_unit_sysd[module]
14781495
if module and unit_of(module) in self._file_for_unit_sysd:
@@ -1575,7 +1592,9 @@ def load_sysd_unit_conf(self, module): # -> conf?
15751592
for name in sorted(drop_in_files):
15761593
path = drop_in_files[name]
15771594
data.read_sysd(path)
1578-
conf = SystemctlConf(data, module)
1595+
assert self._sysd_alias is not None
1596+
module_real = module if not module in self._sysd_alias else self._sysd_alias[module]
1597+
conf = SystemctlConf(data, module_real)
15791598
conf.masked = masked
15801599
conf.nonloaded_path = path # if masked
15811600
conf.drop_in_files = drop_in_files
@@ -1816,6 +1835,20 @@ def list_unit_files_modules(self, *modules): # -> [ (unit,enabled) ]
18161835
else:
18171836
result = self.list_target_unit_files()
18181837
result += self.list_service_unit_files(*modules)
1838+
1839+
filterString = modules[0] if len(modules) >= 1 else None
1840+
if filterString:
1841+
result = [
1842+
service for service in result
1843+
if fnmatch.fnmatch(service[0], filterString)
1844+
]
1845+
if len(self._only_state) > 0:
1846+
result = [
1847+
service for service in result
1848+
if service[1] in self._only_state
1849+
]
1850+
1851+
18191852
if self._no_legend:
18201853
return result
18211854
found = "%s unit files listed." % len(result)
@@ -2991,8 +3024,10 @@ def do_start_service_from(self, conf):
29913024
exe, newcmd = self.exec_newcmd(cmd, env, conf)
29923025
logg.info(" pre-start %s", shell_cmd(newcmd))
29933026
forkpid = os.fork()
2994-
if not forkpid:
2995-
self.execve_from(conf, newcmd, env) # pragma: no cover
3027+
if not forkpid:
3028+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3029+
runAsRoot = permissionsStartOnly
3030+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
29963031
run = subprocess_waitpid(forkpid)
29973032
logg.debug(" pre-start done (%s) <-%s>",
29983033
run.returncode or "OK", run.signal or "")
@@ -3167,7 +3202,9 @@ def do_start_service_from(self, conf):
31673202
logg.info("post-fail %s", shell_cmd(newcmd))
31683203
forkpid = os.fork()
31693204
if not forkpid:
3170-
self.execve_from(conf, newcmd, env) # pragma: no cover
3205+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3206+
runAsRoot = permissionsStartOnly
3207+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
31713208
run = subprocess_waitpid(forkpid)
31723209
logg.debug("post-fail done (%s) <-%s>",
31733210
run.returncode or "OK", run.signal or "")
@@ -3180,7 +3217,9 @@ def do_start_service_from(self, conf):
31803217
logg.info("post-start %s", shell_cmd(newcmd))
31813218
forkpid = os.fork()
31823219
if not forkpid:
3183-
self.execve_from(conf, newcmd, env) # pragma: no cover
3220+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3221+
runAsRoot = permissionsStartOnly
3222+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
31843223
run = subprocess_waitpid(forkpid)
31853224
logg.debug("post-start done (%s) <-%s>",
31863225
run.returncode or "OK", run.signal or "")
@@ -3288,8 +3327,10 @@ def do_start_socket_from(self, conf):
32883327
exe, newcmd = self.exec_newcmd(cmd, env, conf)
32893328
logg.info(" pre-start %s", shell_cmd(newcmd))
32903329
forkpid = os.fork()
3291-
if not forkpid:
3292-
self.execve_from(conf, newcmd, env) # pragma: no cover
3330+
if not forkpid:
3331+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3332+
runAsRoot = permissionsStartOnly
3333+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
32933334
run = subprocess_waitpid(forkpid)
32943335
logg.debug(" pre-start done (%s) <-%s>",
32953336
run.returncode or "OK", run.signal or "")
@@ -3329,7 +3370,9 @@ def do_start_socket_from(self, conf):
33293370
logg.info("post-fail %s", shell_cmd(newcmd))
33303371
forkpid = os.fork()
33313372
if not forkpid:
3332-
self.execve_from(conf, newcmd, env) # pragma: no cover
3373+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3374+
runAsRoot = permissionsStartOnly
3375+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
33333376
run = subprocess_waitpid(forkpid)
33343377
logg.debug("post-fail done (%s) <-%s>",
33353378
run.returncode or "OK", run.signal or "")
@@ -3340,7 +3383,9 @@ def do_start_socket_from(self, conf):
33403383
logg.info("post-start %s", shell_cmd(newcmd))
33413384
forkpid = os.fork()
33423385
if not forkpid:
3343-
self.execve_from(conf, newcmd, env) # pragma: no cover
3386+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3387+
runAsRoot = permissionsStartOnly
3388+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
33443389
run = subprocess_waitpid(forkpid)
33453390
logg.debug("post-start done (%s) <-%s>",
33463391
run.returncode or "OK", run.signal or "")
@@ -3478,6 +3523,8 @@ def expand_list(self, group_lines, conf):
34783523
if item:
34793524
result.append(self.expand_special(item, conf))
34803525
return result
3526+
def get_PermissionsStartOnly(self, conf):
3527+
return conf.getbool("Service", "PermissionsStartOnly", "no")
34813528
def get_User(self, conf):
34823529
return self.expand_special(conf.get(Service, "User", ""), conf)
34833530
def get_Group(self, conf):
@@ -3565,7 +3612,7 @@ def dup2_journal_log(self, conf):
35653612
os.dup2(inp.fileno(), sys.stdin.fileno())
35663613
os.dup2(out.fileno(), sys.stdout.fileno())
35673614
os.dup2(err.fileno(), sys.stderr.fileno())
3568-
def execve_from(self, conf, cmd, env):
3615+
def execve_from(self, conf, cmd, env, runAsRoot = False):
35693616
""" this code is commonly run in a child process // returns exit-code"""
35703617
# |
35713618
runs = conf.get(Service, "Type", "simple").lower()
@@ -3575,6 +3622,10 @@ def execve_from(self, conf, cmd, env):
35753622
#
35763623
runuser = self.get_User(conf)
35773624
rungroup = self.get_Group(conf)
3625+
if runAsRoot is True:
3626+
runuser = "root"
3627+
rungroup = "root"
3628+
""" logg.debug("Executing as %s:%s", runuser, rungroup)"""
35783629
xgroups = self.get_SupplementaryGroups(conf)
35793630
envs = shutil_setuid(runuser, rungroup, xgroups)
35803631
badpath = self.chdir_workingdir(conf) # some dirs need setuid before
@@ -3774,7 +3825,9 @@ def do_stop_service_from(self, conf):
37743825
logg.info("post-stop %s", shell_cmd(newcmd))
37753826
forkpid = os.fork()
37763827
if not forkpid:
3777-
self.execve_from(conf, newcmd, env) # pragma: no cover
3828+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3829+
runAsRoot = permissionsStartOnly
3830+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
37783831
run = subprocess_waitpid(forkpid)
37793832
logg.debug("post-stop done (%s) <-%s>",
37803833
run.returncode or "OK", run.signal or "")
@@ -3812,7 +3865,9 @@ def do_stop_socket_from(self, conf):
38123865
logg.info("post-stop %s", shell_cmd(newcmd))
38133866
forkpid = os.fork()
38143867
if not forkpid:
3815-
self.execve_from(conf, newcmd, env) # pragma: no cover
3868+
permissionsStartOnly = self.get_PermissionsStartOnly(conf)
3869+
runAsRoot = permissionsStartOnly
3870+
self.execve_from(conf, newcmd, env, runAsRoot) # pragma: no cover
38163871
run = subprocess_waitpid(forkpid)
38173872
logg.debug("post-stop done (%s) <-%s>",
38183873
run.returncode or "OK", run.signal or "")
@@ -5036,7 +5091,12 @@ def unmask_unit(self, unit):
50365091
_f = self._force and "-f" or ""
50375092
logg.info("rm {_f} '{target}'".format(**locals()))
50385093
if os.path.islink(target):
5039-
os.remove(target)
5094+
link_target = os.readlink(target)
5095+
if link_target == _dev_null:
5096+
os.remove(target)
5097+
logg.info("Unit %s was masked and has been unmasked.", unit)
5098+
else:
5099+
logg.info("Unit %s is not masked (symlink points to %s); nothing to do.")
50405100
return True
50415101
elif not os.path.exists(target):
50425102
logg.debug("Symlink did not exist anymore: %s", target)
@@ -6451,6 +6511,50 @@ def version_info(self):
64516511
return [self.systemd_version(), self.systemd_features()]
64526512
def test_float(self):
64536513
return 0. # "Unknown result type"
6514+
def getEnvVarsFilePath(self):
6515+
return '/run/systemd/systemd.envs'
6516+
def getEnvVars(self):
6517+
fp = self.getEnvVarsFilePath()
6518+
vars = {}
6519+
if os.path.isfile(fp):
6520+
with open(fp, 'rb') as f:
6521+
vars = pickle.load(f)
6522+
return vars
6523+
def setEnvVar(self, varName, varValue = None):
6524+
vars = self.getEnvVars()
6525+
if varValue is None:
6526+
if varName in vars:
6527+
del vars[varName]
6528+
else:
6529+
vars[varName] = varValue
6530+
with open(self.getEnvVarsFilePath(), 'wb') as f:
6531+
pickle.dump(vars, f)
6532+
def get_environment_modules(self, *args):
6533+
if len(args) == 0:
6534+
return 1
6535+
varName = args[0]
6536+
vars = self.getEnvVars()
6537+
if varName in vars:
6538+
return vars[varName]
6539+
return ''
6540+
def set_environment_modules(self, *args):
6541+
if len(args) == 0:
6542+
return 1
6543+
boom = args[0].split('=', 2)
6544+
if len(boom) != 2:
6545+
return 2
6546+
varName = boom[0]
6547+
varValue = boom[1]
6548+
logg.debug("Set env variable %s to \"%s\"", varName, varValue)
6549+
self.setEnvVar(varName, varValue)
6550+
return 0
6551+
def unset_environment_modules(self, *args):
6552+
if len(args) == 0:
6553+
return 1
6554+
varName = args[0]
6555+
logg.debug("Unset env variable %s", varName)
6556+
self.setEnvVar(varName)
6557+
return 0
64546558

64556559
def print_begin(argv, args):
64566560
script = os.path.realpath(argv[0])
@@ -6560,6 +6664,15 @@ def runcommand(command, *modules):
65606664
exitcode = is_not_ok(systemctl.enable_modules(*modules))
65616665
elif command in ["environment"]:
65626666
print_str_dict(systemctl.environment_of_unit(*modules))
6667+
elif command in ["get-environment"]:
6668+
result = systemctl.get_environment_modules(*modules)
6669+
assert result is not None
6670+
if isinstance(result, int): exitcode = result
6671+
elif isinstance(result, str): print_str(result)
6672+
elif command in ["set-environment"]:
6673+
exitcode = systemctl.set_environment_modules(*modules)
6674+
elif command in ["unset-environment"]:
6675+
exitcode = systemctl.unset_environment_modules(*modules)
65636676
elif command in ["get-default"]:
65646677
print_str(systemctl.get_default_target())
65656678
elif command in ["get-preset"]:

0 commit comments

Comments
 (0)