From a014d5b59fc42881b87a0822be5b0689dad9f4cb Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 01:40:49 +0200 Subject: [PATCH 01/18] Add API to support FirewallD --- bandwidth/bandwidth-lib.pl | 132 +++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index db1982849..59e511c4e 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -220,6 +220,138 @@ sub is_server_port { } +############### functions for FirewallD ################# + +# get_firewalld_rule(family, chain, direction, iface) +# Returns the rich rule for logging packets +sub get_firewalld_rule +{ +my ($family, $chain, $direction, $iface) = @_; + +# Define rule components +my %switch = ( + 'in' => 'i', + 'out' => 'o', + ); + +# Construct and return the rich rule +my $udirection = uc($direction); +my $rule = { + 'family' => $family, + 'table' => 'filter', + 'chain' => uc($chain), + 'priority' => 0, + 'rule' => "-$switch{$direction} $iface -j LOG \ + --log-level 7 --log-prefix BANDWIDTH_$udirection:", + }; +return &firewalld::construct_direct_rule($rule); +} + +# check_firewalld_rules(iface) +# Returns 1 if the FirewallD rules needed are setup, 0 if not +sub check_firewalld_rules +{ +my $iface = shift // $config{'iface'}; +&foreign_require("firewalld"); # Load the firewalld module +my %ip_families = &firewalld::check_ip_family(); +my @directions = ('in', 'out'); +my @chains = ('input', 'output', 'forward'); +my $conflicting = sub { + my ($chain, $direction) = @_; + return 1 if ($direction eq 'out' && $chain eq 'input'); + return 1 if ($direction eq 'in' && $chain eq 'output'); + }; + +# Check each direction for each available IP family +for my $chain (@chains) { + # If IPv4 is available + if ($ip_families{ipv4}) { + foreach my $direction (@directions) { + next if ($conflicting->($chain, $direction)); + return 0 if (!&firewalld::check_direct_rule( + &get_firewalld_rule('ipv4', $chain, + $direction, $iface))); + } + } + # If IPv6 is available + if ($ip_families{ipv6}) { + foreach my $direction (@directions) { + next if ($conflicting->($chain, $direction)); + return 0 if (!&firewalld::check_direct_rule( + &get_firewalld_rule('ipv6', $chain, + $direction, $iface))); + } + } + } +return 1; +} + +# setup_firewalld_rules() +# If any FirewallD rules are missing, add them +sub firewalld_rules_control +{ +my ($action, $iface) = @_; +&foreign_require("firewalld"); +my %ip_families = &firewalld::check_ip_family(); +my @directions = ('in', 'out'); +my @chains = ('input', 'output', 'forward'); +my $conflicting = sub { + my ($chain, $direction) = @_; + return 1 if ($direction eq 'out' && $chain eq 'input'); + return 1 if ($direction eq 'in' && $chain eq 'output'); + }; + +# Add the rules for each direction and IP family +foreach my $chain (@chains) { + if ($ip_families{ipv4}) { + foreach my $direction (@directions) { + next if ($conflicting->($chain, $direction)); + my ($out, $rs) = &firewalld::direct_rule($action, { + 'permanent' => 1, + 'rule' => &get_firewalld_rule('ipv4', $chain, + $direction, $iface), + }); + return $out if ($rs); + } + } + if ($ip_families{ipv6}) { + foreach my $direction (@directions) { + next if ($conflicting->($chain, $direction)); + my ($out, $rs) = &firewalld::direct_rule($action, { + 'permanent' => 1, + 'rule' => &get_firewalld_rule('ipv6', $chain, + $direction, $iface), + }); + return $out if ($rs); + } + } + } +return &firewalld::apply_firewalld(); +} + +# setup_firewalld_rules(iface) +# If any FirewallD rules are missing, add them +sub setup_firewalld_rules +{ +my $iface = shift // $config{'iface'}; +return &firewalld_rules_control('add', $iface); +} + +# delete_firewalld_rules() +# Delete firewall rules for bandwidth logging +sub delete_firewalld_rules +{ +my $iface = shift // $config{'iface'}; +return &firewalld_rules_control('remove', $iface); +} + +# process_firewalld_line(line, &hours, time-now) +# Process an IPtables firewall line, and returns 1 if successful +sub process_firewalld_line +{ +return &process_firewall_line(@_); +} + ############### functions for IPtables ################# # check_firewall_rules() From 40b61a6c221b5bdb7730ab4270fbbab1ebaac777 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 01:42:14 +0200 Subject: [PATCH 02/18] Add FirewallD to config info --- bandwidth/config.info | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bandwidth/config.info b/bandwidth/config.info index f278506b2..5ab519873 100644 --- a/bandwidth/config.info +++ b/bandwidth/config.info @@ -1,3 +1,3 @@ -firewall_system=Firewall type,4,firewall-IPtables,ipfw-IPFW,ipfilter-IPFilter,shorewall-Shorewall,-Detect automatically +firewall_system=Firewall type,4,-Detect automatically,firewalld-FirewallD,firewall-IPtables,ipfw-IPFW,ipfilter-IPFilter,shorewall-Shorewall bandwidth_log=Log file to create for firewall messages,0 bandwidth_dir=Directory for bandwidth data,3,Default (/etc/webmin/bandwidth/hours) From 8e4bfff29e2177439cc85ee883afe8c1682fe096 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 01:54:48 +0200 Subject: [PATCH 03/18] Add check for FirewallD --- bandwidth/bandwidth-lib.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 59e511c4e..6eb50d514 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -163,7 +163,7 @@ return &ui_textbox("$_[2]_hour", $_[0], 2).":". sub detect_firewall_system { local $m; -foreach $m ("shorewall", "firewall", "ipfw", "ipfilter") { +foreach $m ("shorewall", "firewalld", "firewall", "ipfw", "ipfilter") { return $m if (&check_firewall_system($m)); } return undef; From 9160dc5735aff670ce0dbb0f5532f3a1da129930 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 01:55:25 +0200 Subject: [PATCH 04/18] Add missing syslog function --- bandwidth/bandwidth-lib.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 6eb50d514..40acd9a67 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -352,6 +352,12 @@ sub process_firewalld_line return &process_firewall_line(@_); } +# get_firewalld_loglevel() +sub get_firewalld_loglevel +{ +return ( "kern.=debug" ); +} + ############### functions for IPtables ################# # check_firewall_rules() From 9e3918e8131b1b6432496101e7f9f414088f9036 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 02:13:38 +0200 Subject: [PATCH 05/18] Fix language for consistency --- bandwidth/config.info | 2 +- bandwidth/lang/en | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bandwidth/config.info b/bandwidth/config.info index 5ab519873..eef2f6c12 100644 --- a/bandwidth/config.info +++ b/bandwidth/config.info @@ -1,3 +1,3 @@ -firewall_system=Firewall type,4,-Detect automatically,firewalld-FirewallD,firewall-IPtables,ipfw-IPFW,ipfilter-IPFilter,shorewall-Shorewall +firewall_system=Firewall type,4,-Detect automatically,firewalld-Firewalld,firewall-IPtables,ipfw-IPFW,ipfilter-IPFilter,shorewall-Shorewall bandwidth_log=Log file to create for firewall messages,0 bandwidth_dir=Directory for bandwidth data,3,Default (/etc/webmin/bandwidth/hours) diff --git a/bandwidth/lang/en b/bandwidth/lang/en index 285b2b2cb..f5ac9924c 100644 --- a/bandwidth/lang/en +++ b/bandwidth/lang/en @@ -5,7 +5,7 @@ index_elog=The file $1 used for bandwidth logging is actually a directory on you index_edir=The directory for storing bandwidth data $1 does not exist, or is not a directory. Adjust the module configuration to use a different path. index_emod=The Webmin module $1 is not installed on this system or is not supported by your OS. The Bandwidth Monitoring module cannot operate without it. index_esyslog=Neither of the System Logs modules are installed on this system and supported by your OS. The Bandwidth Monitoring module cannot operate without one of them. -index_firesys=Using $1 firewall and $2 +index_firesys=Using $1 with $2 index_setupcannot=However, you do not have permissions to set it up! index_setupdesc=Before this module can report network usage, it needs to be set up to monitor traffic on the chosen network interface. index_setupdesc2=This module will log all network traffic sent or received on the selected interface, which can consume a large amount of disk space and CPU time on a fast network connection. @@ -73,11 +73,13 @@ setup_ecannot=You are not allowed to enable monitoring setup_eiface=Missing or invalid interface name setup_ezone=Failed to find Shorewall zone for the selected interface +system_firewalld=Firewalld system_firewall=IPtables system_ipfw=IPFW system_ipfilter=IPFilter system_shorewall=Shorewall +syslog_journal=Journald syslog_syslog=Syslog syslog_syslog-ng=Syslog-NG From 1506238c7b46a8b4f87f8deb5dc654b623941071 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 02:42:01 +0200 Subject: [PATCH 06/18] Add FirewallD with Journald labels and generalize language for all supported systems --- bandwidth/bandwidth-lib.pl | 1 + bandwidth/index.cgi | 11 ++++++++--- bandwidth/lang/en | 12 ++++++------ bandwidth/setup.cgi | 6 +++++- bandwidth/turnoff.cgi | 6 +++++- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 40acd9a67..7718c7baa 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -18,6 +18,7 @@ elsif (&foreign_installed("syslog")) { } else { $syslog_module = undef; + $syslog_journald = "journald" if (&has_command('journalctl')); } &foreign_require("cron", "cron-lib.pl"); &foreign_require("net", "net-lib.pl"); diff --git a/bandwidth/index.cgi b/bandwidth/index.cgi index 4b46b5710..77158dcf6 100755 --- a/bandwidth/index.cgi +++ b/bandwidth/index.cgi @@ -34,7 +34,7 @@ foreach $m (split(/\s+/, $module_info{'depends'})) { } # Make sure one of the syslog modules works -if (!$syslog_module) { +if (!$syslog_module && !$syslog_journald) { &ui_print_header(undef, $text{'index_title'}, "", "intro", 1, 1); &ui_print_endpage(&text('index_esyslog')); @@ -66,7 +66,7 @@ else { 1, 1, 0, undef, undef, undef, &text('index_firesys', $text{'system_'.$config{'firewall_system'}}, - $text{'syslog_'.$syslog_module})); + $text{'syslog_'.($syslog_module || $syslog_journald)})); # Make sure the needed firewall rules and syslog entry are in place $missingrule = !&check_rules(); @@ -75,12 +75,17 @@ if ($syslog_module eq "syslog") { $conf = &syslog::get_config(); $sysconf = &find_sysconf($conf); } -else { +elsif ($syslog_module eq "syslog-ng") { # Syslog-ng $conf = &syslog_ng::get_config(); ($ngdest, $ngfilter, $nglog) = &find_sysconf_ng($conf); $sysconf = $ngdest && $ngfilter && $nglog; } +elsif ($syslog_journald) { + # Systemd journal + # XXX + # $sysconf = 1; + } if (($missingrule || !$sysconf) && $access{'setup'}) { # Something is missing .. offer to set up diff --git a/bandwidth/lang/en b/bandwidth/lang/en index f5ac9924c..c961343db 100644 --- a/bandwidth/lang/en +++ b/bandwidth/lang/en @@ -4,14 +4,14 @@ index_efiresys2=The configured $1 firewall system was not found on your system. index_elog=The file $1 used for bandwidth logging is actually a directory on your system. Adjust the module configuration to use a different path. index_edir=The directory for storing bandwidth data $1 does not exist, or is not a directory. Adjust the module configuration to use a different path. index_emod=The Webmin module $1 is not installed on this system or is not supported by your OS. The Bandwidth Monitoring module cannot operate without it. -index_esyslog=Neither of the System Logs modules are installed on this system and supported by your OS. The Bandwidth Monitoring module cannot operate without one of them. +index_esyslog=None of the supported system logging systems, such as Journald, Rsyslog, or others, are installed or supported by your OS. This module requires at least one of them to operate correctly. index_firesys=Using $1 with $2 index_setupcannot=However, you do not have permissions to set it up! index_setupdesc=Before this module can report network usage, it needs to be set up to monitor traffic on the chosen network interface. index_setupdesc2=This module will log all network traffic sent or received on the selected interface, which can consume a large amount of disk space and CPU time on a fast network connection. -index_missing3=Several firewall rules must be added, and a syslog configuration entry created. -index_missing2=Several firewall rules must be added. -index_missing1=A syslog configuration entry must be created. +index_missing3=Several firewall rules need to be added, along with a configuration entry for the system logging system. +index_missing2=Several firewall rules need to be added. +index_missing1=A configuration entry for the system logging system must be created. index_iface=Chosen network interface index_other=Other.. index_setup=Setup Now @@ -55,7 +55,7 @@ index_low=Server ports only? index_resolv=Resolve hostnames? index_nomatch=No traffic matched the selected criteria. index_turnoff=Turn Off Monitoring -index_turnoffdesc=Click this button to remove the firewall rules, syslog configuration and Cron job used for bandwidth monitoring. All existing collected data will remain untouched. +index_turnoffdesc=Click this button to remove the firewall rules, related system logging configuration, and Cron job used for bandwidth monitoring. The existing collected data will remain intact. index_rotate=Update Statistics index_rotatedesc=Click this button to process all logged network traffic up to the current time, making it immediately available for reporting. index_eiptables=Warning - Your IPtables configuration has an error : $1. Setting up bandwidth monitoring will clear all firewall rules. @@ -79,7 +79,7 @@ system_ipfw=IPFW system_ipfilter=IPFilter system_shorewall=Shorewall -syslog_journal=Journald +syslog_journald=Journald syslog_syslog=Syslog syslog_syslog-ng=Syslog-NG diff --git a/bandwidth/setup.cgi b/bandwidth/setup.cgi index c1c8c7782..7b8b27d55 100755 --- a/bandwidth/setup.cgi +++ b/bandwidth/setup.cgi @@ -32,7 +32,7 @@ if ($syslog_module eq "syslog") { &error($err) if ($err); } } -else { +elsif ($syslog_module eq "syslog-ng") { # Add syslog-ng entry $conf = &syslog_ng::get_config(); ($dest, $filter, $log) = &find_sysconf_ng($conf); @@ -93,6 +93,10 @@ else { } &unlock_file($syslog_ng::config{'syslogng_conf'}); } +elsif ($syslog_journald) { + # Systemd journal + # XXX +} # Save the interface &lock_file($module_config_file); diff --git a/bandwidth/turnoff.cgi b/bandwidth/turnoff.cgi index edc67a0de..94cb576c0 100755 --- a/bandwidth/turnoff.cgi +++ b/bandwidth/turnoff.cgi @@ -21,7 +21,7 @@ if ($syslog_module eq "syslog") { &error($err) if ($err); } } -else { +elsif ($syslog_module eq "syslog-ng") { # Remove syslog-ng entries $conf = &syslog_ng::get_config(); ($dest, $filter, $log) = &find_sysconf_ng($conf); @@ -37,6 +37,10 @@ else { } &unlock_file($syslog_ng::config{'syslogng_conf'}); } +elsif ($syslog_journald) { + # Systemd journal + # XXX +} # Remove rotation cron job $job = &find_cron_job(); From 39af4f23282d2d00d92584a4cfada7aeffb9b53f Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 14:22:26 +0200 Subject: [PATCH 07/18] Fix to let *tables decide which log level (defaults to 4) --- bandwidth/bandwidth-lib.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 7718c7baa..7c7333086 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -243,7 +243,7 @@ my $rule = { 'chain' => uc($chain), 'priority' => 0, 'rule' => "-$switch{$direction} $iface -j LOG \ - --log-level 7 --log-prefix BANDWIDTH_$udirection:", + --log-prefix BANDWIDTH_$udirection:", }; return &firewalld::construct_direct_rule($rule); } From 1cf778b6271ea4e0ed77b31d2f60003ae8fb33d7 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 15:11:36 +0200 Subject: [PATCH 08/18] Fix to remove comments, as no work required here really --- bandwidth/index.cgi | 3 +-- bandwidth/setup.cgi | 2 +- bandwidth/turnoff.cgi | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bandwidth/index.cgi b/bandwidth/index.cgi index 77158dcf6..cc86c7b65 100755 --- a/bandwidth/index.cgi +++ b/bandwidth/index.cgi @@ -83,8 +83,7 @@ elsif ($syslog_module eq "syslog-ng") { } elsif ($syslog_journald) { # Systemd journal - # XXX - # $sysconf = 1; + $sysconf = 1; # nothing to do } if (($missingrule || !$sysconf) && $access{'setup'}) { diff --git a/bandwidth/setup.cgi b/bandwidth/setup.cgi index 7b8b27d55..e8db64ee5 100755 --- a/bandwidth/setup.cgi +++ b/bandwidth/setup.cgi @@ -95,7 +95,7 @@ elsif ($syslog_module eq "syslog-ng") { } elsif ($syslog_journald) { # Systemd journal - # XXX + # No setup needed } # Save the interface diff --git a/bandwidth/turnoff.cgi b/bandwidth/turnoff.cgi index 94cb576c0..b320f365b 100755 --- a/bandwidth/turnoff.cgi +++ b/bandwidth/turnoff.cgi @@ -39,7 +39,7 @@ elsif ($syslog_module eq "syslog-ng") { } elsif ($syslog_journald) { # Systemd journal - # XXX + # Nothing to do } # Remove rotation cron job From 67391244e4f4494bb6df6c665448c74c9eb19658 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 15:29:33 +0200 Subject: [PATCH 09/18] Fix to lock module config --- bandwidth/rotate.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bandwidth/rotate.pl b/bandwidth/rotate.pl index b667cbf32..d0322e735 100755 --- a/bandwidth/rotate.pl +++ b/bandwidth/rotate.pl @@ -10,7 +10,9 @@ if (!$config{'firewall_system'}) { $sys = &detect_firewall_system(); if ($sys) { $config{'firewall_system'} = $sys; + &lock_file($module_config_file); &save_module_config(); + &unlock_file($module_config_file); } else { die "Failed to detect firewall system!"; From aae7bb5ac88c50ceaae6e5b1fb6bdffdb91d63dd Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 16:19:38 +0200 Subject: [PATCH 10/18] Fix code readability and scoping --- bandwidth/rotate.pl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bandwidth/rotate.pl b/bandwidth/rotate.pl index d0322e735..52bf2bfc5 100755 --- a/bandwidth/rotate.pl +++ b/bandwidth/rotate.pl @@ -20,18 +20,22 @@ if (!$config{'firewall_system'}) { } # See if this process is already running -if ($pid = &check_pid_file($pid_file)) { +if (my $pid = &check_pid_file($pid_file)) { print STDERR "rotate.pl process $pid is already running\n"; exit; } -open(PID, ">$pid_file"); -print PID $$,"\n"; -close(PID); +open(my $pid, ">$pid_file"); +print $pid $$,"\n"; +close($pid); +# Get the current time $time_now = time(); @time_now = localtime($time_now); @hours = ( ); +# Pre-process command +&pre_process(); + # Scan the entries in the log file &pre_process(); open(LOG, "<".$bandwidth_log); From 50be76165b80bd386a5be73a6988a911947afa93 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 18:03:31 +0200 Subject: [PATCH 11/18] Fix to handle output nicely --- bandwidth/lang/en | 3 ++- bandwidth/rotate.cgi | 17 +++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/bandwidth/lang/en b/bandwidth/lang/en index c961343db..541aabbf5 100644 --- a/bandwidth/lang/en +++ b/bandwidth/lang/en @@ -85,6 +85,7 @@ syslog_syslog-ng=Syslog-NG rotate_title=Updating Statistics rotate_doing=Processing logged network traffic .. -rotate_done=.. done. +rotate_done=.. done +rotate_failed=.. failed : $1 __norefs=1 diff --git a/bandwidth/rotate.cgi b/bandwidth/rotate.cgi index 4273962f3..ab0c92842 100755 --- a/bandwidth/rotate.cgi +++ b/bandwidth/rotate.cgi @@ -2,18 +2,11 @@ # Run rotate.pl now require './bandwidth-lib.pl'; -&ui_print_header(undef, $text{'rotate_title'}, ""); - -print "$text{'rotate_doing'}\n"; -print "
";
-open(OUT, "$cron_cmd 2>&1 |");
-while() {
-	print &html_escape($_);
-	}
-close(OUT);
-print "
\n"; -print "$text{'rotate_done'}

\n"; - +&ui_print_unbuffered_header(undef, $text{'rotate_title'}, ""); +print &ui_text_wrap($text{'rotate_doing'}); +my ($out) = &backquote_logged("$cron_cmd 2>&1"); +$out = $out ? &text('rotate_failed', &html_strip($out)) : $text{'rotate_done'}; +print &ui_text_wrap("
$out"); &webmin_log("rotate"); &ui_print_footer("", $text{'index_return'}); From 785eb8646b356e78f0bcc9d6597985ee6a6c67a5 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 18:15:08 +0200 Subject: [PATCH 12/18] Fix exit statuses and use lexical file handler --- bandwidth/rotate.pl | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/bandwidth/rotate.pl b/bandwidth/rotate.pl index 52bf2bfc5..83aba8b30 100755 --- a/bandwidth/rotate.pl +++ b/bandwidth/rotate.pl @@ -2,12 +2,17 @@ # Parse the firewall log and rotate it $no_acl_check++; -require './bandwidth-lib.pl'; use Time::Local; +require './bandwidth-lib.pl'; + +our (%config, $module_config_file, $module_var_directory, $pid_file, + $syslog_module, $syslog_journald); + +my ($logfh, $timestamp_file, $bandwidth_log, $lastline); # Detect firewall system if needed if (!$config{'firewall_system'}) { - $sys = &detect_firewall_system(); + my $sys = &detect_firewall_system(); if ($sys) { $config{'firewall_system'} = $sys; &lock_file($module_config_file); @@ -15,23 +20,23 @@ if (!$config{'firewall_system'}) { &unlock_file($module_config_file); } else { - die "Failed to detect firewall system!"; + die("Failed to detect firewall system!\n"); } } # See if this process is already running if (my $pid = &check_pid_file($pid_file)) { print STDERR "rotate.pl process $pid is already running\n"; - exit; + exit(1); } open(my $pid, ">$pid_file"); print $pid $$,"\n"; close($pid); # Get the current time -$time_now = time(); -@time_now = localtime($time_now); -@hours = ( ); +my $time_now = time(); +my @time_now = localtime($time_now); +my @hours = ( ); # Pre-process command &pre_process(); @@ -57,17 +62,19 @@ while() { close(LOG); # Save all hours -foreach $hour (@hours) { +foreach my $hour (@hours) { &save_hour($hour); } # Truncate the file (if it exists) and notify syslog if (-r $bandwidth_log) { - open(LOG, ">".$bandwidth_log); - close(LOG); + open(my $log, ">".$bandwidth_log); + close($log); } &foreign_call($syslog_module, "signal_syslog"); # Remove PID file unlink($pid_file); +# Exit with success +exit(0); \ No newline at end of file From c0e09b4fa602d02ab3ad609323e1b45270e0e647 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 18:26:08 +0200 Subject: [PATCH 13/18] Add support for collecting bandwidth stats using Journald --- bandwidth/rotate.pl | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/bandwidth/rotate.pl b/bandwidth/rotate.pl index 83aba8b30..45b579909 100755 --- a/bandwidth/rotate.pl +++ b/bandwidth/rotate.pl @@ -41,17 +41,35 @@ my @hours = ( ); # Pre-process command &pre_process(); +# Open the log file or pipe to journalctl +if ($syslog_journald) { + $timestamp_file = "$module_var_directory/last-processed"; + my $last_processed = 0; + if (-r $timestamp_file) { + $last_processed = &read_file_contents($timestamp_file); + chomp($last_processed); + $last_processed = int($last_processed) || 0; + } + my $journal_cmd = &has_command("journalctl"); + $journal_cmd = "$journal_cmd -k --since=\@$last_processed ". + "--until=\@$time_now --grep=\"BANDWIDTH_(IN|OUT):\""; + open($logfh, '-|', $journal_cmd) || + die("Cannot open $journal_cmd pipe: $!\n"); + } +else { + open($logfh, "<".$bandwidth_log) || + die("Cannot open $bandwidth_log: $!\n"); + } + # Scan the entries in the log file -&pre_process(); -open(LOG, "<".$bandwidth_log); -while() { +while(<$logfh>) { if (&process_line($_, \@hours, $time_now)) { # Found a valid line $lastline = $_; } elsif (/last\s+message\s+repeated\s+(\d+)/) { # re-process the last line N-1 times - for($i=0; $i<$1-1; $i++) { + for(my $i=0; $i<$1-1; $i++) { &process_line($lastline, \@hours, $time_now); } } @@ -59,7 +77,7 @@ while() { #print "skipping $_"; } } -close(LOG); +close($logfh); # Save all hours foreach my $hour (@hours) { @@ -71,7 +89,14 @@ if (-r $bandwidth_log) { open(my $log, ">".$bandwidth_log); close($log); } -&foreign_call($syslog_module, "signal_syslog"); +&foreign_call($syslog_module, "signal_syslog") if (!$syslog_journald); + +# Save last collection time to start from here next time +if ($syslog_journald && @hours) { + &lock_file($timestamp_file); + &write_file_contents($timestamp_file, $time_now); + &unlock_file($timestamp_file); + } # Remove PID file unlink($pid_file); From cf79c33e286613bb806056e50532a627b3310390 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 20 Jan 2025 18:39:21 +0200 Subject: [PATCH 14/18] Fix to use var directory for storing data --- bandwidth/bandwidth-lib.pl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 7c7333086..47ac21d41 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -26,9 +26,12 @@ else { %access = &get_module_acl(); $bandwidth_log = $config{'bandwidth_log'} || "/var/log/bandwidth"; -$hours_dir = $config{'bandwidth_dir'} || "$module_config_directory/hours"; +$hours_dir = $config{'bandwidth_dir'} || + (-e "$module_config_directory/hours" ? + "$module_config_directory/hours" : + "$module_var_directory/hours"); $cron_cmd = "$module_config_directory/rotate.pl"; -$pid_file = "$module_config_directory/rotate.pid"; +$pid_file = "$module_var_directory/rotate.pid"; # list_hours() # Returns a list of all hours for which traffic is available From 72fef64c4d55c611f5746728d4c866fd10f58612 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 21 Jan 2025 00:00:02 +0200 Subject: [PATCH 15/18] Fix variable being in the wrong scope --- bandwidth/rotate.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bandwidth/rotate.pl b/bandwidth/rotate.pl index 45b579909..72004a0a6 100755 --- a/bandwidth/rotate.pl +++ b/bandwidth/rotate.pl @@ -6,9 +6,9 @@ use Time::Local; require './bandwidth-lib.pl'; our (%config, $module_config_file, $module_var_directory, $pid_file, - $syslog_module, $syslog_journald); + $syslog_module, $syslog_journald, $bandwidth_log); -my ($logfh, $timestamp_file, $bandwidth_log, $lastline); +my ($logfh, $timestamp_file, $lastline); # Detect firewall system if needed if (!$config{'firewall_system'}) { From 5d31698099121fa6372a8501dc056d6d2bab51f6 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 21 Jan 2025 00:17:25 +0200 Subject: [PATCH 16/18] Fix to always prioritize Journald --- bandwidth/bandwidth-lib.pl | 10 +++++----- bandwidth/index.cgi | 10 +++++----- bandwidth/setup.cgi | 10 +++++----- bandwidth/turnoff.cgi | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 47ac21d41..0bf5e15a1 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -8,7 +8,11 @@ BEGIN { push(@INC, ".."); }; use WebminCore; &init_config(); -if (&foreign_installed("syslog-ng", 1) == 2) { +if (&has_command('journalctl')) { + $syslog_module = undef; + $syslog_journald = "journald" ; + } +elsif (&foreign_installed("syslog-ng", 1) == 2) { &foreign_require("syslog-ng"); $syslog_module = "syslog-ng"; } @@ -16,10 +20,6 @@ elsif (&foreign_installed("syslog")) { &foreign_require("syslog"); $syslog_module = "syslog"; } -else { - $syslog_module = undef; - $syslog_journald = "journald" if (&has_command('journalctl')); - } &foreign_require("cron", "cron-lib.pl"); &foreign_require("net", "net-lib.pl"); diff --git a/bandwidth/index.cgi b/bandwidth/index.cgi index cc86c7b65..43f68fbd3 100755 --- a/bandwidth/index.cgi +++ b/bandwidth/index.cgi @@ -70,7 +70,11 @@ else { # Make sure the needed firewall rules and syslog entry are in place $missingrule = !&check_rules(); -if ($syslog_module eq "syslog") { +if ($syslog_journald) { + # Systemd journal + $sysconf = 1; # nothing to do + } +elsif ($syslog_module eq "syslog") { # Normal syslog $conf = &syslog::get_config(); $sysconf = &find_sysconf($conf); @@ -81,10 +85,6 @@ elsif ($syslog_module eq "syslog-ng") { ($ngdest, $ngfilter, $nglog) = &find_sysconf_ng($conf); $sysconf = $ngdest && $ngfilter && $nglog; } -elsif ($syslog_journald) { - # Systemd journal - $sysconf = 1; # nothing to do - } if (($missingrule || !$sysconf) && $access{'setup'}) { # Something is missing .. offer to set up diff --git a/bandwidth/setup.cgi b/bandwidth/setup.cgi index e8db64ee5..43d0a30c3 100755 --- a/bandwidth/setup.cgi +++ b/bandwidth/setup.cgi @@ -13,7 +13,11 @@ $iface =~ /^\S+$/ || &error($text{'setup_eiface'}); $err = &setup_rules($iface); &error($err) if ($err); -if ($syslog_module eq "syslog") { +if ($syslog_journald) { + # Systemd journal + # No setup needed + } +elsif ($syslog_module eq "syslog") { # Add syslog entry $conf = &syslog::get_config(); $sysconf = &find_sysconf($conf); @@ -93,10 +97,6 @@ elsif ($syslog_module eq "syslog-ng") { } &unlock_file($syslog_ng::config{'syslogng_conf'}); } -elsif ($syslog_journald) { - # Systemd journal - # No setup needed -} # Save the interface &lock_file($module_config_file); diff --git a/bandwidth/turnoff.cgi b/bandwidth/turnoff.cgi index b320f365b..7c01f6448 100755 --- a/bandwidth/turnoff.cgi +++ b/bandwidth/turnoff.cgi @@ -9,7 +9,11 @@ $access{'setup'} || &error($text{'turnoff_ecannot'}); $err = &delete_rules(); &error($err) if ($err); -if ($syslog_module eq "syslog") { +if ($syslog_journald) { + # Systemd journal + # Nothing to do + } +elsif ($syslog_module eq "syslog") { # Remove syslog entry $conf = &syslog::get_config(); $sysconf = &find_sysconf($conf); @@ -37,10 +41,6 @@ elsif ($syslog_module eq "syslog-ng") { } &unlock_file($syslog_ng::config{'syslogng_conf'}); } -elsif ($syslog_journald) { - # Systemd journal - # Nothing to do -} # Remove rotation cron job $job = &find_cron_job(); From dd4ae0964283af967675a3a8fb3a530a6fe5669c Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 21 Jan 2025 01:31:43 +0200 Subject: [PATCH 17/18] Add endlessly better graphs filling and positioning --- bandwidth/index.cgi | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bandwidth/index.cgi b/bandwidth/index.cgi index 43f68fbd3..e654ebd31 100755 --- a/bandwidth/index.cgi +++ b/bandwidth/index.cgi @@ -378,10 +378,13 @@ if (@hours) { push(@cols, $k); } my $bar = sprintf - "", + "". + "", $max ? int($width * $icount{$k}/$max)+1 : 1; $bar .= sprintf - "", + "", $max ? int($width * $ocount{$k}/$max)+1 : 1; push(@cols, $bar); push(@cols, &nice_size($icount{$k}), From 1ce380f0373491883ebd0e8610044793038d56be Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Thu, 23 Jan 2025 12:01:32 +0200 Subject: [PATCH 18/18] Fix to assume that an old process can still be running during upgrade --- bandwidth/bandwidth-lib.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bandwidth/bandwidth-lib.pl b/bandwidth/bandwidth-lib.pl index 0bf5e15a1..265e5cb7f 100755 --- a/bandwidth/bandwidth-lib.pl +++ b/bandwidth/bandwidth-lib.pl @@ -31,7 +31,9 @@ $hours_dir = $config{'bandwidth_dir'} || "$module_config_directory/hours" : "$module_var_directory/hours"); $cron_cmd = "$module_config_directory/rotate.pl"; -$pid_file = "$module_var_directory/rotate.pid"; +$pid_file = -e "$module_config_directory/rotate.pid" ? + "$module_config_directory/rotate.pid" : + "$module_var_directory/rotate.pid"; # list_hours() # Returns a list of all hours for which traffic is available