diff options
author | Alex Kiernan <alex.kiernan@gmail.com> | 2019-05-08 16:57:27 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2019-05-09 16:31:55 +0100 |
commit | 534731e7a7956c3d82c09c9aa01080b19aa818d1 (patch) | |
tree | 1f7bdb0e36d1034fdca2ebd1c60bb89f835ecc49 /meta | |
parent | 7e5124a44c8d589b2dffc1d083653dc3b084d082 (diff) | |
download | poky-534731e7a7956c3d82c09c9aa01080b19aa818d1.tar.gz |
systemd-systemctl: Restore support for enable command
Refactor so that SystemdUnit is its own class, then add support for the
enable command. This restores the ability of systemd.bbclass to create
instances using syntax such as:
SYSTEMD_SERVICE_${PN} = "serial-getty@ttyAMA0.service"
(From OE-Core rev: 9ef6f326ad323b2687440b81b0a983cb3d86a3ab)
Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rwxr-xr-x | meta/recipes-core/systemd/systemd-systemctl/systemctl | 179 |
1 files changed, 102 insertions, 77 deletions
diff --git a/meta/recipes-core/systemd/systemd-systemctl/systemctl b/meta/recipes-core/systemd/systemd-systemctl/systemctl index 7fdaf8ce03..8d7b3ba32d 100755 --- a/meta/recipes-core/systemd/systemd-systemctl/systemctl +++ b/meta/recipes-core/systemd/systemd-systemctl/systemctl | |||
@@ -20,6 +20,8 @@ SYSCONFDIR = Path("etc") | |||
20 | BASE_LIBDIR = Path("lib") | 20 | BASE_LIBDIR = Path("lib") |
21 | LIBDIR = Path("usr", "lib") | 21 | LIBDIR = Path("usr", "lib") |
22 | 22 | ||
23 | locations = list() | ||
24 | |||
23 | 25 | ||
24 | class SystemdFile(): | 26 | class SystemdFile(): |
25 | """Class representing a single systemd configuration file""" | 27 | """Class representing a single systemd configuration file""" |
@@ -111,12 +113,6 @@ class Presets(): | |||
111 | 113 | ||
112 | def _collect_presets(self, scope, root): | 114 | def _collect_presets(self, scope, root): |
113 | """Collect list of preset files""" | 115 | """Collect list of preset files""" |
114 | locations = [SYSCONFDIR / "systemd"] | ||
115 | # Handle the usrmerge case by ignoring /lib when it's a symlink | ||
116 | if not BASE_LIBDIR.is_symlink(): | ||
117 | locations.append(BASE_LIBDIR / "systemd") | ||
118 | locations.append(LIBDIR / "systemd") | ||
119 | |||
120 | presets = dict() | 116 | presets = dict() |
121 | for location in locations: | 117 | for location in locations: |
122 | paths = (root / location / scope).glob("*.preset") | 118 | paths = (root / location / scope).glob("*.preset") |
@@ -146,27 +142,6 @@ class Presets(): | |||
146 | return None | 142 | return None |
147 | 143 | ||
148 | 144 | ||
149 | def collect_services(root): | ||
150 | """Collect list of service files""" | ||
151 | locations = [SYSCONFDIR / "systemd"] | ||
152 | # Handle the usrmerge case by ignoring /lib when it's a symlink | ||
153 | if not BASE_LIBDIR.is_symlink(): | ||
154 | locations.append(BASE_LIBDIR / "systemd") | ||
155 | locations.append(LIBDIR / "systemd") | ||
156 | |||
157 | services = dict() | ||
158 | for location in locations: | ||
159 | paths = (root / location / "system").glob("*") | ||
160 | for path in paths: | ||
161 | if path.is_dir(): | ||
162 | continue | ||
163 | # implement earlier names override later ones | ||
164 | if path.name not in services: | ||
165 | services[path.name] = path | ||
166 | |||
167 | return services | ||
168 | |||
169 | |||
170 | def add_link(path, target): | 145 | def add_link(path, target): |
171 | try: | 146 | try: |
172 | path.parent.mkdir(parents=True) | 147 | path.parent.mkdir(parents=True) |
@@ -177,69 +152,113 @@ def add_link(path, target): | |||
177 | path.symlink_to(target) | 152 | path.symlink_to(target) |
178 | 153 | ||
179 | 154 | ||
180 | def process_deps(root, config, service, location, prop, dirstem): | 155 | class SystemdUnitNotFoundError(Exception): |
181 | systemdir = SYSCONFDIR / "systemd" / "system" | 156 | pass |
182 | 157 | ||
183 | target = ROOT / location.relative_to(root) | ||
184 | try: | ||
185 | for dependent in config.get('Install', prop): | ||
186 | wants = root / systemdir / "{}.{}".format(dependent, dirstem) / service | ||
187 | add_link(wants, target) | ||
188 | 158 | ||
189 | except KeyError: | 159 | class SystemdUnit(): |
190 | pass | 160 | def __init__(self, root, unit): |
161 | self.root = root | ||
162 | self.unit = unit | ||
163 | self.config = None | ||
164 | |||
165 | def _path_for_unit(self, unit): | ||
166 | for location in locations: | ||
167 | path = self.root / location / "system" / unit | ||
168 | if path.exists(): | ||
169 | return path | ||
191 | 170 | ||
171 | raise SystemdUnitNotFoundError(self.root, unit) | ||
192 | 172 | ||
193 | def enable(root, service, location, services): | 173 | def _process_deps(self, config, service, location, prop, dirstem): |
194 | if location.is_symlink(): | 174 | systemdir = self.root / SYSCONFDIR / "systemd" / "system" |
195 | # ignore aliases | 175 | |
196 | return | 176 | target = ROOT / location.relative_to(self.root) |
177 | try: | ||
178 | for dependent in config.get('Install', prop): | ||
179 | wants = systemdir / "{}.{}".format(dependent, dirstem) / service | ||
180 | add_link(wants, target) | ||
181 | |||
182 | except KeyError: | ||
183 | pass | ||
184 | |||
185 | def enable(self): | ||
186 | # if we're enabling an instance, first extract the actual instance | ||
187 | # then figure out what the template unit is | ||
188 | template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", self.unit) | ||
189 | if template: | ||
190 | instance = template.group('instance') | ||
191 | unit = re.sub(r"@[^\.]*\.", "@.", self.unit, 1) | ||
192 | else: | ||
193 | instance = None | ||
194 | unit = self.unit | ||
195 | |||
196 | path = self._path_for_unit(unit) | ||
197 | |||
198 | if path.is_symlink(): | ||
199 | # ignore aliases | ||
200 | return | ||
197 | 201 | ||
198 | config = SystemdFile(root, location) | 202 | config = SystemdFile(self.root, path) |
199 | template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", service) | 203 | if instance == "": |
200 | if template: | ||
201 | instance = template.group('instance') | ||
202 | if not instance: | ||
203 | try: | 204 | try: |
204 | instance = config.get('Install', 'DefaultInstance')[0] | 205 | default_instance = config.get('Install', 'DefaultInstance')[0] |
205 | service = service.replace("@.", "@{}.".format(instance)) | ||
206 | except KeyError: | 206 | except KeyError: |
207 | pass | 207 | # no default instance, so nothing to enable |
208 | if instance is None: | 208 | return |
209 | return | ||
210 | else: | ||
211 | instance = None | ||
212 | 209 | ||
213 | process_deps(root, config, service, location, 'WantedBy', 'wants') | 210 | service = self.unit.replace("@.", |
214 | process_deps(root, config, service, location, 'RequiredBy', 'requires') | 211 | "@{}.".format(default_instance)) |
212 | else: | ||
213 | service = self.unit | ||
215 | 214 | ||
216 | try: | 215 | self._process_deps(config, service, path, 'WantedBy', 'wants') |
217 | for also in config.get('Install', 'Also'): | 216 | self._process_deps(config, service, path, 'RequiredBy', 'requires') |
218 | enable(root, also, services[also], services) | ||
219 | 217 | ||
220 | except KeyError: | 218 | try: |
221 | pass | 219 | for also in config.get('Install', 'Also'): |
220 | SystemdUnit(self.root, also).enable() | ||
222 | 221 | ||
223 | systemdir = root / SYSCONFDIR / "systemd" / "system" | 222 | except KeyError: |
224 | target = ROOT / location.relative_to(root) | 223 | pass |
225 | try: | ||
226 | for dest in config.get('Install', 'Alias'): | ||
227 | alias = systemdir / dest | ||
228 | add_link(alias, target) | ||
229 | 224 | ||
230 | except KeyError: | 225 | systemdir = self.root / SYSCONFDIR / "systemd" / "system" |
231 | pass | 226 | target = ROOT / path.relative_to(self.root) |
227 | try: | ||
228 | for dest in config.get('Install', 'Alias'): | ||
229 | alias = systemdir / dest | ||
230 | add_link(alias, target) | ||
231 | |||
232 | except KeyError: | ||
233 | pass | ||
234 | |||
235 | def mask(self): | ||
236 | systemdir = self.root / SYSCONFDIR / "systemd" / "system" | ||
237 | add_link(systemdir / self.unit, "/dev/null") | ||
238 | |||
239 | |||
240 | def collect_services(root): | ||
241 | """Collect list of service files""" | ||
242 | services = set() | ||
243 | for location in locations: | ||
244 | paths = (root / location / "system").glob("*") | ||
245 | for path in paths: | ||
246 | if path.is_dir(): | ||
247 | continue | ||
248 | services.add(path.name) | ||
249 | |||
250 | return services | ||
232 | 251 | ||
233 | 252 | ||
234 | def preset_all(root): | 253 | def preset_all(root): |
235 | presets = Presets('system-preset', root) | 254 | presets = Presets('system-preset', root) |
236 | services = collect_services(root) | 255 | services = collect_services(root) |
237 | 256 | ||
238 | for service, location in services.items(): | 257 | for service in services: |
239 | state = presets.state(service) | 258 | state = presets.state(service) |
240 | 259 | ||
241 | if state == "enable" or state is None: | 260 | if state == "enable" or state is None: |
242 | enable(root, service, location, services) | 261 | SystemdUnit(root, service).enable() |
243 | 262 | ||
244 | # If we populate the systemd links we also create /etc/machine-id, which | 263 | # If we populate the systemd links we also create /etc/machine-id, which |
245 | # allows systemd to boot with the filesystem read-only before generating | 264 | # allows systemd to boot with the filesystem read-only before generating |
@@ -251,18 +270,13 @@ def preset_all(root): | |||
251 | (root / SYSCONFDIR / "machine-id").touch() | 270 | (root / SYSCONFDIR / "machine-id").touch() |
252 | 271 | ||
253 | 272 | ||
254 | def mask(root, *services): | ||
255 | systemdir = root / SYSCONFDIR / "systemd" / "system" | ||
256 | for service in services: | ||
257 | add_link(systemdir / service, "/dev/null") | ||
258 | |||
259 | |||
260 | def main(): | 273 | def main(): |
261 | if sys.version_info < (3, 4, 0): | 274 | if sys.version_info < (3, 4, 0): |
262 | sys.exit("Python 3.4 or greater is required") | 275 | sys.exit("Python 3.4 or greater is required") |
263 | 276 | ||
264 | parser = argparse.ArgumentParser() | 277 | parser = argparse.ArgumentParser() |
265 | parser.add_argument('command', nargs=1, choices=['mask', 'preset-all']) | 278 | parser.add_argument('command', nargs=1, choices=['enable', 'mask', |
279 | 'preset-all']) | ||
266 | parser.add_argument('service', nargs=argparse.REMAINDER) | 280 | parser.add_argument('service', nargs=argparse.REMAINDER) |
267 | parser.add_argument('--root') | 281 | parser.add_argument('--root') |
268 | parser.add_argument('--preset-mode', | 282 | parser.add_argument('--preset-mode', |
@@ -272,9 +286,20 @@ def main(): | |||
272 | args = parser.parse_args() | 286 | args = parser.parse_args() |
273 | 287 | ||
274 | root = Path(args.root) if args.root else ROOT | 288 | root = Path(args.root) if args.root else ROOT |
289 | |||
290 | locations.append(SYSCONFDIR / "systemd") | ||
291 | # Handle the usrmerge case by ignoring /lib when it's a symlink | ||
292 | if not (root / BASE_LIBDIR).is_symlink(): | ||
293 | locations.append(BASE_LIBDIR / "systemd") | ||
294 | locations.append(LIBDIR / "systemd") | ||
295 | |||
275 | command = args.command[0] | 296 | command = args.command[0] |
276 | if command == "mask": | 297 | if command == "mask": |
277 | mask(root, *args.service) | 298 | for service in args.service: |
299 | SystemdUnit(root, service).mask() | ||
300 | elif command == "enable": | ||
301 | for service in args.service: | ||
302 | SystemdUnit(root, service).enable() | ||
278 | elif command == "preset-all": | 303 | elif command == "preset-all": |
279 | if len(args.service) != 0: | 304 | if len(args.service) != 0: |
280 | sys.exit("Too many arguments.") | 305 | sys.exit("Too many arguments.") |