rmuser.sh revision 111015
1107543Sscottl#!/bin/sh
2107543Sscottl#
3111015Smtm# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
4107543Sscottl#
5107543Sscottl# Redistribution and use in source and binary forms, with or without
6107543Sscottl# modification, are permitted provided that the following conditions
7107543Sscottl# are met:
8107543Sscottl# 1. Redistributions of source code must retain the above copyright
9107543Sscottl#    notice, this list of conditions and the following disclaimer.
10107543Sscottl# 2. Redistributions in binary form must reproduce the above copyright
11107543Sscottl#    notice, this list of conditions and the following disclaimer in the
12107543Sscottl#    documentation and/or other materials provided with the distribution.
13107543Sscottl#
14107543Sscottl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15107543Sscottl# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16107543Sscottl# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17107543Sscottl# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18107543Sscottl# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19107543Sscottl# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20107543Sscottl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21107543Sscottl# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22107543Sscottl# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23107543Sscottl# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24107543Sscottl#
25107543Sscottl#	Email: Mike Makonnen <mtm@identd.net>
26107543Sscottl#
27107543Sscottl# $FreeBSD: head/usr.sbin/adduser/rmuser.sh 111015 2003-02-16 18:06:04Z mtm $
28107543Sscottl#
29107543Sscottl
30107543SscottlATJOBDIR="/var/at/jobs"
31107543SscottlCRONJOBDIR="/var/cron/tabs"
32107543SscottlMAILSPOOL="/var/mail"
33107543SscottlSIGKILL="-KILL"
34107694StjrTEMPDIRS="/tmp /var/tmp"
35107543SscottlTHISCMD=`/usr/bin/basename $0`
36107543Sscottl
37107543Sscottl# err msg
38107543Sscottl#	Display $msg on stderr.
39107543Sscottl#
40107543Sscottlerr() {
41107543Sscottl	echo 1>&2 ${THISCMD}: $*
42107543Sscottl}
43107543Sscottl
44111013Smtm# verbose
45111013Smtm#	Returns 0 if verbose mode is set, 1 if it is not.
46111013Smtm#
47111013Smtmverbose() {
48111013Smtm	[ -n "$vflag" ] && return 0 || return 1
49111013Smtm}
50111013Smtm
51107543Sscottl# rm_files login
52107543Sscottl#	Removes files or empty directories belonging to $login from various
53107543Sscottl#	temporary directories.
54107543Sscottl#
55107543Sscottlrm_files() {
56107543Sscottl	# The argument is required
57107543Sscottl	[ -n $1 ] && login=$1 || return
58107543Sscottl	
59111013Smtm	totalcount=0
60107543Sscottl	for _dir in ${TEMPDIRS} ; do
61111013Smtm		filecount=0
62107543Sscottl		if [ ! -d $_dir ]; then
63107543Sscottl			err "$_dir is not a valid directory."
64107543Sscottl			continue
65107543Sscottl		fi
66111013Smtm		verbose && echo -n "Removing files owned by ($login) in $_dir:"
67107694Stjr		filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print | \
68107694Stjr		    wc -l | sed 's/ *//'`
69111013Smtm		verbose && echo " $filecount removed."
70111013Smtm		totalcount=$(($totalcount + $filecount))
71107543Sscottl	done
72111013Smtm	! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
73107543Sscottl}
74107543Sscottl
75107543Sscottl# rm_mail login
76107543Sscottl#	Removes unix mail and pop daemon files belonging to the user
77107543Sscottl#	specified in the $login argument.
78107543Sscottl#
79107543Sscottlrm_mail() {
80107543Sscottl	# The argument is required
81107543Sscottl	[ -n $1 ] && login=$1 || return
82107543Sscottl
83111013Smtm	verbose && echo -n "Removing mail spool(s) for ($login):"
84107543Sscottl	if [ -f ${MAILSPOOL}/$login ]; then
85111013Smtm		verbose && echo -n " ${MAILSPOOL}/$login" || \
86111013Smtm		    echo -n " mailspool"
87107543Sscottl		rm ${MAILSPOOL}/$login
88107543Sscottl	fi
89107543Sscottl	if [ -f ${MAILSPOOL}/${login}.pop ]; then
90111013Smtm		verbose && echo -n " ${MAILSPOOL}/${login}.pop" || \
91111013Smtm		    echo -n " pop3"
92107543Sscottl		rm ${MAILSPOOL}/${login}.pop
93107543Sscottl	fi
94111013Smtm	verbose && echo '.'
95107543Sscottl}
96107543Sscottl
97107543Sscottl# kill_procs login
98107543Sscottl#	Send a SIGKILL to all processes owned by $login.
99107543Sscottl#
100107543Sscottlkill_procs() {
101107543Sscottl	# The argument is required
102107543Sscottl	[ -n $1 ] && login=$1 || return
103107543Sscottl
104111013Smtm	verbose && echo -n "Terminating all processes owned by ($login):"
105107543Sscottl	killcount=0
106107543Sscottl	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
107107543Sscottl	for _pid in $proclist ; do
108107543Sscottl		kill 2>/dev/null ${SIGKILL} $_pid
109109752Sfjoe		killcount=$(($killcount + 1))
110107543Sscottl	done
111111013Smtm	verbose && echo " ${SIGKILL} signal sent to $killcount processes."
112111013Smtm	! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
113107543Sscottl}
114107543Sscottl
115107543Sscottl# rm_at_jobs login
116107543Sscottl#	Remove at (1) jobs belonging to $login.
117107543Sscottl#
118107543Sscottlrm_at_jobs() {
119107543Sscottl	# The argument is required
120107543Sscottl	[ -n $1 ] && login=$1 || return
121107543Sscottl
122107543Sscottl	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
123107543Sscottl	jobcount=0
124111013Smtm	verbose && echo -n "Removing at(1) jobs owned by ($login):"
125107543Sscottl	for _atjob in $atjoblist ; do
126107543Sscottl		rm -f $_atjob
127109752Sfjoe		jobcount=$(($jobcount + 1))
128107543Sscottl	done
129111013Smtm	verbose && echo " $jobcount removed."
130111013Smtm	! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
131107543Sscottl}
132107543Sscottl
133107543Sscottl# rm_crontab login
134107543Sscottl#	Removes crontab file belonging to user $login.
135107543Sscottl#
136107543Sscottlrm_crontab() {
137107543Sscottl	# The argument is required
138107543Sscottl	[ -n $1 ] && login=$1 || return
139107543Sscottl
140111013Smtm	verbose && echo -n "Removing crontab for ($login):"
141107543Sscottl	if [ -f ${CRONJOBDIR}/$login ]; then
142111013Smtm		verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
143107543Sscottl		rm -f ${CRONJOBDIR}/$login
144107543Sscottl	fi
145111013Smtm	verbose && echo '.'
146107543Sscottl}
147107543Sscottl
148107543Sscottl# rm_user login
149107543Sscottl#	Remove user $login from the system. This subroutine makes use
150107543Sscottl#	of the pw(8) command to remove a user from the system. The pw(8)
151107543Sscottl#	command will remove the specified user from the user database
152107543Sscottl#	and group file and remove any crontabs. His home
153107543Sscottl#	directory will be removed if it is owned by him and contains no 
154107543Sscottl#	files or subdirectories owned by other users. Mail spool files will
155107543Sscottl#	also be removed.
156107543Sscottl#
157107543Sscottlrm_user() {
158107543Sscottl	# The argument is required
159107543Sscottl	[ -n $1 ] && login=$1 || return
160107543Sscottl
161111013Smtm	verbose && echo -n "Removing user ($login)"
162111013Smtm	[ -n "$pw_rswitch" ] && {
163111013Smtm		verbose && echo -n " (including home directory)"
164111013Smtm		! verbose && echo -n " home"
165111013Smtm	}
166111013Smtm	! verbose && echo -n " passwd"
167111013Smtm	verbose && echo -n " from the system:"
168107543Sscottl	pw userdel -n $login $pw_rswitch
169111013Smtm	verbose && echo ' Done.'
170107543Sscottl}
171107543Sscottl
172107543Sscottl# prompt_yesno msg
173107543Sscottl#	Prompts the user with a $msg. The answer is expected to be
174107543Sscottl#	yes, no, or some variation thereof. This subroutine returns 0
175107543Sscottl#	if the answer was yes, 1 if it was not.
176107543Sscottl#
177107543Sscottlprompt_yesno() {
178107543Sscottl	# The argument is required
179109750Sfjoe	[ -n "$1" ] && msg="$1" || return
180107543Sscottl
181107543Sscottl        while : ; do
182109750Sfjoe                echo -n "$msg"
183107543Sscottl                read _ans
184107543Sscottl                case $_ans in
185107543Sscottl                [Nn][Oo]|[Nn])
186107543Sscottl			return 1
187107543Sscottl                        ;;
188107543Sscottl                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
189107543Sscottl                        return 0
190107543Sscottl                        ;;
191107543Sscottl                *)
192107543Sscottl                        ;;
193107543Sscottl                esac
194107543Sscottl	done
195107543Sscottl}
196107543Sscottl
197107543Sscottl# show_usage
198107543Sscottl#	(no arguments)
199107543Sscottl#	Display usage message.
200107543Sscottl#
201107543Sscottlshow_usage() {
202111013Smtm	echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
203107543Sscottl	echo "       if the -y switch is used, either the -f switch or"
204107543Sscottl	echo "       one or more user names must be given"
205107543Sscottl}
206107543Sscottl
207107543Sscottl#### END SUBROUTINE DEFENITION ####
208107543Sscottl
209107543Sscottlffile=
210107543Sscottlfflag=
211107543Sscottlprocowner=
212107543Sscottlpw_rswitch=
213107543Sscottluserlist=
214107543Sscottlyflag=
215111013Smtmvflag=
216107543Sscottl
217107543Sscottlprocowner=`/usr/bin/id -u`
218107543Sscottlif [ "$procowner" != "0" ]; then
219107543Sscottl	err 'you must be root (0) to use this utility.'
220107543Sscottl	exit 1
221107543Sscottlfi
222107543Sscottl
223111013Smtmargs=`getopt 2>/dev/null yvf: $*`
224107543Sscottlif [ "$?" != "0" ]; then
225107543Sscottl	show_usage
226107543Sscottl	exit 1
227107543Sscottlfi
228107543Sscottlset -- $args
229107543Sscottlfor _switch ; do
230107543Sscottl	case $_switch in
231107543Sscottl	-y)
232107543Sscottl		yflag=1
233107543Sscottl		shift
234107543Sscottl		;;
235111013Smtm	-v)
236111013Smtm		vflag=1
237111013Smtm		shift
238111013Smtm		;;
239107543Sscottl	-f)
240107543Sscottl		fflag=1
241107543Sscottl		ffile="$2"
242107543Sscottl		shift; shift
243107543Sscottl		;;
244107543Sscottl	--)
245107543Sscottl		shift
246107543Sscottl		break
247107543Sscottl		;;
248107543Sscottl	esac
249107543Sscottldone
250107543Sscottl
251107543Sscottl# Get user names from a file if the -f switch was used. Otherwise,
252107543Sscottl# get them from the commandline arguments. If we're getting it
253107543Sscottl# from a file, the file must be owned by and writable only by root.
254107543Sscottl#
255107543Sscottlif [ $fflag ]; then
256107543Sscottl	_insecure=`find $ffile ! -user 0 -or -perm +0022`
257107543Sscottl	if [ -n "$_insecure" ]; then
258107543Sscottl		err "file ($ffile) must be owned by and writeable only by root."
259107543Sscottl		exit 1
260107543Sscottl	fi
261107543Sscottl	if [ -r "$ffile" ]; then
262107543Sscottl		userlist=`cat $ffile | while read _user _junk ; do
263107543Sscottl			case $_user in
264107543Sscottl			\#*|'')
265107543Sscottl				;;
266107543Sscottl			*)
267107543Sscottl				echo -n "$userlist $_user"
268107543Sscottl				;;
269107543Sscottl			esac
270107543Sscottl		done`
271107543Sscottl	fi
272107543Sscottlelse
273107543Sscottl	while [ $1 ] ; do
274107543Sscottl		userlist="$userlist $1"
275107543Sscottl		shift
276107543Sscottl	done
277107543Sscottlfi
278107543Sscottl
279107543Sscottl# If the -y or -f switch has been used and the list of users to remove
280107543Sscottl# is empty it is a fatal error. Otherwise, prompt the user for a list
281107543Sscottl# of one or more user names.
282107543Sscottl#
283107543Sscottlif [ ! "$userlist" ]; then
284107543Sscottl	if [ $fflag ]; then
285107543Sscottl		err "($ffile) does not exist or does not contain any user names."
286107543Sscottl		exit 1
287107543Sscottl	elif [ $yflag ]; then
288107543Sscottl		show_usage
289107543Sscottl		exit 1
290107543Sscottl	else
291107543Sscottl		echo -n "Please enter one or more user name's: "
292107543Sscottl		read userlist
293107543Sscottl	fi
294107543Sscottlfi
295107543Sscottl
296107543Sscottl_user=
297107543Sscottl_uid=
298107543Sscottlfor _user in $userlist ; do
299107543Sscottl	# Make sure the name exists in the passwd database and that it
300107543Sscottl	# does not have a uid of 0
301107543Sscottl	#
302107543Sscottl	userrec=`pw 2>/dev/null usershow -n $_user`
303107543Sscottl	if [ "$?" != "0" ]; then
304107543Sscottl		err "user ($_user) does not exist in the password database."
305107543Sscottl		continue
306107543Sscottl	fi
307107543Sscottl	_uid=`echo $userrec | awk -F: '{print $3}'`
308107543Sscottl	if [ "$_uid" = "0" ]; then
309107543Sscottl		err "user ($_user) has uid 0. You may not remove this user."
310107543Sscottl		continue
311107543Sscottl	fi
312107543Sscottl
313107543Sscottl	# If the -y switch was not used ask for confirmation to remove the
314107543Sscottl	# user and home directory.
315107543Sscottl	#
316107543Sscottl	if [ -z "$yflag" ]; then
317107543Sscottl		echo "Matching password entry:"
318107543Sscottl		echo
319107543Sscottl		echo $userrec
320107543Sscottl		echo
321107543Sscottl		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
322107543Sscottl			continue
323107543Sscottl		fi
324107543Sscottl		_homedir=`echo $userrec | awk -F: '{print $9}'`
325109750Sfjoe		if prompt_yesno "Remove user's home directory ($_homedir)? "; then
326107543Sscottl			pw_rswitch="-r"
327107543Sscottl		fi
328107543Sscottl	else
329107543Sscottl		pw_rswitch="-r"
330107543Sscottl	fi
331107543Sscottl
332107543Sscottl	# Disable any further attempts to log into this account
333107543Sscottl	pw 2>/dev/null lock $_user
334107543Sscottl
335107543Sscottl	# Remove crontab, mail spool, etc. Then obliterate the user from
336107543Sscottl	# the passwd and group database.
337111013Smtm	#
338111013Smtm	! verbose && echo -n "Removing user ($_user):"
339107543Sscottl	rm_crontab $_user
340107543Sscottl	rm_at_jobs $_user
341107543Sscottl	kill_procs $_user
342111013Smtm	rm_files $_user
343107543Sscottl	rm_mail $_user
344107543Sscottl	rm_user $_user
345111013Smtm	! verbose && echo "."
346107543Sscottldone
347