summaryrefslogtreecommitdiffstats
path: root/meta
diff options
context:
space:
mode:
authorLouis Rannou <lrannou@baylibre.com>2023-06-15 13:43:53 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-06-17 11:50:57 +0100
commitf5b3ded656a491b59f8c9d5ce12cee665bb5d5f5 (patch)
treec8a5cd0ebc6f2c80d3ef4c3c2361156ce3a6d166 /meta
parent88abdec715ed0c1f613c9b5132cd45db741d5c65 (diff)
downloadpoky-f5b3ded656a491b59f8c9d5ce12cee665bb5d5f5.tar.gz
rootfs-postcommands: change sysusers.d command
The configuration in sysusers.d used to be parsed to create users/groups at build time instead at runtime. This was leading to several conflicts with users/groups defined in base-passwd recipe and specific definitions in recipes inheriting the useradd class. Some of those conflicts raised unseen errors in the do_rootfs command's logs. As an example, the root home directory is set by default to `/home/root` but systemd expects it as `/root`. The new command `systemd_sysusers_check` checks each configuration for users/groups and compare their properties to what is actually defined in the `/etc/passwd` and `/etc/group` of the target rootfs. (From OE-Core rev: 0c7e76df68acfeca059a6b906d2a891d56f01e77) Signed-off-by: Louis Rannou <lrannou@baylibre.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rw-r--r--meta/classes-recipe/rootfs-postcommands.bbclass133
1 files changed, 109 insertions, 24 deletions
diff --git a/meta/classes-recipe/rootfs-postcommands.bbclass b/meta/classes-recipe/rootfs-postcommands.bbclass
index 690fa976aa..652601b95f 100644
--- a/meta/classes-recipe/rootfs-postcommands.bbclass
+++ b/meta/classes-recipe/rootfs-postcommands.bbclass
@@ -43,7 +43,7 @@ ROOTFS_POSTUNINSTALL_COMMAND =+ "write_image_manifest ; "
43POSTINST_LOGFILE ?= "${localstatedir}/log/postinstall.log" 43POSTINST_LOGFILE ?= "${localstatedir}/log/postinstall.log"
44# Set default target for systemd images 44# Set default target for systemd images
45SYSTEMD_DEFAULT_TARGET ?= '${@bb.utils.contains_any("IMAGE_FEATURES", [ "x11-base", "weston" ], "graphical.target", "multi-user.target", d)}' 45SYSTEMD_DEFAULT_TARGET ?= '${@bb.utils.contains_any("IMAGE_FEATURES", [ "x11-base", "weston" ], "graphical.target", "multi-user.target", d)}'
46ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "set_systemd_default_target; systemd_create_users;", "", d)}' 46ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "set_systemd_default_target; systemd_sysusers_check;", "", d)}'
47 47
48ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;' 48ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;'
49 49
@@ -69,29 +69,114 @@ python () {
69 d.appendVar('ROOTFS_POSTPROCESS_COMMAND', 'rootfs_reproducible;') 69 d.appendVar('ROOTFS_POSTPROCESS_COMMAND', 'rootfs_reproducible;')
70} 70}
71 71
72systemd_create_users () { 72# Resolve the ID as described in the sysusers.d(5) manual: ID can be a numeric
73 for conffile in ${IMAGE_ROOTFS}/usr/lib/sysusers.d/*.conf; do 73# uid, a couple uid:gid or uid:groupname or it is '-' meaning leaving it
74 [ -e $conffile ] || continue 74# automatic or it can be a path. In the latter, the uid/gid matches the
75 grep -v "^#" $conffile | sed -e '/^$/d' | while read type name id comment; do 75# user/group owner of that file.
76 if [ "$type" = "u" ]; then 76def resolve_sysusers_id(d, sid):
77 useradd_params="--shell /sbin/nologin" 77 # If the id is a path, the uid/gid matchs to the target's uid/gid in the
78 [ "$id" != "-" ] && useradd_params="$useradd_params --uid $id" 78 # rootfs.
79 [ "$comment" != "-" ] && useradd_params="$useradd_params --comment $comment" 79 if '/' in sid:
80 useradd_params="$useradd_params --system $name" 80 try:
81 eval useradd --root ${IMAGE_ROOTFS} $useradd_params || true 81 osstat = os.stat(os.path.join(d.getVar('IMAGE_ROOTFS'), sid))
82 elif [ "$type" = "g" ]; then 82 except FileNotFoundError:
83 groupadd_params="" 83 bb.error('sysusers.d: file %s is required but it does not exist in the rootfs', sid)
84 [ "$id" != "-" ] && groupadd_params="$groupadd_params --gid $id" 84 return ('-', '-')
85 groupadd_params="$groupadd_params --system $name" 85 return (osstat.st_uid, osstat.st_gid)
86 eval groupadd --root ${IMAGE_ROOTFS} $groupadd_params || true 86 # Else it is a uid:gid or uid:groupname syntax
87 elif [ "$type" = "m" ]; then 87 if ':' in sid:
88 group=$id 88 return sid.split(':')
89 eval groupadd --root ${IMAGE_ROOTFS} --system $group || true 89 else:
90 eval useradd --root ${IMAGE_ROOTFS} --shell /sbin/nologin --system $name --no-user-group || true 90 return (sid, '-')
91 eval usermod --root ${IMAGE_ROOTFS} -a -G $group $name 91
92 fi 92# Check a user exists in the rootfs password file and return its properties
93 done 93def check_user_exists(d, uname=None, uid=None):
94 done 94 with open(os.path.join(d.getVar('IMAGE_ROOTFS'), 'etc/passwd'), 'r') as pwfile:
95 for line in pwfile:
96 (name, _, u_id, gid, comment, homedir, ushell) = line.strip().split(':')
97 if uname == name or uid == u_id:
98 return (name, u_id, gid, comment or '-', homedir or '/', ushell or '-')
99 return None
100
101# Check a group exists in the rootfs group file and return its properties
102def check_group_exists(d, gname=None, gid=None):
103 with open(os.path.join(d.getVar('IMAGE_ROOTFS'), 'etc/group'), 'r') as gfile:
104 for line in gfile:
105 (name, _, g_id, _) = line.strip().split(':')
106 if name == gname or g_id == gid:
107 return (name, g_id)
108 return None
109
110def compare_users(user, e_user):
111 # user and e_user must not have None values. Unset values must be '-'.
112 (name, uid, gid, comment, homedir, ushell) = user
113 (e_name, e_uid, e_gid, e_comment, e_homedir, e_ushell) = e_user
114 # Ignore 'uid', 'gid' or 'comment' if they are not set
115 # Ignore 'shell' and 'ushell' if one is not set
116 return name == e_name \
117 and (uid == '-' or uid == e_uid) \
118 and (gid == '-' or gid == e_gid) \
119 and (comment == '-' or e_comment == '-' or comment.lower() == e_comment.lower()) \
120 and (homedir == '-' or e_homedir == '-' or homedir == e_homedir) \
121 and (ushell == '-' or e_ushell == '-' or ushell == e_ushell)
122
123# Open sysusers.d configuration files and parse each line to check the users and
124# groups are already defined in /etc/passwd and /etc/groups with similar
125# properties. Refer to the sysusers.d(5) manual for its syntax.
126python systemd_sysusers_check() {
127 import glob
128 import re
129
130 pattern_comment = r'(-|\"[^:\"]+\")'
131 pattern_word = r'[^\s]+'
132 pattern_line = r'(' + pattern_word + r')\s+(' + pattern_word + r')\s+(' + pattern_word + r')(\s+' \
133 + pattern_comment + r')?' + r'(\s+(' + pattern_word + r'))?' + r'(\s+(' + pattern_word + r'))?'
134
135 for conffile in glob.glob(os.path.join(d.getVar('IMAGE_ROOTFS'), 'usr/lib/sysusers.d/*.conf')):
136 with open(conffile, 'r') as f:
137 for line in f:
138 line = line.strip()
139 if not len(line) or line[0] == '#': continue
140 ret = re.fullmatch(pattern_line, line.strip())
141 if not ret: continue
142 (stype, sname, sid, _, scomment, _, shomedir, _, sshell) = ret.groups()
143 if stype == 'u':
144 if sid:
145 (suid, sgid) = resolve_sysusers_id(d, sid)
146 if sgid.isalpha():
147 sgid = check_group_exists(d, gname=sgid)
148 elif sgid.isdigit():
149 check_group_exists(d, gid=sgid)
150 else:
151 sgid = '-'
152 else:
153 suid = '-'
154 sgid = '-'
155 scomment = scomment.replace('"', '') if scomment else '-'
156 shomedir = shomedir or '-'
157 sshell = sshell or '-'
158 e_user = check_user_exists(d, uname=sname)
159 if not e_user:
160 bb.warn('User %s has never been defined' % sname)
161 elif not compare_users((sname, suid, sgid, scomment, shomedir, sshell), e_user):
162 bb.warn('User %s has been defined as (%s) but sysusers.d expects it as (%s)'
163 % (sname, ', '.join(e_user),
164 ', '.join((sname, suid, sgid, scomment, shomedir, sshell))))
165 elif stype == 'g':
166 gid = sid or '-'
167 if '/' in gid:
168 (_, gid) = resolve_sysusers_id(d, sid)
169 e_group = check_group_exists(d, gname=sname)
170 if not e_group:
171 bb.warn('Group %s has never been defined' % sname)
172 elif gid != '-':
173 (_, e_gid) = e_group
174 if gid != e_gid:
175 bb.warn('Group %s has been defined with id (%s) but sysusers.d expects gid (%s)'
176 % (sname, e_gid, gid))
177 elif stype == 'm':
178 check_user_exists(d, sname)
179 check_group_exists(d, sid)
95} 180}
96 181
97# 182#