5 #----------------------------------------------------------------------------
11 # No more is configurable below this point
12 #----------------------------------------------------------------------------
14 #----------------------------------------------------------------------------
19 printf "%s: " "${myname}" >&2
20 printf "${fmt}" "${@}" >&2
27 #----------------------------------------------------------------------------
28 if [ ${#} -ne 2 ]; then
29 fail
"usage: %s USERS_TABLE TARGET_DIR\n"
34 PASSWD
="${TARGET_DIR}/etc/passwd"
35 SHADOW
="${TARGET_DIR}/etc/shadow"
36 GROUP
="${TARGET_DIR}/etc/group"
37 # /etc/gshadow is not part of the standard skeleton, so not everybody
38 # will have it, but some may have it, and its content must be in sync
39 # with /etc/group, so any use of gshadow must be conditional.
40 GSHADOW
="${TARGET_DIR}/etc/gshadow"
42 # We can't simply source ${BR2_CONFIG} as it may contains constructs
44 # BR2_DEFCONFIG="$(CONFIG_DIR)/defconfig"
45 # which when sourced from a shell script will eventually try to execute
46 # a command named 'CONFIG_DIR', which is plain wrong for virtually every
48 # So, we have to scan that file instead. Sigh... :-(
49 PASSWD_METHOD
="$( sed -r -e '/^BR2_TARGET_GENERIC_PASSWD_METHOD="(.
*)"$/!d;' \
54 #----------------------------------------------------------------------------
58 awk -F: -v username
="${username}" \
59 '$1 == username { printf( "%d\n", $3 ); }' "${PASSWD}"
62 #----------------------------------------------------------------------------
66 awk -F: -v username
="${username}" \
67 '$1 == username { printf( "%d\n", $4 ); }' "${PASSWD}"
70 #----------------------------------------------------------------------------
74 awk -F: -v group
="${group}" \
75 '$1 == group { printf( "%d\n", $3 ); }' "${GROUP}"
78 #----------------------------------------------------------------------------
82 awk -F: -v uid
="${uid}" \
83 '$3 == uid { printf( "%s\n", $1 ); }' "${PASSWD}"
86 #----------------------------------------------------------------------------
90 awk -F: -v gid
="${gid}" \
91 '$3 == gid { printf( "%s\n", $1 ); }' "${GROUP}"
94 #----------------------------------------------------------------------------
99 ugid
="$( get_ugid "${username}" )"
100 if [ -n "${ugid}" ]; then
105 #----------------------------------------------------------------------------
106 # Sanity-check the new user/group:
107 # - check the gid is not already used for another group
108 # - check the group does not already exist with another gid
109 # - check the user does not already exist with another gid
110 # - check the uid is not already used for another user
111 # - check the user does not already exist with another uid
112 # - check the user does not already exist in another group
113 check_user_validity
() {
114 local username
="${1}"
118 local _uid _ugid _gid _username _group _ugroup
120 _group
="$( get_group "${gid}" )"
121 _gid
="$( get_gid "${group}" )"
122 _ugid
="$( get_ugid "${username}" )"
123 _username
="$( get_username "${uid}" )"
124 _uid
="$( get_uid "${username}" )"
125 _ugroup
="$( get_ugroup "${username}" )"
127 if [ "${username}" = "root" ]; then
128 fail
"invalid username '%s\n'" "${username}"
131 if [ ${gid} -lt -1 -o ${gid} -eq 0 ]; then
132 fail
"invalid gid '%d' for '%s'\n" ${gid} "${username}"
133 elif [ ${gid} -ne -1 ]; then
134 # check the gid is not already used for another group
135 if [ -n "${_group}" -a "${_group}" != "${group}" ]; then
136 fail
"gid '%d' for '%s' is already used by group '%s'\n" \
137 ${gid} "${username}" "${_group}"
140 # check the group does not already exists with another gid
141 # Need to split the check in two, otherwise '[' complains it
142 # is missing arguments when _gid is empty
143 if [ -n "${_gid}" ] && [ ${_gid} -ne ${gid} ]; then
144 fail
"group '%s' for '%s' already exists with gid '%d' (wants '%d')\n" \
145 "${group}" "${username}" ${_gid} ${gid}
148 # check the user does not already exists with another gid
149 # Need to split the check in two, otherwise '[' complains it
150 # is missing arguments when _ugid is empty
151 if [ -n "${_ugid}" ] && [ ${_ugid} -ne ${gid} ]; then
152 fail
"user '%s' already exists with gid '%d' (wants '%d')\n" \
153 "${username}" ${_ugid} ${gid}
157 if [ ${uid} -lt -1 -o ${uid} -eq 0 ]; then
158 fail "invalid uid
'%d' for '%s'\n" ${uid} "${username}"
159 elif [ ${uid} -ne -1 ]; then
160 # check the uid is not already used for another user
161 if [ -n "${_username}" -a "${_username}" != "${username}" ]; then
162 fail "uid
'%d' for '%s' already used by user
'%s'\n" \
163 ${uid} "${username}" "${_username}"
166 # check the user does not already exists with another uid
167 # Need to split the check in two, otherwise '[' complains it
168 # is missing arguments when _uid is empty
169 if [ -n "${_uid}" ] && [ ${_uid} -ne ${uid} ]; then
170 fail "user
'%s' already exists with uid
'%d' (wants
'%d')\n" \
171 "${username}" ${_uid} ${uid}
175 # check the user does not already exist in another group
176 if [ -n "${_ugroup}" -a "${_ugroup}" != "${group}" ]; then
177 fail
"user '%s' already exists with group '%s' (wants '%s')\n" \
178 "${username}" "${_ugroup}" "${group}"
184 #----------------------------------------------------------------------------
185 # Generate a unique GID for given group. If the group already exists,
186 # then simply report its current GID. Otherwise, generate the lowest GID
189 # - comprised in [MIN_GID..MAX_GID]
190 # - not already used by a group
195 gid
="$( get_gid "${group}" )"
196 if [ -z "${gid}" ]; then
197 for(( gid
=MIN_GID
; gid
<=MAX_GID
; gid
++ )); do
198 if [ -z "$( get_group "${gid}" )" ]; then
202 if [ ${gid} -gt ${MAX_GID} ]; then
203 fail
"can not allocate a GID for group '%s'\n" "${group}"
206 printf "%d\n" "${gid}"
209 #----------------------------------------------------------------------------
210 # Add a group; if it does already exist, remove it first
216 # Generate a new GID if needed
217 if [ ${gid} -eq -1 ]; then
218 gid
="$( generate_gid "${group}" )"
221 # Remove any previous instance of this group, and re-add the new one
222 sed -i -e '/^'"${group}"':.*/d;' "${GROUP}"
223 printf "%s:x:%d:\n" "${group}" "${gid}" >>"${GROUP}"
225 # Ditto for /etc/gshadow if it exists
226 if [ -f "${GSHADOW}" ]; then
227 sed -i -e '/^'"${group}"':.*/d;' "${GSHADOW}"
228 printf "%s:*::\n" "${group}" >>"${GSHADOW}"
232 #----------------------------------------------------------------------------
233 # Generate a unique UID for given username. If the username already exists,
234 # then simply report its current UID. Otherwise, generate the lowest UID
237 # - comprised in [MIN_UID..MAX_UID]
238 # - not already used by a user
240 local username
="${1}"
243 uid
="$( get_uid "${username}" )"
244 if [ -z "${uid}" ]; then
245 for(( uid
=MIN_UID
; uid
<=MAX_UID
; uid
++ )); do
246 if [ -z "$( get_username "${uid}" )" ]; then
250 if [ ${uid} -gt ${MAX_UID} ]; then
251 fail
"can not allocate a UID for user '%s'\n" "${username}"
254 printf "%d\n" "${uid}"
257 #----------------------------------------------------------------------------
258 # Add given user to given group, if not already the case
259 add_user_to_group
() {
260 local username
="${1}"
264 for _f
in "${GROUP}" "${GSHADOW}"; do
265 [ -f "${_f}" ] ||
continue
266 sed -r -i -e 's/^('"${group}"':.*:)(([^:]+,)?)'"${username}"'(,[^:]+*)?$/\1\2\4/;' \
267 -e 's/^('"${group}"':.*)$/\1,'"${username}"'/;' \
274 #----------------------------------------------------------------------------
279 mkpasswd
-m "${PASSWD_METHOD}" "${passwd}"
282 #----------------------------------------------------------------------------
283 # Add a user; if it does already exist, remove it first
285 local username
="${1}"
294 local _f _group _home _shell _gid _passwd
296 # First, sanity-check the user
297 check_user_validity
"${username}" "${uid}" "${group}" "${gid}"
299 # Generate a new UID if needed
300 if [ ${uid} -eq -1 ]; then
301 uid
="$( generate_uid "${username}" )"
304 # Remove any previous instance of this user
305 for _f
in "${PASSWD}" "${SHADOW}"; do
306 sed -r -i -e '/^'"${username}"':.*/d;' "${_f}"
309 _gid
="$( get_gid "${group}" )"
311 if [ "${shell}" = "-" ]; then
316 /) fail
"home can not explicitly be '/'\n";;
317 /*) _home
="${home}";;
318 *) fail
"home must be an absolute path\n";;
325 _passwd
='!'"$( encode_password "${passwd#!=}" )"
328 _passwd
="$( encode_password "${passwd#=}" )"
335 printf "%s:x:%d:%d:%s:%s:%s\n" \
336 "${username}" "${uid}" "${_gid}" \
337 "${comment}" "${_home}" "${_shell}" \
339 printf "%s:%s:::::::\n" \
340 "${username}" "${_passwd}" \
343 # Add the user to its additional groups
344 if [ "${groups}" != "-" ]; then
345 for _group
in ${groups//,/ }; do
346 add_user_to_group
"${username}" "${_group}"
350 # If the user has a home, chown it
351 # (Note: stdout goes to the fakeroot-script)
352 if [ "${home}" != "-" ]; then
353 mkdir
-p "${TARGET_DIR}/${home}"
354 printf "chown -h -R %d:%d '%s'\n" "${uid}" "${_gid}" "${TARGET_DIR}/${home}"
358 #----------------------------------------------------------------------------
360 local username uid group gid passwd home shell groups comment
365 if [ ${MIN_UID} -le 0 ]; then
366 fail
"MIN_UID must be >0 (currently %d)\n" ${MIN_UID}
368 if [ ${MIN_GID} -le 0 ]; then
369 fail
"MIN_GID must be >0 (currently %d)\n" ${MIN_GID}
372 # Read in all the file in memory, exclude empty lines and comments
374 ENTRIES
+=( "${line}" )
375 done < <( sed -r -e 's/#.*//; /^[[:space:]]*$/d;' "${USERS_TABLE}" )
377 # We first create groups whose gid is not -1, and then we create groups
378 # whose gid is -1 (automatic), so that, if a group is defined both with
379 # a specified gid and an automatic gid, we ensure the specified gid is
380 # used, rather than a different automatic gid is computed.
382 # First, create all the main groups which gid is *not* automatic
383 for line
in "${ENTRIES[@]}"; do
384 read username uid group gid passwd home shell groups comment
<<<"${line}"
385 [ ${gid} -ge 0 ] ||
continue # Automatic gid
386 add_one_group
"${group}" "${gid}"
389 # Then, create all the main groups which gid *is* automatic
390 for line
in "${ENTRIES[@]}"; do
391 read username uid group gid passwd home shell groups comment
<<<"${line}"
392 [ ${gid} -eq -1 ] ||
continue # Non-automatic gid
393 add_one_group
"${group}" "${gid}"
396 # Then, create all the additional groups
397 # If any additional group is already a main group, we should use
398 # the gid of that main group; otherwise, we can use any gid
399 for line
in "${ENTRIES[@]}"; do
400 read username uid group gid passwd home shell groups comment
<<<"${line}"
401 if [ "${groups}" != "-" ]; then
402 for g
in ${groups//,/ }; do
403 add_one_group
"${g}" -1
408 # When adding users, we do as for groups, in case two packages create
409 # the same user, one with an automatic uid, the other with a specified
410 # uid, to ensure the specified uid is used, rather than an incompatible
413 # Now, add users whose uid is *not* automatic
414 for line
in "${ENTRIES[@]}"; do
415 read username uid group gid passwd home shell groups comment
<<<"${line}"
416 [ "${username}" != "-" ] ||
continue # Magic string to skip user creation
417 [ ${uid} -ge 0 ] ||
continue # Automatic uid
418 add_one_user
"${username}" "${uid}" "${group}" "${gid}" "${passwd}" \
419 "${home}" "${shell}" "${groups}" "${comment}"
422 # Finally, add users whose uid *is* automatic
423 for line
in "${ENTRIES[@]}"; do
424 read username uid group gid passwd home shell groups comment
<<<"${line}"
425 [ "${username}" != "-" ] ||
continue # Magic string to skip user creation
426 [ ${uid} -eq -1 ] ||
continue # Non-automatic uid
427 add_one_user
"${username}" "${uid}" "${group}" "${gid}" "${passwd}" \
428 "${home}" "${shell}" "${groups}" "${comment}"
432 #----------------------------------------------------------------------------