summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/systemd/systemd-systemctl/systemctl
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/systemd/systemd-systemctl/systemctl')
-rwxr-xr-xmeta/recipes-core/systemd/systemd-systemctl/systemctl61
1 files changed, 49 insertions, 12 deletions
diff --git a/meta/recipes-core/systemd/systemd-systemctl/systemctl b/meta/recipes-core/systemd/systemd-systemctl/systemctl
index de733e255b..2229bc7b6d 100755
--- a/meta/recipes-core/systemd/systemd-systemctl/systemctl
+++ b/meta/recipes-core/systemd/systemd-systemctl/systemctl
@@ -11,6 +11,7 @@ import re
11import sys 11import sys
12 12
13from collections import namedtuple 13from collections import namedtuple
14from itertools import chain
14from pathlib import Path 15from pathlib import Path
15 16
16version = 1.0 17version = 1.0
@@ -25,12 +26,19 @@ locations = list()
25 26
26class SystemdFile(): 27class SystemdFile():
27 """Class representing a single systemd configuration file""" 28 """Class representing a single systemd configuration file"""
28 def __init__(self, root, path): 29
30 _clearable_keys = ['WantedBy']
31
32 def __init__(self, root, path, instance_unit_name):
29 self.sections = dict() 33 self.sections = dict()
30 self._parse(root, path) 34 self._parse(root, path)
31 dirname = os.path.basename(path.name) + ".d" 35 dirname = os.path.basename(path.name) + ".d"
32 for location in locations: 36 for location in locations:
33 for path2 in sorted((root / location / "system" / dirname).glob("*.conf")): 37 files = (root / location / "system" / dirname).glob("*.conf")
38 if instance_unit_name:
39 inst_dirname = instance_unit_name + ".d"
40 files = chain(files, (root / location / "system" / inst_dirname).glob("*.conf"))
41 for path2 in sorted(files):
34 self._parse(root, path2) 42 self._parse(root, path2)
35 43
36 def _parse(self, root, path): 44 def _parse(self, root, path):
@@ -75,6 +83,14 @@ class SystemdFile():
75 v = m.group('value') 83 v = m.group('value')
76 if k not in section: 84 if k not in section:
77 section[k] = list() 85 section[k] = list()
86
87 # If we come across a "key=" line for a "clearable key", then
88 # forget all preceding assignments. This works because we are
89 # processing files in correct parse order.
90 if k in self._clearable_keys and not v:
91 del section[k]
92 continue
93
78 section[k].extend(v.split()) 94 section[k].extend(v.split())
79 95
80 def get(self, section, prop): 96 def get(self, section, prop):
@@ -160,7 +176,9 @@ def add_link(path, target):
160 176
161 177
162class SystemdUnitNotFoundError(Exception): 178class SystemdUnitNotFoundError(Exception):
163 pass 179 def __init__(self, path, unit):
180 self.path = path
181 self.unit = unit
164 182
165 183
166class SystemdUnit(): 184class SystemdUnit():
@@ -177,24 +195,29 @@ class SystemdUnit():
177 195
178 raise SystemdUnitNotFoundError(self.root, unit) 196 raise SystemdUnitNotFoundError(self.root, unit)
179 197
180 def _process_deps(self, config, service, location, prop, dirstem): 198 def _process_deps(self, config, service, location, prop, dirstem, instance):
181 systemdir = self.root / SYSCONFDIR / "systemd" / "system" 199 systemdir = self.root / SYSCONFDIR / "systemd" / "system"
182 200
183 target = ROOT / location.relative_to(self.root) 201 target = ROOT / location.relative_to(self.root)
184 try: 202 try:
185 for dependent in config.get('Install', prop): 203 for dependent in config.get('Install', prop):
204 # expand any %i to instance (ignoring escape sequence %%)
205 dependent = re.sub("([^%](%%)*)%i", "\\g<1>{}".format(instance), dependent)
186 wants = systemdir / "{}.{}".format(dependent, dirstem) / service 206 wants = systemdir / "{}.{}".format(dependent, dirstem) / service
187 add_link(wants, target) 207 add_link(wants, target)
188 208
189 except KeyError: 209 except KeyError:
190 pass 210 pass
191 211
192 def enable(self): 212 def enable(self, units_enabled=[]):
193 # if we're enabling an instance, first extract the actual instance 213 # if we're enabling an instance, first extract the actual instance
194 # then figure out what the template unit is 214 # then figure out what the template unit is
195 template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", self.unit) 215 template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", self.unit)
216 instance_unit_name = None
196 if template: 217 if template:
197 instance = template.group('instance') 218 instance = template.group('instance')
219 if instance != "":
220 instance_unit_name = self.unit
198 unit = re.sub(r"@[^\.]*\.", "@.", self.unit, 1) 221 unit = re.sub(r"@[^\.]*\.", "@.", self.unit, 1)
199 else: 222 else:
200 instance = None 223 instance = None
@@ -206,7 +229,7 @@ class SystemdUnit():
206 # ignore aliases 229 # ignore aliases
207 return 230 return
208 231
209 config = SystemdFile(self.root, path) 232 config = SystemdFile(self.root, path, instance_unit_name)
210 if instance == "": 233 if instance == "":
211 try: 234 try:
212 default_instance = config.get('Install', 'DefaultInstance')[0] 235 default_instance = config.get('Install', 'DefaultInstance')[0]
@@ -219,12 +242,17 @@ class SystemdUnit():
219 else: 242 else:
220 service = self.unit 243 service = self.unit
221 244
222 self._process_deps(config, service, path, 'WantedBy', 'wants') 245 self._process_deps(config, service, path, 'WantedBy', 'wants', instance)
223 self._process_deps(config, service, path, 'RequiredBy', 'requires') 246 self._process_deps(config, service, path, 'RequiredBy', 'requires', instance)
224 247
225 try: 248 try:
226 for also in config.get('Install', 'Also'): 249 for also in config.get('Install', 'Also'):
227 SystemdUnit(self.root, also).enable() 250 try:
251 units_enabled.append(unit)
252 if also not in units_enabled:
253 SystemdUnit(self.root, also).enable(units_enabled)
254 except SystemdUnitNotFoundError as e:
255 sys.exit("Error: Systemctl also enable issue with %s (%s)" % (service, e.unit))
228 256
229 except KeyError: 257 except KeyError:
230 pass 258 pass
@@ -265,7 +293,10 @@ def preset_all(root):
265 state = presets.state(service) 293 state = presets.state(service)
266 294
267 if state == "enable" or state is None: 295 if state == "enable" or state is None:
268 SystemdUnit(root, service).enable() 296 try:
297 SystemdUnit(root, service).enable()
298 except SystemdUnitNotFoundError:
299 sys.exit("Error: Systemctl preset_all issue in %s" % service)
269 300
270 # If we populate the systemd links we also create /etc/machine-id, which 301 # If we populate the systemd links we also create /etc/machine-id, which
271 # allows systemd to boot with the filesystem read-only before generating 302 # allows systemd to boot with the filesystem read-only before generating
@@ -307,10 +338,16 @@ def main():
307 338
308 if command == "mask": 339 if command == "mask":
309 for service in args.service: 340 for service in args.service:
310 SystemdUnit(root, service).mask() 341 try:
342 SystemdUnit(root, service).mask()
343 except SystemdUnitNotFoundError as e:
344 sys.exit("Error: Systemctl main mask issue in %s (%s)" % (service, e.unit))
311 elif command == "enable": 345 elif command == "enable":
312 for service in args.service: 346 for service in args.service:
313 SystemdUnit(root, service).enable() 347 try:
348 SystemdUnit(root, service).enable()
349 except SystemdUnitNotFoundError as e:
350 sys.exit("Error: Systemctl main enable issue in %s (%s)" % (service, e.unit))
314 elif command == "preset-all": 351 elif command == "preset-all":
315 if len(args.service) != 0: 352 if len(args.service) != 0:
316 sys.exit("Too many arguments.") 353 sys.exit("Too many arguments.")