1#!/bin/sh 2# 3# Copyright (c) 2002 Michael Telahun Makonnen. All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. The name of the author may not be used to endorse or promote products 14# derived from this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26# 27# Email: Mike Makonnen <mtm@identd.net> 28# 29# $FreeBSD$ 30# 31 32ATJOBDIR="/var/at/jobs" 33CRONJOBDIR="/var/cron/tabs" 34MAILSPOOL="/var/mail" 35SIGKILL="-KILL" 36TEMPDIRS="/tmp /var/tmp" 37THISCMD=`/usr/bin/basename $0` 38 39# err msg 40# Display $msg on stderr. 41# 42err() { 43 echo 1>&2 ${THISCMD}: $* 44} 45 46# verbose 47# Returns 0 if verbose mode is set, 1 if it is not. 48# 49verbose() { 50 [ -n "$vflag" ] && return 0 || return 1 51} 52 53# rm_files login 54# Removes files or empty directories belonging to $login from various 55# temporary directories. 56# 57rm_files() { 58 # The argument is required 59 [ -n $1 ] && login=$1 || return 60 61 totalcount=0 62 for _dir in ${TEMPDIRS} ; do 63 filecount=0 64 if [ ! -d $_dir ]; then 65 err "$_dir is not a valid directory." 66 continue 67 fi 68 verbose && echo -n "Removing files owned by ($login) in $_dir:" 69 filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print | \ 70 wc -l | sed 's/ *//'` 71 verbose && echo " $filecount removed." 72 totalcount=$(($totalcount + $filecount)) 73 done 74 ! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)" 75} 76 77# rm_mail login 78# Removes unix mail and pop daemon files belonging to the user 79# specified in the $login argument. 80# 81rm_mail() { 82 # The argument is required 83 [ -n $1 ] && login=$1 || return 84 85 verbose && echo -n "Removing mail spool(s) for ($login):" 86 if [ -f ${MAILSPOOL}/$login ]; then 87 verbose && echo -n " ${MAILSPOOL}/$login" || \ 88 echo -n " mailspool" 89 rm ${MAILSPOOL}/$login 90 fi 91 if [ -f ${MAILSPOOL}/${login}.pop ]; then 92 verbose && echo -n " ${MAILSPOOL}/${login}.pop" || \ 93 echo -n " pop3" 94 rm ${MAILSPOOL}/${login}.pop 95 fi 96 verbose && echo '.' 97} 98 99# kill_procs login 100# Send a SIGKILL to all processes owned by $login. 101# 102kill_procs() { 103 # The argument is required 104 [ -n $1 ] && login=$1 || return 105 106 verbose && echo -n "Terminating all processes owned by ($login):" 107 killcount=0 108 proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'` 109 for _pid in $proclist ; do 110 kill 2>/dev/null ${SIGKILL} $_pid 111 killcount=$(($killcount + 1)) 112 done 113 verbose && echo " ${SIGKILL} signal sent to $killcount processes." 114 ! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})" 115} 116 117# rm_at_jobs login 118# Remove at (1) jobs belonging to $login. 119# 120rm_at_jobs() { 121 # The argument is required 122 [ -n $1 ] && login=$1 || return 123 124 atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print` 125 jobcount=0 126 verbose && echo -n "Removing at(1) jobs owned by ($login):" 127 for _atjob in $atjoblist ; do 128 rm -f $_atjob 129 jobcount=$(($jobcount + 1)) 130 done 131 verbose && echo " $jobcount removed." 132 ! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)" 133} 134 135# rm_crontab login 136# Removes crontab file belonging to user $login. 137# 138rm_crontab() { 139 # The argument is required 140 [ -n $1 ] && login=$1 || return 141 142 verbose && echo -n "Removing crontab for ($login):" 143 if [ -f ${CRONJOBDIR}/$login ]; then 144 verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab" 145 rm -f ${CRONJOBDIR}/$login 146 fi 147 verbose && echo '.' 148} 149 150# rm_user login 151# Remove user $login from the system. This subroutine makes use 152# of the pw(8) command to remove a user from the system. The pw(8) 153# command will remove the specified user from the user database 154# and group file and remove any crontabs. His home 155# directory will be removed if it is owned by him and contains no 156# files or subdirectories owned by other users. Mail spool files will 157# also be removed. 158# 159rm_user() { 160 # The argument is required 161 [ -n $1 ] && login=$1 || return 162 163 verbose && echo -n "Removing user ($login)" 164 [ -n "$pw_rswitch" ] && { 165 verbose && echo -n " (including home directory)" 166 ! verbose && echo -n " home" 167 } 168 ! verbose && echo -n " passwd" 169 verbose && echo -n " from the system:" 170 pw userdel -n $login $pw_rswitch 171 verbose && echo ' Done.' 172} 173 174# prompt_yesno msg 175# Prompts the user with a $msg. The answer is expected to be 176# yes, no, or some variation thereof. This subroutine returns 0 177# if the answer was yes, 1 if it was not. 178# 179prompt_yesno() { 180 # The argument is required 181 [ -n "$1" ] && msg="$1" || return 182 183 while : ; do 184 echo -n "$msg" 185 read _ans 186 case $_ans in 187 [Nn][Oo]|[Nn]) 188 return 1 189 ;; 190 [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 191 return 0 192 ;; 193 *) 194 ;; 195 esac 196 done 197} 198 199# show_usage 200# (no arguments) 201# Display usage message. 202# 203show_usage() { 204 echo "usage: ${THISCMD} [-yv] [-f file] [user ...]" 205 echo " if the -y switch is used, either the -f switch or" 206 echo " one or more user names must be given" 207} 208 209#### END SUBROUTINE DEFENITION #### 210 211ffile= 212fflag= 213procowner= 214pw_rswitch= 215userlist= 216yflag= 217vflag= 218 219procowner=`/usr/bin/id -u` 220if [ "$procowner" != "0" ]; then 221 err 'you must be root (0) to use this utility.' 222 exit 1 223fi 224 225args=`getopt 2>/dev/null yvf: $*` 226if [ "$?" != "0" ]; then 227 show_usage 228 exit 1 229fi 230set -- $args 231for _switch ; do 232 case $_switch in 233 -y) 234 yflag=1 235 shift 236 ;; 237 -v) 238 vflag=1 239 shift 240 ;; 241 -f) 242 fflag=1 243 ffile="$2" 244 shift; shift 245 ;; 246 --) 247 shift 248 break 249 ;; 250 esac 251done 252 253# Get user names from a file if the -f switch was used. Otherwise, 254# get them from the commandline arguments. If we're getting it 255# from a file, the file must be owned by and writable only by root. 256# 257if [ $fflag ]; then 258 _insecure=`find $ffile ! -user 0 -or -perm +0022` 259 if [ -n "$_insecure" ]; then 260 err "file ($ffile) must be owned by and writeable only by root." 261 exit 1 262 fi 263 if [ -r "$ffile" ]; then 264 userlist=`cat $ffile | while read _user _junk ; do 265 case $_user in 266 \#*|'') 267 ;; 268 *) 269 echo -n "$userlist $_user" 270 ;; 271 esac 272 done` 273 fi 274else 275 while [ $1 ] ; do 276 userlist="$userlist $1" 277 shift 278 done 279fi 280 281# If the -y or -f switch has been used and the list of users to remove 282# is empty it is a fatal error. Otherwise, prompt the user for a list 283# of one or more user names. 284# 285if [ ! "$userlist" ]; then 286 if [ $fflag ]; then 287 err "($ffile) does not exist or does not contain any user names." 288 exit 1 289 elif [ $yflag ]; then 290 show_usage 291 exit 1 292 else 293 echo -n "Please enter one or more user name's: " 294 read userlist 295 fi 296fi 297 298_user= 299_uid= 300for _user in $userlist ; do 301 # Make sure the name exists in the passwd database and that it 302 # does not have a uid of 0 303 # 304 userrec=`pw 2>/dev/null usershow -n $_user` 305 if [ "$?" != "0" ]; then 306 err "user ($_user) does not exist in the password database." 307 continue 308 fi 309 _uid=`echo $userrec | awk -F: '{print $3}'` 310 if [ "$_uid" = "0" ]; then 311 err "user ($_user) has uid 0. You may not remove this user." 312 continue 313 fi 314 315 # If the -y switch was not used ask for confirmation to remove the 316 # user and home directory. 317 # 318 if [ -z "$yflag" ]; then 319 echo "Matching password entry:" 320 echo 321 echo $userrec 322 echo 323 if ! prompt_yesno "Is this the entry you wish to remove? " ; then 324 continue 325 fi 326 _homedir=`echo $userrec | awk -F: '{print $9}'` 327 if prompt_yesno "Remove user's home directory ($_homedir)? "; then 328 pw_rswitch="-r" 329 fi 330 else 331 pw_rswitch="-r" 332 fi 333 334 # Disable any further attempts to log into this account 335 pw 2>/dev/null lock $_user 336 337 # Remove crontab, mail spool, etc. Then obliterate the user from 338 # the passwd and group database. 339 # 340 ! verbose && echo -n "Removing user ($_user):" 341 rm_crontab $_user 342 rm_at_jobs $_user 343 kill_procs $_user 344 rm_files $_user 345 rm_mail $_user 346 rm_user $_user 347 ! verbose && echo "." 348done 349