| Provision-Unix documentation | Contained in the Provision-Unix distribution. |
Create's a tarball of the users home directory. Typically done right before you rm -rf their home directory as part of a de-provisioning step.
if ( $prov->user_archive("user") )
{
print "user archived";
};
returns a boolean.
Provision::Unix::User::FreeBSD - Provision Unix Accounts on FreeBSD systems
Handles provisioning operations (create, modify, destroy) for system users on UNIX based operating systems.
use Provision::Unix::User::FreeBSD;
my $user_fbsd = Provision::Unix::User::FreeBSD->new();
...
Verify that new master.passwd is the right size. I found this necessary on some versions of FreeBSD as a race condition would cause the master.passwd file to get corrupted. Now I verify that after I'm finished making my changes, the new file is a small amount larger (or smaller) than the original.
Matt Simerson, <matt at tnpi.net>
Please report any bugs or feature requests to bug-unix-provision-user at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Provision-Unix. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
You can find documentation for this module with the perldoc command.
perldoc Provision::Unix
You can also look for information at:
Copyright 2008 Matt Simerson
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| Provision-Unix documentation | Contained in the Provision-Unix distribution. |
package Provision::Unix::User::FreeBSD; use warnings; use strict; our $VERSION = '0.10'; use English qw( -no_match_vars ); use Carp; use Params::Validate qw( :all ); use lib 'lib'; my ( $prov, $user, $util ); sub new { my $class = shift; my %p = validate( @_, { prov => { type => OBJECT }, user => { type => OBJECT }, debug => { type => BOOLEAN, optional => 1, default => 1 }, fatal => { type => BOOLEAN, optional => 1, default => 1 }, } ); my $self = { prov => $p{prov}, user => $p{user}, debug => $p{debug}, fatal => $p{fatal}, }; bless( $self, $class ); $prov = $p{prov}; $user = $p{user}; $prov->audit("loaded User/FreeBSD"); $util = $prov->get_util; return $self; } sub create { my $self = shift; my %p = validate( @_, { 'username' => { type => SCALAR }, 'uid' => { type => SCALAR, optional => 1 }, 'gid' => { type => SCALAR, optional => 1 }, 'shell' => { type => SCALAR | UNDEF, optional => 1 }, 'password' => { type => SCALAR | UNDEF, optional => 1 }, 'homedir' => { type => SCALAR | UNDEF, optional => 1 }, 'gecos' => { type => SCALAR | UNDEF, optional => 1 }, 'domain' => { type => SCALAR | UNDEF, optional => 1 }, 'expire' => { type => SCALAR | UNDEF, optional => 1 }, 'quota' => { type => SCALAR | UNDEF, optional => 1 }, 'debug' => { type => SCALAR, optional => 1, default => 1 }, 'test_mode' => { type => SCALAR, optional => 1 }, } ); my $debug = $p{'debug'}; $prov->audit("creating FreeBSD user"); my $username = $p{'username'}; my $shell = $p{'shell'} || "/sbin/nologin"; my $homedir = $p{'homedir'}; my $uid = $p{'uid'}; my $gid = $p{'gid'}; $user->{username} = $username; $user->_is_valid_username() or return; my $bak = $util->file_archive( "/etc/master.passwd", fatal => 0, debug => 0, ); $prov->audit("user->create: backed up master.passwd to $bak"); # pw creates accounts using defaults from /etc/pw.conf my $pw = $util->find_bin( "pw", debug => 0 ); my $pwcmd = "$pw useradd -n $username "; $pwcmd .= "-d $homedir " if $homedir; $pwcmd .= "-u $uid " if $uid; $pwcmd .= "-g $gid " if $gid; $pwcmd .= "-c '$p{'gecos'}' " if $p{'gecos'}; $pwcmd .= "-u 89 -g 89 -c Vpopmail-Master " if ( $username eq "vpopmail" ); $pwcmd .= "-n $username -d /nonexistent -c Clam-AntiVirus " if ( $username eq "clamav" ); $pwcmd .= "-s $shell "; $pwcmd .= "-m "; $prov->audit("pw command is: $pwcmd"); return 1 if $p{test_mode}; if ( $p{password} ) { $prov->audit("pw command is: $pwcmd -h 0 (****)"); ## no critic my $FH; unless ( open $FH, "| $pwcmd -h 0" ) { return $prov->error( "user_add: opening pw failed for $username" ); } print $FH "$p{password}\n"; close $FH; ## use critic } else { $prov->audit("pw command is: $pwcmd -h-"); $util->syscmd( "$pwcmd -h-", debug => 0 ); } ### TODO # call verify_master_passwd # set up user quotas return $self->exists($username) ? $prov->progress( num => 10, desc => 'created successfully' ) : $prov->error( 'create user failed' ); } sub create_group { my $self = shift; my %p = validate( @_, { 'group' => { type => SCALAR }, 'gid' => { type => SCALAR, optional => 1, }, 'debug' => { type => SCALAR, optional => 1, default => 1 }, 'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, } ); # see if the group exists if ( $self->exists_group( $p{group} ) ) { $prov->audit("create_group: $p{group} exists"); return 2; } $prov->audit("create_group: installing $p{group} on $OSNAME"); # use the pw tool to add the user my $pw = $util->find_bin( "pw", debug => $p{debug} ); $pw .= " groupadd -n $p{group}"; $pw .= " -g $p{gid}" if $p{gid}; return $util->syscmd( $pw, debug => $p{debug} ); } sub destroy { my $self = shift; my %p = validate( @_, { 'username' => { type => SCALAR, }, 'homedir' => { type => SCALAR, optional => 1, }, 'archive' => { type => BOOLEAN, optional => 1, default => 0 }, 'prompt' => { type => BOOLEAN, optional => 1, default => 0 }, 'test_mode' => { type => BOOLEAN, optional => 1, default => 0 }, 'fatal' => { type => SCALAR, optional => 1, default => 1 }, 'debug' => { type => SCALAR, optional => 1, default => 1 }, }, ); $prov->audit("destroy user $p{username} on FreeBSD"); $prov->progress( num => 1, desc => 'validating' ); return 1 if $p{test_mode}; # make sure user exists if ( !$user->exists( $p{username} ) ) { return $prov->progress( num => 10, desc => 'error', err => 'no such user' ); } ### TODO # this would be a good time to archive the user if desired. my $bak = $util->file_archive( "/etc/master.passwd", fatal => $p{fatal}, debug => $p{debug}, ); $prov->progress( num => 2, desc => "backed up master.passwd." ); my $pw = $util->find_bin( 'pw', debug => 0 ); $pw .= " userdel -n $p{username} -r"; my $r = $util->syscmd( $pw, debug => $p{debug}, fatal => $p{fatal} ); $prov->progress( num => 3, desc => "deleted user" ); ## TODO # verify_master_passwd # validate that the user was removed if ( !$user->exists( $p{username} ) ) { return $prov->progress( num => 10, desc => 'completed' ); } return $prov->progress( num => 10, desc => 'error', 'err' => $prov->{errors}->[-1]->{errmsg}, ); } sub destroy_group { my $self = shift; my %p = validate( @_, { 'group' => { type => SCALAR, }, 'gid' => { type => SCALAR, optional => 1 }, 'test_mode' => { type => BOOLEAN, optional => 1, default => 0 }, 'fatal' => { type => SCALAR, optional => 1, default => 1 }, 'debug' => { type => SCALAR, optional => 1, default => 1 }, }, ); $prov->audit("destroy group $p{group} on FreeBSD"); $prov->progress( num => 1, desc => 'validating' ); # make sure group exists if ( !$user->exists_group( $p{group} ) ) { return $prov->progress( num => 10, desc => 'error', err => 'no such group' ); } my $bak = $util->file_archive( "/etc/group", fatal => $p{fatal}, debug => $p{debug}, ); $prov->progress( num => 2, desc => "backed up /etc/group" ); my $pw = $util->find_bin( 'pw', debug => 0 ); $pw .= " groupdel -n $p{group}"; my $r = $util->syscmd( $pw, debug => $p{debug} ); $prov->progress( num => 3, desc => "deleted group" ); # validate that the group was removed if ( !$user->exists_group( $p{group} ) ) { return $prov->progress( num => 10, desc => 'completed' ); } return $prov->progress( num => 10, desc => 'error', 'err' => $prov->{errors}->[-1]->{errmsg}, ); } sub exists { my $self = shift; my $username = lc(shift) || $user->{username} || die "missing user"; my $uid = getpwnam($username); $self->{uid} = $uid; return ( $uid && $uid > 0 ) ? $uid : undef; } sub exists_group { my $self = shift; my $group = lc(shift) or die "missing group"; my $gid = getgrnam($group); return ( $gid && $gid > 0 ) ? $gid : undef; } sub verify_master_passwd { # my $r = $user->verify_master_passwd ($passwd, $change, $debug) # $r->{'error_code'} == 200 ? print "success" : print $r->{'error_desc'}; # $passwd is the filename of your master.passwd file. # $change is whether the file should "shrink" or "grow" my ( $self, $passwd, $change, $debug ) = @_; my %r; my $new = ( stat($passwd) )[7]; my $old = ( stat("$passwd.bak") )[7]; # do we expect it to change? if ($change) { if ( $change eq "grow" ) { if ( $new > $old ) { # yay, it grew. response with a success code print "verify_master_passwd: The file grew ($old to $new) bytes.\n" if $debug; $r{'error_code'} = 200; $r{'error_desc'} = "Success: the file grew from $old to $new bytes."; return \%r; } else { # boo, it didn't grow. return a failure code and # make an archived copy of it for recovery print "verify_master_passwd: WARNING: new $passwd size ($new) is not larger than $old and we expected it to $change.\n" if $debug; $util->file_archive( "$passwd.bak" ); $r{'error_code'} = 500; $r{'error_desc'} = "new $passwd size ($new) is not larger than $old and we expected it to $change.\n"; return \%r; } } elsif ( $change eq "shrink" ) { if ( $new < $old ) { # yay, it shrank. response with a success code print "verify_master_passwd: The file shrank ($old to $new) bytes.\n" if $debug; $r{'error_code'} = 200; $r{'error_desc'} = "The file shrank from $old to $new bytes.\n"; return \%r; } else { # boo, it didn't shrink. return a failure code and # make an archived copy of it for recovery print "verify_master_passwd: WARNING: new $passwd size ($new) is not smaller than $old and we expected it to $change.\n" if $debug; $r{'error_code'} = 500; $r{'error_desc'} = "new $passwd size ($new) is not smaller than $old and we expected it to $change.\n"; $util->file_archive( "$passwd.bak" ); return \%r; } } } # just report if ( $new == $old ) { print "verify_master_passwd: The files are the same size ($new)!\n" if $debug; } else { print "verify_master_passwd: The files are different sizes new: $new old: $old!\n" if $debug; } } sub archive {
my ( $self, $user, $debug ) = @_;
my $tar = $util->find_bin( "tar" );
my $rm = $util->find_bin( "rm" );
if ( !$self->exists($user) ) {
$prov->error( "user $user does not exist!" );
}
my $homedir = ( getpwnam($user) )[7];
unless ( -d $homedir ) {
$prov->error( "The home directory does not exist!" );
}
my ( $path, $userdir ) = $util->path_parse( { dir => $homedir } );
chdir($path)
or $prov->error( "couldn't cd to $path: $!\n" );
if ( -e "$path/$user.tar.gz" && -d "$path/$user" ) {
carp "user_archive:\tReplacing old tarfile $path/$user.tar.gz.\n";
system "$rm $path/$user.tar.gz";
}
print "\tArchiving $user\'s files to $path/$user.tar.gz...." if $debug;
print "$tar -Pzcf $homedir.tar.gz $userdir\n";
system "$tar -Pzcf $homedir.tar.gz $userdir";
if ( -e "${homedir}.tar.gz" ) {
print "done.\n" if $debug;
return 1;
}
else {
carp "\nFAILED: user_archive couldn't complete $homedir.tar.gz.\n\n";
return 0;
}
}
1;