freebsd-ports/Mk/Scripts/do-users-groups.sh
Mathieu Arnold 5fb8017b6b Fix using more than one files in UID_FILES and GID_FILES.
Repeat after me: If you change IFS, it will break something unexpected.

The problem is that we use IFS to change read's field separator.  This
has the side effect of changing how sh(1) splits all string, including
in command parsing functions.

In this case, unless quoted, the strings are always splitted using IFS.
So changing IFS will change how these strings are splitted, and you end
up having a headache.  For example:

    $ GID_FILES="foo bar"
    $ set -x
    $ echo $GID_FILES
    + echo foo bar
    foo bar

    $ IFS=:
    $ GID_FILES="foo bar"
    $ set -x
    $ echo $GID_FILES
    + echo 'foo bar'
    foo bar

In the first case, it runs echo with two arguments, first is foo, second is bar.
In the second case, it runs echo with one argument, 'foo bar'.

To fix this, restrict the time during which IFS changes to only one
command, set, and use positional parameters to extract values.

Reported by:	feld
Sponsored by:	Absolight
Differential Revision:	https://reviews.freebsd.org/D11632
2017-07-19 11:55:46 +00:00

213 lines
5.4 KiB
Bash

#!/bin/sh
# $FreeBSD$
#
# MAINTAINER: portmgr@FreeBSD.org
set -e
. "${dp_SCRIPTSDIR}/functions.sh"
validate_env dp_ECHO_MSG dp_GID_FILES dp_GID_OFFSET dp_GROUPS_BLACKLIST \
dp_INSTALL dp_OPSYS dp_OSVERSION dp_PREFIX dp_PW dp_SCRIPTSDIR \
dp_UG_DEINSTALL dp_UG_INSTALL dp_UID_FILES dp_UID_OFFSET \
dp_USERS_BLACKLIST
[ -n "${DEBUG_MK_SCRIPTS}" -o -n "${DEBUG_MK_SCRIPTS_DO_USERS_GROUPS}" ] && set -x
set -u
USERS=$1
GROUPS=$2
error() {
${dp_ECHO_MSG} "${1}"
exit 1
}
# Lines from GID and UID files both contain *. As we do not need any pathname
# expansion, disable globbing.
set -f
rm -f "${dp_UG_INSTALL}" "${dp_UG_DEINSTALL}" || :
# Before FreeBSD 10.2, PW did not have -R support.
if [ "${dp_OPSYS}" = FreeBSD ] ; then
cat >> "${dp_UG_INSTALL}" <<-eot
if [ -n "\${PKG_ROOTDIR}" ] && [ "\${PKG_ROOTDIR}" != "/" ]; then
PW="${dp_PW} -R \${PKG_ROOTDIR}"
else
PW=${dp_PW}
fi
eot
else
echo "PW=${dp_PW}" >> "${dp_UG_INSTALL}"
fi
# Both scripts need to start the same, so
cp -f "${dp_UG_INSTALL}" "${dp_UG_DEINSTALL}"
if [ -n "${GROUPS}" ]; then
for file in ${dp_GID_FILES}; do
if [ ! -f "${file}" ]; then
error "** ${file} doesn't exist. Exiting."
fi
done
${dp_ECHO_MSG} "===> Creating groups."
echo "echo \"===> Creating groups.\"" >> "${dp_UG_INSTALL}"
for group in ${GROUPS}; do
# _bgpd:*:130:
if ! grep -q "^${group}:" ${dp_GID_FILES}; then \
error "** Cannot find any information about group \`${group}' in ${dp_GID_FILES}."
fi
while read -r line; do
# Do not change IFS for more than one command, if we
# changed IFS around the while read, it would mess up
# the string splitting in the heredoc command.
o_IFS=${IFS}
IFS=":"
set -- ${line}
IFS=${o_IFS}
group=$1
gid=$3
if [ -z "${gid}" ]; then
error "Group line for group ${group} has no gid"
fi
gid=$((gid+dp_GID_OFFSET))
cat >> "${dp_UG_INSTALL}" <<-eot2
if ! \${PW} groupshow $group >/dev/null 2>&1; then
echo "Creating group '$group' with gid '$gid'."
\${PW} groupadd $group -g $gid
else
echo "Using existing group '$group'."
fi
eot2
done <<-eot
$(grep -h "^${group}:" ${dp_GID_FILES} | head -n 1)
eot
done
fi
if [ -n "${USERS}" ]; then
for file in ${dp_UID_FILES}; do
if [ ! -f "${file}" ]; then
error "** ${file} doesn't exist. Exiting."
fi
done
${dp_ECHO_MSG} "===> Creating users"
echo "echo \"===> Creating users\"" >> "${dp_UG_INSTALL}"
for user in ${USERS}; do
# _bgpd:*:130:130:BGP Daemon:/var/empty:/sbin/nologin
if ! grep -q "^${user}:" ${dp_UID_FILES} ; then
error "** Cannot find any information about user \`${user}' in ${dp_UID_FILES}."
fi
while read -r line; do
# Do not change IFS for more than one command, if we
# changed IFS around the while read, it would mess up
# the string splitting in the heredoc command.
o_IFS=${IFS}
IFS=":"
set -- ${line}
IFS=${o_IFS}
login=$1
uid=$3
gid=$4
class=$5
gecos=$8
homedir=$9
shell=${10}
if [ -z "$uid" ] || [ -z "$gid" ] || [ -z "$homedir" ] || [ -z "$shell" ]; then
error "User line for ${user} is invalid"
fi
uid=$((uid+dp_UID_OFFSET))
gid=$((gid+dp_GID_OFFSET))
if [ -n "$class" ]; then
class="-L $class"
fi
homedir=$(echo "$homedir" | sed "s|^/usr/local|${dp_PREFIX}|")
cat >> "${dp_UG_INSTALL}" <<-eot2
if ! \${PW} usershow $login >/dev/null 2>&1; then
echo "Creating user '$login' with uid '$uid'."
\${PW} useradd $login -u $uid -g $gid $class -c "$gecos" -d $homedir -s $shell
else
echo "Using existing user '$login'."
fi
eot2
case $homedir in
/|/nonexistent|/var/empty)
;;
*)
group=$(awk -F: -v gid=${gid} '$3 == gid { print $1 }' ${dp_GID_FILES})
echo "${dp_INSTALL} -d -g $group -o $login $homedir" >> "${dp_UG_INSTALL}"
;;
esac
done <<-eot
$(grep -h "^${user}:" ${dp_UID_FILES} | head -n 1)
eot
done
fi
if [ -n "${GROUPS}" ]; then
for group in ${GROUPS}; do
# mail:*:6:postfix,clamav
while read -r line; do
# Do not change IFS for more than one command, if we
# changed IFS around the while read, it would mess up
# the string splitting in the heredoc command.
o_IFS=${IFS}
IFS=":"
# As some lines do not have a fourth argument, provide
# one so $4 always exists.
set -- ${line} ""
IFS=${o_IFS}
group=$1
gid=$3
members=$4
gid=$((gid+dp_GID_OFFSET))
o_IFS=${IFS}
IFS=","
set -- ${members}
IFS=${o_IFS}
for login in "$@"; do
for user in ${USERS}; do
if [ -n "${user}" ] && [ "${user}" = "${login}" ]; then
cat >> "${dp_UG_INSTALL}" <<-eot2
if ! \${PW} groupshow ${group} | grep -qw ${login}; then
echo "Adding user '${login}' to group '${group}'."
\${PW} groupmod ${group} -m ${login}
fi
eot2
fi
done
done
done <<-eot
$(grep -h "^${group}:" ${dp_GID_FILES} | head -n 1)
eot
done
fi
if [ -n "${USERS}" ]; then
for user in ${USERS}; do
if ! echo "${dp_USERS_BLACKLIST}" | grep -qw "${user}"; then
cat >> "${dp_UG_DEINSTALL}" <<-eot
if \${PW} usershow ${user} >/dev/null 2>&1; then
echo "==> You should manually remove the \"${user}\" user. "
fi
eot
fi
done
fi
if [ -n "${GROUPS}" ]; then
for group in ${GROUPS}; do
if ! echo "${dp_GROUPS_BLACKLIST}" | grep -qw "${group}"; then
cat >> "${dp_UG_DEINSTALL}" <<-eot
if \${PW} groupshow ${group} >/dev/null 2>&1; then
echo "==> You should manually remove the \"${group}\" group "
fi
eot
fi
done
fi