rmuser.sh revision 146556
11553Srgrimes#!/bin/sh 21553Srgrimes# 31553Srgrimes# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved. 41553Srgrimes# 51553Srgrimes# Redistribution and use in source and binary forms, with or without 61553Srgrimes# modification, are permitted provided that the following conditions 71553Srgrimes# are met: 81553Srgrimes# 1. Redistributions of source code must retain the above copyright 91553Srgrimes# notice, this list of conditions and the following disclaimer. 101553Srgrimes# 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes# notice, this list of conditions and the following disclaimer in the 121553Srgrimes# documentation and/or other materials provided with the distribution. 131553Srgrimes# 141553Srgrimes# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 151553Srgrimes# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 161553Srgrimes# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 171553Srgrimes# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 181553Srgrimes# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 191553Srgrimes# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 201553Srgrimes# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 211553Srgrimes# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 221553Srgrimes# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 231553Srgrimes# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 241553Srgrimes# 251553Srgrimes# Email: Mike Makonnen <mtm@FreeBSD.Org> 261553Srgrimes# 271553Srgrimes# $FreeBSD: head/usr.sbin/adduser/rmuser.sh 146556 2005-05-24 04:50:07Z adamw $ 281553Srgrimes# 291553Srgrimes 301553SrgrimesATJOBDIR="/var/at/jobs" 311553SrgrimesCRONJOBDIR="/var/cron/tabs" 321553SrgrimesMAILSPOOL="/var/mail" 331553SrgrimesSIGKILL="-KILL" 341553SrgrimesTEMPDIRS="/tmp /var/tmp" 351553SrgrimesTHISCMD=`/usr/bin/basename $0` 361553Srgrimes 371553Srgrimes# err msg 381553Srgrimes# Display $msg on stderr. 391553Srgrimes# 401553Srgrimeserr() { 411553Srgrimes echo 1>&2 ${THISCMD}: $* 421553Srgrimes} 431553Srgrimes 441553Srgrimes# verbose 451553Srgrimes# Returns 0 if verbose mode is set, 1 if it is not. 461553Srgrimes# 471553Srgrimesverbose() { 481553Srgrimes [ -n "$vflag" ] && return 0 || return 1 491553Srgrimes} 501553Srgrimes 511553Srgrimes# rm_files login 521553Srgrimes# Removes files or empty directories belonging to $login from various 531553Srgrimes# temporary directories. 541553Srgrimes# 551553Srgrimesrm_files() { 561553Srgrimes # The argument is required 571553Srgrimes [ -n $1 ] && login=$1 || return 581553Srgrimes 591553Srgrimes totalcount=0 601553Srgrimes for _dir in ${TEMPDIRS} ; do 611553Srgrimes filecount=0 621553Srgrimes if [ ! -d $_dir ]; then 631553Srgrimes err "$_dir is not a valid directory." 641553Srgrimes continue 651553Srgrimes fi 661553Srgrimes verbose && echo -n "Removing files owned by ($login) in $_dir:" 671553Srgrimes filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print | 681553Srgrimes wc -l | sed 's/ *//'` 691553Srgrimes verbose && echo " $filecount removed." 7010087Sjkh totalcount=$(($totalcount + $filecount)) 7110087Sjkh done 7210087Sjkh ! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)" 7310087Sjkh} 7410087Sjkh 7510087Sjkh# rm_mail login 7610087Sjkh# Removes unix mail and pop daemon files belonging to the user 7710087Sjkh# specified in the $login argument. 7810087Sjkh# 7910087Sjkhrm_mail() { 8010087Sjkh # The argument is required 8110087Sjkh [ -n $1 ] && login=$1 || return 8210087Sjkh 8310087Sjkh verbose && echo -n "Removing mail spool(s) for ($login):" 8410087Sjkh if [ -f ${MAILSPOOL}/$login ]; then 8510087Sjkh verbose && echo -n " ${MAILSPOOL}/$login" || 8610087Sjkh echo -n " mailspool" 8710087Sjkh rm ${MAILSPOOL}/$login 8810087Sjkh fi 8910087Sjkh if [ -f ${MAILSPOOL}/${login}.pop ]; then 9010087Sjkh verbose && echo -n " ${MAILSPOOL}/${login}.pop" || 9110087Sjkh echo -n " pop3" 9210087Sjkh rm ${MAILSPOOL}/${login}.pop 9310087Sjkh fi 9410087Sjkh verbose && echo '.' 9510087Sjkh} 9610087Sjkh 9710087Sjkh# kill_procs login 9810087Sjkh# Send a SIGKILL to all processes owned by $login. 9910087Sjkh# 10010087Sjkhkill_procs() { 10110087Sjkh # The argument is required 10210087Sjkh [ -n $1 ] && login=$1 || return 10310087Sjkh 10410087Sjkh verbose && echo -n "Terminating all processes owned by ($login):" 10510087Sjkh killcount=0 10610087Sjkh proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'` 10710087Sjkh for _pid in $proclist ; do 10810087Sjkh kill 2>/dev/null ${SIGKILL} $_pid 10910087Sjkh killcount=$(($killcount + 1)) 11010087Sjkh done 11110087Sjkh verbose && echo " ${SIGKILL} signal sent to $killcount processes." 11210087Sjkh ! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})" 1131553Srgrimes} 1141553Srgrimes 1151553Srgrimes# rm_at_jobs login 1161553Srgrimes# Remove at (1) jobs belonging to $login. 1171553Srgrimes# 1181553Srgrimesrm_at_jobs() { 1191553Srgrimes # The argument is required 1201553Srgrimes [ -n $1 ] && login=$1 || return 1211553Srgrimes 1221553Srgrimes atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print` 1231553Srgrimes jobcount=0 1241553Srgrimes verbose && echo -n "Removing at(1) jobs owned by ($login):" 1251553Srgrimes for _atjob in $atjoblist ; do 1261553Srgrimes rm -f $_atjob 1271553Srgrimes jobcount=$(($jobcount + 1)) 1281553Srgrimes done 1291553Srgrimes verbose && echo " $jobcount removed." 1301553Srgrimes ! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)" 1311553Srgrimes} 1321553Srgrimes 1331553Srgrimes# rm_crontab login 1341553Srgrimes# Removes crontab file belonging to user $login. 1351553Srgrimes# 1361553Srgrimesrm_crontab() { 1371553Srgrimes # The argument is required 1381553Srgrimes [ -n $1 ] && login=$1 || return 1391553Srgrimes 1401553Srgrimes verbose && echo -n "Removing crontab for ($login):" 1411553Srgrimes if [ -f ${CRONJOBDIR}/$login ]; then 1421553Srgrimes verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab" 1431553Srgrimes rm -f ${CRONJOBDIR}/$login 1441553Srgrimes fi 1451553Srgrimes verbose && echo '.' 1461553Srgrimes} 1471553Srgrimes 1481553Srgrimes# rm_ipc login 1491553Srgrimes# Remove all IPC mechanisms which are owned by $login. 1501553Srgrimes# 1511553Srgrimesrm_ipc() { 1521553Srgrimes verbose && echo -n "Removing IPC mechanisms" 1531553Srgrimes for i in s m q; do 1541553Srgrimes ipcs -$i | 15510087Sjkh awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' | 1561553Srgrimes xargs -n 1 ipcrm -$i 1571553Srgrimes done 1581553Srgrimes verbose && echo '.' 1591553Srgrimes} 1601553Srgrimes 1611553Srgrimes# rm_user login 1621553Srgrimes# Remove user $login from the system. This subroutine makes use 1631553Srgrimes# of the pw(8) command to remove a user from the system. The pw(8) 1641553Srgrimes# command will remove the specified user from the user database 1651553Srgrimes# and group file and remove any crontabs. His home 1661553Srgrimes# directory will be removed if it is owned by him and contains no 1671553Srgrimes# files or subdirectories owned by other users. Mail spool files will 16810087Sjkh# also be removed. 16910087Sjkh# 17010087Sjkhrm_user() { 17110087Sjkh # The argument is required 17210087Sjkh [ -n $1 ] && login=$1 || return 17310087Sjkh 17410087Sjkh verbose && echo -n "Removing user ($login)" 17510087Sjkh [ -n "$pw_rswitch" ] && { 17610087Sjkh verbose && echo -n " (including home directory)" 17710087Sjkh ! verbose && echo -n " home" 17810087Sjkh } 17910087Sjkh ! verbose && echo -n " passwd" 18010087Sjkh verbose && echo -n " from the system:" 18110087Sjkh pw userdel -n $login $pw_rswitch 18210087Sjkh verbose && echo ' Done.' 18310087Sjkh} 18410087Sjkh 18510087Sjkh# prompt_yesno msg 18610087Sjkh# Prompts the user with a $msg. The answer is expected to be 18710087Sjkh# yes, no, or some variation thereof. This subroutine returns 0 18810087Sjkh# if the answer was yes, 1 if it was not. 1891553Srgrimes# 1901553Srgrimesprompt_yesno() { 1911553Srgrimes # The argument is required 1921553Srgrimes [ -n "$1" ] && msg="$1" || return 1931553Srgrimes 19410087Sjkh while : ; do 19510087Sjkh echo -n "$msg" 19610087Sjkh read _ans 19710087Sjkh case $_ans in 19810087Sjkh [Nn][Oo]|[Nn]) 19910087Sjkh return 1 20010087Sjkh ;; 2011553Srgrimes [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 20210087Sjkh return 0 2031553Srgrimes ;; 2041553Srgrimes *) 2051553Srgrimes ;; 2061553Srgrimes esac 2071553Srgrimes done 2081553Srgrimes} 2091553Srgrimes 2101553Srgrimes# show_usage 2111553Srgrimes# (no arguments) 2121553Srgrimes# Display usage message. 2131553Srgrimes# 2141553Srgrimesshow_usage() { 2151553Srgrimes echo "usage: ${THISCMD} [-yv] [-f file] [user ...]" 2161553Srgrimes echo " if the -y switch is used, either the -f switch or" 2171553Srgrimes echo " one or more user names must be given" 2181553Srgrimes} 2191553Srgrimes 2201553Srgrimes#### END SUBROUTINE DEFENITION #### 2211553Srgrimes 2221553Srgrimesffile= 2231553Srgrimesfflag= 2241553Srgrimesprocowner= 2251553Srgrimespw_rswitch= 2261553Srgrimesuserlist= 2271553Srgrimesyflag= 2281553Srgrimesvflag= 2291553Srgrimes 23010087Sjkhprocowner=`/usr/bin/id -u` 2311553Srgrimesif [ "$procowner" != "0" ]; then 2321553Srgrimes err 'you must be root (0) to use this utility.' 2331553Srgrimes exit 1 2341553Srgrimesfi 2351553Srgrimes 2361553Srgrimesargs=`getopt 2>/dev/null yvf: $*` 2371553Srgrimesif [ "$?" != "0" ]; then 2381553Srgrimes show_usage 2391553Srgrimes exit 1 2401553Srgrimesfi 2411553Srgrimesset -- $args 2421553Srgrimesfor _switch ; do 2431553Srgrimes case $_switch in 2441553Srgrimes -y) 2451553Srgrimes yflag=1 2461553Srgrimes shift 2471553Srgrimes ;; 2481553Srgrimes -v) 2491553Srgrimes vflag=1 2501553Srgrimes shift 2511553Srgrimes ;; 2521553Srgrimes -f) 2531553Srgrimes fflag=1 2541553Srgrimes ffile="$2" 2551553Srgrimes shift; shift 2561553Srgrimes ;; 2571553Srgrimes --) 2581553Srgrimes shift 2591553Srgrimes break 2601553Srgrimes ;; 2611553Srgrimes esac 2621553Srgrimesdone 2631553Srgrimes 2641553Srgrimes# Get user names from a file if the -f switch was used. Otherwise, 2651553Srgrimes# get them from the commandline arguments. If we're getting it 2661553Srgrimes# from a file, the file must be owned by and writable only by root. 2671553Srgrimes# 2681553Srgrimesif [ $fflag ]; then 2691553Srgrimes _insecure=`find $ffile ! -user 0 -or -perm +0022` 2701553Srgrimes if [ -n "$_insecure" ]; then 2711553Srgrimes err "file ($ffile) must be owned by and writeable only by root." 2721553Srgrimes exit 1 2731553Srgrimes fi 2741553Srgrimes if [ -r "$ffile" ]; then 2751553Srgrimes userlist=`cat $ffile | while read _user _junk ; do 2761553Srgrimes case $_user in 2771553Srgrimes \#*|'') 2781553Srgrimes ;; 2791553Srgrimes *) 2801553Srgrimes echo -n "$userlist $_user" 2811553Srgrimes ;; 2821553Srgrimes esac 2831553Srgrimes done` 2841553Srgrimes fi 2851553Srgrimeselse 2861553Srgrimes while [ $1 ] ; do 2871553Srgrimes userlist="$userlist $1" 2881553Srgrimes shift 2891553Srgrimes done 2901553Srgrimesfi 2911553Srgrimes 2921553Srgrimes# If the -y or -f switch has been used and the list of users to remove 2931553Srgrimes# is empty it is a fatal error. Otherwise, prompt the user for a list 2941553Srgrimes# of one or more user names. 2951553Srgrimes# 2961553Srgrimesif [ ! "$userlist" ]; then 2971553Srgrimes if [ $fflag ]; then 2981553Srgrimes err "($ffile) does not exist or does not contain any user names." 2991553Srgrimes exit 1 3001553Srgrimes elif [ $yflag ]; then 3011553Srgrimes show_usage 3021553Srgrimes exit 1 3031553Srgrimes else 3041553Srgrimes echo -n "Please enter one or more usernames: " 3051553Srgrimes read userlist 3061553Srgrimes fi 3071553Srgrimesfi 3081553Srgrimes 3091553Srgrimes_user= 3101553Srgrimes_uid= 3111553Srgrimesfor _user in $userlist ; do 3121553Srgrimes # Make sure the name exists in the passwd database and that it 3131553Srgrimes # does not have a uid of 0 3141553Srgrimes # 3151553Srgrimes userrec=`pw 2>/dev/null usershow -n $_user` 3161553Srgrimes if [ "$?" != "0" ]; then 3171553Srgrimes err "user ($_user) does not exist in the password database." 3181553Srgrimes continue 3191553Srgrimes fi 3201553Srgrimes _uid=`echo $userrec | awk -F: '{print $3}'` 3211553Srgrimes if [ "$_uid" = "0" ]; then 3221553Srgrimes err "user ($_user) has uid 0. You may not remove this user." 3231553Srgrimes continue 3241553Srgrimes fi 3251553Srgrimes 3261553Srgrimes # If the -y switch was not used ask for confirmation to remove the 3271553Srgrimes # user and home directory. 3281553Srgrimes # 3291553Srgrimes if [ -z "$yflag" ]; then 3301553Srgrimes echo "Matching password entry:" 3311553Srgrimes echo 3321553Srgrimes echo $userrec 3331553Srgrimes echo 3341553Srgrimes if ! prompt_yesno "Is this the entry you wish to remove? " ; then 3351553Srgrimes continue 3361553Srgrimes fi 3371553Srgrimes _homedir=`echo $userrec | awk -F: '{print $9}'` 3381553Srgrimes if prompt_yesno "Remove user's home directory ($_homedir)? "; then 3391553Srgrimes pw_rswitch="-r" 3401553Srgrimes fi 3411553Srgrimes else 3421553Srgrimes pw_rswitch="-r" 3431553Srgrimes fi 3441553Srgrimes 3451553Srgrimes # Disable any further attempts to log into this account 3461553Srgrimes pw 2>/dev/null lock $_user 3471553Srgrimes 3481553Srgrimes # Remove crontab, mail spool, etc. Then obliterate the user from 3491553Srgrimes # the passwd and group database. 3501553Srgrimes # 3511553Srgrimes ! verbose && echo -n "Removing user ($_user):" 3521553Srgrimes rm_crontab $_user 3531553Srgrimes rm_at_jobs $_user 3541553Srgrimes rm_ipc $_user 3551553Srgrimes kill_procs $_user 3561553Srgrimes rm_files $_user 3571553Srgrimes rm_mail $_user 3581553Srgrimes rm_user $_user 3591553Srgrimes ! verbose && echo "." 3601553Srgrimesdone 3611553Srgrimes