xref: /openbmc/openbmc/poky/meta/classes-recipe/systemd.bbclass (revision 96e4b4e121e0e2da1535d7d537d6a982a6ff5bc0)
192b42cb3SPatrick Williams#
292b42cb3SPatrick Williams# Copyright OpenEmbedded Contributors
392b42cb3SPatrick Williams#
492b42cb3SPatrick Williams# SPDX-License-Identifier: MIT
592b42cb3SPatrick Williams#
692b42cb3SPatrick Williams
792b42cb3SPatrick Williams# The list of packages that should have systemd packaging scripts added.  For
892b42cb3SPatrick Williams# each entry, optionally have a SYSTEMD_SERVICE:[package] that lists the service
992b42cb3SPatrick Williams# files in this package.  If this variable isn't set, [package].service is used.
1092b42cb3SPatrick WilliamsSYSTEMD_PACKAGES ?= "${PN}"
1192b42cb3SPatrick WilliamsSYSTEMD_PACKAGES:class-native ?= ""
1292b42cb3SPatrick WilliamsSYSTEMD_PACKAGES:class-nativesdk ?= ""
1392b42cb3SPatrick Williams
1492b42cb3SPatrick Williams# Whether to enable or disable the services on installation.
1592b42cb3SPatrick WilliamsSYSTEMD_AUTO_ENABLE ??= "enable"
1692b42cb3SPatrick Williams
1792b42cb3SPatrick Williams# This class will be included in any recipe that supports systemd init scripts,
1892b42cb3SPatrick Williams# even if systemd is not in DISTRO_FEATURES.  As such don't make any changes
1992b42cb3SPatrick Williams# directly but check the DISTRO_FEATURES first.
2092b42cb3SPatrick Williamspython __anonymous() {
2192b42cb3SPatrick Williams    # If the distro features have systemd but not sysvinit, inhibit update-rcd
2292b42cb3SPatrick Williams    # from doing any work so that pure-systemd images don't have redundant init
2392b42cb3SPatrick Williams    # files.
2492b42cb3SPatrick Williams    if bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d):
2592b42cb3SPatrick Williams        d.appendVar("DEPENDS", " systemd-systemctl-native")
2692b42cb3SPatrick Williams        d.appendVar("PACKAGE_WRITE_DEPS", " systemd-systemctl-native")
2792b42cb3SPatrick Williams        if not bb.utils.contains('DISTRO_FEATURES', 'sysvinit', True, False, d):
2892b42cb3SPatrick Williams            d.setVar("INHIBIT_UPDATERCD_BBCLASS", "1")
2992b42cb3SPatrick Williams}
3092b42cb3SPatrick Williams
3192b42cb3SPatrick Williamssystemd_postinst() {
3292b42cb3SPatrick Williamsif systemctl >/dev/null 2>/dev/null; then
3392b42cb3SPatrick Williams	OPTS=""
3492b42cb3SPatrick Williams
3592b42cb3SPatrick Williams	if [ -n "$D" ]; then
3692b42cb3SPatrick Williams		OPTS="--root=$D"
3792b42cb3SPatrick Williams	fi
3892b42cb3SPatrick Williams
3992b42cb3SPatrick Williams	if [ "${SYSTEMD_AUTO_ENABLE}" = "enable" ]; then
40*96e4b4e1SPatrick Williams		for service in ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}; do
4192b42cb3SPatrick Williams			systemctl ${OPTS} enable "$service"
4292b42cb3SPatrick Williams		done
43*96e4b4e1SPatrick Williams
44*96e4b4e1SPatrick Williams		for service in ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", True, d)}; do
45*96e4b4e1SPatrick Williams			systemctl --global ${OPTS} enable "$service"
46*96e4b4e1SPatrick Williams		done
4792b42cb3SPatrick Williams	fi
4892b42cb3SPatrick Williams
4992b42cb3SPatrick Williams	if [ -z "$D" ]; then
50*96e4b4e1SPatrick Williams		# Reload only system service manager
51*96e4b4e1SPatrick Williams		# --global for daemon-reload is not supported: https://github.com/systemd/systemd/issues/19284
5292b42cb3SPatrick Williams		systemctl daemon-reload
53*96e4b4e1SPatrick Williams		[ -n "${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}" ] && \
54*96e4b4e1SPatrick Williams			systemctl preset ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}
55*96e4b4e1SPatrick Williams
56*96e4b4e1SPatrick Williams		[ -n "${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", True, d)}" ] && \
57*96e4b4e1SPatrick Williams			systemctl --global preset ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", True, d)}
5892b42cb3SPatrick Williams
5992b42cb3SPatrick Williams		if [ "${SYSTEMD_AUTO_ENABLE}" = "enable" ]; then
60*96e4b4e1SPatrick Williams			# --global flag for restart is not supported by systemd (see above)
61*96e4b4e1SPatrick Williams			[ -n "${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}" ] && \
62*96e4b4e1SPatrick Williams				systemctl --no-block restart ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}
6392b42cb3SPatrick Williams		fi
6492b42cb3SPatrick Williams	fi
6592b42cb3SPatrick Williamsfi
6692b42cb3SPatrick Williams}
6792b42cb3SPatrick Williams
6892b42cb3SPatrick Williamssystemd_prerm() {
6992b42cb3SPatrick Williamsif systemctl >/dev/null 2>/dev/null; then
7092b42cb3SPatrick Williams	if [ -z "$D" ]; then
71*96e4b4e1SPatrick Williams		if [ -n "${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}" ]; then
72*96e4b4e1SPatrick Williams			systemctl stop ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}
73*96e4b4e1SPatrick Williams			systemctl disable ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", False, d)}
74*96e4b4e1SPatrick Williams		fi
7592b42cb3SPatrick Williams
76*96e4b4e1SPatrick Williams		# same as above, --global flag is not supported for stop so do disable only
77*96e4b4e1SPatrick Williams		[ -n "${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", True, d)}" ] && \
78*96e4b4e1SPatrick Williams			systemctl --global disable ${@systemd_filter_services("${SYSTEMD_SERVICE_ESCAPED}", True, d)}
7992b42cb3SPatrick Williams	fi
8092b42cb3SPatrick Williamsfi
8192b42cb3SPatrick Williams}
8292b42cb3SPatrick Williams
8392b42cb3SPatrick Williams
8492b42cb3SPatrick Williamssystemd_populate_packages[vardeps] += "systemd_prerm systemd_postinst"
8592b42cb3SPatrick Williamssystemd_populate_packages[vardepsexclude] += "OVERRIDES"
8692b42cb3SPatrick Williams
8792b42cb3SPatrick Williams
88*96e4b4e1SPatrick Williamsdef systemd_service_path(service, searchpaths, d):
89*96e4b4e1SPatrick Williams    path_found = ''
90*96e4b4e1SPatrick Williams
91*96e4b4e1SPatrick Williams    # Deal with adding, for example, 'ifplugd@eth0.service' from
92*96e4b4e1SPatrick Williams    # 'ifplugd@.service'
93*96e4b4e1SPatrick Williams    base = None
94*96e4b4e1SPatrick Williams    at = service.find('@')
95*96e4b4e1SPatrick Williams    if at != -1:
96*96e4b4e1SPatrick Williams        ext = service.rfind('.')
97*96e4b4e1SPatrick Williams        base = service[:at] + '@' + service[ext:]
98*96e4b4e1SPatrick Williams
99*96e4b4e1SPatrick Williams    for path in searchpaths:
100*96e4b4e1SPatrick Williams        if os.path.lexists(oe.path.join(d.getVar("D"), path, service)):
101*96e4b4e1SPatrick Williams            path_found = path
102*96e4b4e1SPatrick Williams            break
103*96e4b4e1SPatrick Williams        elif base is not None:
104*96e4b4e1SPatrick Williams            if os.path.exists(oe.path.join(d.getVar("D"), path, base)):
105*96e4b4e1SPatrick Williams                path_found = path
106*96e4b4e1SPatrick Williams                break
107*96e4b4e1SPatrick Williams
108*96e4b4e1SPatrick Williams    return path_found, base
109*96e4b4e1SPatrick Williams
110*96e4b4e1SPatrick Williamsdef systemd_service_searchpaths(user, d):
111*96e4b4e1SPatrick Williams    if user:
112*96e4b4e1SPatrick Williams        return [
113*96e4b4e1SPatrick Williams            oe.path.join(d.getVar("sysconfdir"), "systemd", "user"),
114*96e4b4e1SPatrick Williams            d.getVar("systemd_user_unitdir"),
115*96e4b4e1SPatrick Williams        ]
116*96e4b4e1SPatrick Williams    else:
117*96e4b4e1SPatrick Williams        return [
118*96e4b4e1SPatrick Williams            oe.path.join(d.getVar("sysconfdir"), "systemd", "system"),
119*96e4b4e1SPatrick Williams            d.getVar("systemd_system_unitdir"),
120*96e4b4e1SPatrick Williams        ]
121*96e4b4e1SPatrick Williams
122*96e4b4e1SPatrick Williamsdef systemd_service_exists(service, user, d):
123*96e4b4e1SPatrick Williams    searchpaths = systemd_service_searchpaths(user, d)
124*96e4b4e1SPatrick Williams    path, _ = systemd_service_path(service, searchpaths, d)
125*96e4b4e1SPatrick Williams
126*96e4b4e1SPatrick Williams    return path != ''
127*96e4b4e1SPatrick Williams
128*96e4b4e1SPatrick Williamsdef systemd_filter_services(services, user, d):
129*96e4b4e1SPatrick Williams    return ' '.join(service for service in services.split() if systemd_service_exists(service, user, d))
130*96e4b4e1SPatrick Williams
13192b42cb3SPatrick Williamspython systemd_populate_packages() {
13292b42cb3SPatrick Williams    import re
13392b42cb3SPatrick Williams    import shlex
13492b42cb3SPatrick Williams
13592b42cb3SPatrick Williams    if not bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d):
13692b42cb3SPatrick Williams        return
13792b42cb3SPatrick Williams
13892b42cb3SPatrick Williams    def get_package_var(d, var, pkg):
13992b42cb3SPatrick Williams        val = (d.getVar('%s:%s' % (var, pkg)) or "").strip()
14092b42cb3SPatrick Williams        if val == "":
14192b42cb3SPatrick Williams            val = (d.getVar(var) or "").strip()
14292b42cb3SPatrick Williams        return val
14392b42cb3SPatrick Williams
14492b42cb3SPatrick Williams    # Check if systemd-packages already included in PACKAGES
14592b42cb3SPatrick Williams    def systemd_check_package(pkg_systemd):
14692b42cb3SPatrick Williams        packages = d.getVar('PACKAGES')
14792b42cb3SPatrick Williams        if not pkg_systemd in packages.split():
148edff4923SAndrew Geissler            bb.error('%s is marked for packaging systemd scripts, but it does not appear in package list, please add it to PACKAGES or adjust SYSTEMD_PACKAGES accordingly' % pkg_systemd)
14992b42cb3SPatrick Williams
15092b42cb3SPatrick Williams
15192b42cb3SPatrick Williams    def systemd_generate_package_scripts(pkg):
15292b42cb3SPatrick Williams        bb.debug(1, 'adding systemd calls to postinst/postrm for %s' % pkg)
15392b42cb3SPatrick Williams
15492b42cb3SPatrick Williams        paths_escaped = ' '.join(shlex.quote(s) for s in d.getVar('SYSTEMD_SERVICE:' + pkg).split())
15592b42cb3SPatrick Williams        d.setVar('SYSTEMD_SERVICE_ESCAPED:' + pkg, paths_escaped)
15692b42cb3SPatrick Williams
15792b42cb3SPatrick Williams        # Add pkg to the overrides so that it finds the SYSTEMD_SERVICE:pkg
15892b42cb3SPatrick Williams        # variable.
15992b42cb3SPatrick Williams        localdata = d.createCopy()
16092b42cb3SPatrick Williams        localdata.prependVar("OVERRIDES", pkg + ":")
16192b42cb3SPatrick Williams
16292b42cb3SPatrick Williams        postinst = d.getVar('pkg_postinst:%s' % pkg)
16392b42cb3SPatrick Williams        if not postinst:
16492b42cb3SPatrick Williams            postinst = '#!/bin/sh\n'
16592b42cb3SPatrick Williams        postinst += localdata.getVar('systemd_postinst')
16692b42cb3SPatrick Williams        d.setVar('pkg_postinst:%s' % pkg, postinst)
16792b42cb3SPatrick Williams
16892b42cb3SPatrick Williams        prerm = d.getVar('pkg_prerm:%s' % pkg)
16992b42cb3SPatrick Williams        if not prerm:
17092b42cb3SPatrick Williams            prerm = '#!/bin/sh\n'
17192b42cb3SPatrick Williams        prerm += localdata.getVar('systemd_prerm')
17292b42cb3SPatrick Williams        d.setVar('pkg_prerm:%s' % pkg, prerm)
17392b42cb3SPatrick Williams
17492b42cb3SPatrick Williams
17592b42cb3SPatrick Williams    # Add files to FILES:*-systemd if existent and not already done
17692b42cb3SPatrick Williams    def systemd_append_file(pkg_systemd, file_append):
17792b42cb3SPatrick Williams        appended = False
17892b42cb3SPatrick Williams        if os.path.exists(oe.path.join(d.getVar("D"), file_append)):
17992b42cb3SPatrick Williams            var_name = "FILES:" + pkg_systemd
18092b42cb3SPatrick Williams            files = d.getVar(var_name, False) or ""
18192b42cb3SPatrick Williams            if file_append not in files.split():
18292b42cb3SPatrick Williams                d.appendVar(var_name, " " + file_append)
18392b42cb3SPatrick Williams                appended = True
18492b42cb3SPatrick Williams        return appended
18592b42cb3SPatrick Williams
18692b42cb3SPatrick Williams    # Add systemd files to FILES:*-systemd, parse for Also= and follow recursive
187*96e4b4e1SPatrick Williams    def systemd_add_files_and_parse(pkg_systemd, path, service):
18892b42cb3SPatrick Williams        # avoid infinite recursion
18992b42cb3SPatrick Williams        if systemd_append_file(pkg_systemd, oe.path.join(path, service)):
19092b42cb3SPatrick Williams            fullpath = oe.path.join(d.getVar("D"), path, service)
19192b42cb3SPatrick Williams            if service.find('.service') != -1:
19292b42cb3SPatrick Williams                # for *.service add *@.service
19392b42cb3SPatrick Williams                service_base = service.replace('.service', '')
194*96e4b4e1SPatrick Williams                systemd_add_files_and_parse(pkg_systemd, path, service_base + '@.service')
195*96e4b4e1SPatrick Williams                # Add the socket unit which is referred by the Also= in this service file to the same package.
196*96e4b4e1SPatrick Williams                with open(fullpath, 'r') as unit_f:
197*96e4b4e1SPatrick Williams                    for line in unit_f:
198*96e4b4e1SPatrick Williams                        if line.startswith('Also'):
199*96e4b4e1SPatrick Williams                            also_unit = line.split('=', 1)[1].strip()
200*96e4b4e1SPatrick Williams                            if also_unit.find('.socket') != -1:
201*96e4b4e1SPatrick Williams                                systemd_add_files_and_parse(pkg_systemd, path, also_unit)
20292b42cb3SPatrick Williams            if service.find('.socket') != -1:
20392b42cb3SPatrick Williams                # for *.socket add *.service and *@.service
20492b42cb3SPatrick Williams                service_base = service.replace('.socket', '')
205*96e4b4e1SPatrick Williams                systemd_add_files_and_parse(pkg_systemd, path, service_base + '.service')
206*96e4b4e1SPatrick Williams                systemd_add_files_and_parse(pkg_systemd, path, service_base + '@.service')
20792b42cb3SPatrick Williams
20892b42cb3SPatrick Williams    # Check service-files and call systemd_add_files_and_parse for each entry
20992b42cb3SPatrick Williams    def systemd_check_services():
210*96e4b4e1SPatrick Williams        searchpaths = systemd_service_searchpaths(False, d)
211*96e4b4e1SPatrick Williams        searchpaths.extend(systemd_service_searchpaths(True, d))
212*96e4b4e1SPatrick Williams
21392b42cb3SPatrick Williams        systemd_packages = d.getVar('SYSTEMD_PACKAGES')
21492b42cb3SPatrick Williams
21592b42cb3SPatrick Williams        # scan for all in SYSTEMD_SERVICE[]
21692b42cb3SPatrick Williams        for pkg_systemd in systemd_packages.split():
21792b42cb3SPatrick Williams            for service in get_package_var(d, 'SYSTEMD_SERVICE', pkg_systemd).split():
218*96e4b4e1SPatrick Williams                path_found, base = systemd_service_path(service, searchpaths, d)
21992b42cb3SPatrick Williams
22092b42cb3SPatrick Williams                if path_found != '':
221*96e4b4e1SPatrick Williams                    systemd_add_files_and_parse(pkg_systemd, path_found, service)
22292b42cb3SPatrick Williams                else:
22392b42cb3SPatrick Williams                    bb.fatal("Didn't find service unit '{0}', specified in SYSTEMD_SERVICE:{1}. {2}".format(
22492b42cb3SPatrick Williams                        service, pkg_systemd, "Also looked for service unit '{0}'.".format(base) if base is not None else ""))
22592b42cb3SPatrick Williams
226*96e4b4e1SPatrick Williams    def systemd_create_presets(pkg, action, user):
227*96e4b4e1SPatrick Williams        # Check there is at least one service of given type (system/user), don't
228*96e4b4e1SPatrick Williams        # create empty files.
229*96e4b4e1SPatrick Williams        needs_preset = False
230*96e4b4e1SPatrick Williams        for service in d.getVar('SYSTEMD_SERVICE:%s' % pkg).split():
231*96e4b4e1SPatrick Williams            if systemd_service_exists(service, user, d):
232*96e4b4e1SPatrick Williams                needs_preset = True
233*96e4b4e1SPatrick Williams                break
234*96e4b4e1SPatrick Williams
235*96e4b4e1SPatrick Williams        if not needs_preset:
236*96e4b4e1SPatrick Williams            return
237*96e4b4e1SPatrick Williams
238*96e4b4e1SPatrick Williams        prefix = "user" if user else "system"
239*96e4b4e1SPatrick Williams        presetf = oe.path.join(d.getVar("PKGD"), d.getVar("systemd_unitdir"), "%s-preset/98-%s.preset" % (prefix, pkg))
24092b42cb3SPatrick Williams        bb.utils.mkdirhier(os.path.dirname(presetf))
24192b42cb3SPatrick Williams        with open(presetf, 'a') as fd:
24292b42cb3SPatrick Williams            for service in d.getVar('SYSTEMD_SERVICE:%s' % pkg).split():
243*96e4b4e1SPatrick Williams                if not systemd_service_exists(service, user, d):
244*96e4b4e1SPatrick Williams                    continue
24592b42cb3SPatrick Williams                fd.write("%s %s\n" % (action,service))
246*96e4b4e1SPatrick Williams        d.appendVar("FILES:%s" % pkg, ' ' + oe.path.join(d.getVar("systemd_unitdir"), "%s-preset/98-%s.preset" % (prefix, pkg)))
24792b42cb3SPatrick Williams
24892b42cb3SPatrick Williams    # Run all modifications once when creating package
24992b42cb3SPatrick Williams    if os.path.exists(d.getVar("D")):
25092b42cb3SPatrick Williams        for pkg in d.getVar('SYSTEMD_PACKAGES').split():
25192b42cb3SPatrick Williams            systemd_check_package(pkg)
25292b42cb3SPatrick Williams            if d.getVar('SYSTEMD_SERVICE:' + pkg):
25392b42cb3SPatrick Williams                systemd_generate_package_scripts(pkg)
25492b42cb3SPatrick Williams                action = get_package_var(d, 'SYSTEMD_AUTO_ENABLE', pkg)
25592b42cb3SPatrick Williams                if action in ("enable", "disable"):
256*96e4b4e1SPatrick Williams                    systemd_create_presets(pkg, action, False)
257*96e4b4e1SPatrick Williams                    systemd_create_presets(pkg, action, True)
25892b42cb3SPatrick Williams                elif action not in ("mask", "preset"):
25992b42cb3SPatrick Williams                    bb.fatal("SYSTEMD_AUTO_ENABLE:%s '%s' is not 'enable', 'disable', 'mask' or 'preset'" % (pkg, action))
26092b42cb3SPatrick Williams        systemd_check_services()
26192b42cb3SPatrick Williams}
26292b42cb3SPatrick Williams
263517393d9SAndrew GeisslerPACKAGESPLITFUNCS =+ "systemd_populate_packages"
26492b42cb3SPatrick Williams
2658460358cSPatrick Williamsrm_systemd_unitdir() {
2668460358cSPatrick Williams	rm -rf ${D}${systemd_unitdir}
2678460358cSPatrick Williams	# Change into ${D} and use a relative path with rmdir -p to avoid
2688460358cSPatrick Williams	# having it remove ${D} if it becomes empty.
2698460358cSPatrick Williams	(cd ${D} && rmdir -p $(dirname ${systemd_unitdir#/}) 2>/dev/null || :)
27092b42cb3SPatrick Williams}
27192b42cb3SPatrick Williams
2728460358cSPatrick Williamsrm_sysvinit_initddir() {
2738460358cSPatrick Williams	local sysv_initddir=${INIT_D_DIR}
2748460358cSPatrick Williams	: ${sysv_initddir:=${sysconfdir}/init.d}
27592b42cb3SPatrick Williams
27692b42cb3SPatrick Williams	# If systemd_system_unitdir contains anything, delete sysv_initddir
2778460358cSPatrick Williams	if [ "$(ls -A ${D}${systemd_system_unitdir} 2>/dev/null)" ]; then
2788460358cSPatrick Williams		rm -rf ${D}$sysv_initddir
2798460358cSPatrick Williams		rmdir -p $(dirname ${D}$sysv_initddir) 2>/dev/null || :
2808460358cSPatrick Williams	fi
28192b42cb3SPatrick Williams}
28292b42cb3SPatrick Williams
28392b42cb3SPatrick Williamsdo_install[postfuncs] += "${RMINITDIR}"
2848460358cSPatrick WilliamsRMINITDIR = " \
2858460358cSPatrick Williams    ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', '', 'rm_systemd_unitdir', d)} \
2868460358cSPatrick Williams    ${@'rm_sysvinit_initddir' if bb.utils.contains('DISTRO_FEATURES', 'systemd', True, False, d) and \
2878460358cSPatrick Williams                                 not bb.utils.contains('DISTRO_FEATURES', 'sysvinit', True, False, d) else ''} \
2888460358cSPatrick Williams"
2898460358cSPatrick WilliamsRMINITDIR:class-native = ""
290