18d20e303SChris Pressey#!/bin/sh 28d20e303SChris Pressey# 38d20e303SChris Pressey# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved. 48d20e303SChris Pressey# 58d20e303SChris Pressey# Redistribution and use in source and binary forms, with or without 68d20e303SChris Pressey# modification, are permitted provided that the following conditions 78d20e303SChris Pressey# are met: 88d20e303SChris Pressey# 1. Redistributions of source code must retain the above copyright 98d20e303SChris Pressey# notice, this list of conditions and the following disclaimer. 108d20e303SChris Pressey# 2. Redistributions in binary form must reproduce the above copyright 118d20e303SChris Pressey# notice, this list of conditions and the following disclaimer in the 128d20e303SChris Pressey# documentation and/or other materials provided with the distribution. 138d20e303SChris Pressey# 148d20e303SChris Pressey# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 158d20e303SChris Pressey# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 168d20e303SChris Pressey# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 178d20e303SChris Pressey# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 188d20e303SChris Pressey# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 198d20e303SChris Pressey# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 208d20e303SChris Pressey# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 218d20e303SChris Pressey# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 228d20e303SChris Pressey# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 238d20e303SChris Pressey# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 248d20e303SChris Pressey# 258d20e303SChris Pressey# Email: Mike Makonnen <mtm@FreeBSD.Org> 268d20e303SChris Pressey# 279ed5bb8aSSascha Wildner# $FreeBSD: src/usr.sbin/adduser/rmuser.sh,v 1.11 2008/07/30 18:37:21 jhb Exp $ 288d20e303SChris Pressey# 298d20e303SChris Pressey 308d20e303SChris PresseyATJOBDIR="/var/at/jobs" 318d20e303SChris PresseyCRONJOBDIR="/var/cron/tabs" 328d20e303SChris PresseyMAILSPOOL="/var/mail" 338d20e303SChris PresseySIGKILL="-KILL" 348d20e303SChris PresseyTEMPDIRS="/tmp /var/tmp" 358d20e303SChris PresseyTHISCMD=`/usr/bin/basename $0` 369ed5bb8aSSascha WildnerPWCMD="${PWCMD:-/usr/sbin/pw}" 378d20e303SChris Pressey 388d20e303SChris Pressey# err msg 398d20e303SChris Pressey# Display $msg on stderr. 408d20e303SChris Pressey# 418d20e303SChris Presseyerr() { 428d20e303SChris Pressey echo 1>&2 ${THISCMD}: $* 438d20e303SChris Pressey} 448d20e303SChris Pressey 458d20e303SChris Pressey# verbose 468d20e303SChris Pressey# Returns 0 if verbose mode is set, 1 if it is not. 478d20e303SChris Pressey# 488d20e303SChris Presseyverbose() { 498d20e303SChris Pressey [ -n "$vflag" ] && return 0 || return 1 508d20e303SChris Pressey} 518d20e303SChris Pressey 528d20e303SChris Pressey# rm_files login 538d20e303SChris Pressey# Removes files or empty directories belonging to $login from various 548d20e303SChris Pressey# temporary directories. 558d20e303SChris Pressey# 568d20e303SChris Presseyrm_files() { 578d20e303SChris Pressey # The argument is required 588d20e303SChris Pressey [ -n $1 ] && login=$1 || return 598d20e303SChris Pressey 608d20e303SChris Pressey totalcount=0 618d20e303SChris Pressey for _dir in ${TEMPDIRS} ; do 628d20e303SChris Pressey filecount=0 638d20e303SChris Pressey if [ ! -d $_dir ]; then 648d20e303SChris Pressey err "$_dir is not a valid directory." 658d20e303SChris Pressey continue 668d20e303SChris Pressey fi 678d20e303SChris Pressey verbose && echo -n "Removing files owned by ($login) in $_dir:" 688d20e303SChris Pressey filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print | 698d20e303SChris Pressey wc -l | sed 's/ *//'` 708d20e303SChris Pressey verbose && echo " $filecount removed." 718d20e303SChris Pressey totalcount=$(($totalcount + $filecount)) 728d20e303SChris Pressey done 738d20e303SChris Pressey ! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)" 748d20e303SChris Pressey} 758d20e303SChris Pressey 768d20e303SChris Pressey# rm_mail login 778d20e303SChris Pressey# Removes unix mail and pop daemon files belonging to the user 788d20e303SChris Pressey# specified in the $login argument. 798d20e303SChris Pressey# 808d20e303SChris Presseyrm_mail() { 818d20e303SChris Pressey # The argument is required 828d20e303SChris Pressey [ -n $1 ] && login=$1 || return 838d20e303SChris Pressey 848d20e303SChris Pressey verbose && echo -n "Removing mail spool(s) for ($login):" 858d20e303SChris Pressey if [ -f ${MAILSPOOL}/$login ]; then 868d20e303SChris Pressey verbose && echo -n " ${MAILSPOOL}/$login" || 878d20e303SChris Pressey echo -n " mailspool" 888d20e303SChris Pressey rm ${MAILSPOOL}/$login 898d20e303SChris Pressey fi 909ed5bb8aSSascha Wildner if [ -f ${MAILSPOOL}/.${login}.pop ]; then 919ed5bb8aSSascha Wildner verbose && echo -n " ${MAILSPOOL}/.${login}.pop" || 928d20e303SChris Pressey echo -n " pop3" 939ed5bb8aSSascha Wildner rm ${MAILSPOOL}/.${login}.pop 948d20e303SChris Pressey fi 958d20e303SChris Pressey verbose && echo '.' 968d20e303SChris Pressey} 978d20e303SChris Pressey 988d20e303SChris Pressey# kill_procs login 998d20e303SChris Pressey# Send a SIGKILL to all processes owned by $login. 1008d20e303SChris Pressey# 1018d20e303SChris Presseykill_procs() { 1028d20e303SChris Pressey # The argument is required 1038d20e303SChris Pressey [ -n $1 ] && login=$1 || return 1048d20e303SChris Pressey 1058d20e303SChris Pressey verbose && echo -n "Terminating all processes owned by ($login):" 1068d20e303SChris Pressey killcount=0 1078d20e303SChris Pressey proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'` 1088d20e303SChris Pressey for _pid in $proclist ; do 1098d20e303SChris Pressey kill 2>/dev/null ${SIGKILL} $_pid 1108d20e303SChris Pressey killcount=$(($killcount + 1)) 1118d20e303SChris Pressey done 1128d20e303SChris Pressey verbose && echo " ${SIGKILL} signal sent to $killcount processes." 1138d20e303SChris Pressey ! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})" 1148d20e303SChris Pressey} 1158d20e303SChris Pressey 1168d20e303SChris Pressey# rm_at_jobs login 1178d20e303SChris Pressey# Remove at (1) jobs belonging to $login. 1188d20e303SChris Pressey# 1198d20e303SChris Presseyrm_at_jobs() { 1208d20e303SChris Pressey # The argument is required 1218d20e303SChris Pressey [ -n $1 ] && login=$1 || return 1228d20e303SChris Pressey 1238d20e303SChris Pressey atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print` 1248d20e303SChris Pressey jobcount=0 1258d20e303SChris Pressey verbose && echo -n "Removing at(1) jobs owned by ($login):" 1268d20e303SChris Pressey for _atjob in $atjoblist ; do 1278d20e303SChris Pressey rm -f $_atjob 1288d20e303SChris Pressey jobcount=$(($jobcount + 1)) 1298d20e303SChris Pressey done 1308d20e303SChris Pressey verbose && echo " $jobcount removed." 1318d20e303SChris Pressey ! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)" 1328d20e303SChris Pressey} 1338d20e303SChris Pressey 1348d20e303SChris Pressey# rm_crontab login 1358d20e303SChris Pressey# Removes crontab file belonging to user $login. 1368d20e303SChris Pressey# 1378d20e303SChris Presseyrm_crontab() { 1388d20e303SChris Pressey # The argument is required 1398d20e303SChris Pressey [ -n $1 ] && login=$1 || return 1408d20e303SChris Pressey 1418d20e303SChris Pressey verbose && echo -n "Removing crontab for ($login):" 1428d20e303SChris Pressey if [ -f ${CRONJOBDIR}/$login ]; then 1438d20e303SChris Pressey verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab" 1448d20e303SChris Pressey rm -f ${CRONJOBDIR}/$login 1458d20e303SChris Pressey fi 1468d20e303SChris Pressey verbose && echo '.' 1478d20e303SChris Pressey} 1488d20e303SChris Pressey 1498d20e303SChris Pressey# rm_ipc login 1508d20e303SChris Pressey# Remove all IPC mechanisms which are owned by $login. 1518d20e303SChris Pressey# 1528d20e303SChris Presseyrm_ipc() { 1538d20e303SChris Pressey verbose && echo -n "Removing IPC mechanisms" 1548d20e303SChris Pressey for i in s m q; do 1558d20e303SChris Pressey ipcs -$i | 1568d20e303SChris Pressey awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' | 1578d20e303SChris Pressey xargs -n 1 ipcrm -$i 1588d20e303SChris Pressey done 1598d20e303SChris Pressey verbose && echo '.' 1608d20e303SChris Pressey} 1618d20e303SChris Pressey 1628d20e303SChris Pressey# rm_user login 1638d20e303SChris Pressey# Remove user $login from the system. This subroutine makes use 1648d20e303SChris Pressey# of the pw(8) command to remove a user from the system. The pw(8) 1658d20e303SChris Pressey# command will remove the specified user from the user database 1668d20e303SChris Pressey# and group file and remove any crontabs. His home 1678d20e303SChris Pressey# directory will be removed if it is owned by him and contains no 1688d20e303SChris Pressey# files or subdirectories owned by other users. Mail spool files will 1698d20e303SChris Pressey# also be removed. 1708d20e303SChris Pressey# 1718d20e303SChris Presseyrm_user() { 1728d20e303SChris Pressey # The argument is required 1738d20e303SChris Pressey [ -n $1 ] && login=$1 || return 1748d20e303SChris Pressey 1758d20e303SChris Pressey verbose && echo -n "Removing user ($login)" 1768d20e303SChris Pressey [ -n "$pw_rswitch" ] && { 1778d20e303SChris Pressey verbose && echo -n " (including home directory)" 1788d20e303SChris Pressey ! verbose && echo -n " home" 1798d20e303SChris Pressey } 1808d20e303SChris Pressey ! verbose && echo -n " passwd" 1818d20e303SChris Pressey verbose && echo -n " from the system:" 1829ed5bb8aSSascha Wildner ${PWCMD} userdel -n $login $pw_rswitch 1838d20e303SChris Pressey verbose && echo ' Done.' 1848d20e303SChris Pressey} 1858d20e303SChris Pressey 1868d20e303SChris Pressey# prompt_yesno msg 1878d20e303SChris Pressey# Prompts the user with a $msg. The answer is expected to be 1888d20e303SChris Pressey# yes, no, or some variation thereof. This subroutine returns 0 1898d20e303SChris Pressey# if the answer was yes, 1 if it was not. 1908d20e303SChris Pressey# 1918d20e303SChris Presseyprompt_yesno() { 1928d20e303SChris Pressey # The argument is required 1938d20e303SChris Pressey [ -n "$1" ] && msg="$1" || return 1948d20e303SChris Pressey 1958d20e303SChris Pressey while : ; do 1968d20e303SChris Pressey echo -n "$msg" 1978d20e303SChris Pressey read _ans 1988d20e303SChris Pressey case $_ans in 1998d20e303SChris Pressey [Nn][Oo]|[Nn]) 2008d20e303SChris Pressey return 1 2018d20e303SChris Pressey ;; 2028d20e303SChris Pressey [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 2038d20e303SChris Pressey return 0 2048d20e303SChris Pressey ;; 2058d20e303SChris Pressey *) 2068d20e303SChris Pressey ;; 2078d20e303SChris Pressey esac 2088d20e303SChris Pressey done 2098d20e303SChris Pressey} 2108d20e303SChris Pressey 2118d20e303SChris Pressey# show_usage 2128d20e303SChris Pressey# (no arguments) 2138d20e303SChris Pressey# Display usage message. 2148d20e303SChris Pressey# 2158d20e303SChris Presseyshow_usage() { 2168d20e303SChris Pressey echo "usage: ${THISCMD} [-yv] [-f file] [user ...]" 2178d20e303SChris Pressey echo " if the -y switch is used, either the -f switch or" 2188d20e303SChris Pressey echo " one or more user names must be given" 2198d20e303SChris Pressey} 2208d20e303SChris Pressey 2218d20e303SChris Pressey#### END SUBROUTINE DEFENITION #### 2228d20e303SChris Pressey 2238d20e303SChris Presseyprocowner= 2248d20e303SChris Presseypw_rswitch= 225*15a59a19SAaron LIffile= 226*15a59a19SAaron LIfflag= 2278d20e303SChris Presseyvflag= 228*15a59a19SAaron LIyflag= 2298d20e303SChris Pressey 2308d20e303SChris Presseyprocowner=`/usr/bin/id -u` 2318d20e303SChris Presseyif [ "$procowner" != "0" ]; then 2328d20e303SChris Pressey err 'you must be root (0) to use this utility.' 2338d20e303SChris Pressey exit 1 2348d20e303SChris Presseyfi 2358d20e303SChris Pressey 236*15a59a19SAaron LIwhile getopts :f:hvy opt; do 237*15a59a19SAaron LI case $opt in 238*15a59a19SAaron LI f) 239*15a59a19SAaron LI fflag=1 240*15a59a19SAaron LI ffile="$OPTARG" 241*15a59a19SAaron LI ;; 242*15a59a19SAaron LI h) 2438d20e303SChris Pressey show_usage 2448d20e303SChris Pressey exit 1 2458d20e303SChris Pressey ;; 246*15a59a19SAaron LI v) 2478d20e303SChris Pressey vflag=1 2488d20e303SChris Pressey ;; 249*15a59a19SAaron LI y) 250*15a59a19SAaron LI yflag=1 2518d20e303SChris Pressey ;; 252*15a59a19SAaron LI \?) 253*15a59a19SAaron LI err "Invalid option -${OPTARG}" 254*15a59a19SAaron LI show_usage 255*15a59a19SAaron LI exit 1 256*15a59a19SAaron LI ;; 257*15a59a19SAaron LI :) 258*15a59a19SAaron LI err "Option -${OPTARG} requires an argument" 259*15a59a19SAaron LI show_usage 260*15a59a19SAaron LI exit 1 2618d20e303SChris Pressey ;; 2628d20e303SChris Pressey esac 2638d20e303SChris Presseydone 2648d20e303SChris Pressey 265*15a59a19SAaron LIshift $((OPTIND - 1)) 266*15a59a19SAaron LI 2678d20e303SChris Pressey# Get user names from a file if the -f switch was used. Otherwise, 2688d20e303SChris Pressey# get them from the commandline arguments. If we're getting it 2698d20e303SChris Pressey# from a file, the file must be owned by and writable only by root. 2708d20e303SChris Pressey# 271*15a59a19SAaron LIif [ -n "$fflag" ]; then 2728d20e303SChris Pressey _insecure=`find $ffile ! -user 0 -or -perm +0022` 2738d20e303SChris Pressey if [ -n "$_insecure" ]; then 2748d20e303SChris Pressey err "file ($ffile) must be owned by and writeable only by root." 2758d20e303SChris Pressey exit 1 2768d20e303SChris Pressey fi 2778d20e303SChris Pressey if [ -r "$ffile" ]; then 2788d20e303SChris Pressey userlist=`cat $ffile | while read _user _junk ; do 2798d20e303SChris Pressey case $_user in 2808d20e303SChris Pressey \#*|'') 2818d20e303SChris Pressey ;; 2828d20e303SChris Pressey *) 283*15a59a19SAaron LI echo -n "$_user " 2848d20e303SChris Pressey ;; 2858d20e303SChris Pressey esac 2868d20e303SChris Pressey done` 287*15a59a19SAaron LI else 288*15a59a19SAaron LI err "($ffile) does not exist or does not contain any user names." 289*15a59a19SAaron LI exit 1 2908d20e303SChris Pressey fi 2918d20e303SChris Presseyelse 292*15a59a19SAaron LI userlist="$@" 2938d20e303SChris Presseyfi 2948d20e303SChris Pressey 295*15a59a19SAaron LI# If the -y switch has been used and the list of users to remove 2968d20e303SChris Pressey# is empty it is a fatal error. Otherwise, prompt the user for a list 2978d20e303SChris Pressey# of one or more user names. 2988d20e303SChris Pressey# 299*15a59a19SAaron LIuserlist=`echo "$userlist" | sed -e 's/[[:space:]]*$//'` 300*15a59a19SAaron LIif [ -z "$userlist" ]; then 301*15a59a19SAaron LI if [ -n "$yflag" ]; then 3028d20e303SChris Pressey show_usage 3038d20e303SChris Pressey exit 1 3048d20e303SChris Pressey else 3059ed5bb8aSSascha Wildner echo -n "Please enter one or more user names: " 3068d20e303SChris Pressey read userlist 3078d20e303SChris Pressey fi 3088d20e303SChris Presseyfi 3098d20e303SChris Pressey 3108d20e303SChris Pressey_user= 3118d20e303SChris Pressey_uid= 3128d20e303SChris Presseyfor _user in $userlist ; do 3138d20e303SChris Pressey # Make sure the name exists in the passwd database and that it 3148d20e303SChris Pressey # does not have a uid of 0 3158d20e303SChris Pressey # 3169ed5bb8aSSascha Wildner userrec=`${PWCMD} 2>/dev/null usershow -n $_user` 3178d20e303SChris Pressey if [ "$?" != "0" ]; then 3188d20e303SChris Pressey err "user ($_user) does not exist in the password database." 3198d20e303SChris Pressey continue 3208d20e303SChris Pressey fi 3218d20e303SChris Pressey _uid=`echo $userrec | awk -F: '{print $3}'` 3228d20e303SChris Pressey if [ "$_uid" = "0" ]; then 3238d20e303SChris Pressey err "user ($_user) has uid 0. You may not remove this user." 3248d20e303SChris Pressey continue 3258d20e303SChris Pressey fi 3268d20e303SChris Pressey 3278d20e303SChris Pressey # If the -y switch was not used ask for confirmation to remove the 3288d20e303SChris Pressey # user and home directory. 3298d20e303SChris Pressey # 3308d20e303SChris Pressey if [ -z "$yflag" ]; then 3318d20e303SChris Pressey echo "Matching password entry:" 3328d20e303SChris Pressey echo 3338d20e303SChris Pressey echo $userrec 3348d20e303SChris Pressey echo 3358d20e303SChris Pressey if ! prompt_yesno "Is this the entry you wish to remove? " ; then 3368d20e303SChris Pressey continue 3378d20e303SChris Pressey fi 3388d20e303SChris Pressey _homedir=`echo $userrec | awk -F: '{print $9}'` 3398d20e303SChris Pressey if prompt_yesno "Remove user's home directory ($_homedir)? "; then 3408d20e303SChris Pressey pw_rswitch="-r" 3418d20e303SChris Pressey fi 3428d20e303SChris Pressey else 3438d20e303SChris Pressey pw_rswitch="-r" 3448d20e303SChris Pressey fi 3458d20e303SChris Pressey 3468d20e303SChris Pressey # Disable any further attempts to log into this account 3479ed5bb8aSSascha Wildner ${PWCMD} 2>/dev/null lock $_user 3488d20e303SChris Pressey 3498d20e303SChris Pressey # Remove crontab, mail spool, etc. Then obliterate the user from 3508d20e303SChris Pressey # the passwd and group database. 3518d20e303SChris Pressey # 3528d20e303SChris Pressey ! verbose && echo -n "Removing user ($_user):" 3538d20e303SChris Pressey rm_crontab $_user 3548d20e303SChris Pressey rm_at_jobs $_user 3558d20e303SChris Pressey rm_ipc $_user 3568d20e303SChris Pressey kill_procs $_user 3578d20e303SChris Pressey rm_files $_user 3588d20e303SChris Pressey rm_mail $_user 3598d20e303SChris Pressey rm_user $_user 3608d20e303SChris Pressey ! verbose && echo "." 3618d20e303SChris Presseydone 362