Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

nugid - new user/group ids

by grinder (Bishop)
on Jun 25, 2001 at 13:28 UTC ( [id://91220]=sourcecode: print w/replies, xml ) Need Help??
Category: Utility Scripts
Author/Contact Info grinder
Description:

When you have a large host with hundreds of users, managing file ownerships becomes hard. People come and go, but their files remain, and these files have to assigned to other users. By itself the chown(1) program is too severe, as it will operate on anything it can get its hands on. This can be very disruptive in a directory tree where files may be owned by dozens of individuals.

So I wrote a script that is a little more selective, in its own words, it will

Selectively modify user and group ownerships of files and directories depending on current ownerships. All files that match the given group id or user id will be changed to the new specified id. Numeric and symbolic ids are recognised.

Comments, suggestions and criticisms welcomed. I think the script name sucks, but I can't think of a better one.

#! /usr/bin/perl -w

use strict;
use Getopt::Mixed;
use vars qw/$VERSION/;

BEGIN { $VERSION = '1.0' }

die "Not running as root!\n" if $<;

my( $IS_UID, $OLD_ID, $NEW_ID, $RECURSE, $QUIET, $VERBOSE ) =
  (  0,       undef,   undef,   undef,    undef,  undef   );

Getopt::Mixed::init( q{g=s gid>g group>g
    u=s uid>u user>u
    n=s new>n
    r   recurse>r R>r 
    q   quiet>q
    v   verbose>v
    V   version>V
});

OPTS: {
    # switch -u and -g are mutually exclusive
    my( $u_seen, $g_seen ) = (undef, undef);

    while( my( $option, $value, $pretty ) = Getopt::Mixed::nextOption(
+) ) {
        $u_seen  = 1      if $option eq 'u';
        $g_seen  = 1      if $option eq 'g';
        $OLD_ID  = $value if $option eq 'g' or $option eq 'u';
        $IS_UID  = 1      if $option eq 'u';
        $NEW_ID  = $value if $option eq 'n';
        $RECURSE = 1      if $option eq 'r';
        $QUIET   = 1      if $option eq 'q';
        $VERBOSE = 1      if $option eq 'v';
        if( $option eq 'V' ) {
            print "$VERSION\n";
            exit;
        }
        die "Cannot specify both -u and -g at the same time.\n"
            if defined $u_seen and defined $g_seen;
    }
}
my $id_name = $IS_UID ? 'user' : 'group';

defined $OLD_ID or die "Source ID not specified via -u/--user or via -
+g/--group.\n";
$OLD_ID = resolve_id( $OLD_ID ) if( $OLD_ID !~ /^\d+$/ );

defined $NEW_ID or die "New $id_name ID not specified via -n/--new.\n"
+;
$NEW_ID = resolve_id( $NEW_ID ) if( $NEW_ID !~ /^\d+$/ );

if( $RECURSE ) {
    require File::Find;
    File::Find->import();
}

local @ARGV = $RECURSE
        ? qw/./
        : (do {
            opendir D, '.' or die "Cannot open directory . for input: 
+$!\n";
            my @files = readdir D;
            close D;
            @files
        })
    unless @ARGV;

foreach my $spec ( @ARGV ) {
    if( $RECURSE ) {
        $VERBOSE and -d $spec and print "$spec\n";
        find( sub {
                return if $_ eq '..';
                try_chown( $_ );
            },
            $spec
        );
    }
    else {
        try_chown( $spec );
    }
}

sub resolve_id {
    my $text_id = shift;
    my $numeric_id = $IS_UID
        ? (getpwnam($text_id))[2]
        : (getgrnam($text_id))[2];
    die "Can't determine $id_name ID for $text_id.\n"
        unless defined $numeric_id;
    $VERBOSE and print "numeric $id_name ID for $text_id resolves to $
+numeric_id.\n";
    $numeric_id;
}

sub try_chown {
    my $file = shift;
    die "$file: no such file or directory.\n" unless -e $file;

    my( $uid, $gid ) = (stat $file)[4,5];
    return unless defined $uid and defined $gid;

    if( $IS_UID and $uid == $OLD_ID) {
        print "\tchown $uid->$NEW_ID, $gid, $file\n" unless $QUIET;
        if( !chown $NEW_ID, $gid, $file ) {
            $QUIET or warn "Cannot change uid for $file from $uid to $
+NEW_ID: $!\n";
        }
    }
    elsif( !$IS_UID and $gid == $OLD_ID ) {
        print "\tchown $uid, $gid->$NEW_ID, $file\n" unless $QUIET;
        if( !chown $uid, $NEW_ID, $file ) {
            $QUIET or warn "Cannot change gid for $file from $gid to $
+NEW_ID: $!\n";
        }
    }
}

=head1 NAME

nugid - new user/group id

=head1 SYNOPSIS

B<nugid> [B<-qrvV>] {B<-g> group|B<-u> user} -n ID [filespec] [filespe
+c...]

=head1 DESCRIPTION

Selectively modify user and group ownerships of files and directories
depending on current ownerships. All files that match the given group
id or user id will be changed to the new specified id. Numeric and
symbolic ids are recognised.

=head1 OPTIONS

=over 5

=item B<-g> B<--group> B<--gid>

Group. Specifiy the existing group id (gid) to match. The id may eithe
+r be numeric,
or symbolic. In the latter case, it is an error to specify a non-exist
+ant group
name. This switch and the [B<-u>] switch are mutually exclusive.

=item B<-u> B<--uid> B<--user>

User. Specifiy the existing user id (uid) to match. The id may either 
+be numeric,
or symbolic. In the latter case, it is an error to specify a non-exist
+ant user name.
This switch and the [B<-g>] switch are mutually exclusive.

=item B<-n> B<--new>

New. Specify the new id that will be applied to the files that are own
+ed by the
group or user specified via the B<-g> or B<-u> switches.

=item B<-r> B<-R> B<--recurse>

Recurse. All subdirectories of the directories specified on the comman
+d line will be
examined.

=item B<-q> B<--quiet>

Quiet. By default, the script will print out every change made as well
+ as errors
resulting from the inability to perform the change. This switch will c
+ause the script
to run silently.

=item B<-v> B<--verbose>

Verbose. The script will print out a little more information than usua
+l, notably the
name of each directory root given when performing a recursive search, 
+and the resolved
numeric id when a symbolic group or user is given.

=item B<-V> B<--version>

Version. Print out the current version of the script and exit with per
+forming any
changes.

=back

If no file specifications are given on the command line, one of two ac
+tions will
be taken. If recursion is specified then the current directory `.' wil
+l be used
as a starting point. If recursion is not specified, the current direct
+ory is
globbed and all entries are processed.

The script must be run by the superuser, and will exit immediately if 
+this
condition is not met.

=head1 EXAMPLES

C<nugid -u 1245 -n operator -rq />

Starting from the root directory, find all files who are owned by user
+ 1245
(which may be an account that has been deleted from the host) and chan
+ge the
ownership to that of the `operator' account. Nothing will be printed.

C<nugid -g staff -n wheel>

Change the files in the current directory that are owned by the group 
+`staff'
to the group `wheel'. The output will look something like:

    chown 410, 5->15, ./porpentine
    chown 203, 5->15, ./slothrop
    chown 600, 5->15, ./stencil

There may be many more files than these three in the current directory
+, however,
these would be the only ones belonging to the group `staff'. In this e
+xample,
the numeric id of the staff group is 5, and the id of the `wheel' grou
+p is 15.

C<nugid -g 45 -n 85 -r>

Change all files owned by group 45 to group 85. Note that neither of t
+hese ids
necessarily equate to a known group name (as per C</etc/groups> or equ
+ivalent).
This may have dire consequences on the health of your host, use with c
+aution.

=head1 INSTALLATION

As this this script is useable only by the superuser, it makes sense t
+o install
it with no permissions for anyone other than root. The following comma
+nds are
thus suitable for installing the script:

    cp thisfile /usr/local/bin/nugid
    chown root /usr/local/bin/nugid
    chmod 700 /usr/local/bin/nugid

=head1 COPYRIGHT

Copyright (c) 2001 David Landgren.

This script is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 AUTHOR

     David "grinder" Landgren
     grinder on perlmonks (http://www.perlmonks.org/)
     eval {join chr(64) => qw[landgren bpinet.com]}

=cut

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://91220]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (7)
As of 2024-04-18 03:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found