xref: /dragonfly/tools/pw-update.sh (revision a765cedf)
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#
46add_users() {
47	local etcdir="$1"
48	local fpasswd="$2"
49	local fgroup="$3"
50	local _name _pw _uid _gid _gids _group item
51	local _class _change _expire _gecos _home _shell _members
52
53	echo "===> Adding new users ..."
54	_gids=""
55	while IFS=':' read -r _name _pw _uid _gid _class \
56			_change _expire _gecos _home _shell; do
57		case ${_name} in
58		'' | \#*) continue ;;
59		esac
60		if pw -V ${etcdir} usershow ${_name} -q >/dev/null; then
61			continue
62		fi
63		echo "   * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}"
64
65		_group=${_gid}
66		if ! pw -V ${etcdir} groupshow ${_gid} -q >/dev/null; then
67			# Primary group doesn't exist yet, so first assign to
68			# the 'nogroup' group, and then adjust it after
69			# creating the group.
70			_group="nogroup"
71			_gids="${_gids} ${_name}:${_gid}"
72		fi
73
74		# NOTE: The shell field can be empty (e.g., user 'toor') and
75		#       would default to '/bin/sh'.
76		# NOTE: Use '-o' option to allow to create user of duplicate
77		#       UID, which is required by the 'toor' user (same UID
78		#       as 'root').
79		pw -V ${etcdir} useradd ${_name} \
80			-o \
81			-u ${_uid} \
82			-g ${_group} \
83			-d "${_home}" \
84			-s "${_shell}" \
85			-L "${_class}" \
86			-c "${_gecos}"
87	done < ${fpasswd}
88
89	echo "===> Adding new groups ..."
90	while IFS=':' read -r _name _pw _gid _members; do
91		case ${_name} in
92		'' | \#*) continue ;;
93		esac
94		if pw -V ${etcdir} groupshow ${_name} -q >/dev/null; then
95			continue
96		fi
97		echo "   * ${_name}: ${_gid}, ${_members}"
98		pw -V ${etcdir} groupadd ${_name} -g ${_gid} -M "${_members}"
99	done < ${fgroup}
100
101	echo "===> Adjusting the group of new users ..."
102	for item in ${_gids}; do
103		_name=${item%:*}
104		_gid=${item#*:}
105		echo "   * ${_name}: ${_gid}"
106		pw -V ${etcdir} usermod ${_name} -g ${_gid}
107	done
108}
109
110# Usage: update_user <user> <etcdir> <master.passwd>
111#
112# Update an existing user in <etcdir> according to the given <master.passwd>.
113#
114update_user() {
115	local user="$1"
116	local etcdir="$2"
117	local fpasswd="$3"
118	local _line
119	local _name _pw _uid _gid _class _change _expire _gecos _home _shell
120
121	_line=$(grep "^${user}:" ${fpasswd}) || true
122	if [ -z "${_line}" ]; then
123		echo "ERROR: no such user '${user}'" >&2
124		exit 1
125	fi
126
127	echo "${_line}" | {
128		IFS=':' read -r _name _pw _uid _gid _class \
129			_change _expire _gecos _home _shell
130		echo "===> Updating user ${user} ..."
131		echo "   * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}"
132		pw -V ${etcdir} usermod ${user} \
133			-u ${_uid} \
134			-g ${_gid} \
135			-d ${_home} \
136			-s ${_shell} \
137			-L "${_class}" \
138			-c "${_gecos}"
139	}
140}
141
142# Usage: update_group <group> <etcdir> <group>
143#
144# Update an existing group in <etcdir> according to the given <group> file.
145#
146update_group() {
147	local group="$1"
148	local etcdir="$2"
149	local fgroup="$3"
150	local _line
151	local _name _pw _gid _members
152
153	_line=$(grep "^${group}:" ${fgroup}) || true
154	if [ -z "${_line}" ]; then
155		echo "ERROR: no such group '${group}'" >&2
156		exit 1
157	fi
158
159	echo "${_line}" | {
160		IFS=':' read -r _name _pw _gid _members
161		echo "===> Updating group ${group} ..."
162		echo "   * ${_name}: ${_gid}, ${_members}"
163		pw -V ${etcdir} groupmod ${group} -g ${_gid} -M "${_members}"
164	}
165}
166
167usage() {
168	cat > /dev/stderr << _EOF_
169Add/update users and groups.
170
171Usage: ${0##*/} -d <etc-dir> -g <group-file> -p <master.passwd-file>
172	[-G group] [-U user]
173
174_EOF_
175
176	exit 1
177}
178
179ETC_DIR=
180GROUP_FILE=
181PASSWD_FILE=
182UPDATE_GROUP=
183UPDATE_USER=
184
185while getopts :d:G:g:hp:U: opt; do
186	case ${opt} in
187	d)
188		ETC_DIR=${OPTARG}
189		;;
190	G)
191		UPDATE_GROUP=${OPTARG}
192		;;
193	g)
194		GROUP_FILE=${OPTARG}
195		;;
196	p)
197		PASSWD_FILE=${OPTARG}
198		;;
199	U)
200		UPDATE_USER=${OPTARG}
201		;;
202	h | \? | :)
203		usage
204		;;
205	esac
206done
207
208shift $((OPTIND - 1))
209[ $# -eq 0 ] || usage
210[ -n "${ETC_DIR}" ] || usage
211[ -n "${GROUP_FILE}" ] || usage
212[ -n "${PASSWD_FILE}" ] || usage
213
214if [ -z "${UPDATE_GROUP}" ] && [ -z "${UPDATE_USER}" ]; then
215	add_users "${ETC_DIR}" "${PASSWD_FILE}" "${GROUP_FILE}"
216else
217	if [ -n "${UPDATE_GROUP}" ]; then
218		update_group "${UPDATE_GROUP}" "${ETC_DIR}" "${GROUP_FILE}"
219	fi
220	if [ -n "${UPDATE_USER}" ]; then
221		update_user "${UPDATE_USER}" "${ETC_DIR}" "${PASSWD_FILE}"
222	fi
223fi
224