[OE-core] [PATCH 1/1] useradd.bbclass: use locking of bb.utils to avoid lock race issue of useradd/groupadd
jackie.huang at windriver.com
jackie.huang at windriver.com
Sun Jul 22 06:53:03 UTC 2012
From: Jackie Huang <jackie.huang at windriver.com>
A race condition can occur when adding users and groups to the
passwd and group files, in [YOCTO #1794], 10 times retry added
but it is not fixed completely.
This fix re-writes the useradd_preinst and useradd_sysroot with
python and use locking of bb.utils to lock the passwd and group
files before executing useradd/groupadd commands to avoid the
lock race themselves.
[YOCTO #2779]
Signed-off-by: Jackie Huang <jackie.huang at windriver.com>
---
meta/classes/useradd.bbclass | 284 ++++++++++++++++++------------------------
1 files changed, 124 insertions(+), 160 deletions(-)
diff --git a/meta/classes/useradd.bbclass b/meta/classes/useradd.bbclass
index bb8f42b..ed5ed69 100644
--- a/meta/classes/useradd.bbclass
+++ b/meta/classes/useradd.bbclass
@@ -14,126 +14,90 @@ USERADDDEPENDS_virtclass-nativesdk = ""
# 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="--root $D"
-
- # Add groups and users defined for all recipe packages
- GROUPADD_PARAM="${@get_all_cmd_params(d, 'group')}"
- USERADD_PARAM="${@get_all_cmd_params(d, 'user')}"
-else
- # Installing onto a target
- # Add groups and users defined only for this package
- GROUPADD_PARAM="${GROUPADD_PARAM}"
- USERADD_PARAM="${USERADD_PARAM}"
-fi
-
-# Perform group additions first, since user additions may depend
-# on these groups existing
-if test "x$GROUPADD_PARAM" != "x"; then
- echo "Running groupadd commands..."
- # Invoke multiple instances of groupadd for parameter lists
- # separated by ';'
- opts=`echo "$GROUPADD_PARAM" | cut -d ';' -f 1`
- remaining=`echo "$GROUPADD_PARAM" | cut -d ';' -f 2-`
- while test "x$opts" != "x"; do
- groupname=`echo "$opts" | awk '{ print $NF }'`
- group_exists=`grep "^$groupname:" $SYSROOT/etc/group || true`
- if test "x$group_exists" = "x"; then
- count=1
- while true; do
- eval $PSEUDO groupadd $OPT $opts || true
- group_exists=`grep "^$groupname:" $SYSROOT/etc/group || true`
- if test "x$group_exists" = "x"; then
- # File locking issues can require us to retry the command
- echo "WARNING: groupadd command did not succeed. Retrying..."
- sleep 1
- else
- break
- fi
- count=`expr $count + 1`
- if test $count = 11; then
- echo "ERROR: tried running groupadd command 10 times without success, giving up"
- exit 1
- fi
- done
- else
- echo "Note: group $groupname already exists, not re-creating it"
- fi
-
- if test "x$opts" = "x$remaining"; then
- break
- fi
- opts=`echo "$remaining" | cut -d ';' -f 1`
- remaining=`echo "$remaining" | cut -d ';' -f 2-`
- done
-fi
-
-if test "x$USERADD_PARAM" != "x"; then
- echo "Running useradd commands..."
- # Invoke multiple instances of useradd for parameter lists
- # separated by ';'
- opts=`echo "$USERADD_PARAM" | cut -d ';' -f 1`
- remaining=`echo "$USERADD_PARAM" | cut -d ';' -f 2-`
- while test "x$opts" != "x"; do
- # useradd does not have a -f option, so we have to check if the
- # username already exists manually
- username=`echo "$opts" | awk '{ print $NF }'`
- user_exists=`grep "^$username:" $SYSROOT/etc/passwd || true`
- if test "x$user_exists" = "x"; then
- count=1
- while true; do
- eval $PSEUDO useradd $OPT $opts || true
- user_exists=`grep "^$username:" $SYSROOT/etc/passwd || true`
- if test "x$user_exists" = "x"; then
- # File locking issues can require us to retry the command
- echo "WARNING: useradd command did not succeed. Retrying..."
- sleep 1
- else
- break
- fi
- count=`expr $count + 1`
- if test $count = 11; then
- echo "ERROR: tried running useradd command 10 times without success, giving up"
- exit 1
- fi
- done
- else
- echo "Note: username $username already exists, not re-creating it"
- fi
-
- if test "x$opts" = "x$remaining"; then
- break
- fi
- opts=`echo "$remaining" | cut -d ';' -f 1`
- remaining=`echo "$remaining" | cut -d ';' -f 2-`
- done
-fi
+def useradd_preinst(d):
+ import re
+
+ sysroot = ""
+ opt = ""
+
+ dir = d.getVar('STAGING_DIR_TARGET', True)
+ if dir:
+ # Installing into a sysroot
+ sysroot = dir
+ opt = "--root %s" % dir
+
+ # Add groups and users defined for all recipe packages
+ groupadd_param = get_all_cmd_params(d, 'group')
+ useradd_param = get_all_cmd_params(d, 'user')
+ else:
+ # Installing onto a target
+ # Add groups and users defined only for this package
+ groupadd_param = d.getVar('GROUPADD_PARAM', True)
+ useradd_param = d.getVar('GROUPADD_PARAM', True)
+
+ group_file = '%s/etc/group' % sysroot
+ user_file = '%s/etc/passwd' % sysroot
+
+ # Use the locking of bb to the group/passwd file to avoid the
+ # locking issue of groupadd/useradd
+ group_lock = '%s.locked' % group_file
+ user_lock = '%s.locked' % user_file
+ lockfiles = [group_lock, user_lock]
+
+ with bb.utils.fileslocked(lockfiles):
+ # Perform group additions first, since user additions may depend
+ # on these groups existing
+ if groupadd_param and sysroot:
+ bb.debug(1, "Running groupadd commands ...")
+ # Invoke multiple instances of groupadd for parameter lists
+ # separated by ';'
+ param_list = groupadd_param.split(';')
+ for opts in param_list:
+ groupname = opts.split()[-1]
+ with open(group_file, 'r') as f:
+ passwd_lines = f.read()
+ group_re = re.compile('\n%s' % groupname)
+ if group_re.search(passwd_lines):
+ bb.note("Note: groupname %s already exists, not re-creating it" % groupname)
+ continue
+ try:
+ output, err = bb.process.run('groupadd %s %s' % (opt, opts))
+ except bb.process.CmdError as exc:
+ bb.error("Failed to add group: %s" % exc)
+ else:
+ bb.note("Successful to add group %s" % groupname)
+
+ if useradd_param:
+ bb.debug(1, "Running useradd commands ...")
+ # Invoke multiple instances of useradd for parameter lists
+ # separated by ';'
+ param_list = useradd_param.split(';')
+ for opts in param_list:
+ # useradd does not have a -f option, so we have to check if the
+ # username already exists manually
+ username = opts.split()[-1]
+ with open(user_file, 'r') as f:
+ passwd_lines = f.read()
+ user_re = re.compile('\n%s' % username)
+ if user_re.search(passwd_lines):
+ bb.note("Note: username %s already exists, not re-creating it" % username)
+ continue
+ try:
+ output, err = bb.process.run('useradd %s %s' % (opt, opts))
+ except bb.process.CmdError as exc:
+ bb.error("Failed to add user: %s" % exc)
+ else:
+ bb.note("Successful to add user %s" % username)
+
+fakeroot python useradd_sysroot () {
+ useradd_preinst(d)
}
-useradd_sysroot () {
- # Pseudo may (do_install) 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.
- export PSEUDO="${FAKEROOTENV} PSEUDO_LOCALSTATEDIR=${STAGING_DIR_TARGET}${localstatedir}/pseudo ${STAGING_DIR_NATIVE}${bindir}/pseudo"
-
- # Explicitly set $D since it isn't set to anything
- # before do_install
- D=${STAGING_DIR_TARGET}
- useradd_preinst
+fakeroot python useradd_sysroot_sstate () {
+ if d.getVar("BB_CURRENTTASK", True) == "package_setscene":
+ useradd_preinst(d)
}
-useradd_sysroot_sstate () {
- if [ "${BB_CURRENTTASK}" = "package_setscene" ]
- then
- useradd_sysroot
- fi
-}
do_install[prefuncs] += "${SYSROOTFUNC}"
SYSROOTFUNC = "useradd_sysroot"
@@ -146,7 +110,7 @@ SYSROOTPOSTFUNC_virtclass-cross = ""
SYSROOTPOSTFUNC_virtclass-native = ""
SYSROOTPOSTFUNC_virtclass-nativesdk = ""
-USERADDSETSCENEDEPS = "${MLPREFIX}base-passwd:do_populate_sysroot_setscene shadow-native:do_populate_sysroot_setscene ${MLPREFIX}shadow-sysroot:do_populate_sysroot_setscene"
+USERADDSETSCENEDEPS = "base-passwd:do_populate_sysroot_setscene shadow-native:do_populate_sysroot_setscene ${MLPREFIX}shadow-sysroot:do_populate_sysroot_setscene"
USERADDSETSCENEDEPS_virtclass-cross = ""
USERADDSETSCENEDEPS_virtclass-native = ""
USERADDSETSCENEDEPS_virtclass-nativesdk = ""
@@ -154,61 +118,61 @@ do_package_setscene[depends] = "${USERADDSETSCENEDEPS}"
# Recipe parse-time sanity checks
def update_useradd_after_parse(d):
- useradd_packages = d.getVar('USERADD_PACKAGES', True)
+ useradd_packages = d.getVar('USERADD_PACKAGES', True)
- if not useradd_packages:
- raise bb.build.FuncFailed, "%s inherits useradd but doesn't set USERADD_PACKAGES" % d.getVar('FILE')
+ if not useradd_packages:
+ raise bb.build.FuncFailed, "%s inherits useradd but doesn't set USERADD_PACKAGES" % d.getVar('FILE')
- for pkg in useradd_packages.split():
- if not d.getVar('USERADD_PARAM_%s' % pkg, True) and not d.getVar('GROUPADD_PARAM_%s' % pkg, True):
- raise bb.build.FuncFailed, "%s inherits useradd but doesn't set USERADD_PARAM or GROUPADD_PARAM for package %s" % (d.getVar('FILE'), pkg)
+ for pkg in useradd_packages.split():
+ if not d.getVar('USERADD_PARAM_%s' % pkg, True) and not d.getVar('GROUPADD_PARAM_%s' % pkg, True):
+ raise bb.build.FuncFailed, "%s inherits useradd but doesn't set USERADD_PARAM or GROUPADD_PARAM for package %s" % (d.getVar('FILE'), pkg)
python __anonymous() {
- update_useradd_after_parse(d)
+ update_useradd_after_parse(d)
}
# Return a single [GROUP|USER]ADD_PARAM formatted string which includes the
# [group|user]add parameters for all USERADD_PACKAGES in this recipe
def get_all_cmd_params(d, cmd_type):
- import string
-
- param_type = cmd_type.upper() + "ADD_PARAM_%s"
- params = []
+ import string
+
+ param_type = cmd_type.upper() + "ADD_PARAM_%s"
+ params = []
- useradd_packages = d.getVar('USERADD_PACKAGES', True) or ""
- for pkg in useradd_packages.split():
- param = d.getVar(param_type % pkg, True)
- if param:
- params.append(param)
+ useradd_packages = d.getVar('USERADD_PACKAGES', True) or ""
+ for pkg in useradd_packages.split():
+ param = d.getVar(param_type % pkg, True)
+ if param:
+ params.append(param)
- return string.join(params, "; ")
+ return string.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, True) or d.getVar('pkg_preinst', True)
- if not preinst:
- preinst = '#!/bin/sh\n'
- preinst += d.getVar('useradd_preinst', True)
- d.setVar('pkg_preinst_%s' % pkg, preinst)
-
- # RDEPENDS setup
- rdepends = d.getVar("RDEPENDS_%s" % pkg, True) or ""
- rdepends += ' ' + d.getVar('MLPREFIX') + 'base-passwd'
- rdepends += ' ' + d.getVar('MLPREFIX') + 'shadow'
- 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):
- useradd_packages = d.getVar('USERADD_PACKAGES', True) or ""
- for pkg in useradd_packages.split():
- update_useradd_package(pkg)
+ 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, True) or d.getVar('pkg_preinst', True)
+ if not preinst:
+ preinst = '#!/bin/sh\n'
+ preinst += d.getVar('useradd_preinst', True)
+ d.setVar('pkg_preinst_%s' % pkg, preinst)
+
+ # RDEPENDS setup
+ rdepends = d.getVar("RDEPENDS_%s" % pkg, True) or ""
+ rdepends += ' ' + d.getVar('MLPREFIX') + 'base-passwd'
+ rdepends += ' ' + d.getVar('MLPREFIX') + 'shadow'
+ 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):
+ useradd_packages = d.getVar('USERADD_PACKAGES', True) or ""
+ for pkg in useradd_packages.split():
+ update_useradd_package(pkg)
}
--
1.7.4
More information about the Openembedded-core
mailing list