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