[OE-core] [PATCH 2/2] systemd: add users/groups at build rather than runtime

Stephano Cetola stephano.cetola at linux.intel.com
Mon May 9 23:28:36 UTC 2016


This fix ensures that systemd users are created at build and that any
staticids for the users or groups are preserved. This functionality only
existed for readonly filesystems, however it seems wise to be sure these
users are added rather than relying on systemd to create them.

[ YOCTO #9497 ]

Signed-off-by: Stephano Cetola <stephano.cetola at linux.intel.com>
---
 meta/classes/rootfs-postcommands.bbclass | 66 +++++++++++++++++++++-----------
 meta/classes/useradd-staticids.bbclass   | 56 +--------------------------
 useradd-staticids-list.bbclass           | 56 +++++++++++++++++++++++++++
 3 files changed, 100 insertions(+), 78 deletions(-)
 create mode 100644 useradd-staticids-list.bbclass

diff --git a/meta/classes/rootfs-postcommands.bbclass b/meta/classes/rootfs-postcommands.bbclass
index 95d28af..db4cc75 100644
--- a/meta/classes/rootfs-postcommands.bbclass
+++ b/meta/classes/rootfs-postcommands.bbclass
@@ -1,4 +1,4 @@
-
+inherit useradd-staticids-list
 # Zap the root password if debug-tweaks feature is not enabled
 ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains_any("IMAGE_FEATURES", [ 'debug-tweaks', 'empty-root-password' ], "", "zap_empty_root_password ; ",d)}'
 
@@ -23,6 +23,9 @@ POSTINST_LOGFILE ?= "${localstatedir}/log/postinstall.log"
 SYSTEMD_DEFAULT_TARGET ?= '${@bb.utils.contains("IMAGE_FEATURES", "x11-base", "graphical.target", "multi-user.target", d)}'
 ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "set_systemd_default_target; ", "", d)}'
 
+# Create systemd users at build rather than at runtime
+ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "create_systemd_sysusers; ", "", d)}'
+
 ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;'
 
 # Disable DNS lookups, the SSH_DISABLE_DNS_LOOKUP can be overridden to allow
@@ -30,7 +33,45 @@ ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;'
 SSH_DISABLE_DNS_LOOKUP ?= " ssh_disable_dns_lookup ; "
 ROOTFS_POSTPROCESS_COMMAND_append_qemuall = "${SSH_DISABLE_DNS_LOOKUP}"
 
-
+# Add systemd users checking for any staticids 
+python create_systemd_sysusers () {
+    import shlex
+    import subprocess
+    s_users = merge_files(get_passwd_list(d), 7)
+    s_groups = merge_files(get_group_list(d), 4)
+    confs = ['%s/usr/lib/sysusers.d/systemd.conf' % d.getVar('IMAGE_ROOTFS', d), '%s/usr/lib/sysusers.d/systemd-remote.conf' % d.getVar('IMAGE_ROOTFS', d)]
+    for conf in confs:
+        if os.path.isfile(conf):
+            with open(conf, 'r') as f:
+                for line in f:
+                    if line.startswith('#'):
+                        continue
+                    fields = shlex.split(line.rstrip())
+                    if not fields:
+                        continue
+                    if fields[0] == 'u':
+                        useradd_params = ''
+                        if fields[1] in s_users:
+                            useradd_params += '--uid %s' % s_users[fields[1]][2]
+                            bb.warn("Changing user %s's uid to (%s), verify configuration files!" % (fields[1], s_users[fields[1]][2]))
+                        elif fields[2] != '-':
+                            useradd_params += '--uid %s' % fields[2]
+                        if fields[3] != '-':
+                            useradd_params += ' --comment "%s"' % fields[3]
+                        useradd_params += ' --root "%s" --shell /sbin/nologin  --system %s' % (d.getVar("IMAGE_ROOTFS", d), fields[1])
+                        code = subprocess.call('useradd %s' % useradd_params, shell=True)
+                    elif fields[0] == 'g':
+                        groupadd_params = ""
+                        if fields[1] in s_groups:
+                            groupadd_params += '--gid %s' % s_groups[fields[1]][2]
+                            bb.warn("Changing group %s's gid to (%s), verify configuration files!" % (fields[1], s_groups[fields[1]][2]))
+                        elif fields[2] != '-':
+                            groupadd_params += '--gid %s' % fields[2]
+                        if fields[3] != '-':
+                            groupadd_params += ' --comment "%s"' % (d.getVar("IMAGE_ROOTFS", d), fields[3])
+                        groupadd_params += ' --root "%s" --system %s' % (d.getVar("IMAGE_ROOTFS", d), fields[1])
+                        code = subprocess.call('groupadd %s' % groupadd_params, shell=True)
+}
 
 #
 # A hook function to support read-only-rootfs IMAGE_FEATURES
@@ -73,27 +114,6 @@ read_only_rootfs_hook () {
 			${IMAGE_ROOTFS}/etc/init.d/populate-volatile.sh
 		fi
 	fi
-
-	if ${@bb.utils.contains("DISTRO_FEATURES", "systemd", "true", "false", d)}; then
-	    # Update user database files so that services don't fail for a read-only systemd system
-	    for conffile in ${IMAGE_ROOTFS}/usr/lib/sysusers.d/systemd.conf ${IMAGE_ROOTFS}/usr/lib/sysusers.d/systemd-remote.conf; do
-		[ -e $conffile ] || continue
-		grep -v "^#" $conffile | sed -e '/^$/d' | while read type name id comment; do
-		    if [ "$type" = "u" ]; then
-			useradd_params=""
-			[ "$id" != "-" ] && useradd_params="$useradd_params --uid $id"
-			[ "$comment" != "-" ] && useradd_params="$useradd_params --comment $comment"
-			useradd_params="$useradd_params --system $name"
-			eval useradd --root ${IMAGE_ROOTFS} $useradd_params || true
-		    elif [ "$type" = "g" ]; then
-			groupadd_params=""
-			[ "$id" != "-" ] && groupadd_params="$groupadd_params --gid $id"
-			groupadd_params="$groupadd_params --system $name"
-			eval groupadd --root ${IMAGE_ROOTFS} $groupadd_params || true
-		    fi
-		done
-	    done
-	fi
 }
 
 #
diff --git a/meta/classes/useradd-staticids.bbclass b/meta/classes/useradd-staticids.bbclass
index a9b506d..e9dfe69 100644
--- a/meta/classes/useradd-staticids.bbclass
+++ b/meta/classes/useradd-staticids.bbclass
@@ -1,3 +1,4 @@
+inherit useradd-staticids-list
 # In order to support a deterministic set of 'dynamic' users/groups,
 # we need a function to reformat the params based on a static file
 def update_useradd_static_config(d):
@@ -17,35 +18,6 @@ def update_useradd_static_config(d):
         def error(self, message):
             raise bb.build.FuncFailed(message)
 
-    def list_extend(iterable, length, obj = None):
-        """Ensure that iterable is the specified length by extending with obj
-        and return it as a list"""
-        return list(itertools.islice(itertools.chain(iterable, itertools.repeat(obj)), length))
-
-    def merge_files(file_list, exp_fields):
-        """Read each passwd/group file in file_list, split each line and create
-        a dictionary with the user/group names as keys and the split lines as
-        values. If the user/group name already exists in the dictionary, then
-        update any fields in the list with the values from the new list (if they
-        are set)."""
-        id_table = dict()
-        for conf in file_list.split():
-            if os.path.exists(conf):
-                f = open(conf, "r")
-                for line in f:
-                    if line.startswith('#'):
-                        continue
-                    # Make sure there always are at least exp_fields elements in
-                    # the field list. This allows for leaving out trailing
-                    # colons in the files.
-                    fields = list_extend(line.rstrip().split(":"), exp_fields)
-                    if fields[0] not in id_table:
-                        id_table[fields[0]] = fields
-                    else:
-                        id_table[fields[0]] = list(itertools.imap(lambda x, y: x or y, fields, id_table[fields[0]]))
-
-        return id_table
-
     # We parse and rewrite the useradd components
     def rewrite_useradd(params):
         # The following comes from --help on useradd from shadow
@@ -73,19 +45,6 @@ def update_useradd_static_config(d):
         parser.add_argument("-U", "--user-group", help="create a group with the same name as the user", action="store_const", const=True)
         parser.add_argument("LOGIN", help="Login name of the new user")
 
-        # Return a list of configuration files based on either the default
-        # files/passwd or the contents of USERADD_UID_TABLES
-        # paths are resolved via BBPATH
-        def get_passwd_list(d):
-            str = ""
-            bbpath = d.getVar('BBPATH', True)
-            passwd_tables = d.getVar('USERADD_UID_TABLES', True)
-            if not passwd_tables:
-                passwd_tables = 'files/passwd'
-            for conf_file in passwd_tables.split():
-                str += " %s" % bb.utils.which(bbpath, conf_file)
-            return str
-
         newparams = []
         users = None
         for param in re.split('''[ \t]*;[ \t]*(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', params):
@@ -206,19 +165,6 @@ def update_useradd_static_config(d):
         parser.add_argument("-r", "--system", help="create a system account", action="store_true")
         parser.add_argument("GROUP", help="Group name of the new group")
 
-        # Return a list of configuration files based on either the default
-        # files/group or the contents of USERADD_GID_TABLES
-        # paths are resolved via BBPATH
-        def get_group_list(d):
-            str = ""
-            bbpath = d.getVar('BBPATH', True)
-            group_tables = d.getVar('USERADD_GID_TABLES', True)
-            if not group_tables:
-                group_tables = 'files/group'
-            for conf_file in group_tables.split():
-                str += " %s" % bb.utils.which(bbpath, conf_file)
-            return str
-
         newparams = []
         groups = None
         for param in re.split('''[ \t]*;[ \t]*(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', params):
diff --git a/useradd-staticids-list.bbclass b/useradd-staticids-list.bbclass
new file mode 100644
index 0000000..377f748
--- /dev/null
+++ b/useradd-staticids-list.bbclass
@@ -0,0 +1,56 @@
+# Return a list of configuration files based on either the default
+# files/group or the contents of USERADD_GID_TABLES
+# paths are resolved via BBPATH
+def get_group_list(d):
+    str = ""
+    bbpath = d.getVar('BBPATH', True)
+    group_tables = d.getVar('USERADD_GID_TABLES', True)
+    if not group_tables:
+        group_tables = 'files/group'
+    for conf_file in group_tables.split():
+        str += " %s" % bb.utils.which(bbpath, conf_file)
+    return str
+
+def list_extend(iterable, length, obj = None):
+    import itertools
+    """Ensure that iterable is the specified length by extending with obj
+    and return it as a list"""
+    return list(itertools.islice(itertools.chain(iterable, itertools.repeat(obj)), length))
+
+def merge_files(file_list, exp_fields):
+    import itertools
+    """Read each passwd/group file in file_list, split each line and create
+    a dictionary with the user/group names as keys and the split lines as
+    values. If the user/group name already exists in the dictionary, then
+    update any fields in the list with the values from the new list (if they
+    are set)."""
+    id_table = dict()
+    for conf in file_list.split():
+        if os.path.exists(conf):
+            f = open(conf, "r")
+            for line in f:
+                if line.startswith('#'):
+                    continue
+                # Make sure there always are at least exp_fields elements in
+                # the field list. This allows for leaving out trailing
+                # colons in the files.
+                fields = list_extend(line.rstrip().split(":"), exp_fields)
+                if fields[0] not in id_table:
+                    id_table[fields[0]] = fields
+                else:
+                    id_table[fields[0]] = list(itertools.imap(lambda x, y: x or y, fields, id_table[fields[0]]))
+
+    return id_table
+
+# Return a list of configuration files based on either the default
+# files/passwd or the contents of USERADD_UID_TABLES
+# paths are resolved via BBPATH
+def get_passwd_list(d):
+    str = ""
+    bbpath = d.getVar('BBPATH', True)
+    passwd_tables = d.getVar('USERADD_UID_TABLES', True)
+    if not passwd_tables:
+        passwd_tables = 'files/passwd'
+    for conf_file in passwd_tables.split():
+        str += " %s" % bb.utils.which(bbpath, conf_file)
+    return str
-- 
2.8.0




More information about the Openembedded-core mailing list