From: Jackie Huang <[email protected]>

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 <[email protected]>
---
 meta/classes/useradd.bbclass |  200 +++++++++++++++++------------------------
 1 files changed, 83 insertions(+), 117 deletions(-)

diff --git a/meta/classes/useradd.bbclass b/meta/classes/useradd.bbclass
index bb8f42b..81bcb56 100644
--- a/meta/classes/useradd.bbclass
+++ b/meta/classes/useradd.bbclass
@@ -14,126 +14,92 @@ 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
+       import commands
+       import os
+
+       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"
@@ -171,7 +137,7 @@ python __anonymous() {
 # [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 = []
 
-- 
1.7.4


_______________________________________________
Openembedded-core mailing list
[email protected]
http://lists.linuxtogo.org/cgi-bin/mailman/listinfo/openembedded-core

Reply via email to