xref: /dragonfly/tools/pw-update.sh (revision 6b921297)
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		# Assign to the 'nogroup' group, and then adjust later.
70		pw -V ${etcdir} useradd ${_name} \
71			-u ${_uid} \
72			-g nogroup \
73			-d ${_home} \
74			-s ${_shell} \
75			-L "${_class}" \
76			-c "${_gecos}"
77		_gids="${_gids} ${_name}:${_gid}"
78	done < ${fpasswd}
79
80	echo "===> Adding new groups ..."
81	while IFS=':' read -r _name _pw _gid _members; do
82		case ${_name} in
83		'' | \#*) continue ;;
84		esac
85		if pw -V ${etcdir} groupshow ${_name} -q >/dev/null; then
86			continue
87		fi
88		echo "   * ${_name}: ${_gid}, ${_members}"
89		pw -V ${etcdir} groupadd ${_name} -g ${_gid} -M "${_members}"
90	done < ${fgroup}
91
92	echo "===> Adjusting the group of new users ..."
93	for item in ${_gids}; do
94		_name=${item%:*}
95		_gid=${item#*:}
96		echo "   * ${_name}: ${_gid}"
97		pw -V ${etcdir} usermod ${_name} -g ${_gid}
98	done
99}
100
101# Usage: update_user <user> <etcdir> <master.passwd>
102#
103# Update an existing user in <etcdir> according to the given <master.passwd>.
104#
105update_user() {
106	local user="$1"
107	local etcdir="$2"
108	local fpasswd="$3"
109	local _line
110	local _name _pw _uid _gid _class _change _expire _gecos _home _shell
111
112	_line=$(grep "^${user}:" ${fpasswd}) || true
113	if [ -z "${_line}" ]; then
114		echo "ERROR: no such user '${user}'" >&2
115		exit 1
116	fi
117
118	echo "${_line}" | {
119		IFS=':' read -r _name _pw _uid _gid _class \
120			_change _expire _gecos _home _shell
121		echo "===> Updating user ${user} ..."
122		echo "   * ${_name}: ${_uid}, ${_gid}, ${_gecos}, ${_home}, ${_shell}"
123		pw -V ${etcdir} usermod ${user} \
124			-u ${_uid} \
125			-g ${_gid} \
126			-d ${_home} \
127			-s ${_shell} \
128			-L "${_class}" \
129			-c "${_gecos}"
130	}
131}
132
133# Usage: update_group <group> <etcdir> <group>
134#
135# Update an existing group in <etcdir> according to the given <group> file.
136#
137update_group() {
138	local group="$1"
139	local etcdir="$2"
140	local fgroup="$3"
141	local _line
142	local _name _pw _gid _members
143
144	_line=$(grep "^${group}:" ${fgroup}) || true
145	if [ -z "${_line}" ]; then
146		echo "ERROR: no such group '${group}'" >&2
147		exit 1
148	fi
149
150	echo "${_line}" | {
151		IFS=':' read -r _name _pw _gid _members
152		echo "===> Updating group ${group} ..."
153		echo "   * ${_name}: ${_gid}, ${_members}"
154		pw -V ${etcdir} groupmod ${group} -g ${_gid} -M "${_members}"
155	}
156}
157
158usage() {
159	cat > /dev/stderr << _EOF_
160Add/update users and groups.
161
162Usage: ${0##*/} -d <etc-dir> -g <group-file> -p <master.passwd-file>
163	[-G group] [-U user]
164
165_EOF_
166
167	exit 1
168}
169
170ETC_DIR=
171GROUP_FILE=
172PASSWD_FILE=
173UPDATE_GROUP=
174UPDATE_USER=
175
176while getopts :d:G:g:hp:U: opt; do
177	case ${opt} in
178	d)
179		ETC_DIR=${OPTARG}
180		;;
181	G)
182		UPDATE_GROUP=${OPTARG}
183		;;
184	g)
185		GROUP_FILE=${OPTARG}
186		;;
187	p)
188		PASSWD_FILE=${OPTARG}
189		;;
190	U)
191		UPDATE_USER=${OPTARG}
192		;;
193	h | \? | :)
194		usage
195		;;
196	esac
197done
198
199shift $((OPTIND - 1))
200[ $# -eq 0 ] || usage
201[ -n "${ETC_DIR}" ] || usage
202[ -n "${GROUP_FILE}" ] || usage
203[ -n "${PASSWD_FILE}" ] || usage
204
205if [ -z "${UPDATE_GROUP}" ] && [ -z "${UPDATE_USER}" ]; then
206	add_users "${ETC_DIR}" "${PASSWD_FILE}" "${GROUP_FILE}"
207else
208	if [ -n "${UPDATE_GROUP}" ]; then
209		update_group "${UPDATE_GROUP}" "${ETC_DIR}" "${GROUP_FILE}"
210	fi
211	if [ -n "${UPDATE_USER}" ]; then
212		update_user "${UPDATE_USER}" "${ETC_DIR}" "${PASSWD_FILE}"
213	fi
214fi
215