Article 2827 of comp.lang.perl: Xref: feenix.metronet.com comp.lang.perl:2827 Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.utdallas.edu!tamsun.tamu.edu!cs.utexas.edu!asuvax!ncar!tres From: tres@rap.ucar.edu (Tres Hofmeister) Subject: Re: SOURCE: change UIDs thoughout your filesystems Message-ID: <1993May17.215055.3313@ncar.ucar.edu> Sender: news@ncar.ucar.edu (USENET Maintenance) Organization: NCAR, Research Applications Program References: <1993May17.171218.24669@imagen.com> Date: Mon, 17 May 1993 21:50:55 GMT Lines: 286 In article <1993May17.171218.24669@imagen.com> pomeranz@imagen.com (Hal Pomeranz) writes: > I had the fun of changing UIDs on my systems last week, so I wrote > the following little proglet to descend through my directories and > change ownerships everywhere. The script expects one argument which > is the name of a file containing lines with the old UID and the UID > that it's changing to, separated by whitespace (one pair per line). > There's an optional second argument if you want to start somewhere > other than the root. The script will not descend into NFS filesystems. In article <1993Apr27.074925.26400@colorado.edu> tchrist@convex.COM (Tom Christiansen) writes: > From the keyboard of david@ccc.govt.nz: > : P.S. How do I learn perl, where do I start? > > Read other people's code. Understand it. Copy it. Change it. > --tom With Tom's advice in mind, here's a modified version of his `mvids' script, which performs the same function as Hal's, and more. -- Tres Hofmeister tres@ncar.ucar.edu #!/local/bin/perl # mvids: "moves" uids and gids # Tom Christiansen # Modified by: Tres Hofmeister # 3/29/93 # Use find.pl and `lstat' rather than reading a pipe from find(1): # For better portability, as not all finds support -fstype or -ls; # indexing into the find string breaks on device special files and # filenames containing whitespace; `split' was breaking up the # string inconsistently because of leading whitespace (using # split(' ') and changing the indexing could also fix this). # Fixed the non-existent user/group tests, which were using the wrong # array names; # Allow negative uid/gid's. # Delete temporary files if -n is specified. # Changed the name of the default directions file to `mvids.cf'. # Various minor or cosmetic changes, including addition of comments. # # Usage: mvids [-n] [-d] [-f dfile] [startdir] # # Takes a file of new user and group id's, creates and installs new # passwd and group files, and traverses local filesystems starting with # startdir (default is `/'), updating all changed id's. # # Read descriptions from `mvids.cf' file with format: # type name number # e.g.: # user tom 1023 # group staff 200 # # Options: # -n Find files and create the new passwd and group files, but don't # actually change anything. # -f Specify an alternate description file than the default `mvids.cf'. # -d Use the passwd and group files in the current directory. require 'find.pl'; require 'getopts.pl'; $| = 1; $oops = 0; ($prog = $0) =~ s#.*/##; &Getopts('dnf:'); $FILE = $opt_f || "mvids.cf"; $DIR = $opt_d ? "." : "/etc"; $topdir = shift || '/'; die "Usage: $prog [-n] [-d] [-f dfile] [startdir]\n" if $#ARGV > -1; die "$topdir: Not a directory" unless -d $topdir; # Process directions file. open FILE || die "Can't open directions file \"$FILE\": $!\n"; while () { s/\s*#.*//; next if /^$/; unless (/^(user|group)\s+(\w+)\s+(\d+)/) { print STDERR "malformed line at line $. of $FILE: $_"; $oops++; next; } if ($3 > 32767) { print STDERR "$1 $2 has id that's too big ($3)\n"; $oops++; next; } if ($3 == 0) { print STDERR "Too dangerous to move $1 $2 to 0\n"; $oops++; next; } if ($2 eq 'root') { print STDERR "You don't really want to move root!\n"; $oops++; $next; } if ($1 eq 'user') { if (defined $n_pwn2i{$2}) { print STDERR "Saw user $2 again at line $. of $FILE\n"; $oops++; next; } if (defined $n_pwi2n{$3}) { print STDERR "Saw uid $3 again at line $. of $FILE\n"; $oops++; next; } $uids++; # Build %n_pwn2i and %n_pwi2n. $n_pwn2i{$2} = $3; $n_pwi2n{$3} = $2; } else { if (defined $n_grn2i{$2}) { print STDERR "Saw group $2 again at line $. of $FILE\n"; $oops++; next; } if (defined $n_gri2n{$3}) { print STDERR "Saw gid $3 again at line $. of $FILE\n"; $oops++; next; } $gids++; # Build %n_grn2i and %n_gri2n. $n_grn2i{$2} = $3; $n_gri2n{$3} = $2; } } # Process the existing passwd file, build the new one. if ($uids) { $PWD = "$DIR/passwd"; $NPWD = "$DIR/passwd.new"; open PWD || die "Can't open $PWD: $!\n"; open (NPWD, ">$NPWD") || die "Can't create $NPWD: $!\n"; while () { ((($name, $uid) = /^(\w+):[^:]*:(-?[\d]+):/)) || die "Bad passwd entry at line $.\n"; if (defined $n_pwi2n{$uid} && !defined $n_pwn2i{$name}) { printf STDERR "Can't move user %s to uid %d, %s already has it\n", $n_pwi2n{$uid}, $uid, $name; $oops++; next; } # Build %pwn2i. $pwn2i{$name} = $uid; # Edit the current line if necessary. s/:$uid:/:$n_pwn2i{$name}:/ if defined $n_pwn2i{$name}; print NPWD; } close PWD; close NPWD; foreach $user (keys %n_pwn2i) { unless (defined $pwn2i{$user}) { print STDERR "Can't move non-existent user $user\n"; $oops++; } } } # Process the existing group file, build the new one. if ($gids) { $GRP = "$DIR/group"; $NGRP = "$DIR/group.new"; open GRP || die "Can't open $GRP: $!\n"; open (NGRP, ">$NGRP") || die "Can't create $NGRP: $!\n"; while () { ((($name, $gid) = /^(\w+):[^:]*:(-?[\d]+):/)) || die "Bad group entry at line $.\n"; if (defined $n_gri2n{$gid} && !defined $n_grn2i{$name}) { printf STDERR "Can't move gid %s to %d, %s already has it\n", $n_gri2n{$gid}, $gid, $name; $oops++; next; } # Build %grn2i. $grn2i{$name} = $gid; # Edit the current line if necessary. s/:$gid:/:$n_grn2i{$name}:/ if defined $n_grn2i{$name}; print NGRP; } close GRP; close NGRP; foreach $group (keys %n_grn2i) { unless (defined $grn2i{$group}) { print STDERR "Can't move non-existent group $group\n"; $oops++; } } } # Exit if there were errors processing files, or if there's nothing to do. die "$prog: no ids to move\n" unless $uids || $gids; die "$prog: $oops error" . ($oops > 1 ? "s" : "") . " in remapping directions.\n" if $oops; # Ok, now do it. # Build %pwi2n from %pwn2i. foreach $key (keys %pwn2i) { $pwi2n{$pwn2i{$key}} = $key; } # Build %gri2n from %grn2i. foreach $key (keys %grn2i) { $gri2n{$grn2i{$key}} = $key; } &find("$topdir"); sub wanted { # Called by &find. $name contains the current pathname, # $_ contains the filename component of the pathname. # Prune NFS filesystems. unless ((($dev, $user, $group) = (lstat($_))[0,4,5]) && $dev < 0 && ($prune = 1)) { $uid = $gid = -1; $file = $name; # Convert numeric id's to names. $user = $pwi2n{$user}; $group = $gri2n{$group}; # If this file is owned by a user to be changed... if (defined $n_pwn2i{$user}) { $uid = $n_pwn2i{$user}; print "changing owner $user of $file from ", "$pwn2i{$user} to $n_pwn2i{$user}\n"; } # If this file is in a group to be changed... if (defined $n_grn2i{$group}) { $gid = $n_grn2i{$group}; print "changing group $group of $file from ", "$grn2i{$group} to $n_grn2i{$group}\n"; } # Change the uid and/or gid of the file. If both are still -1, # this file doesn't need changing. Passing chown -1 for the # uid or gid leaves it unchanged. if (!$opt_n && ($uid != -1 || $gid != -1)) { if (!chown $uid, $gid, $_) { print STDERR "couldn't chown $file to $uid.$gid: $!\n"; $oops++; } } } } # Install the new passwd and group files... unless ($opt_n) { if ($uids) { rename($PWD, "$PWD.bak") || die "Can't mv $PWD to $PWD.bak: $!\n"; rename($NPWD, $PWD) || die "Can't mv $NPWD to $PWD: $!\n"; } if ($gids) { rename($GRP, "$GRP.bak") || die "Can't mv $GRP to $GRP.bak: $!\n"; rename($NGRP, $GRP) || die "Can't mv $NGRP to $GRP: $!\n"; } } else { # Clean up if we were just testing. unlink("$NPWD", "$NGRP"); } exit ($oops != 0); -- Tres Hofmeister tres@ncar.ucar.edu