initial commit of file from CVS for e-smith-quota on Wed 12 Jul 09:07:49 BST 2023
This commit is contained in:
294
root/sbin/e-smith/warnquota
Normal file
294
root/sbin/e-smith/warnquota
Normal file
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
package esmith;
|
||||
|
||||
use strict;
|
||||
|
||||
use esmith::ConfigDB;
|
||||
use esmith::util;
|
||||
use esmith::db;
|
||||
|
||||
use Text::Template;
|
||||
|
||||
sub getUserQuotas();
|
||||
sub alertUsersOverQuota($$);
|
||||
sub sendAdminSummary($$$$);
|
||||
|
||||
sub toMB ($);
|
||||
sub toMBNoDecimalPlaces ($);
|
||||
sub toKB ($);
|
||||
|
||||
my $conf = esmith::ConfigDB->open_ro;
|
||||
|
||||
=for testing
|
||||
is( system("$^X -cw $Original_File 2>&1"), 0, 'it compiles' );
|
||||
|
||||
|
||||
=head1 Automated quota reporting
|
||||
|
||||
This script iterates through each user account on the system and sends
|
||||
an email report to each user when they are over quota.
|
||||
|
||||
It sends a combined summary report to the administrator.
|
||||
|
||||
=cut
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Set qmail environment variables so that we get decent looking mail
|
||||
# headers (From, Return-Path)
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
my $domain = $conf->get_value("DomainName") || 'localhost';
|
||||
|
||||
my ($quotaOffenders, $quotaUsers, $quotalessUsers)
|
||||
= getUserQuotas();
|
||||
|
||||
alertUsersOverQuota($conf, $quotaOffenders);
|
||||
sendAdminSummary($conf, $quotaOffenders, $quotaUsers, $quotalessUsers);
|
||||
|
||||
=head2 getUserQuotas()
|
||||
|
||||
For each user on the system, determines their current disk usage, soft
|
||||
and hard quota, and grace period remaining.
|
||||
|
||||
It returns three data structures containing users whom are over quota,
|
||||
users whom are under quota, and those with no quotas:
|
||||
|
||||
%hash = {
|
||||
'user1' => {
|
||||
'hardQuota',
|
||||
'softQuota',
|
||||
'username',
|
||||
'usage',
|
||||
'fullname',
|
||||
'gracePeriod'
|
||||
},
|
||||
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
'userX' => {
|
||||
'hardQuota',
|
||||
'softQuota',
|
||||
'username',
|
||||
'usage',
|
||||
'fullname',
|
||||
'gracePeriod'
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
=cut
|
||||
|
||||
sub getUserQuotas()
|
||||
{
|
||||
use Quota;
|
||||
|
||||
use esmith::AccountsDB;
|
||||
my $adb = esmith::AccountsDB->open_ro();
|
||||
|
||||
my %usersOverQuota;
|
||||
my %usersUnderQuota;
|
||||
my %usersWithNoQuota;
|
||||
|
||||
# Make $dev arg for quota query calls
|
||||
my $dev = Quota::getqcarg('/home/e-smith/files');
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Loop through all user accounts and collect stats.
|
||||
#------------------------------------------------------------
|
||||
|
||||
foreach my $user ($adb->users)
|
||||
{
|
||||
my $uid = getpwnam($user->key);
|
||||
unless ($uid)
|
||||
{
|
||||
warn("Could not get uid for user \"" . $user->key ."\"");
|
||||
next;
|
||||
}
|
||||
my ($bc, $bs, $bh, $bt, $ic, $is, $ih, $it) =
|
||||
Quota::query($dev, $uid);
|
||||
|
||||
if( !defined $bc ) # Quota::query failed
|
||||
{
|
||||
warn "Cannot query quota for '" . $user->key . "' on '$dev': ", Quota::strerr(), "\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $name = $user->prop('FirstName') . " " . $user->prop('LastName');
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Collect user stats if over quota.
|
||||
# Everyone over quota goes into %usersOverQuota
|
||||
# Everyone under quota goes into %usersUnderQuota
|
||||
# Everyone else, goes into %usersWithNoQuota.
|
||||
#------------------------------------------------------------
|
||||
|
||||
if ( ($bs == 0) && ($bh == 0) )
|
||||
{
|
||||
# User has no quota
|
||||
# Add user to data structure.
|
||||
${usersWithNoQuota{$user->key}} = {
|
||||
username => $user->key,
|
||||
fullname => $name,
|
||||
usage => toMB($bc),
|
||||
softQuota => toMB($bs),
|
||||
hardQuota => toMB($bh),
|
||||
gracePeriod => $bt
|
||||
};
|
||||
}
|
||||
elsif ( (($bc > $bh) && ($bh > 0)) || (($bc > $bs) && ($bs > 0)) )
|
||||
{
|
||||
# User is over quota
|
||||
# gracePeriod is 0 if over hard limit
|
||||
|
||||
# Add user to data structure.
|
||||
${usersOverQuota{$user->key}} = {
|
||||
username => $user->key,
|
||||
fullname => $name,
|
||||
usage => toMB($bc),
|
||||
softQuota => toMB($bs),
|
||||
hardQuota => toMB($bh),
|
||||
gracePeriod => $bt
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
# User is within quota
|
||||
# Add user to data structure.
|
||||
${usersUnderQuota{$user->key}} = {
|
||||
username => $user->key,
|
||||
fullname => $name,
|
||||
usage => toMB($bc),
|
||||
softQuota => toMB($bs),
|
||||
hardQuota => toMB($bh),
|
||||
gracePeriod => $bt
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return \%usersOverQuota, \%usersUnderQuota, \%usersWithNoQuota;
|
||||
|
||||
}
|
||||
|
||||
=head2 alertUsersOverQuota($)
|
||||
|
||||
This function iterates through all users who are over quota (contained
|
||||
in data structure above), and sends email to each one.
|
||||
|
||||
It doesn't bother sending mail to those users who have exceeded their
|
||||
hard quota, since they probably won't be able to receive e-mail, unless
|
||||
there's either a delegate mail server defined, or their mail spool is
|
||||
elsewhere.
|
||||
|
||||
=cut
|
||||
|
||||
sub alertUsersOverQuota($$)
|
||||
{
|
||||
my ($conf, $usersOverQuota) = @_;
|
||||
|
||||
return unless keys %$usersOverQuota;
|
||||
|
||||
foreach my $user (keys %$usersOverQuota)
|
||||
{
|
||||
#------------------------------------------------------------------
|
||||
# Don't send email to users who are over their hard quota
|
||||
# They likely won't be able to receive it.
|
||||
# Plus, they've already had 7 days of warnings.
|
||||
#------------------------------------------------------------------
|
||||
|
||||
next if ( ($usersOverQuota->{$user}{usage}
|
||||
> $usersOverQuota->{$user}{hardQuota}) and
|
||||
($usersOverQuota->{$user}{hardQuota} != 0) );
|
||||
|
||||
my $templates = '/etc/e-smith/templates';
|
||||
my $source = '/usr/lib/e-smith-quota/userOverQuota.tmpl';
|
||||
|
||||
# Use templates-custom version by preference if it exists
|
||||
-f "${templates}-custom${source}" and $templates .= "-custom";
|
||||
|
||||
my $t = new Text::Template(TYPE => 'FILE',
|
||||
SOURCE => "${templates}${source}");
|
||||
|
||||
open(QMAIL, "|/var/qmail/bin/qmail-inject -fdo-not-reply\@$domain $user")
|
||||
|| die "Could not send mail via qmail-inject!\n";
|
||||
|
||||
print QMAIL $t->fill_in( HASH => {
|
||||
conf => \$conf,
|
||||
data => $usersOverQuota->{$user}
|
||||
});
|
||||
|
||||
close QMAIL;
|
||||
}
|
||||
}
|
||||
|
||||
=head2
|
||||
|
||||
sendAdminSummary($$$$)
|
||||
|
||||
Generates an email summary of users and their quota statuses. It takes
|
||||
as arguments a reference to a tied configuration database hash, and
|
||||
three data structures (format above) containing:
|
||||
|
||||
- users over quota
|
||||
- users under quota
|
||||
- users without quotas.
|
||||
|
||||
At the moment, it only emails those users who are over quota to the
|
||||
administrator.
|
||||
|
||||
=cut
|
||||
|
||||
sub sendAdminSummary($$$$)
|
||||
{
|
||||
my ($conf, $usersOverQuota, $usersUnderQuota, $usersWithNoQuota) = @_;
|
||||
|
||||
return unless keys %$usersOverQuota;
|
||||
|
||||
my $templates = '/etc/e-smith/templates';
|
||||
my $source = '/usr/lib/e-smith-quota/adminQuotaSummary.tmpl';
|
||||
|
||||
# Use templates-custom version by preference if it exists
|
||||
-f "${templates}-custom${source}" and $templates .= "-custom";
|
||||
|
||||
my $t = new Text::Template(TYPE => 'FILE',
|
||||
SOURCE => "${templates}${source}");
|
||||
|
||||
open(QMAIL, "|/var/qmail/bin/qmail-inject -fdo-not-reply\@$domain admin")
|
||||
|| die "Could not send mail via qmail-inject!\n";
|
||||
|
||||
print QMAIL $t->fill_in( HASH => {
|
||||
conf => \$conf,
|
||||
usersOverQuota => $usersOverQuota,
|
||||
usersUnderQuota => $usersUnderQuota
|
||||
});
|
||||
|
||||
close QMAIL;
|
||||
|
||||
}
|
||||
|
||||
=head2
|
||||
|
||||
toMB($)
|
||||
|
||||
Takes a number as input, and output the number divided by 1024, but also
|
||||
formatted to two decimal places.
|
||||
|
||||
A helper function to convert kilobytes to megabytes.
|
||||
|
||||
=cut
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Helper function to convert kilobytes to megabytes.
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub toMB ($)
|
||||
{
|
||||
my ($kb) = @_;
|
||||
return sprintf("%.2f", $kb / 1024);
|
||||
}
|
Reference in New Issue
Block a user