#!/bin/sh # # Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Email: Mike Makonnen # # $FreeBSD: stable/11/usr.sbin/adduser/rmuser.sh 181006 2008-07-30 18:37:21Z jhb $ # setglobal ATJOBDIR = '"/var/at/jobs'" setglobal CRONJOBDIR = '"/var/cron/tabs'" setglobal MAILSPOOL = '"/var/mail'" setglobal SIGKILL = '"-KILL'" setglobal TEMPDIRS = '"/tmp /var/tmp'" setglobal THISCMD = $[/usr/bin/basename $0] setglobal PWCMD = $(PWCMD:-/usr/sbin/pw) # err msg # Display $msg on stderr. # proc err { echo 1>&2 $(THISCMD): $ifsjoin(Argv)!1 > !2 ${THISCMD}: $* } # verbose # Returns 0 if verbose mode is set, 1 if it is not. # proc verbose { test -n $vflag && return 0 || return 1 } # rm_files login # Removes files or empty directories belonging to $login from various # temporary directories. # proc rm_files { # The argument is required test -n $1 && setglobal login = $1 || return setglobal totalcount = '0' for _dir in [$(TEMPDIRS)] { setglobal filecount = '0' if test ! -d $_dir { err "$_dir is not a valid directory." continue } verbose && echo -n "Removing files owned by ($login) in $_dir:" setglobal filecount = $[find 2>/dev/null $_dir -user $login -delete -print!2 >/dev/null "$_dir" -user "$login" -delete -print | wc -l | sed 's/ *//] verbose && echo " $filecount removed." setglobal totalcount = $shExpr('$totalcount + $filecount') } ! verbose && test $totalcount -ne 0 && echo -n " files($totalcount)" } # rm_mail login # Removes unix mail and pop daemon files belonging to the user # specified in the $login argument. # proc rm_mail { # The argument is required test -n $1 && setglobal login = $1 || return verbose && echo -n "Removing mail spool(s) for ($login):" if test -f $(MAILSPOOL)/$login { verbose && echo -n " $(MAILSPOOL)/$login" || echo -n " mailspool" rm $(MAILSPOOL)/$login } if test -f $(MAILSPOOL)/.$(login).pop { verbose && echo -n " $(MAILSPOOL)/.$(login).pop" || echo -n " pop3" rm $(MAILSPOOL)/.$(login).pop } verbose && echo '.' } # kill_procs login # Send a SIGKILL to all processes owned by $login. # proc kill_procs { # The argument is required test -n $1 && setglobal login = $1 || return verbose && echo -n "Terminating all processes owned by ($login):" setglobal killcount = '0' setglobal proclist = $[ps 2>/dev/null -U $login!2 >/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}] for _pid in [$proclist] { kill 2>/dev/null $(SIGKILL) $_pid!2 >/dev/null ${SIGKILL} $_pid setglobal killcount = $shExpr('$killcount + 1') } verbose && echo " $(SIGKILL) signal sent to $killcount processes." ! verbose && test $killcount -ne 0 && echo -n " processes($(killcount))" } # rm_at_jobs login # Remove at (1) jobs belonging to $login. # proc rm_at_jobs { # The argument is required test -n $1 && setglobal login = $1 || return setglobal atjoblist = $[find 2>/dev/null $(ATJOBDIR) -maxdepth 1 -user $login -print!2 >/dev/null] setglobal jobcount = '0' verbose && echo -n "Removing at(1) jobs owned by ($login):" for _atjob in [$atjoblist] { rm -f $_atjob setglobal jobcount = $shExpr('$jobcount + 1') } verbose && echo " $jobcount removed." ! verbose && test $jobcount -ne 0 && echo -n " at($jobcount)" } # rm_crontab login # Removes crontab file belonging to user $login. # proc rm_crontab { # The argument is required test -n $1 && setglobal login = $1 || return verbose && echo -n "Removing crontab for ($login):" if test -f $(CRONJOBDIR)/$login { verbose && echo -n " $(CRONJOBDIR)/$login" || echo -n " crontab" rm -f $(CRONJOBDIR)/$login } verbose && echo '.' } # rm_ipc login # Remove all IPC mechanisms which are owned by $login. # proc rm_ipc { verbose && echo -n "Removing IPC mechanisms" for i in [s m q] { ipcs -$i | awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' | xargs -n 1 ipcrm -$i } verbose && echo '.' } # rm_user login # Remove user $login from the system. This subroutine makes use # of the pw(8) command to remove a user from the system. The pw(8) # command will remove the specified user from the user database # and group file and remove any crontabs. His home # directory will be removed if it is owned by him and contains no # files or subdirectories owned by other users. Mail spool files will # also be removed. # proc rm_user { # The argument is required test -n $1 && setglobal login = $1 || return verbose && echo -n "Removing user ($login)" test -n $pw_rswitch && do { verbose && echo -n " (including home directory)" ! verbose && echo -n " home" } ! verbose && echo -n " passwd" verbose && echo -n " from the system:" $(PWCMD) userdel -n $login $pw_rswitch verbose && echo ' Done.' } # prompt_yesno msg # Prompts the user with a $msg. The answer is expected to be # yes, no, or some variation thereof. This subroutine returns 0 # if the answer was yes, 1 if it was not. # proc prompt_yesno { # The argument is required test -n $1 && setglobal msg = $1 || return while : { echo -n $msg read _ans match $_ans { with [Nn][Oo]|[Nn] return 1 with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] return 0 with * } } } # show_usage # (no arguments) # Display usage message. # proc show_usage { echo "usage: $(THISCMD) [-yv] [-f file] [user ...]" echo " if the -y switch is used, either the -f switch or" echo " one or more user names must be given" } #### END SUBROUTINE DEFENITION #### setglobal ffile = '' setglobal fflag = '' setglobal procowner = '' setglobal pw_rswitch = '' setglobal userlist = '' setglobal yflag = '' setglobal vflag = '' setglobal procowner = $[/usr/bin/id -u] if test $procowner != "0" { err 'you must be root (0) to use this utility.' exit 1 } setglobal args = $[getopt 2>/dev/null yvf: $ifsjoin(Argv)!2 >/dev/null] if test "$Status" != "0" { show_usage exit 1 } set -- $argsfor _switch in @Argv { match $_switch { with -y setglobal yflag = '1' shift with -v setglobal vflag = '1' shift with -f setglobal fflag = '1' setglobal ffile = $2 shift; shift with -- shift break } } # Get user names from a file if the -f switch was used. Otherwise, # get them from the commandline arguments. If we're getting it # from a file, the file must be owned by and writable only by root. # if test $fflag { setglobal _insecure = $[find $ffile ! -user 0 -or -perm +0022] if test -n $_insecure { err "file ($ffile) must be owned by and writeable only by root." exit 1 } if test -r $ffile { setglobal userlist = $[cat $ffile | while read _user _junk { match $_user { with \#*|'' with * echo -n "$userlist $_user" } }] } } else { while test $1 { setglobal userlist = ""$userlist $1"" shift } } # If the -y or -f switch has been used and the list of users to remove # is empty it is a fatal error. Otherwise, prompt the user for a list # of one or more user names. # if test ! $userlist { if test $fflag { err "($ffile) does not exist or does not contain any user names." exit 1 } elif test $yflag { show_usage exit 1 } else { echo -n "Please enter one or more usernames: " read userlist } } setglobal _user = '' setglobal _uid = '' for _user in [$userlist] { # Make sure the name exists in the passwd database and that it # does not have a uid of 0 # setglobal userrec = $[pw 2>/dev/null usershow -n $_user!2 >/dev/null] if test "$Status" != "0" { err "user ($_user) does not exist in the password database." continue } setglobal _uid = $[echo $userrec | awk -F: '{print $3}] if test $_uid = "0" { err "user ($_user) has uid 0. You may not remove this user." continue } # If the -y switch was not used ask for confirmation to remove the # user and home directory. # if test -z $yflag { echo "Matching password entry:" echo echo $userrec echo if ! prompt_yesno "Is this the entry you wish to remove? " { continue } setglobal _homedir = $[echo $userrec | awk -F: '{print $9}] if prompt_yesno "Remove user's home directory ($_homedir)? " { setglobal pw_rswitch = '"-r'" } } else { setglobal pw_rswitch = '"-r'" } # Disable any further attempts to log into this account $(PWCMD) 2>/dev/null lock $_user!2 >/dev/null lock $_user # Remove crontab, mail spool, etc. Then obliterate the user from # the passwd and group database. # ! verbose && echo -n "Removing user ($_user):" rm_crontab $_user rm_at_jobs $_user rm_ipc $_user kill_procs $_user rm_files $_user rm_mail $_user rm_user $_user ! verbose && echo "." }