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