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