1# 2# Copyright OpenEmbedded Contributors 3# 4# SPDX-License-Identifier: MIT 5# 6 7inherit useradd_base 8 9# base-passwd-cross provides the default passwd and group files in the 10# target sysroot, and shadow -native and -sysroot provide the utilities 11# and support files needed to add and modify user and group accounts 12DEPENDS:append:class-target = " base-files shadow-native shadow-sysroot shadow base-passwd" 13PACKAGE_WRITE_DEPS += "shadow-native" 14 15# This preinstall function can be run in four different contexts: 16# 17# a) Before do_install 18# b) At do_populate_sysroot_setscene when installing from sstate packages 19# c) As the preinst script in the target package at do_rootfs time 20# d) As the preinst script in the target package on device as a package upgrade 21# 22useradd_preinst () { 23OPT="" 24SYSROOT="" 25 26if test "x$D" != "x"; then 27 # Installing into a sysroot 28 SYSROOT="$D" 29 OPT="--root $D" 30 31 # Make sure login.defs is there, this is to make debian package backend work 32 # correctly while doing rootfs. 33 # The problem here is that if /etc/login.defs is treated as a config file for 34 # shadow package, then while performing preinsts for packages that depend on 35 # shadow, there might only be /etc/login.def.dpkg-new there in root filesystem. 36 if [ ! -e $D${sysconfdir}/login.defs -a -e $D${sysconfdir}/login.defs.dpkg-new ]; then 37 cp $D${sysconfdir}/login.defs.dpkg-new $D${sysconfdir}/login.defs 38 fi 39 40 # user/group lookups should match useradd/groupadd --root 41 export PSEUDO_PASSWD="$SYSROOT" 42fi 43 44# If we're not doing a special SSTATE/SYSROOT install 45# then set the values, otherwise use the environment 46if test "x$UA_SYSROOT" = "x"; then 47 # Installing onto a target 48 # Add groups and users defined only for this package 49 GROUPADD_PARAM="${GROUPADD_PARAM}" 50 USERADD_PARAM="${USERADD_PARAM}" 51 GROUPMEMS_PARAM="${GROUPMEMS_PARAM}" 52fi 53 54# Perform group additions first, since user additions may depend 55# on these groups existing 56if test "x`echo $GROUPADD_PARAM | tr -d '[:space:]'`" != "x"; then 57 echo "Running groupadd commands..." 58 # Invoke multiple instances of groupadd for parameter lists 59 # separated by ';' 60 opts=`echo "$GROUPADD_PARAM" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` 61 remaining=`echo "$GROUPADD_PARAM" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` 62 while test "x$opts" != "x"; do 63 perform_groupadd "$SYSROOT" "$OPT $opts" 64 if test "x$opts" = "x$remaining"; then 65 break 66 fi 67 opts=`echo "$remaining" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` 68 remaining=`echo "$remaining" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` 69 done 70fi 71 72if test "x`echo $USERADD_PARAM | tr -d '[:space:]'`" != "x"; then 73 echo "Running useradd commands..." 74 # Invoke multiple instances of useradd for parameter lists 75 # separated by ';' 76 opts=`echo "$USERADD_PARAM" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` 77 remaining=`echo "$USERADD_PARAM" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` 78 while test "x$opts" != "x"; do 79 perform_useradd "$SYSROOT" "$OPT $opts" 80 if test "x$opts" = "x$remaining"; then 81 break 82 fi 83 opts=`echo "$remaining" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` 84 remaining=`echo "$remaining" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` 85 done 86fi 87 88if test "x`echo $GROUPMEMS_PARAM | tr -d '[:space:]'`" != "x"; then 89 echo "Running groupmems commands..." 90 # Invoke multiple instances of groupmems for parameter lists 91 # separated by ';' 92 opts=`echo "$GROUPMEMS_PARAM" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` 93 remaining=`echo "$GROUPMEMS_PARAM" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` 94 while test "x$opts" != "x"; do 95 perform_groupmems "$SYSROOT" "$OPT $opts" 96 if test "x$opts" = "x$remaining"; then 97 break 98 fi 99 opts=`echo "$remaining" | cut -d ';' -f 1 | sed -e 's#[ \t]*$##'` 100 remaining=`echo "$remaining" | cut -d ';' -f 2- | sed -e 's#[ \t]*$##'` 101 done 102fi 103} 104 105useradd_sysroot () { 106 user_group_groupmems_add_sysroot user 107} 108 109groupadd_sysroot () { 110 user_group_groupmems_add_sysroot group 111} 112 113groupmemsadd_sysroot () { 114 user_group_groupmems_add_sysroot groupmems 115} 116 117user_group_groupmems_add_sysroot () { 118 # Pseudo may (do_prepare_recipe_sysroot) or may not (do_populate_sysroot_setscene) be running 119 # at this point so we're explicit about the environment so pseudo can load if 120 # not already present. 121 # PSEUDO_SYSROOT can contain references to the build architecture and COMPONENT_DIR 122 # so needs the STAGING_FIXME below 123 export PSEUDO="${FAKEROOTENV} ${PSEUDO_SYSROOT}${bindir_native}/pseudo" 124 125 # Explicitly set $D since it isn't set to anything 126 # before do_prepare_recipe_sysroot 127 D=${STAGING_DIR_TARGET} 128 129 # base-passwd's postinst may not have run yet in which case we'll get called later, just exit. 130 # Beware that in some cases we might see the fake pseudo passwd here, in which case we also must 131 # exit. 132 if [ ! -f $D${sysconfdir}/passwd ] || 133 grep -q this-is-the-pseudo-passwd $D${sysconfdir}/passwd; then 134 exit 0 135 fi 136 137 # It is also possible we may be in a recipe which doesn't have useradd dependencies and hence the 138 # useradd/groupadd tools are unavailable. If there is no dependency, we assume we don't want to 139 # create users in the sysroot 140 if ! command -v useradd; then 141 bbwarn "command useradd not found!" 142 exit 0 143 fi 144 145 # Add groups and users defined for all recipe packages 146 if test "$1" = "group"; then 147 GROUPADD_PARAM="${@get_all_cmd_params(d, 'groupadd')}" 148 elif test "$1" = "user"; then 149 USERADD_PARAM="${@get_all_cmd_params(d, 'useradd')}" 150 elif test "$1" = "groupmems"; then 151 GROUPMEMS_PARAM="${@get_all_cmd_params(d, 'groupmems')}" 152 elif test "x$1" = "x"; then 153 bbwarn "missing type of passwd db action" 154 fi 155 156 # Tell the system to use the environment vars 157 UA_SYSROOT=1 158 159 useradd_preinst 160} 161 162# The export of PSEUDO in useradd_sysroot() above contains references to 163# ${PSEUDO_SYSROOT} and ${PSEUDO_LOCALSTATEDIR}. Additionally, the logging 164# shell functions use ${LOGFIFO}. These need to be handled when restoring 165# postinst-useradd-${PN} from the sstate cache. 166EXTRA_STAGING_FIXMES += "PSEUDO_SYSROOT PSEUDO_LOCALSTATEDIR LOGFIFO" 167 168python useradd_sysroot_sstate () { 169 for type, sort_prefix in [("group", "01"), ("user", "02"), ("groupmems", "03")]: 170 scriptfile = None 171 task = d.getVar("BB_CURRENTTASK") 172 if task == "package_setscene": 173 bb.build.exec_func(type + "add_sysroot", d) 174 elif task == "prepare_recipe_sysroot": 175 # Used to update this recipe's own sysroot so the user/groups are available to do_install 176 177 # If do_populate_sysroot is triggered and we write the file here, there would be an overlapping 178 # files. See usergrouptests.UserGroupTests.test_add_task_between_p_sysroot_and_package 179 scriptfile = d.expand("${RECIPE_SYSROOT}${bindir}/postinst-useradd-" + sort_prefix + type + "-${PN}-recipedebug") 180 181 bb.build.exec_func(type + "add_sysroot", d) 182 elif task == "populate_sysroot": 183 # Used when installed in dependent task sysroots 184 scriptfile = d.expand("${SYSROOT_DESTDIR}${bindir}/postinst-useradd-" + sort_prefix + type + "-${PN}") 185 186 if scriptfile: 187 bb.utils.mkdirhier(os.path.dirname(scriptfile)) 188 with open(scriptfile, 'w') as script: 189 script.write("#!/bin/sh -e\n") 190 bb.data.emit_func(type + "add_sysroot", script, d) 191 script.write(type + "add_sysroot\n") 192 os.chmod(scriptfile, 0o755) 193} 194 195do_prepare_recipe_sysroot[postfuncs] += "${SYSROOTFUNC}" 196SYSROOTFUNC:class-target = "useradd_sysroot_sstate" 197SYSROOTFUNC = "" 198 199SYSROOT_PREPROCESS_FUNCS += "${SYSROOTFUNC}" 200 201SSTATEPREINSTFUNCS:append:class-target = " useradd_sysroot_sstate" 202 203USERADD_DEPENDS ??= "" 204DEPENDS += "${USERADD_DEPENDS}" 205do_package_setscene[depends] += "${USERADDSETSCENEDEPS}" 206do_populate_sysroot_setscene[depends] += "${USERADDSETSCENEDEPS}" 207USERADDSETSCENEDEPS: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()])}" 208USERADDSETSCENEDEPS = "" 209 210# Recipe parse-time sanity checks 211def update_useradd_after_parse(d): 212 useradd_packages = d.getVar('USERADD_PACKAGES') 213 214 if not useradd_packages: 215 bb.fatal("%s inherits useradd but doesn't set USERADD_PACKAGES" % d.getVar('FILE', False)) 216 217 for pkg in useradd_packages.split(): 218 d.appendVarFlag("do_populate_sysroot", "vardeps", " USERADD_PARAM:%s GROUPADD_PARAM:%s GROUPMEMS_PARAM:%s" % (pkg, pkg, pkg)) 219 if not d.getVar('USERADD_PARAM:%s' % pkg) and not d.getVar('GROUPADD_PARAM:%s' % pkg) and not d.getVar('GROUPMEMS_PARAM:%s' % pkg): 220 bb.fatal("%s inherits useradd but doesn't set USERADD_PARAM, GROUPADD_PARAM or GROUPMEMS_PARAM for package %s" % (d.getVar('FILE', False), pkg)) 221 222python __anonymous() { 223 if not bb.data.inherits_class('nativesdk', d) \ 224 and not bb.data.inherits_class('native', d): 225 update_useradd_after_parse(d) 226} 227 228# Return a single [GROUP|USER]ADD_PARAM formatted string which includes the 229# [group|user]add parameters for all USERADD_PACKAGES in this recipe 230def get_all_cmd_params(d, cmd_type): 231 import string 232 233 param_type = cmd_type.upper() + "_PARAM:%s" 234 params = [] 235 236 useradd_packages = d.getVar('USERADD_PACKAGES') or "" 237 for pkg in useradd_packages.split(): 238 param = d.getVar(param_type % pkg) 239 if param: 240 params.append(param.rstrip(" ;")) 241 242 return "; ".join(params) 243 244# Adds the preinst script into generated packages 245fakeroot python populate_packages:prepend () { 246 def update_useradd_package(pkg): 247 bb.debug(1, 'adding user/group calls to preinst for %s' % pkg) 248 249 """ 250 useradd preinst is appended here because pkg_preinst may be 251 required to execute on the target. Not doing so may cause 252 useradd preinst to be invoked twice, causing unwanted warnings. 253 """ 254 preinst = d.getVar('pkg_preinst:%s' % pkg) or d.getVar('pkg_preinst') 255 if not preinst: 256 preinst = '#!/bin/sh\n' 257 preinst += 'bbnote () {\n\techo "NOTE: $*"\n}\n' 258 preinst += 'bbwarn () {\n\techo "WARNING: $*"\n}\n' 259 preinst += 'bbfatal () {\n\techo "ERROR: $*"\n\texit 1\n}\n' 260 preinst += 'perform_groupadd () {\n%s}\n' % d.getVar('perform_groupadd') 261 preinst += 'perform_useradd () {\n%s}\n' % d.getVar('perform_useradd') 262 preinst += 'perform_groupmems () {\n%s}\n' % d.getVar('perform_groupmems') 263 preinst += d.getVar('useradd_preinst') 264 # Expand out the *_PARAM variables to the package specific versions 265 for rep in ["GROUPADD_PARAM", "USERADD_PARAM", "GROUPMEMS_PARAM"]: 266 val = d.getVar(rep + ":" + pkg) or "" 267 preinst = preinst.replace("${" + rep + "}", val) 268 d.setVar('pkg_preinst:%s' % pkg, preinst) 269 270 # RDEPENDS setup 271 rdepends = d.getVar("RDEPENDS:%s" % pkg) or "" 272 rdepends += ' ' + d.getVar('MLPREFIX', False) + 'base-passwd' 273 rdepends += ' ' + d.getVar('MLPREFIX', False) + 'shadow' 274 # base-files is where the default /etc/skel is packaged 275 rdepends += ' ' + d.getVar('MLPREFIX', False) + 'base-files' 276 d.setVar("RDEPENDS:%s" % pkg, rdepends) 277 278 # Add the user/group preinstall scripts and RDEPENDS requirements 279 # to packages specified by USERADD_PACKAGES 280 if not bb.data.inherits_class('nativesdk', d) \ 281 and not bb.data.inherits_class('native', d): 282 useradd_packages = d.getVar('USERADD_PACKAGES') or "" 283 for pkg in useradd_packages.split(): 284 update_useradd_package(pkg) 285} 286 287# Use the following to extend the useradd with custom functions 288USERADDEXTENSION ?= "" 289 290inherit_defer ${USERADDEXTENSION} 291