#!/usr/local/bin/perl
# Work out the bandwidth usage for all virtual servers.
# For those that are over their limit, send a warning.

package virtual_server;
$main::no_acl_check++;
require './virtual-server-lib.pl';

# Are we already running? If so, die
if (&test_lock($bandwidth_dir)) {
	exit(0);
	}
&lock_file($bandwidth_dir);

# Work out the start of the monitoring period
$now = time();
$day = int($now / (24*60*60));
$start_day = &bandwidth_period_start();

if ($ARGV[0]) {
	$onedom = &get_domain_by("dom", $ARGV[0]);
	$onedom || die "Server $ARGV[0] not found";
	@doms = ( $onedom );
	}
else {
	@doms = &list_domains();
	}
@bwdoms = grep { &can_monitor_bandwidth($_) } @doms;

# Get bandwidth info map for all domains
foreach $d (@bwdoms) {
	$bwinfo = &get_bandwidth($d);
	$bwinfomap{$d->{'id'}} = $bwinfo;
	}

# For each feature that has a function for doing bandwidth for all domains
# at once, call it
foreach $f (@features) {
	local $bwfunc = "bandwidth_all_$f";
	if (defined(&$bwfunc)) {
		local %starts = map { $_, $bwinfomap{$_}->{"last_$f"} }
				    (keys %bwinfomap);
		local $newstarts = &$bwfunc(\@bwdoms, \%starts, \%bwinfomap);
		foreach my $did (keys %$newstarts) {
			$bwinfomap{$did}->{"last_$f"} = $newstarts->{$did};
			}
		}
	}

# For each server, scan it's log files for all usage since the last check, and
# update the count for each day.
$maxdays = $config{'bw_maxdays'} || 366;
foreach $d (@bwdoms) {
	# Add bandwidth for all features
	$bwinfo = $bwinfomap{$d->{'id'}};
	foreach $f (@features) {
		local $bwfunc = "bandwidth_$f";
		if (defined(&$bwfunc)) {
			$bwinfo->{"last_$f"} =
				&$bwfunc($d, $bwinfo->{"last_$f"}, $bwinfo);
			}
		}

	# Add bandwidth for all plugins
	foreach $f (@feature_plugins) {
		if (&plugin_defined($f, "feature_bandwidth")) {
			$bwinfo->{"last_$f"} =
				&plugin_call($f, "feature_bandwidth", $d,
					     $bwinfo->{"last_$f"}, $bwinfo);
			}
		}

	# Prune days more than 1 year old
	foreach $k (keys %$bwinfo) {
		if ($k =~ /^(\S+)_(\d+)$/ && $2 < $day - $maxdays) {
			delete($bwinfo->{$k});
			}
		}
	&save_bandwidth($d, $bwinfo);
	}

# For each server, sum up usage over the monitoring period to find those
# that are over their limit
foreach $d (@doms) {
	next if ($d->{'parent'});

	# Sum up usage for domain and sub-domains
	$usage = 0;
	%usage = ( );
	foreach $dd ($d, &get_domain_by("parent", $d->{'id'})) {
		$bwinfo = &get_bandwidth($dd);
		local $usage_only = 0;
		local %usage_only = ( );
		foreach $k (keys %$bwinfo) {
			if ($k =~ /^(\S+)_(\d+)$/ && $2 >= $start_day) {
				$usage += $bwinfo->{$k};
				$usage_only += $bwinfo->{$k};
				$usage{$1} += $bwinfo->{$k};
				$usage_only{$1} += $bwinfo->{$k};
				}
			}
		$dd->{'bw_usage_only'} = $usage_only;
		$dd->{'bw_start'} = $start_day;
		foreach $f (@features) {
			delete($dd->{"bw_usage_only_$f"});
			}
		foreach $k (keys %usage_only) {
			$dd->{'bw_usage_only_'.$k} = $usage_only{$k};
			}
		if ($d ne $dd) {
			&save_domain($dd);
			}
		}
	$d->{'bw_usage'} = $usage;
	foreach $f (@features) {
		delete($d->{"bw_usage_$f"});
		}
	foreach $k (keys %usage) {
		$d->{'bw_usage_'.$k} = $usage{$k};
		}
	if ($d->{'bw_limit'} && $usage > $d->{'bw_limit'}) {
		# Over the limit! But check limit on how often to notify
		$etime = $now - $d->{'bw_notify'} > $config{'bw_notify'}*60*60;
		if ($etime) {
			# Time to email ..
			$tmpl = $config{'bw_template'} eq 'default' ?
				"$module_config_directory/bw-template" :
				$config{'bw_template'};
			%tkeys = %$d;
			$tkeys{'bw_limit'} = &nice_size($tkeys{'bw_limit'});
			$tkeys{'bw_usage'} = &nice_size($tkeys{'bw_usage'});
			$tkeys{'bw_period'} = $config{'bw_period'};
			$tkeys{'bw_percent'} = int(100*$usage/$d->{'bw_limit'});
			foreach $k (keys %usage) {
				$tkeys{'bw_usage_'.$k} =
					&nice_size($tkeys{'bw_usage_'}.$k);
				}
			local @addrs;
			push(@addrs, $d->{'email'} ||
				   $d->{'user'}.'@'.&get_system_hostname() )
				if ($config{'bw_owner'});
			push(@addrs, split(/\s+,\s+/, $config{'bw_email'}));
			@erv = &send_template_email(
				&cat_file($tmpl),
				join(", ", @addrs),
				\%tkeys,
				&text('newbw_subject', $d->{'dom'}));
			if ($erv[0]) {
				$d->{'bw_notify'} = $now;
				}
			else {
				print STDERR "Failed to send email : $erv[1]\n";
				}
			}
		if (!$d->{'disabled'} && $etime && $config{'bw_disable'} &&
		    !$d->{'bw_no_disable'}) {
			# Time to disable
			&set_all_null_print();
			@disable = &get_disable_features($d);
			%disable = map { $_, 1 } @disable;

			# Run the before command
			&set_domain_envs($d, "DISABLE_DOMAIN");
			$merr = &making_changes();
			&reset_domain_envs($d);
			next if ($merr);

			# Disable all configured features
			my $f;
			foreach $f (@features) {
				if ($d->{$f} && $disable{$f}) {
					local $dfunc = "disable_$f";
					&$dfunc($d);
					push(@disabled, $f);
					}
				}
			foreach $f (@feature_plugins) {
				if ($d->{$f} && $disable{$f}) {
					&plugin_call($f, "feature_disable", $d);
					push(@disabled, $f);
					}
				}

			# Save new domain details
			$d->{'disabled'} = join(",", @disabled);
			$d->{'disabled_reason'} = 'bw';

			# Run the after command
			&run_post_actions();
			&set_domain_envs($d, "DISABLE_DOMAIN");
			&made_changes();
			&reset_domain_envs($d);
			}
		}
	elsif ($d->{'bw_limit'} && $config{'bw_warn'} &&
	       $usage > $d->{'bw_limit'}*$config{'bw_warn'}/100) {
		# Reached the warning limit! But check limit on how often warn
		if ($now - $d->{'bw_warnnotify'} > $config{'bw_notify'}*60*60) {
			# Time to email ..
			$tmpl = $config{'warnbw_template'} eq 'default' ?
				"$module_config_directory/warnbw-template" :
				$config{'warnbw_template'};
			%tkeys = %$d;
			$tkeys{'bw_limit'} = &nice_size($tkeys{'bw_limit'});
			$tkeys{'bw_usage'} = &nice_size($tkeys{'bw_usage'});
			$tkeys{'bw_period'} = $config{'bw_period'};
			$tkeys{'bw_percent'} = int(100*$usage/$d->{'bw_limit'});
			foreach $k (keys %usage) {
				$tkeys{'bw_usage_'.$k} =
					&nice_size($tkeys{'bw_usage_'}.$k);
				}
			$tkeys{'bw_warn'} = $config{'bw_warn'};
			local @addrs;
			push(@addrs, $d->{'email'} ||
				   $d->{'user'}.'@'.&get_system_hostname() )
				if ($config{'bw_owner'});
			push(@addrs, split(/\s+,\s+/, $config{'bw_email'}));
			@erv = &send_template_email(
				&cat_file($tmpl),
				join(", ", @addrs),
				\%tkeys,
				&text('newbw_warnsubject', $d->{'dom'}));
			if ($erv[0]) {
				$d->{'bw_warnnotify'} = $now;
				}
			else {
				print STDERR "Failed to send email : $erv[1]\n";
				}
			}
		}

	if ($config{'bw_enable'} &&
	    ($usage < $d->{'bw_limit'} || !$d->{'bw_limit'}) &&
	    $d->{'disabled'} && $d->{'disabled_reason'} eq 'bw') {
		# Falled below the disable limit .. re-enable
		&set_all_null_print();
		@enable = &get_enable_features($d);
		%enable = map { $_, 1 } @enable;

		# Run the before command
		&set_domain_envs($d, "ENABLE_DOMAIN");
		$merr = &making_changes();
		&reset_domain_envs($d);
		next if ($merr);

		# Enable all disabled features
		my $f;
		foreach $f (@features) {
			if ($d->{$f} && $enable{$f}) {
				local $efunc = "enable_$f";
				&try_function($f, $efunc, $d);
				}
			}
		foreach $f (@feature_plugins) {
			if ($d->{$f} && $enable{$f}) {
				&plugin_call($f, "feature_enable", $d);
				}
			}

		# Save new domain details
		delete($d->{'disabled'});
		delete($d->{'disabled_reason'});
		delete($d->{'disabled_why'});
		&save_domain($d);

		# Run the after command
		&run_post_actions();
		&set_domain_envs($d, "ENABLE_DOMAIN");
		&made_changes();
		&reset_domain_envs($d);
		}
	&save_domain($d);
	}

# Release running lock
&unlock_file($bandwidth_dir);


syntax highlighted by Code2HTML, v. 0.9.1