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#
25116624Smtm#	Email: Mike Makonnen <mtm@FreeBSD.Org>
26107543Sscottl#
27107543Sscottl# $FreeBSD$
28107543Sscottl#
29107543Sscottl
30107543SscottlATJOBDIR="/var/at/jobs"
31107543SscottlCRONJOBDIR="/var/cron/tabs"
32107543SscottlMAILSPOOL="/var/mail"
33107543SscottlSIGKILL="-KILL"
34107694StjrTEMPDIRS="/tmp /var/tmp"
35107543SscottlTHISCMD=`/usr/bin/basename $0`
36181006SjhbPWCMD="${PWCMD:-/usr/sbin/pw}"
37107543Sscottl
38107543Sscottl# err msg
39107543Sscottl#	Display $msg on stderr.
40107543Sscottl#
41107543Sscottlerr() {
42107543Sscottl	echo 1>&2 ${THISCMD}: $*
43107543Sscottl}
44107543Sscottl
45111013Smtm# verbose
46111013Smtm#	Returns 0 if verbose mode is set, 1 if it is not.
47111013Smtm#
48111013Smtmverbose() {
49111013Smtm	[ -n "$vflag" ] && return 0 || return 1
50111013Smtm}
51111013Smtm
52107543Sscottl# rm_files login
53107543Sscottl#	Removes files or empty directories belonging to $login from various
54107543Sscottl#	temporary directories.
55107543Sscottl#
56107543Sscottlrm_files() {
57107543Sscottl	# The argument is required
58107543Sscottl	[ -n $1 ] && login=$1 || return
59126398Sschweikh
60111013Smtm	totalcount=0
61107543Sscottl	for _dir in ${TEMPDIRS} ; do
62111013Smtm		filecount=0
63107543Sscottl		if [ ! -d $_dir ]; then
64107543Sscottl			err "$_dir is not a valid directory."
65107543Sscottl			continue
66107543Sscottl		fi
67111013Smtm		verbose && echo -n "Removing files owned by ($login) in $_dir:"
68126398Sschweikh		filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print |
69107694Stjr		    wc -l | sed 's/ *//'`
70111013Smtm		verbose && echo " $filecount removed."
71111013Smtm		totalcount=$(($totalcount + $filecount))
72107543Sscottl	done
73111013Smtm	! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
74107543Sscottl}
75107543Sscottl
76107543Sscottl# rm_mail login
77107543Sscottl#	Removes unix mail and pop daemon files belonging to the user
78107543Sscottl#	specified in the $login argument.
79107543Sscottl#
80107543Sscottlrm_mail() {
81107543Sscottl	# The argument is required
82107543Sscottl	[ -n $1 ] && login=$1 || return
83107543Sscottl
84111013Smtm	verbose && echo -n "Removing mail spool(s) for ($login):"
85107543Sscottl	if [ -f ${MAILSPOOL}/$login ]; then
86126398Sschweikh		verbose && echo -n " ${MAILSPOOL}/$login" ||
87111013Smtm		    echo -n " mailspool"
88107543Sscottl		rm ${MAILSPOOL}/$login
89107543Sscottl	fi
90172791Smtm	if [ -f ${MAILSPOOL}/.${login}.pop ]; then
91172791Smtm		verbose && echo -n " ${MAILSPOOL}/.${login}.pop" ||
92111013Smtm		    echo -n " pop3"
93172791Smtm		rm ${MAILSPOOL}/.${login}.pop
94107543Sscottl	fi
95111013Smtm	verbose && echo '.'
96107543Sscottl}
97107543Sscottl
98107543Sscottl# kill_procs login
99107543Sscottl#	Send a SIGKILL to all processes owned by $login.
100107543Sscottl#
101107543Sscottlkill_procs() {
102107543Sscottl	# The argument is required
103107543Sscottl	[ -n $1 ] && login=$1 || return
104107543Sscottl
105111013Smtm	verbose && echo -n "Terminating all processes owned by ($login):"
106107543Sscottl	killcount=0
107107543Sscottl	proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
108107543Sscottl	for _pid in $proclist ; do
109107543Sscottl		kill 2>/dev/null ${SIGKILL} $_pid
110109752Sfjoe		killcount=$(($killcount + 1))
111107543Sscottl	done
112111013Smtm	verbose && echo " ${SIGKILL} signal sent to $killcount processes."
113111013Smtm	! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
114107543Sscottl}
115107543Sscottl
116107543Sscottl# rm_at_jobs login
117107543Sscottl#	Remove at (1) jobs belonging to $login.
118107543Sscottl#
119107543Sscottlrm_at_jobs() {
120107543Sscottl	# The argument is required
121107543Sscottl	[ -n $1 ] && login=$1 || return
122107543Sscottl
123107543Sscottl	atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
124107543Sscottl	jobcount=0
125111013Smtm	verbose && echo -n "Removing at(1) jobs owned by ($login):"
126107543Sscottl	for _atjob in $atjoblist ; do
127107543Sscottl		rm -f $_atjob
128109752Sfjoe		jobcount=$(($jobcount + 1))
129107543Sscottl	done
130111013Smtm	verbose && echo " $jobcount removed."
131111013Smtm	! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
132107543Sscottl}
133107543Sscottl
134107543Sscottl# rm_crontab login
135107543Sscottl#	Removes crontab file belonging to user $login.
136107543Sscottl#
137107543Sscottlrm_crontab() {
138107543Sscottl	# The argument is required
139107543Sscottl	[ -n $1 ] && login=$1 || return
140107543Sscottl
141111013Smtm	verbose && echo -n "Removing crontab for ($login):"
142107543Sscottl	if [ -f ${CRONJOBDIR}/$login ]; then
143111013Smtm		verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
144107543Sscottl		rm -f ${CRONJOBDIR}/$login
145107543Sscottl	fi
146111013Smtm	verbose && echo '.'
147107543Sscottl}
148107543Sscottl
149126398Sschweikh# rm_ipc login
150126398Sschweikh#	Remove all IPC mechanisms which are owned by $login.
151126398Sschweikh#
152126398Sschweikhrm_ipc() {
153126398Sschweikh	verbose && echo -n "Removing IPC mechanisms"
154126398Sschweikh	for i in s m q; do
155126398Sschweikh		ipcs -$i |
156126398Sschweikh		awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' |
157126398Sschweikh		xargs -n 1 ipcrm -$i
158126398Sschweikh	done
159126398Sschweikh	verbose && echo '.'
160126398Sschweikh}
161126398Sschweikh
162107543Sscottl# rm_user login
163107543Sscottl#	Remove user $login from the system. This subroutine makes use
164107543Sscottl#	of the pw(8) command to remove a user from the system. The pw(8)
165107543Sscottl#	command will remove the specified user from the user database
166107543Sscottl#	and group file and remove any crontabs. His home
167107543Sscottl#	directory will be removed if it is owned by him and contains no 
168107543Sscottl#	files or subdirectories owned by other users. Mail spool files will
169107543Sscottl#	also be removed.
170107543Sscottl#
171107543Sscottlrm_user() {
172107543Sscottl	# The argument is required
173107543Sscottl	[ -n $1 ] && login=$1 || return
174107543Sscottl
175111013Smtm	verbose && echo -n "Removing user ($login)"
176111013Smtm	[ -n "$pw_rswitch" ] && {
177111013Smtm		verbose && echo -n " (including home directory)"
178111013Smtm		! verbose && echo -n " home"
179111013Smtm	}
180111013Smtm	! verbose && echo -n " passwd"
181111013Smtm	verbose && echo -n " from the system:"
182181006Sjhb	${PWCMD} userdel -n $login $pw_rswitch
183111013Smtm	verbose && echo ' Done.'
184107543Sscottl}
185107543Sscottl
186107543Sscottl# prompt_yesno msg
187107543Sscottl#	Prompts the user with a $msg. The answer is expected to be
188107543Sscottl#	yes, no, or some variation thereof. This subroutine returns 0
189107543Sscottl#	if the answer was yes, 1 if it was not.
190107543Sscottl#
191107543Sscottlprompt_yesno() {
192107543Sscottl	# The argument is required
193109750Sfjoe	[ -n "$1" ] && msg="$1" || return
194107543Sscottl
195107543Sscottl        while : ; do
196109750Sfjoe                echo -n "$msg"
197107543Sscottl                read _ans
198107543Sscottl                case $_ans in
199107543Sscottl                [Nn][Oo]|[Nn])
200107543Sscottl			return 1
201107543Sscottl                        ;;
202107543Sscottl                [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
203107543Sscottl                        return 0
204107543Sscottl                        ;;
205107543Sscottl                *)
206107543Sscottl                        ;;
207107543Sscottl                esac
208107543Sscottl	done
209107543Sscottl}
210107543Sscottl
211107543Sscottl# show_usage
212107543Sscottl#	(no arguments)
213107543Sscottl#	Display usage message.
214107543Sscottl#
215107543Sscottlshow_usage() {
216111013Smtm	echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
217107543Sscottl	echo "       if the -y switch is used, either the -f switch or"
218107543Sscottl	echo "       one or more user names must be given"
219107543Sscottl}
220107543Sscottl
221107543Sscottl#### END SUBROUTINE DEFENITION ####
222107543Sscottl
223107543Sscottlffile=
224107543Sscottlfflag=
225107543Sscottlprocowner=
226107543Sscottlpw_rswitch=
227107543Sscottluserlist=
228107543Sscottlyflag=
229111013Smtmvflag=
230107543Sscottl
231107543Sscottlprocowner=`/usr/bin/id -u`
232107543Sscottlif [ "$procowner" != "0" ]; then
233107543Sscottl	err 'you must be root (0) to use this utility.'
234107543Sscottl	exit 1
235107543Sscottlfi
236107543Sscottl
237111013Smtmargs=`getopt 2>/dev/null yvf: $*`
238107543Sscottlif [ "$?" != "0" ]; then
239107543Sscottl	show_usage
240107543Sscottl	exit 1
241107543Sscottlfi
242107543Sscottlset -- $args
243107543Sscottlfor _switch ; do
244107543Sscottl	case $_switch in
245107543Sscottl	-y)
246107543Sscottl		yflag=1
247107543Sscottl		shift
248107543Sscottl		;;
249111013Smtm	-v)
250111013Smtm		vflag=1
251111013Smtm		shift
252111013Smtm		;;
253107543Sscottl	-f)
254107543Sscottl		fflag=1
255107543Sscottl		ffile="$2"
256107543Sscottl		shift; shift
257107543Sscottl		;;
258107543Sscottl	--)
259107543Sscottl		shift
260107543Sscottl		break
261107543Sscottl		;;
262107543Sscottl	esac
263107543Sscottldone
264107543Sscottl
265107543Sscottl# Get user names from a file if the -f switch was used. Otherwise,
266107543Sscottl# get them from the commandline arguments. If we're getting it
267107543Sscottl# from a file, the file must be owned by and writable only by root.
268107543Sscottl#
269107543Sscottlif [ $fflag ]; then
270107543Sscottl	_insecure=`find $ffile ! -user 0 -or -perm +0022`
271107543Sscottl	if [ -n "$_insecure" ]; then
272107543Sscottl		err "file ($ffile) must be owned by and writeable only by root."
273107543Sscottl		exit 1
274107543Sscottl	fi
275107543Sscottl	if [ -r "$ffile" ]; then
276107543Sscottl		userlist=`cat $ffile | while read _user _junk ; do
277107543Sscottl			case $_user in
278107543Sscottl			\#*|'')
279107543Sscottl				;;
280107543Sscottl			*)
281107543Sscottl				echo -n "$userlist $_user"
282107543Sscottl				;;
283107543Sscottl			esac
284107543Sscottl		done`
285107543Sscottl	fi
286107543Sscottlelse
287107543Sscottl	while [ $1 ] ; do
288107543Sscottl		userlist="$userlist $1"
289107543Sscottl		shift
290107543Sscottl	done
291107543Sscottlfi
292107543Sscottl
293107543Sscottl# If the -y or -f switch has been used and the list of users to remove
294107543Sscottl# is empty it is a fatal error. Otherwise, prompt the user for a list
295107543Sscottl# of one or more user names.
296107543Sscottl#
297107543Sscottlif [ ! "$userlist" ]; then
298107543Sscottl	if [ $fflag ]; then
299107543Sscottl		err "($ffile) does not exist or does not contain any user names."
300107543Sscottl		exit 1
301107543Sscottl	elif [ $yflag ]; then
302107543Sscottl		show_usage
303107543Sscottl		exit 1
304107543Sscottl	else
305146556Sadamw		echo -n "Please enter one or more usernames: "
306107543Sscottl		read userlist
307107543Sscottl	fi
308107543Sscottlfi
309107543Sscottl
310107543Sscottl_user=
311107543Sscottl_uid=
312107543Sscottlfor _user in $userlist ; do
313107543Sscottl	# Make sure the name exists in the passwd database and that it
314107543Sscottl	# does not have a uid of 0
315107543Sscottl	#
316107543Sscottl	userrec=`pw 2>/dev/null usershow -n $_user`
317107543Sscottl	if [ "$?" != "0" ]; then
318107543Sscottl		err "user ($_user) does not exist in the password database."
319107543Sscottl		continue
320107543Sscottl	fi
321107543Sscottl	_uid=`echo $userrec | awk -F: '{print $3}'`
322107543Sscottl	if [ "$_uid" = "0" ]; then
323107543Sscottl		err "user ($_user) has uid 0. You may not remove this user."
324107543Sscottl		continue
325107543Sscottl	fi
326107543Sscottl
327107543Sscottl	# If the -y switch was not used ask for confirmation to remove the
328107543Sscottl	# user and home directory.
329107543Sscottl	#
330107543Sscottl	if [ -z "$yflag" ]; then
331107543Sscottl		echo "Matching password entry:"
332107543Sscottl		echo
333107543Sscottl		echo $userrec
334107543Sscottl		echo
335107543Sscottl		if ! prompt_yesno "Is this the entry you wish to remove? " ; then
336107543Sscottl			continue
337107543Sscottl		fi
338107543Sscottl		_homedir=`echo $userrec | awk -F: '{print $9}'`
339109750Sfjoe		if prompt_yesno "Remove user's home directory ($_homedir)? "; then
340107543Sscottl			pw_rswitch="-r"
341107543Sscottl		fi
342107543Sscottl	else
343107543Sscottl		pw_rswitch="-r"
344107543Sscottl	fi
345107543Sscottl
346107543Sscottl	# Disable any further attempts to log into this account
347181006Sjhb	${PWCMD} 2>/dev/null lock $_user
348107543Sscottl
349107543Sscottl	# Remove crontab, mail spool, etc. Then obliterate the user from
350107543Sscottl	# the passwd and group database.
351111013Smtm	#
352111013Smtm	! verbose && echo -n "Removing user ($_user):"
353107543Sscottl	rm_crontab $_user
354107543Sscottl	rm_at_jobs $_user
355126398Sschweikh	rm_ipc $_user
356107543Sscottl	kill_procs $_user
357111013Smtm	rm_files $_user
358107543Sscottl	rm_mail $_user
359107543Sscottl	rm_user $_user
360111013Smtm	! verbose && echo "."
361107543Sscottldone
362