xref: /dragonfly/tools/pw-update.sh (revision 54af8c6b)
1#!/bin/sh
2#
3# Copyright (c) 2020 The DragonFly Project.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in
14#    the documentation and/or other materials provided with the
15#    distribution.
16# 3. Neither the name of The DragonFly Project nor the names of its
17#    contributors may be used to endorse or promote products derived
18#    from this software without specific, prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23# FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
24# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
26# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31# SUCH DAMAGE.
32#
33
34# Exit if any untested command fails in non-interactive mode
35set -e
36# Exit when an undefined variable is referenced
37set -u
38
39# Usage: add_users <etcdir> <master.passwd> <group>
40#
41# Add new users and groups in <etcdir> according to the given <master.passwd>
42# and <group> files.
43#
44# NOTE: Existing users and groups are not modified.
45#
46# NOTE: There is circular dependence: 'groupadd' requires the members
47#       already exist, while 'useradd' requires the group exists.
48#       So first assign new users to the 'nogroup' group, and then make
49#       adjustments after group creation.
50#
51add_users() {
52	local etcdir="$1"
53	local fpasswd="$2"
54	local fgroup="$3"
55	local _name _pw _uid _gid _gids item
56	local _class _change _expire _gecos _home _shell _members
57
58	echo "===> Adding new users ..."
59	_gids=""
60	while IFS=':' read -r _name _pw _uid _gid _class \
61			_change _expire _gecos _home _shell; do
62		case ${_name} in
63		'' | \#*) continue ;;
64		esac
65		if pw -V ${etcdir} usershow ${_name} -q >/dev/null; then
66			continue
67		fi
68		echo "   * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}"
69		# NOTE: The shell field can be empty (e.g., user 'toor') and
70		#       would default to '/bin/sh'.
71		# NOTE: Use '-o' option to allow to create user of duplicate
72		#       UID, which is required by the 'toor' user (same UID
73		#       as 'root').
74		pw -V ${etcdir} useradd ${_name} \
75			-o \
76			-u ${_uid} \
77			-g nogroup \
78			-d "${_home}" \
79			-s "${_shell}" \
80			-L "${_class}" \
81			-c "${_gecos}"
82		_gids="${_gids} ${_name}:${_gid}"
83	done < ${fpasswd}
84
85	echo "===> Adding new groups ..."
86	while IFS=':' read -r _name _pw _gid _members; do
87		case ${_name} in
88		'' | \#*) continue ;;
89		esac
90		if pw -V ${etcdir} groupshow ${_name} -q >/dev/null; then
91			continue
92		fi
93		echo "   * ${_name}: ${_gid}, ${_members}"
94		pw -V ${etcdir} groupadd ${_name} -g ${_gid} -M "${_members}"
95	done < ${fgroup}
96
97	echo "===> Adjusting the group of new users ..."
98	for item in ${_gids}; do
99		_name=${item%:*}
100		_gid=${item#*:}
101		echo "   * ${_name}: ${_gid}"
102		pw -V ${etcdir} usermod ${_name} -g ${_gid}
103	done
104}
105
106# Usage: update_user <user> <etcdir> <master.passwd>
107#
108# Update an existing user in <etcdir> according to the given <master.passwd>.
109#
110update_user() {
111	local user="$1"
112	local etcdir="$2"
113	local fpasswd="$3"
114	local _line
115	local _name _pw _uid _gid _class _change _expire _gecos _home _shell
116
117	_line=$(grep "^${user}:" ${fpasswd}) || true
118	if [ -z "${_line}" ]; then
119		echo "ERROR: no such user '${user}'" >&2
120		exit 1
121	fi
122
123	echo "${_line}" | {
124		IFS=':' read -r _name _pw _uid _gid _class \
125			_change _expire _gecos _home _shell
126		echo "===> Updating user ${user} ..."
127		echo "   * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}"
128		pw -V ${etcdir} usermod ${user} \
129			-u ${_uid} \
130			-g ${_gid} \
131			-d ${_home} \
132			-s ${_shell} \
133			-L "${_class}" \
134			-c "${_gecos}"
135	}
136}
137
138# Usage: update_group <group> <etcdir> <group>
139#
140# Update an existing group in <etcdir> according to the given <group> file.
141#
142update_group() {
143	local group="$1"
144	local etcdir="$2"
145	local fgroup="$3"
146	local _line
147	local _name _pw _gid _members
148
149	_line=$(grep "^${group}:" ${fgroup}) || true
150	if [ -z "${_line}" ]; then
151		echo "ERROR: no such group '${group}'" >&2
152		exit 1
153	fi
154
155	echo "${_line}" | {
156		IFS=':' read -r _name _pw _gid _members
157		echo "===> Updating group ${group} ..."
158		echo "   * ${_name}: ${_gid}, ${_members}"
159		pw -V ${etcdir} groupmod ${group} -g ${_gid} -M "${_members}"
160	}
161}
162
163usage() {
164	cat > /dev/stderr << _EOF_
165Add/update users and groups.
166
167Usage: ${0##*/} -d <etc-dir> -g <group-file> -p <master.passwd-file>
168	[-G group] [-U user]
169
170_EOF_
171
172	exit 1
173}
174
175ETC_DIR=
176GROUP_FILE=
177PASSWD_FILE=
178UPDATE_GROUP=
179UPDATE_USER=
180
181while getopts :d:G:g:hp:U: opt; do
182	case ${opt} in
183	d)
184		ETC_DIR=${OPTARG}
185		;;
186	G)
187		UPDATE_GROUP=${OPTARG}
188		;;
189	g)
190		GROUP_FILE=${OPTARG}
191		;;
192	p)
193		PASSWD_FILE=${OPTARG}
194		;;
195	U)
196		UPDATE_USER=${OPTARG}
197		;;
198	h | \? | :)
199		usage
200		;;
201	esac
202done
203
204shift $((OPTIND - 1))
205[ $# -eq 0 ] || usage
206[ -n "${ETC_DIR}" ] || usage
207[ -n "${GROUP_FILE}" ] || usage
208[ -n "${PASSWD_FILE}" ] || usage
209
210if [ -z "${UPDATE_GROUP}" ] && [ -z "${UPDATE_USER}" ]; then
211	add_users "${ETC_DIR}" "${PASSWD_FILE}" "${GROUP_FILE}"
212else
213	if [ -n "${UPDATE_GROUP}" ]; then
214		update_group "${UPDATE_GROUP}" "${ETC_DIR}" "${GROUP_FILE}"
215	fi
216	if [ -n "${UPDATE_USER}" ]; then
217		update_user "${UPDATE_USER}" "${ETC_DIR}" "${PASSWD_FILE}"
218	fi
219fi
220