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