* [pve-devel] [PATCH common firewall] Optonal `since` and `until` firewall log filtering
@ 2023-01-09 15:07 Christian Ebner
2023-01-09 15:07 ` [pve-devel] [PATCH firewall 1/1] api: Add optional parameters `since` and `until` for timestamp filter Christian Ebner
2023-01-09 15:07 ` [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles Christian Ebner
0 siblings, 2 replies; 6+ messages in thread
From: Christian Ebner @ 2023-01-09 15:07 UTC (permalink / raw)
To: pve-devel
This patch series introduces 2 optional api parameters `since` and `until` to
firewall log endpoints, in order to make them filterable.
Filtering of the firewall logs is performed by a callback function.
If the `include_rotated_logs` flag is set, also rotated logfiles are included.
---
Changes since RFC version:
- common: Use callback function filter instead of `since` `until` params
- common: code reuse for `dump_logfile` and `dump_fw_logfile`
- firewall: Style fixes and use of callback function
common:
Christian Ebner (1):
tools: Add callback based filtering for firewall logfiles
src/PVE/Tools.pm | 108 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 91 insertions(+), 17 deletions(-)
firewall:
Christian Ebner (1):
api: Add optional parameters `since` and `until` for timestamp filter
src/PVE/API2/Firewall/Host.pm | 34 ++++++++++++++++++++++++++++-
src/PVE/API2/Firewall/VM.pm | 40 +++++++++++++++++++++++++++++++----
2 files changed, 69 insertions(+), 5 deletions(-)
--
2.30.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* [pve-devel] [PATCH firewall 1/1] api: Add optional parameters `since` and `until` for timestamp filter
2023-01-09 15:07 [pve-devel] [PATCH common firewall] Optonal `since` and `until` firewall log filtering Christian Ebner
@ 2023-01-09 15:07 ` Christian Ebner
2023-01-09 15:07 ` [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles Christian Ebner
1 sibling, 0 replies; 6+ messages in thread
From: Christian Ebner @ 2023-01-09 15:07 UTC (permalink / raw)
To: pve-devel
The optional unix epoch timestamps parameters `since` and `until` are introduced
in order to filter firewall logs files. If one of these flags is set, also
rotated logfiles are included.
Filtering is now performed based on a callback function passed to
`dump_fw_logfile`.
This patch depends on the corresponding patch in the pve-common repository.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
src/PVE/API2/Firewall/Host.pm | 34 ++++++++++++++++++++++++++++-
src/PVE/API2/Firewall/VM.pm | 40 +++++++++++++++++++++++++++++++----
2 files changed, 69 insertions(+), 5 deletions(-)
diff --git a/src/PVE/API2/Firewall/Host.pm b/src/PVE/API2/Firewall/Host.pm
index dfeccd0..cec440d 100644
--- a/src/PVE/API2/Firewall/Host.pm
+++ b/src/PVE/API2/Firewall/Host.pm
@@ -11,6 +11,7 @@ use PVE::Firewall;
use PVE::API2::Firewall::Rules;
+use Date::Parse qw(str2time);
use base qw(PVE::RESTHandler);
__PACKAGE__->register_method ({
@@ -172,6 +173,18 @@ __PACKAGE__->register_method({
minimum => 0,
optional => 1,
},
+ since => {
+ type => 'integer',
+ minimum => 0,
+ description => "Display log since this UNIX epoch.",
+ optional => 1,
+ },
+ until => {
+ type => 'integer',
+ minimum => 0,
+ description => "Display log until this UNIX epoch.",
+ optional => 1,
+ },
},
},
returns => {
@@ -196,8 +209,27 @@ __PACKAGE__->register_method({
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
my $node = $param->{node};
+ my $filename = "/var/log/pve-firewall.log";
+ my ($start, $limit, $since, $until) =
+ $param->@{qw(start limit since until)};
+
+ my $filter = sub {
+ my ($line) = @_;
+
+ if ($since || $until) {
+ my @words = split / /, $line;
+ my $timestamp = str2time($words[3], $words[4]);
+ return undef if $since && $timestamp < $since;
+ return undef if $until && $timestamp > $until;
+ }
+
+ return $line;
+ };
+
+ my $include_rotated_logs = defined($since) || defined($until);
- my ($count, $lines) = PVE::Tools::dump_logfile("/var/log/pve-firewall.log", $param->{start}, $param->{limit});
+ my ($count, $lines) = PVE::Tools::dump_fw_logfile(
+ $filename, $start, $limit, $filter, $include_rotated_logs);
$rpcenv->set_result_attrib('total', $count);
diff --git a/src/PVE/API2/Firewall/VM.pm b/src/PVE/API2/Firewall/VM.pm
index 48b8c5f..f245788 100644
--- a/src/PVE/API2/Firewall/VM.pm
+++ b/src/PVE/API2/Firewall/VM.pm
@@ -11,6 +11,7 @@ use PVE::API2::Firewall::Rules;
use PVE::API2::Firewall::Aliases;
+use Date::Parse qw(str2time);
use base qw(PVE::RESTHandler);
my $option_properties = $PVE::Firewall::vm_option_properties;
@@ -176,6 +177,18 @@ sub register_handlers {
minimum => 0,
optional => 1,
},
+ since => {
+ type => 'integer',
+ minimum => 0,
+ description => "Display log since this UNIX epoch.",
+ optional => 1,
+ },
+ until => {
+ type => 'integer',
+ minimum => 0,
+ description => "Display log until this UNIX epoch.",
+ optional => 1,
+ },
},
},
returns => {
@@ -199,11 +212,30 @@ sub register_handlers {
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
- my $vmid = $param->{vmid};
+ my $filename = "/var/log/pve-firewall.log";
+ my ($start, $limit, $vmid, $since, $until) =
+ $param->@{qw(start limit vmid since until)};
+
+ my $filter = sub {
+ my ($line) = @_;
+ my $reg = "^$vmid ";
+
+ return undef if $line !~ m/$reg/;
+
+ if ($since || $until) {
+ my @words = split / /, $line;
+ my $timestamp = str2time($words[3], $words[4]);
+ return undef if $since && $timestamp < $since;
+ return undef if $until && $timestamp > $until;
+ }
+
+ return $line;
+ };
+
+ my $include_rotated_logs = defined($since) || defined($until);
- my ($count, $lines) = PVE::Tools::dump_logfile("/var/log/pve-firewall.log",
- $param->{start}, $param->{limit},
- "^$vmid ");
+ my ($count, $lines) = PVE::Tools::dump_fw_logfile(
+ $filename, $start, $limit, $filter, $include_rotated_logs);
$rpcenv->set_result_attrib('total', $count);
--
2.30.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles
2023-01-09 15:07 [pve-devel] [PATCH common firewall] Optonal `since` and `until` firewall log filtering Christian Ebner
2023-01-09 15:07 ` [pve-devel] [PATCH firewall 1/1] api: Add optional parameters `since` and `until` for timestamp filter Christian Ebner
@ 2023-01-09 15:07 ` Christian Ebner
2023-01-10 12:36 ` Wolfgang Bumiller
1 sibling, 1 reply; 6+ messages in thread
From: Christian Ebner @ 2023-01-09 15:07 UTC (permalink / raw)
To: pve-devel
This patch introduces callback based filtering functionality for firewall logs.
In addition, the contents of rotated logfiles are included by setting the
`include_rotated_logs` flag.
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
src/PVE/Tools.pm | 108 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 91 insertions(+), 17 deletions(-)
diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
index cdbee6d..cafc2f7 100644
--- a/src/PVE/Tools.pm
+++ b/src/PVE/Tools.pm
@@ -17,6 +17,7 @@ use IO::Handle;
use IO::Pipe;
use IO::Select;
use IO::Socket::IP;
+use IO::Zlib;
use IPC::Open3;
use JSON;
use POSIX qw(EINTR EEXIST EOPNOTSUPP);
@@ -1265,29 +1266,19 @@ sub split_args {
return $str ? [ Text::ParseWords::shellwords($str) ] : [];
}
-sub dump_logfile {
- my ($filename, $start, $limit, $filter) = @_;
-
- my $lines = [];
- my $count = 0;
-
- my $fh = IO::File->new($filename, "r");
- if (!$fh) {
- $count++;
- push @$lines, { n => $count, t => "unable to open file - $!"};
- return ($count, $lines);
- }
+sub dump_logfile_by_filehandle {
+ my ($fh, $start, $limit, $filter, $count, $lines, $read_until_end) = @_;
- $start = $start // 0;
- $limit = $limit // 50;
-
- my $read_until_end = $limit == 0;
my $line;
if ($filter) {
# duplicate code, so that we do not slow down normal path
while (defined($line = <$fh>)) {
- next if $line !~ m/$filter/;
+ if (ref($filter) eq 'CODE') {
+ next if !$filter->($line);
+ } else {
+ next if $line !~ m/$filter/;
+ }
next if $count++ < $start;
if (!$read_until_end) {
next if $limit <= 0;
@@ -1308,6 +1299,29 @@ sub dump_logfile {
}
}
+ return ($count, $lines, $limit);
+}
+
+sub dump_logfile {
+ my ($filename, $start, $limit, $filter) = @_;
+
+ $start = $start // 0;
+ $limit = $limit // 50;
+
+ my $read_until_end = $limit == 0;
+ my $lines = [];
+ my $count = 0;
+
+ my $fh = IO::File->new($filename, "r");
+ if (!$fh) {
+ $count++;
+ push @$lines, { n => $count, t => "unable to open file - $!"};
+ return ($count, $lines);
+ }
+
+ ($count, $lines, $limit) = dump_logfile_by_filehandle(
+ $fh, $start, $limit, $filter, $count, $lines, $read_until_end);
+
close($fh);
# HACK: ExtJS store.guaranteeRange() does not like empty array
@@ -1320,6 +1334,66 @@ sub dump_logfile {
return ($count, $lines);
}
+sub dump_fw_logfile {
+ my ($filename, $start, $limit, $filter, $include_rotated_logs) = @_;
+
+ if (!$include_rotated_logs) {
+ return dump_logfile($filename, $start, $limit, $filter);
+ }
+
+ $start = $start // 0;
+ $limit = $limit // 50;
+
+ # Take into consideration also rotated logs
+ my ($basename, $logdir, $type) = fileparse($filename);
+ my @files = ();
+
+ opendir(LOGDIR, $logdir) || die "Cannot open $logdir";
+ my $entry;
+ while ($entry = readdir(LOGDIR)) {
+ my $namefilter = $basename."*";
+ next if $entry !~ m/$namefilter/;
+ push @files, $entry;
+ }
+ closedir(LOGDIR);
+ @files = reverse sort @files;
+
+ my $read_until_end = $limit == 0;
+ my $lines = [];
+ my $count = 0;
+
+ foreach (@files) {
+ my ($base, $path, $type) = fileparse($_, ".gz");
+
+ my $fh;
+ if ($type eq '.gz') {
+ $fh = IO::Zlib->new($logdir.$_, "r");
+ } else {
+ $fh = IO::File->new($logdir.$_, "r");
+ }
+
+ if (!$fh) {
+ $count++;
+ push @$lines, { n => $count, t => "unable to open file - $!"};
+ return ($count, $lines);
+ }
+
+ ($count, $lines, $limit) = dump_logfile_by_filehandle(
+ $fh, $start, $limit, $filter, $count, $lines, $read_until_end);
+
+ close($fh);
+ }
+
+ # HACK: ExtJS store.guaranteeRange() does not like empty array
+ # so we add a line
+ if (!$count) {
+ $count++;
+ push @$lines, { n => $count, t => "no content"};
+ }
+
+ return ($count, $lines);
+}
+
sub dump_journal {
my ($start, $limit, $since, $until, $service) = @_;
--
2.30.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles
2023-01-09 15:07 ` [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles Christian Ebner
@ 2023-01-10 12:36 ` Wolfgang Bumiller
2023-01-11 8:36 ` Christian Ebner
0 siblings, 1 reply; 6+ messages in thread
From: Wolfgang Bumiller @ 2023-01-10 12:36 UTC (permalink / raw)
To: Christian Ebner; +Cc: pve-devel
On Mon, Jan 09, 2023 at 04:07:06PM +0100, Christian Ebner wrote:
> This patch introduces callback based filtering functionality for firewall logs.
> In addition, the contents of rotated logfiles are included by setting the
> `include_rotated_logs` flag.
>
> Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
> ---
> src/PVE/Tools.pm | 108 +++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 91 insertions(+), 17 deletions(-)
>
> diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
> index cdbee6d..cafc2f7 100644
> --- a/src/PVE/Tools.pm
> +++ b/src/PVE/Tools.pm
> @@ -17,6 +17,7 @@ use IO::Handle;
> use IO::Pipe;
> use IO::Select;
> use IO::Socket::IP;
> +use IO::Zlib;
> use IPC::Open3;
> use JSON;
> use POSIX qw(EINTR EEXIST EOPNOTSUPP);
> @@ -1265,29 +1266,19 @@ sub split_args {
> return $str ? [ Text::ParseWords::shellwords($str) ] : [];
> }
>
> -sub dump_logfile {
> - my ($filename, $start, $limit, $filter) = @_;
> -
> - my $lines = [];
> - my $count = 0;
> -
> - my $fh = IO::File->new($filename, "r");
> - if (!$fh) {
> - $count++;
> - push @$lines, { n => $count, t => "unable to open file - $!"};
> - return ($count, $lines);
> - }
> +sub dump_logfile_by_filehandle {
> + my ($fh, $start, $limit, $filter, $count, $lines, $read_until_end) = @_;
^ I think it'll be easier if we move start, limit, count and lines into
a `$state` hash, and keep $read_until_end internal here.
This way we don't need to pass them back and forth via paramters &
return values.
We can pull them out as needed here and also do the defaults, eg.
my $limit = ($state->{limit} //= 50);
my $count = ($state->{count} //= 0);
my $lines = ($state->{lines} //= []);
my $read_until_end = $limit == 0;
and for $start I think it should only hit the first file anyway:
my $start = delete($state->{start}) // 0;
>
> - $start = $start // 0;
> - $limit = $limit // 50;
> -
> - my $read_until_end = $limit == 0;
> my $line;
>
> if ($filter) {
> # duplicate code, so that we do not slow down normal path
> while (defined($line = <$fh>)) {
> - next if $line !~ m/$filter/;
> + if (ref($filter) eq 'CODE') {
> + next if !$filter->($line);
> + } else {
> + next if $line !~ m/$filter/;
> + }
> next if $count++ < $start;
> if (!$read_until_end) {
> next if $limit <= 0;
> @@ -1308,6 +1299,29 @@ sub dump_logfile {
> }
> }
and then fill the numbers back into the hash here and return nothing.
($lines would be a reference, so no need to write it back)
>
> + return ($count, $lines, $limit);
> +}
> +
> +sub dump_logfile {
> + my ($filename, $start, $limit, $filter) = @_;
> +
> + $start = $start // 0;
> + $limit = $limit // 50;
> +
> + my $read_until_end = $limit == 0;
> + my $lines = [];
> + my $count = 0;
^ then the above won't be needed
> +
> + my $fh = IO::File->new($filename, "r");
> + if (!$fh) {
> + $count++;
> + push @$lines, { n => $count, t => "unable to open file - $!"};
> + return ($count, $lines);
^ this doesn't really need $count or $lines since they're 0 and [], so
this could just do
return (1, [{ n => 1, t => "..."}]);
directly
> + }
> +
> + ($count, $lines, $limit) = dump_logfile_by_filehandle(
> + $fh, $start, $limit, $filter, $count, $lines, $read_until_end);
> +
> close($fh);
>
> # HACK: ExtJS store.guaranteeRange() does not like empty array
^ Maybe the hack part should happen in `dump_logfile_by_filehandle` by
passing a `$final` parameter to?
> @@ -1320,6 +1334,66 @@ sub dump_logfile {
> return ($count, $lines);
> }
>
> +sub dump_fw_logfile {
Because then *this* one here an actually go into pve-firewall and also
drop the start/limit default logic.
> + my ($filename, $start, $limit, $filter, $include_rotated_logs) = @_;
> +
> + if (!$include_rotated_logs) {
> + return dump_logfile($filename, $start, $limit, $filter);
> + }
> +
> + $start = $start // 0;
> + $limit = $limit // 50;
> +
> + # Take into consideration also rotated logs
^ trailing whitespace
> + my ($basename, $logdir, $type) = fileparse($filename);
> + my @files = ();
^ trailing whitespace
> +
> + opendir(LOGDIR, $logdir) || die "Cannot open $logdir";
> + my $entry;
> + while ($entry = readdir(LOGDIR)) {
> + my $namefilter = $basename."*";
^ This regex looks wrong (glob vs regex?) and should be built outside
the loop as it stays the same
Maybe our `dir_glob_foreach()` can shorten this whole block?
> + next if $entry !~ m/$namefilter/;
> + push @files, $entry;
> + }
> + closedir(LOGDIR);
> + @files = reverse sort @files;
> +
> + my $read_until_end = $limit == 0;
> + my $lines = [];
> + my $count = 0;
> +
> + foreach (@files) {
IMO a bit too large to use $_.
Use
for my $filename (@$files) {
> + my ($base, $path, $type) = fileparse($_, ".gz");
You use neither $base nor $path, a simple `if ($filename =~ /\.gz$/) {`
should suffice.
> +
> + my $fh;
> + if ($type eq '.gz') {
> + $fh = IO::Zlib->new($logdir.$_, "r");
> + } else {
> + $fh = IO::File->new($logdir.$_, "r");
> + }
> +
> + if (!$fh) {
> + $count++;
> + push @$lines, { n => $count, t => "unable to open file - $!"};
I'd like this error to only happen if the error isn't ENOENT, as that
would just be a TOCTOU race against a cleanup.
> + return ($count, $lines);
^ And just break out of the loop instead of returning.
> + }
> +
> + ($count, $lines, $limit) = dump_logfile_by_filehandle(
> + $fh, $start, $limit, $filter, $count, $lines, $read_until_end);
> +
> + close($fh);
> + }
> +
> + # HACK: ExtJS store.guaranteeRange() does not like empty array
> + # so we add a line
> + if (!$count) {
> + $count++;
> + push @$lines, { n => $count, t => "no content"};
> + }
> +
> + return ($count, $lines);
> +}
> +
> sub dump_journal {
> my ($start, $limit, $since, $until, $service) = @_;
>
> --
> 2.30.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles
2023-01-10 12:36 ` Wolfgang Bumiller
@ 2023-01-11 8:36 ` Christian Ebner
2023-01-11 8:42 ` Wolfgang Bumiller
0 siblings, 1 reply; 6+ messages in thread
From: Christian Ebner @ 2023-01-11 8:36 UTC (permalink / raw)
To: Wolfgang Bumiller; +Cc: pve-devel
Thank you for the detailed review Wolfgang, I am still unsure about one of your comments, see below. Maybe you could clarify.
Thx.
> On 10.01.2023 13:36 CET Wolfgang Bumiller <w.bumiller@proxmox.com> wrote:
>
>
> On Mon, Jan 09, 2023 at 04:07:06PM +0100, Christian Ebner wrote:
> > This patch introduces callback based filtering functionality for firewall logs.
> > In addition, the contents of rotated logfiles are included by setting the
> > `include_rotated_logs` flag.
> >
> > Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
> > ---
> > src/PVE/Tools.pm | 108 +++++++++++++++++++++++++++++++++++++++--------
> > 1 file changed, 91 insertions(+), 17 deletions(-)
> >
> > diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
> > index cdbee6d..cafc2f7 100644
> > --- a/src/PVE/Tools.pm
> > +++ b/src/PVE/Tools.pm
> > @@ -17,6 +17,7 @@ use IO::Handle;
> > use IO::Pipe;
> > use IO::Select;
> > use IO::Socket::IP;
> > +use IO::Zlib;
> > use IPC::Open3;
> > use JSON;
> > use POSIX qw(EINTR EEXIST EOPNOTSUPP);
> > @@ -1265,29 +1266,19 @@ sub split_args {
> > return $str ? [ Text::ParseWords::shellwords($str) ] : [];
> > }
> >
> > -sub dump_logfile {
> > - my ($filename, $start, $limit, $filter) = @_;
> > -
> > - my $lines = [];
> > - my $count = 0;
> > -
> > - my $fh = IO::File->new($filename, "r");
> > - if (!$fh) {
> > - $count++;
> > - push @$lines, { n => $count, t => "unable to open file - $!"};
> > - return ($count, $lines);
> > - }
> > +sub dump_logfile_by_filehandle {
> > + my ($fh, $start, $limit, $filter, $count, $lines, $read_until_end) = @_;
>
> ^ I think it'll be easier if we move start, limit, count and lines into
> a `$state` hash, and keep $read_until_end internal here.
> This way we don't need to pass them back and forth via paramters &
> return values.
>
> We can pull them out as needed here and also do the defaults, eg.
>
> my $limit = ($state->{limit} //= 50);
> my $count = ($state->{count} //= 0);
> my $lines = ($state->{lines} //= []);
> my $read_until_end = $limit == 0;
>
I don't see why $start should only be set to a value != 0 (if given anyway) for the first file.
The line/count the output should start from might be located in any file. So
my $start = ($state->{start} //= 0);
would make more sense to me, or am I missing something?
> and for $start I think it should only hit the first file anyway:
>
> my $start = delete($state->{start}) // 0;
>
> >
> > - $start = $start // 0;
> > - $limit = $limit // 50;
> > -
> > - my $read_until_end = $limit == 0;
> > my $line;
> >
> > if ($filter) {
> > # duplicate code, so that we do not slow down normal path
> > while (defined($line = <$fh>)) {
> > - next if $line !~ m/$filter/;
> > + if (ref($filter) eq 'CODE') {
> > + next if !$filter->($line);
> > + } else {
> > + next if $line !~ m/$filter/;
> > + }
> > next if $count++ < $start;
> > if (!$read_until_end) {
> > next if $limit <= 0;
> > @@ -1308,6 +1299,29 @@ sub dump_logfile {
> > }
> > }
>
> and then fill the numbers back into the hash here and return nothing.
> ($lines would be a reference, so no need to write it back)
> >
> > + return ($count, $lines, $limit);
> > +}
> > +
> > +sub dump_logfile {
> > + my ($filename, $start, $limit, $filter) = @_;
> > +
> > + $start = $start // 0;
> > + $limit = $limit // 50;
> > +
> > + my $read_until_end = $limit == 0;
> > + my $lines = [];
> > + my $count = 0;
>
> ^ then the above won't be needed
>
> > +
> > + my $fh = IO::File->new($filename, "r");
> > + if (!$fh) {
> > + $count++;
> > + push @$lines, { n => $count, t => "unable to open file - $!"};
> > + return ($count, $lines);
>
> ^ this doesn't really need $count or $lines since they're 0 and [], so
> this could just do
> return (1, [{ n => 1, t => "..."}]);
> directly
>
> > + }
> > +
> > + ($count, $lines, $limit) = dump_logfile_by_filehandle(
> > + $fh, $start, $limit, $filter, $count, $lines, $read_until_end);
> > +
> > close($fh);
> >
> > # HACK: ExtJS store.guaranteeRange() does not like empty array
>
> ^ Maybe the hack part should happen in `dump_logfile_by_filehandle` by
> passing a `$final` parameter to?
>
> > @@ -1320,6 +1334,66 @@ sub dump_logfile {
> > return ($count, $lines);
> > }
> >
> > +sub dump_fw_logfile {
>
> Because then *this* one here an actually go into pve-firewall and also
> drop the start/limit default logic.
>
> > + my ($filename, $start, $limit, $filter, $include_rotated_logs) = @_;
> > +
> > + if (!$include_rotated_logs) {
> > + return dump_logfile($filename, $start, $limit, $filter);
> > + }
> > +
> > + $start = $start // 0;
> > + $limit = $limit // 50;
> > +
> > + # Take into consideration also rotated logs
>
> ^ trailing whitespace
>
> > + my ($basename, $logdir, $type) = fileparse($filename);
> > + my @files = ();
>
> ^ trailing whitespace
>
> > +
> > + opendir(LOGDIR, $logdir) || die "Cannot open $logdir";
> > + my $entry;
> > + while ($entry = readdir(LOGDIR)) {
> > + my $namefilter = $basename."*";
>
> ^ This regex looks wrong (glob vs regex?) and should be built outside
> the loop as it stays the same
>
> Maybe our `dir_glob_foreach()` can shorten this whole block?
>
> > + next if $entry !~ m/$namefilter/;
> > + push @files, $entry;
> > + }
> > + closedir(LOGDIR);
> > + @files = reverse sort @files;
> > +
> > + my $read_until_end = $limit == 0;
> > + my $lines = [];
> > + my $count = 0;
> > +
> > + foreach (@files) {
>
> IMO a bit too large to use $_.
> Use
> for my $filename (@$files) {
>
> > + my ($base, $path, $type) = fileparse($_, ".gz");
>
> You use neither $base nor $path, a simple `if ($filename =~ /\.gz$/) {`
> should suffice.
>
> > +
> > + my $fh;
> > + if ($type eq '.gz') {
> > + $fh = IO::Zlib->new($logdir.$_, "r");
> > + } else {
> > + $fh = IO::File->new($logdir.$_, "r");
> > + }
> > +
> > + if (!$fh) {
> > + $count++;
> > + push @$lines, { n => $count, t => "unable to open file - $!"};
>
> I'd like this error to only happen if the error isn't ENOENT, as that
> would just be a TOCTOU race against a cleanup.
>
> > + return ($count, $lines);
>
> ^ And just break out of the loop instead of returning.
>
> > + }
> > +
> > + ($count, $lines, $limit) = dump_logfile_by_filehandle(
> > + $fh, $start, $limit, $filter, $count, $lines, $read_until_end);
> > +
> > + close($fh);
> > + }
> > +
> > + # HACK: ExtJS store.guaranteeRange() does not like empty array
> > + # so we add a line
> > + if (!$count) {
> > + $count++;
> > + push @$lines, { n => $count, t => "no content"};
> > + }
> > +
> > + return ($count, $lines);
> > +}
> > +
> > sub dump_journal {
> > my ($start, $limit, $since, $until, $service) = @_;
> >
> > --
> > 2.30.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles
2023-01-11 8:36 ` Christian Ebner
@ 2023-01-11 8:42 ` Wolfgang Bumiller
0 siblings, 0 replies; 6+ messages in thread
From: Wolfgang Bumiller @ 2023-01-11 8:42 UTC (permalink / raw)
To: Christian Ebner; +Cc: pve-devel
> On 01/11/2023 9:36 AM CET Christian Ebner <c.ebner@proxmox.com> wrote:
>
>
> Thank you for the detailed review Wolfgang, I am still unsure about one of your comments, see below. Maybe you could clarify.
>
> Thx.
>
> > On 10.01.2023 13:36 CET Wolfgang Bumiller <w.bumiller@proxmox.com> wrote:
> >
> >
> > On Mon, Jan 09, 2023 at 04:07:06PM +0100, Christian Ebner wrote:
> > > This patch introduces callback based filtering functionality for firewall logs.
> > > In addition, the contents of rotated logfiles are included by setting the
> > > `include_rotated_logs` flag.
> > >
> > > Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
> > > ---
> > > src/PVE/Tools.pm | 108 +++++++++++++++++++++++++++++++++++++++--------
> > > 1 file changed, 91 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
> > > index cdbee6d..cafc2f7 100644
> > > --- a/src/PVE/Tools.pm
> > > +++ b/src/PVE/Tools.pm
> > > @@ -17,6 +17,7 @@ use IO::Handle;
> > > use IO::Pipe;
> > > use IO::Select;
> > > use IO::Socket::IP;
> > > +use IO::Zlib;
> > > use IPC::Open3;
> > > use JSON;
> > > use POSIX qw(EINTR EEXIST EOPNOTSUPP);
> > > @@ -1265,29 +1266,19 @@ sub split_args {
> > > return $str ? [ Text::ParseWords::shellwords($str) ] : [];
> > > }
> > >
> > > -sub dump_logfile {
> > > - my ($filename, $start, $limit, $filter) = @_;
> > > -
> > > - my $lines = [];
> > > - my $count = 0;
> > > -
> > > - my $fh = IO::File->new($filename, "r");
> > > - if (!$fh) {
> > > - $count++;
> > > - push @$lines, { n => $count, t => "unable to open file - $!"};
> > > - return ($count, $lines);
> > > - }
> > > +sub dump_logfile_by_filehandle {
> > > + my ($fh, $start, $limit, $filter, $count, $lines, $read_until_end) = @_;
> >
> > ^ I think it'll be easier if we move start, limit, count and lines into
> > a `$state` hash, and keep $read_until_end internal here.
> > This way we don't need to pass them back and forth via paramters &
> > return values.
> >
> > We can pull them out as needed here and also do the defaults, eg.
> >
> > my $limit = ($state->{limit} //= 50);
> > my $count = ($state->{count} //= 0);
> > my $lines = ($state->{lines} //= []);
> > my $read_until_end = $limit == 0;
> >
> I don't see why $start should only be set to a value != 0 (if given anyway) for the first file.
>
> The line/count the output should start from might be located in any file. So
> my $start = ($state->{start} //= 0);
> would make more sense to me, or am I missing something?
Yeah sorry I somehow missed the interaction with $count...
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-01-11 8:42 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-09 15:07 [pve-devel] [PATCH common firewall] Optonal `since` and `until` firewall log filtering Christian Ebner
2023-01-09 15:07 ` [pve-devel] [PATCH firewall 1/1] api: Add optional parameters `since` and `until` for timestamp filter Christian Ebner
2023-01-09 15:07 ` [pve-devel] [PATCH common 1/1] tools: Add callback based filtering for firewall logfiles Christian Ebner
2023-01-10 12:36 ` Wolfgang Bumiller
2023-01-11 8:36 ` Christian Ebner
2023-01-11 8:42 ` Wolfgang Bumiller
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal