| # | |
| # Copyright OpenEmbedded Contributors | |
| # | |
| # SPDX-License-Identifier: MIT | |
| # | |
| inherit useradd_base | |
| # base-passwd-cross provides the default passwd and group files in the | |
| # target sysroot, and shadow -native and -sysroot provide the utilities | |
| # and support files needed to add and modify user and group accounts | |
| DEPENDS:append:class-target = " base-files shadow-native shadow-sysroot shadow base-passwd" | |
| PACKAGE_WRITE_DEPS += "shadow-native" | |
| # This preinstall function can be run in four different contexts: | |
| # | |
| # a) Before do_install | |
| # b) At do_populate_sysroot_setscene when installing from sstate packages | |
| # c) As the preinst script in the target package at do_rootfs time | |
| # d) As the preinst script in the target package on device as a package upgrade | |
| # | |
| useradd_preinst() { | |
| OPT="" | |
| SYSROOT="" | |
| if test "x$D" != "x"; then | |
| # Installing into a sysroot | |
| SYSROOT="$D" | |
| OPT="--prefix $D" | |
| # Make sure login.defs is there, this is to make debian package backend work | |
| # correctly while doing rootfs. | |
| # The problem here is that if /etc/login.defs is treated as a config file for | |
| # shadow package, then while performing preinsts for packages that depend on | |
| # shadow, there might only be /etc/login.def.dpkg-new there in root filesystem. | |
| if [ ! -e $D${sysconfdir}/login.defs -a -e $D${sysconfdir}/login.defs.dpkg-new ]; then | |
| cp $D${sysconfdir}/login.defs.dpkg-new $D${sysconfdir}/login.defs | |
| fi | |
| # user/group lookups should match useradd/groupadd --prefix | |
| export PSEUDO_PASSWD="$SYSROOT" | |
| fi | |
| # If we're not doing a special SSTATE/SYSROOT install | |
| # then set the values, otherwise use the environment | |
| if test "x$UA_SYSROOT" = "x"; then | |
| # Installing onto a target | |
| # Add groups and users defined only for this package | |
| GROUPADD_PARAM="${GROUPADD_PARAM}" | |
| USERADD_PARAM="${USERADD_PARAM}" | |
| USERMOD_PARAM="${USERMOD_PARAM}" | |
| fi | |
| # Perform group additions first, since user additions may depend | |
| # on these groups existing | |
| if test "x`echo $GROUPADD_PARAM | tr -d '[:space:]'`" != "x"; then | |
| echo "Running groupadd commands..." | |
| # Invoke multiple instances of groupadd for parameter lists | |
| # separated by ';' | |
| opts=`echo "$GROUPADD_PARAM" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` | |
| remaining=`echo "$GROUPADD_PARAM" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` | |
| while test "x$opts" != "x"; do | |
| perform_groupadd "$SYSROOT" "$OPT $opts" | |
| if test "x$opts" = "x$remaining"; then | |
| break | |
| fi | |
| opts=`echo "$remaining" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` | |
| remaining=`echo "$remaining" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` | |
| done | |
| fi | |
| if test "x`echo $USERADD_PARAM | tr -d '[:space:]'`" != "x"; then | |
| echo "Running useradd commands..." | |
| # Invoke multiple instances of useradd for parameter lists | |
| # separated by ';' | |
| opts=`echo "$USERADD_PARAM" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` | |
| remaining=`echo "$USERADD_PARAM" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` | |
| while test "x$opts" != "x"; do | |
| perform_useradd "$SYSROOT" "$OPT $opts" | |
| if test "x$opts" = "x$remaining"; then | |
| break | |
| fi | |
| opts=`echo "$remaining" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` | |
| remaining=`echo "$remaining" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` | |
| done | |
| fi | |
| if test "x`echo $USERMOD_PARAM | tr -d '[:space:]'`" != "x"; then | |
| echo "Running usermod commands..." | |
| # Invoke multiple instances of usermod for parameter lists | |
| # separated by ';' | |
| opts=`echo "$USERMOD_PARAM" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` | |
| remaining=`echo "$USERMOD_PARAM" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` | |
| while test "x$opts" != "x"; do | |
| perform_usermod "$SYSROOT" "$OPT $opts" | |
| if test "x$opts" = "x$remaining"; then | |
| break | |
| fi | |
| opts=`echo "$remaining" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` | |
| remaining=`echo "$remaining" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` | |
| done | |
| fi | |
| } | |
| groupadd_sysroot() { | |
| common_useradd_sysroot groupadd | |
| } | |
| useradd_sysroot() { | |
| common_useradd_sysroot useradd | |
| } | |
| usermod_sysroot() { | |
| common_useradd_sysroot usermod | |
| } | |
| common_useradd_sysroot() { | |
| # Pseudo may (do_prepare_recipe_sysroot) or may not (do_populate_sysroot_setscene) be running | |
| # at this point so we're explicit about the environment so pseudo can load if | |
| # not already present. | |
| # PSEUDO_SYSROOT can contain references to the build architecture and COMPONENT_DIR | |
| # so needs the STAGING_FIXME below | |
| export PSEUDO="${FAKEROOTENV} ${PSEUDO_SYSROOT}${bindir_native}/pseudo" | |
| # Explicitly set $D since it isn't set to anything | |
| # before do_prepare_recipe_sysroot | |
| D=${STAGING_DIR_TARGET} | |
| # base-passwd's postinst may not have run yet in which case we'll get called later, just exit. | |
| # Beware that in some cases we might see the fake pseudo passwd here, in which case we also must | |
| # exit. | |
| if [ ! -f $D${sysconfdir}/passwd ] || | |
| grep -q this-is-the-pseudo-passwd $D${sysconfdir}/passwd; then | |
| exit 0 | |
| fi | |
| # Add groups and users defined for all recipe packages | |
| case "$1" in | |
| groupadd) GROUPADD_PARAM="${@get_all_cmd_params(d, 'groupadd')}";; | |
| useradd) USERADD_PARAM="${@get_all_cmd_params(d, 'useradd')}";; | |
| usermod) USERMOD_PARAM="${@get_all_cmd_params(d, 'usermod')}";; | |
| esac | |
| # It is also possible we may be in a recipe which doesn't have useradd dependencies and hence the | |
| # useradd/groupadd tools are unavailable. If there is no dependency, we assume we don't want to | |
| # create users in the sysroot | |
| if ! command -v "$1"; then | |
| bbwarn "The $1 command could not be found!" | |
| exit 0 | |
| fi | |
| # Tell the system to use the environment vars | |
| UA_SYSROOT=1 | |
| useradd_preinst | |
| } | |
| # The export of PSEUDO in common_useradd_sysroot() above contains references to | |
| # ${PSEUDO_SYSROOT} and ${PSEUDO_LOCALSTATEDIR}. Additionally, the logging | |
| # shell functions use ${LOGFIFO}. These need to be handled when restoring | |
| # postinst-useradd-${PN} from the sstate cache. | |
| EXTRA_STAGING_FIXMES += "PSEUDO_SYSROOT PSEUDO_LOCALSTATEDIR LOGFIFO" | |
| python useradd_sysroot_sstate() { | |
| for cmd, sort_prefix in [("groupadd", "01"), ("useradd", "02"), ("usermod", "03")]: | |
| scriptfile = None | |
| task = d.getVar("BB_CURRENTTASK") | |
| if task == "package_setscene": | |
| bb.build.exec_func(f"{cmd}_sysroot", d) | |
| elif task == "prepare_recipe_sysroot": | |
| # Used to update this recipe's own sysroot so the user/groups are available to do_install | |
| # If do_populate_sysroot is triggered and we write the file here, there would be an overlapping | |
| # file. See usergrouptests.UserGroupTests.test_add_task_between_p_sysroot_and_package | |
| scriptfile = d.expand("${RECIPE_SYSROOT}${bindir}/postinst-useradd-" + sort_prefix + cmd + "-${PN}-recipedebug") | |
| bb.build.exec_func(f"{cmd}_sysroot", d) | |
| elif task == "populate_sysroot": | |
| # Used when installed in dependent task sysroots | |
| scriptfile = d.expand("${SYSROOT_DESTDIR}${bindir}/postinst-useradd-" + sort_prefix + cmd + "-${PN}") | |
| if scriptfile: | |
| bb.utils.mkdirhier(os.path.dirname(scriptfile)) | |
| with open(scriptfile, 'w') as script: | |
| script.write("#!/bin/sh -e\n") | |
| bb.data.emit_func(f"{cmd}_sysroot", script, d) | |
| script.write(f"{cmd}_sysroot\n") | |
| os.chmod(scriptfile, 0o755) | |
| } | |
| do_prepare_recipe_sysroot[postfuncs] += "${SYSROOTFUNC}" | |
| SYSROOTFUNC:class-target = "useradd_sysroot_sstate" | |
| SYSROOTFUNC = "" | |
| SYSROOT_PREPROCESS_FUNCS += "${SYSROOTFUNC}" | |
| SSTATEPREINSTFUNCS:append:class-target = " useradd_sysroot_sstate" | |
| USERADD_DEPENDS ??= "" | |
| DEPENDS += "${USERADD_DEPENDS}" | |
| do_package_setscene[depends] += "${USERADDSETSCENEDEPS}" | |
| do_populate_sysroot_setscene[depends] += "${USERADDSETSCENEDEPS}" | |
| USERADDSETSCENEDEPS:class-target = "${MLPREFIX}base-passwd:do_populate_sysroot_setscene pseudo-native:do_populate_sysroot_setscene shadow-native:do_populate_sysroot_setscene ${MLPREFIX}shadow-sysroot:do_populate_sysroot_setscene ${@' '.join(['%s:do_populate_sysroot_setscene' % pkg for pkg in d.getVar("USERADD_DEPENDS").split()])}" | |
| USERADDSETSCENEDEPS = "" | |
| # Recipe parse-time sanity checks | |
| def update_useradd_after_parse(d): | |
| useradd_packages = d.getVar('USERADD_PACKAGES') | |
| if not useradd_packages: | |
| # It's valid to inherit useradd and only set USERADD_DEPENDS to | |
| # depend on users/groups created by another recipe, without | |
| # creating any users/groups in this recipe. | |
| if d.getVar('USERADD_DEPENDS'): | |
| return | |
| bb.fatal("%s inherits useradd but doesn't set USERADD_PACKAGES" % d.getVar('FILE', False)) | |
| for pkg in useradd_packages.split(): | |
| d.appendVarFlag("do_populate_sysroot", "vardeps", f" USERADD_PARAM:{pkg} GROUPADD_PARAM:{pkg} USERMOD_PARAM:{pkg}") | |
| if not any(d.getVar(f"{name}_PARAM:{pkg}") for name in ["USERADD", "GROUPADD", "USERMOD"]): | |
| bb.fatal("%s inherits useradd but doesn't set USERADD_PARAM, GROUPADD_PARAM or USERMOD_PARAM for package %s" % (d.getVar('FILE', False), pkg)) | |
| python __anonymous() { | |
| if not bb.data.inherits_class('nativesdk', d) \ | |
| and not bb.data.inherits_class('native', d): | |
| update_useradd_after_parse(d) | |
| } | |
| # Return a single (GROUPADD|USERADD|USERMOD)_PARAM formatted string which includes the | |
| # (groupadd|useradd|usermod) parameters for all USERADD_PACKAGES in this recipe | |
| def get_all_cmd_params(d, cmd_type): | |
| import string | |
| param_type = cmd_type.upper() + "_PARAM:%s" | |
| params = [] | |
| useradd_packages = d.getVar('USERADD_PACKAGES') or "" | |
| for pkg in useradd_packages.split(): | |
| param = d.getVar(param_type % pkg) | |
| if param: | |
| params.append(param.rstrip(" ;")) | |
| return "; ".join(params) | |
| # Adds the preinst script into generated packages | |
| fakeroot python populate_packages:prepend() { | |
| def update_useradd_package(pkg): | |
| bb.debug(1, 'adding user/group calls to preinst for %s' % pkg) | |
| """ | |
| useradd preinst is appended here because pkg_preinst may be | |
| required to execute on the target. Not doing so may cause | |
| useradd preinst to be invoked twice, causing unwanted warnings. | |
| """ | |
| preinst = d.getVar('pkg_preinst:%s' % pkg) or d.getVar('pkg_preinst') | |
| if not preinst: | |
| preinst = '#!/bin/sh\n' | |
| preinst += 'bbnote () {\n\techo "NOTE: $*"\n}\n' | |
| preinst += 'bbwarn () {\n\techo "WARNING: $*"\n}\n' | |
| preinst += 'bbfatal () {\n\techo "ERROR: $*"\n\texit 1\n}\n' | |
| preinst += 'perform_groupadd () {\n%s}\n' % d.getVar('perform_groupadd') | |
| preinst += 'perform_useradd () {\n%s}\n' % d.getVar('perform_useradd') | |
| preinst += 'perform_usermod () {\n%s}\n' % d.getVar('perform_usermod') | |
| preinst += d.getVar('useradd_preinst') | |
| # Expand out the *_PARAM variables to the package specific versions | |
| for rep in ["GROUPADD_PARAM", "USERADD_PARAM", "USERMOD_PARAM"]: | |
| val = d.getVar(rep + ":" + pkg) or "" | |
| preinst = preinst.replace("${" + rep + "}", val) | |
| d.setVar('pkg_preinst:%s' % pkg, preinst) | |
| # RDEPENDS setup | |
| rdepends = d.getVar("RDEPENDS:%s" % pkg) or "" | |
| rdepends += ' ' + d.getVar('MLPREFIX', False) + 'base-passwd' | |
| rdepends += ' ' + d.getVar('MLPREFIX', False) + 'shadow' | |
| # base-files is where the default /etc/skel is packaged | |
| rdepends += ' ' + d.getVar('MLPREFIX', False) + 'base-files' | |
| d.setVar("RDEPENDS:%s" % pkg, rdepends) | |
| # Add the user/group preinstall scripts and RDEPENDS requirements | |
| # to packages specified by USERADD_PACKAGES | |
| if not bb.data.inherits_class('nativesdk', d) \ | |
| and not bb.data.inherits_class('native', d): | |
| useradd_packages = d.getVar('USERADD_PACKAGES') or "" | |
| for pkg in useradd_packages.split(): | |
| update_useradd_package(pkg) | |
| } | |
| # Use the following to extend the useradd with custom functions | |
| USERADDEXTENSION ?= "" | |
| inherit_defer ${USERADDEXTENSION} |