1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: MIT
5#
6
7# The list of packages that should have systemd packaging scripts added.  For
8# each entry, optionally have a SYSTEMD_SERVICE:[package] that lists the service
9# files in this package.  If this variable isn't set, [package].service is used.
10SYSTEMD_PACKAGES ?= "${PN}"
11SYSTEMD_PACKAGES:class-native ?= ""
12SYSTEMD_PACKAGES:class-nativesdk ?= ""
13
14# Whether to enable or disable the services on installation.
15SYSTEMD_AUTO_ENABLE ??= "enable"
16
17# This class will be included in any recipe that supports systemd init scripts,
18# even if systemd is not in DISTRO_FEATURES.  As such don't make any changes
19# directly but check the DISTRO_FEATURES first.
20python __anonymous() {
21    # If the distro features have systemd but not sysvinit, inhibit update-rcd
22    # from doing any work so that pure-systemd images don't have redundant init
23    # files.
24    if bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d):
25        d.appendVar("DEPENDS", " systemd-systemctl-native")
26        d.appendVar("PACKAGE_WRITE_DEPS", " systemd-systemctl-native")
27        if not bb.utils.contains('DISTRO_FEATURES', 'sysvinit', True, False, d):
28            d.setVar("INHIBIT_UPDATERCD_BBCLASS", "1")
29}
30
31systemd_postinst() {
32if systemctl >/dev/null 2>/dev/null; then
33	OPTS=""
34
35	if [ -n "$D" ]; then
36		OPTS="--root=$D"
37	fi
38
39	if [ "${SYSTEMD_AUTO_ENABLE}" = "enable" ]; then
40		for service in ${SYSTEMD_SERVICE_ESCAPED}; do
41			systemctl ${OPTS} enable "$service"
42		done
43	fi
44
45	if [ -z "$D" ]; then
46		systemctl daemon-reload
47		systemctl preset ${SYSTEMD_SERVICE_ESCAPED}
48
49		if [ "${SYSTEMD_AUTO_ENABLE}" = "enable" ]; then
50			systemctl --no-block restart ${SYSTEMD_SERVICE_ESCAPED}
51		fi
52	fi
53fi
54}
55
56systemd_prerm() {
57if systemctl >/dev/null 2>/dev/null; then
58	if [ -z "$D" ]; then
59		systemctl stop ${SYSTEMD_SERVICE_ESCAPED}
60
61		systemctl disable ${SYSTEMD_SERVICE_ESCAPED}
62	fi
63fi
64}
65
66
67systemd_populate_packages[vardeps] += "systemd_prerm systemd_postinst"
68systemd_populate_packages[vardepsexclude] += "OVERRIDES"
69
70
71python systemd_populate_packages() {
72    import re
73    import shlex
74
75    if not bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d):
76        return
77
78    def get_package_var(d, var, pkg):
79        val = (d.getVar('%s:%s' % (var, pkg)) or "").strip()
80        if val == "":
81            val = (d.getVar(var) or "").strip()
82        return val
83
84    # Check if systemd-packages already included in PACKAGES
85    def systemd_check_package(pkg_systemd):
86        packages = d.getVar('PACKAGES')
87        if not pkg_systemd in packages.split():
88            bb.error('%s does not appear in package list, please add it' % pkg_systemd)
89
90
91    def systemd_generate_package_scripts(pkg):
92        bb.debug(1, 'adding systemd calls to postinst/postrm for %s' % pkg)
93
94        paths_escaped = ' '.join(shlex.quote(s) for s in d.getVar('SYSTEMD_SERVICE:' + pkg).split())
95        d.setVar('SYSTEMD_SERVICE_ESCAPED:' + pkg, paths_escaped)
96
97        # Add pkg to the overrides so that it finds the SYSTEMD_SERVICE:pkg
98        # variable.
99        localdata = d.createCopy()
100        localdata.prependVar("OVERRIDES", pkg + ":")
101
102        postinst = d.getVar('pkg_postinst:%s' % pkg)
103        if not postinst:
104            postinst = '#!/bin/sh\n'
105        postinst += localdata.getVar('systemd_postinst')
106        d.setVar('pkg_postinst:%s' % pkg, postinst)
107
108        prerm = d.getVar('pkg_prerm:%s' % pkg)
109        if not prerm:
110            prerm = '#!/bin/sh\n'
111        prerm += localdata.getVar('systemd_prerm')
112        d.setVar('pkg_prerm:%s' % pkg, prerm)
113
114
115    # Add files to FILES:*-systemd if existent and not already done
116    def systemd_append_file(pkg_systemd, file_append):
117        appended = False
118        if os.path.exists(oe.path.join(d.getVar("D"), file_append)):
119            var_name = "FILES:" + pkg_systemd
120            files = d.getVar(var_name, False) or ""
121            if file_append not in files.split():
122                d.appendVar(var_name, " " + file_append)
123                appended = True
124        return appended
125
126    # Add systemd files to FILES:*-systemd, parse for Also= and follow recursive
127    def systemd_add_files_and_parse(pkg_systemd, path, service, keys):
128        # avoid infinite recursion
129        if systemd_append_file(pkg_systemd, oe.path.join(path, service)):
130            fullpath = oe.path.join(d.getVar("D"), path, service)
131            if service.find('.service') != -1:
132                # for *.service add *@.service
133                service_base = service.replace('.service', '')
134                systemd_add_files_and_parse(pkg_systemd, path, service_base + '@.service', keys)
135            if service.find('.socket') != -1:
136                # for *.socket add *.service and *@.service
137                service_base = service.replace('.socket', '')
138                systemd_add_files_and_parse(pkg_systemd, path, service_base + '.service', keys)
139                systemd_add_files_and_parse(pkg_systemd, path, service_base + '@.service', keys)
140            for key in keys.split():
141                # recurse all dependencies found in keys ('Also';'Conflicts';..) and add to files
142                cmd = "grep %s %s | sed 's,%s=,,g' | tr ',' '\\n'" % (key, shlex.quote(fullpath), key)
143                pipe = os.popen(cmd, 'r')
144                line = pipe.readline()
145                while line:
146                    line = line.replace('\n', '')
147                    systemd_add_files_and_parse(pkg_systemd, path, line, keys)
148                    line = pipe.readline()
149                pipe.close()
150
151    # Check service-files and call systemd_add_files_and_parse for each entry
152    def systemd_check_services():
153        searchpaths = [oe.path.join(d.getVar("sysconfdir"), "systemd", "system"),]
154        searchpaths.append(d.getVar("systemd_system_unitdir"))
155        searchpaths.append(d.getVar("systemd_user_unitdir"))
156        systemd_packages = d.getVar('SYSTEMD_PACKAGES')
157
158        keys = 'Also'
159        # scan for all in SYSTEMD_SERVICE[]
160        for pkg_systemd in systemd_packages.split():
161            for service in get_package_var(d, 'SYSTEMD_SERVICE', pkg_systemd).split():
162                path_found = ''
163
164                # Deal with adding, for example, 'ifplugd@eth0.service' from
165                # 'ifplugd@.service'
166                base = None
167                at = service.find('@')
168                if at != -1:
169                    ext = service.rfind('.')
170                    base = service[:at] + '@' + service[ext:]
171
172                for path in searchpaths:
173                    if os.path.lexists(oe.path.join(d.getVar("D"), path, service)):
174                        path_found = path
175                        break
176                    elif base is not None:
177                        if os.path.exists(oe.path.join(d.getVar("D"), path, base)):
178                            path_found = path
179                            break
180
181                if path_found != '':
182                    systemd_add_files_and_parse(pkg_systemd, path_found, service, keys)
183                else:
184                    bb.fatal("Didn't find service unit '{0}', specified in SYSTEMD_SERVICE:{1}. {2}".format(
185                        service, pkg_systemd, "Also looked for service unit '{0}'.".format(base) if base is not None else ""))
186
187    def systemd_create_presets(pkg, action):
188        presetf = oe.path.join(d.getVar("PKGD"), d.getVar("systemd_unitdir"), "system-preset/98-%s.preset" % pkg)
189        bb.utils.mkdirhier(os.path.dirname(presetf))
190        with open(presetf, 'a') as fd:
191            for service in d.getVar('SYSTEMD_SERVICE:%s' % pkg).split():
192                fd.write("%s %s\n" % (action,service))
193        d.appendVar("FILES:%s" % pkg, ' ' + oe.path.join(d.getVar("systemd_unitdir"), "system-preset/98-%s.preset" % pkg))
194
195    # Run all modifications once when creating package
196    if os.path.exists(d.getVar("D")):
197        for pkg in d.getVar('SYSTEMD_PACKAGES').split():
198            systemd_check_package(pkg)
199            if d.getVar('SYSTEMD_SERVICE:' + pkg):
200                systemd_generate_package_scripts(pkg)
201                action = get_package_var(d, 'SYSTEMD_AUTO_ENABLE', pkg)
202                if action in ("enable", "disable"):
203                    systemd_create_presets(pkg, action)
204                elif action not in ("mask", "preset"):
205                    bb.fatal("SYSTEMD_AUTO_ENABLE:%s '%s' is not 'enable', 'disable', 'mask' or 'preset'" % (pkg, action))
206        systemd_check_services()
207}
208
209PACKAGESPLITFUNCS =+ "systemd_populate_packages"
210
211python rm_systemd_unitdir (){
212    import shutil
213    if not bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d):
214        systemd_unitdir = oe.path.join(d.getVar("D"), d.getVar('systemd_unitdir'))
215        if os.path.exists(systemd_unitdir):
216            shutil.rmtree(systemd_unitdir)
217        systemd_libdir = os.path.dirname(systemd_unitdir)
218        if (os.path.exists(systemd_libdir) and not os.listdir(systemd_libdir)):
219            os.rmdir(systemd_libdir)
220}
221
222python rm_sysvinit_initddir (){
223    import shutil
224    sysv_initddir = oe.path.join(d.getVar("D"), (d.getVar('INIT_D_DIR') or "/etc/init.d"))
225
226    if bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d) and \
227        not bb.utils.contains('DISTRO_FEATURES', 'sysvinit', True, False, d) and \
228        os.path.exists(sysv_initddir):
229        systemd_system_unitdir = oe.path.join(d.getVar("D"), d.getVar('systemd_system_unitdir'))
230
231        # If systemd_system_unitdir contains anything, delete sysv_initddir
232        if (os.path.exists(systemd_system_unitdir) and os.listdir(systemd_system_unitdir)):
233            shutil.rmtree(sysv_initddir)
234}
235
236do_install[postfuncs] += "${RMINITDIR} "
237RMINITDIR:class-target = " rm_sysvinit_initddir rm_systemd_unitdir "
238RMINITDIR:class-nativesdk = " rm_sysvinit_initddir rm_systemd_unitdir "
239RMINITDIR = ""
240
241