]>
git.cameronkatri.com Git - pw-darwin.git/blob - adduser/rmuser.perl
3 # Copyright 1995, 1996, 1997 Guy Helmer, Ames, Iowa 50014.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer as
11 # the first lines of this file unmodified.
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 # 3. The name of the author may not be used to endorse or promote products
16 # derived from this software without specific prior written permission.
18 # THIS SOFTWARE IS PROVIDED BY GUY HELMER ``AS IS'' AND ANY EXPRESS OR
19 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 # IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 # rmuser - Perl script to remove users
31 # Guy Helmer <ghelmer@cs.iastate.edu>, 02/23/97
43 $ENV { "PATH" } = "/bin:/sbin:/usr/bin:/usr/sbin" ;
46 $passwd_file = "/etc/master.passwd" ;
47 $passwd_tmp = "/etc/ptmp" ;
48 $group_file = "/etc/group" ;
49 $new_group_file = "${group_file}.new. $$ " ;
50 $mail_dir = "/var/mail" ;
51 $crontab_dir = "/var/cron/tabs" ;
59 print STDERR
"Caught signal SIG $sig -- cleaning up. \n " ;
61 if (- e
$new_passwd_file ) {
62 unlink $new_passwd_file ;
68 # Open the password file for reading
69 if (! open ( MASTER_PW
, " $passwd_file " )) {
70 print STDERR
"${whoami}: Error: Couldn't open ${passwd_file}: $! \n " ;
73 # Set the close-on-exec flag just in case
74 fcntl ( MASTER_PW
, & F_SETFD
, 1 );
75 # Apply an advisory lock the password file
76 if (! flock ( MASTER_PW
, & LOCK_EX
|& LOCK_NB
)) {
77 print STDERR
"${whoami}: Error: Couldn't lock ${passwd_file}: $! \n " ;
83 flock ( MASTER_PW
, & LOCK_UN
);
86 $SIG { 'INT' } = 'cleanup' ;
87 $SIG { 'QUIT' } = 'cleanup' ;
88 $SIG { 'HUP' } = 'cleanup' ;
89 $SIG { 'TERM' } = 'cleanup' ;
91 if ( $#ARGV == 1 && $ARGV [ 0 ] eq '-y' ) {
97 print STDERR
"usage: ${whoami} [-y] [username] \n " ;
102 print STDERR
"${whoami}: Error: you must be root to use ${whoami} \n " ;
109 # Username was given as a parameter
110 $login_name = pop ( @ARGV );
113 print STDERR
"${whoami}: Error: -y option given without username! \n " ;
117 # Get the user name from the user
118 $login_name = & get_login_name
;
121 ( $name , $password , $uid , $gid , $change , $class , $gecos , $home_dir , $shell ) =
122 ( getpwnam ( " $login_name " ));
125 print STDERR
"${whoami}: Error: User ${login_name} not in password database \n " ;
131 print "${whoami}: Error: I'd rather not remove a user with a uid of 0. \n " ;
137 print "Matching password entry: \n\n $name \: $password \: $uid \: $gid \: $class \: $change \:0\: $gecos \: $home_dir \: $shell \n\n " ;
139 $ans = & get_yn
( "Is this the entry you wish to remove? " );
142 print "${whoami}: Informational: User ${login_name} not removed. \n " ;
149 # Get owner of user's home directory; don't remove home dir if not
150 # owned by $login_name
152 $remove_directory = 1 ;
155 $real_home_dir = & resolvelink
( $home_dir );
157 $real_home_dir = $home_dir ;
161 # If home_dir is a symlink and points to something that isn't a directory,
162 # or if home_dir is not a symlink and is not a directory, don't remove
163 # home_dir -- seems like a good thing to do, but probably isn't necessary...
165 if (((- l
$home_dir ) && ((- e
$real_home_dir ) && !(- d
$real_home_dir ))) ||
166 (!(- l
$home_dir ) && !(- d
$home_dir ))) {
167 print STDERR
"${whoami}: Informational: Home ${home_dir} is not a directory, so it won't be removed \n " ;
168 $remove_directory = 0 ;
171 if ( length ( $real_home_dir ) && - d
$real_home_dir ) {
172 $dir_owner = ( stat ( $real_home_dir ))[ 4 ]; # UID
173 if ( $dir_owner != $uid ) {
174 print STDERR
"${whoami}: Informational: Home dir ${real_home_dir} is" .
175 " not owned by ${login_name} (uid ${dir_owner}) \n ," .
176 " \t so it won't be removed \n " ;
177 $remove_directory = 0 ;
181 if ( $remove_directory && ! $affirm ) {
182 $ans = & get_yn
( "Remove user's home directory ( $home_dir )? " );
184 $remove_directory = 0 ;
191 # Remove the user's crontab, if there is one
192 # (probably needs to be done before password databases are updated)
194 if (- e
" $crontab_dir/$login_name " ) {
195 print STDERR
"Removing user's crontab:" ;
196 system ( '/usr/bin/crontab' , '-u' , $login_name , '-r' );
197 print STDERR
" done. \n " ;
201 # Remove the user's at jobs, if any
202 # (probably also needs to be done before password databases are updated)
204 & remove_at_jobs
( $login_name );
207 # Kill all the user's processes
209 & kill_users_processes
( $login_name , $uid );
212 # Copy master password file to new file less removed user's entry
217 # Remove the user from all groups in /etc/group
219 & update_group_file
( $login_name );
222 # Remove the user's home directory
224 if ( $remove_directory ) {
225 print STDERR
"Removing user's home directory ( $home_dir ):" ;
226 & remove_dir
( $home_dir );
227 print STDERR
" done. \n " ;
231 # Remove files related to the user from the mail directory
233 #&remove_files_from_dir($mail_dir, $login_name, $uid);
234 $file = " $mail_dir/$login_name " ;
235 if (- e
$file || - l
$file ) {
236 print STDERR
"Removing user's incoming mail file ${file}:" ;
238 print STDERR
" \n ${whoami}: Warning: unlink on $file failed ( $! ) - continuing \n " ;
239 print STDERR
" done. \n " ;
243 # Remove some pop daemon's leftover file
245 $file = " $mail_dir/ .${login_name}.pop" ;
246 if (- e
$file || - l
$file ) {
247 print STDERR
"Removing pop daemon's temporary mail file ${file}:" ;
249 print STDERR
" \n ${whoami}: Warning: unlink on $file failed ( $! ) - continuing \n " ;
250 print STDERR
" done. \n " ;
254 # Remove files belonging to the user from the directories /tmp, /var/tmp,
255 # and /var/tmp/vi.recover. Note that this doesn't take care of the
256 # problem where a user may have directories or symbolic links in those
257 # directories -- only regular files are removed.
259 & remove_files_from_dir
( '/tmp' , $login_name , $uid );
260 & remove_files_from_dir
( '/var/tmp' , $login_name , $uid );
261 & remove_files_from_dir
( '/var/tmp/vi.recover' , $login_name , $uid )
262 if (- e
'/var/tmp/vi.recover' );
271 # Get new user's name
272 local ( $done , $login_name );
274 for ( $done = 0 ; ! $done ; ) {
275 print "Enter login name for user to remove: " ;
278 if ( not getpwnam ( " $login_name " )) {
279 print STDERR
"Sorry, login name not in password database. \n " ;
285 print "User name is ${login_name} \n " if $debug ;
291 # Get a yes or no answer; return 'Y' or 'N'
295 for ( $done = 0 ; ! $done ; ) {
300 if (!( $ans =~ /^[YN]/ )) {
301 print STDERR
"Please answer (y)es or (n)o. \n " ;
307 return ( substr ( $ans , 0 , 1 ));
310 sub update_passwd_file
{
313 print STDERR
"Updating password file," ;
314 seek ( MASTER_PW
, 0 , 0 );
316 sysopen ( NEW_PW
, $passwd_tmp , O_RDWR
| O_CREAT
| O_EXCL
, 0600 ) ||
317 die " \n ${whoami}: Error: Couldn't open file ${passwd_tmp}: \n $! \n " ;
320 while (< MASTER_PW
>) {
321 if ( /^\Q$login_name:/o ) {
322 print STDERR
"Dropped entry for $login_name \n " if $debug ;
326 # The other perl password tools assume all lowercase entries.
327 # Add a warning to help unsuspecting admins who might be
328 # using the wrong tool for the job, or might otherwise
329 # be unwittingly holding a loaded foot-shooting device.
330 if ( /^\Q$login_name:/io ) {
333 print STDERR
" \n\n\t There is also an entry for $name in your" ,
334 "password file. \n\t This can cause problems in some " ,
340 seek ( MASTER_PW
, 0 , 0 );
343 print STDERR
" \n ${whoami}: Whoops! Didn't find ${login_name}'s entry second time around! \n " ;
344 unlink ( $passwd_tmp ) ||
345 print STDERR
" \n ${whoami}: Warning: couldn't unlink $passwd_tmp ( $! ) \n\t Please investigate, as this file should not be left in the filesystem \n " ;
351 # Run pwd_mkdb to install the updated password files and databases
353 print STDERR
" updating databases," ;
354 system ( '/usr/sbin/pwd_mkdb' , '-p' , ${ passwd_tmp
});
355 print STDERR
" done. \n " ;
357 close ( MASTER_PW
); # Not useful anymore
360 sub update_group_file
{
361 local ( $login_name ) = @_ ;
363 local ( $i , $j , $grmember_list , $new_grent , $changes );
364 local ( $grname , $grpass , $grgid , $grmember_list , @grmembers );
367 print STDERR
"Updating group file:" ;
368 open ( GROUP
, $group_file ) ||
369 die " \n ${whoami}: Error: couldn't open ${group_file}: $! \n " ;
370 if (! flock ( GROUP
, & LOCK_EX
|& LOCK_NB
)) {
371 print STDERR
" \n ${whoami}: Error: couldn't lock ${group_file}: $! \n " ;
374 local ( $group_perms , $group_uid , $group_gid ) =
375 ( stat ( GROUP
))[ 2 , 4 , 5 ]; # File Mode, uid, gid
376 open ( NEW_GROUP
, "> $new_group_file " ) ||
377 die " \n ${whoami}: Error: couldn't open ${new_group_file}: $! \n " ;
378 chmod ( $group_perms , $new_group_file ) ||
379 printf STDERR
" \n ${whoami}: Warning: could not set permissions of new group file to %o ( $! ) \n\t Continuing, but please check permissions of $group_file! \n " , $group_perms ;
380 chown ( $group_uid , $group_gid , $new_group_file ) ||
381 print STDERR
" \n ${whoami}: Warning: could not set owner/group of new group file to ${group_uid}/${group_gid} ( $! ) \n\r Continuing, but please check ownership of $group_file! \n " ;
382 while ( $i = < GROUP
>) {
383 if (!( $i =~ /\Q$login_name\E/ )) {
384 # Line doesn't contain any references to the user, so just add it
389 # Remove the user from the group
393 ( $grname , $grpass , $grgid , $grmember_list ) = split ( /:/ , $i );
394 @grmembers = split ( /,/ , $grmember_list );
395 undef @new_grmembers ;
396 local ( @new_grmembers );
397 foreach $j ( @grmembers ) {
398 if ( $j ne $login_name ) {
399 push ( @new_grmembers , $j );
401 print STDERR
" $grname " ;
405 if ( $grname eq $login_name && $#new_grmembers == - 1 ) {
406 # Remove a user's personal group if empty
407 print STDERR
" (removing group $grname -- personal group is empty)" ;
410 $grmember_list = join ( ',' , @new_grmembers );
411 $new_grent = join ( ':' , $grname , $grpass , $grgid , $grmember_list );
412 print NEW_GROUP
" $new_grent \n " ;
417 rename ( $new_group_file , $group_file ) || # Replace old group file with new
418 die " \n ${whoami}: Error: couldn't rename $new_group_file to $group_file ( $! ) \n " ;
419 close ( GROUP
); # File handle is worthless now
420 print STDERR
" (no changes)" if (! $changes );
421 print STDERR
" done. \n " ;
425 # Remove the user's home directory
430 $linkdir = & resolvelink
( $dir );
431 # Remove the symbolic link
433 warn "${whoami}: Warning: could not unlink symlink $dir : $! \n " ;
434 if (!(- e
$linkdir )) {
436 # Dangling symlink - just return now
439 # Set dir to be the resolved pathname
443 print STDERR
"${whoami}: Warning: $dir is not a directory \n " ;
444 unlink ( $dir ) || warn "${whoami}: Warning: could not unlink $dir : $! \n " ;
447 system ( '/bin/rm' , '-rf' , $dir );
450 sub remove_files_from_dir
{
451 local ( $dir , $login_name , $uid ) = @_ ;
452 local ( $path , $i , $owner );
454 print STDERR
"Removing files belonging to ${login_name} from ${dir}:" ;
456 if (! opendir ( DELDIR
, $dir )) {
457 print STDERR
" \n ${whoami}: Warning: couldn't open directory ${dir} ( $! ) \n " ;
460 while ( $i = readdir ( DELDIR
)) {
464 $owner = ( stat ( " $dir/$i " ))[ 4 ]; # UID
465 if ( $uid == $owner ) {
469 print STDERR
" \n ${whoami}: Warning: unlink on ${dir}/${i} failed ( $! ) - continuing \n " ;
471 print STDERR
" ( $i not a regular file - skipped)" ;
477 printf STDERR
" done. \n " ;
483 my ( $user ) = ( shift || "" );
484 my ( $path_atq ) = "/usr/bin/atq" ;
488 return @at if ( $user eq "" );
490 if (! defined ( $pid = open ( ATQ
, "-|" ))) {
491 die ( "creating pipe to atq: $! \n " );
492 } elsif ( $pid == 0 ) {
493 exec ( $path_atq , $user );
494 die ( "executing $path_atq : $! \n " );
497 while ( defined ( $_ = < ATQ
>)) {
499 if ( /^\d\d.\d\d.\d\d\s+\d\d.\d\d.\d\d\s+(\S+)\s+\S+\s+(\d+)$/ ) {
500 push ( @at , $2 ) if ( $1 eq $user );
509 my ( $user ) = ( shift || "" );
510 my ( $path_atrm ) = "/usr/bin/atrm" ;
515 return "Invalid arguments" if (( $user eq "" ) || ( $#jobs == - 1 ));
517 if (! defined ( $pid = open ( ATRM
, "-|" ))) {
518 die ( "creating pipe to atrm: $! \n " );
519 } elsif ( $pid == 0 ) {
520 exec ( $path_atrm , $user , @jobs );
523 while ( defined ( $_ = < ATRM
>)) {
531 my ( $user ) = ( shift || "" );
534 return 1 if ( $user eq "" );
536 @at = invoke_atq
( $user );
537 return 0 if ( $#at == - 1 );
539 print STDERR
"Removing user's at jobs:" ;
540 print STDERR
" @at :" ;
541 $atrm = invoke_atrm
( $user , @at );
543 print STDERR
" -- $atrm \n " ;
547 print STDERR
" done. \n " ;
555 while (- l
$path && - e
$path ) {
556 if (! defined ( $l = readlink ( $path ))) {
557 die "${whoami}: readlink on $path failed (but it should have worked!): $! \n " ;
564 $path =~ s/\/[^\/ ]+ \
/?$/ \
/$l/ ; # Replace last component of path
570 sub kill_users_processes
{
571 local ( $login_name , $uid ) = @_ ;
572 local ( $pid , $result );
575 # Do something a little complex: fork a child that changes its
576 # real and effective UID to that of the removed user, then issues
577 # a "kill(9, -1)" to kill all processes of the same uid as the sender
578 # (see kill(2) for details).
579 # The parent waits for the exit of the child and then returns.
584 } elsif ( defined $pid ) {
588 if ($< != $uid || $> != $uid ) {
589 print STDERR
"${whoami}: Error (kill_users_processes): \n " .
590 " \t Couldn't reset uid/euid to ${uid}: current uid/euid's are $< and $> \n " ;
593 $result = kill ( 9 , - 1 );
594 print STDERR
"Killed process(es) belonging to $login_name . \n "
599 print STDERR
"${whoami}: Error: couldn't fork to kill ${login_name}'s processes - continuing \n " ;