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