]> git.proxmox.com Git - pve-firewall.git/blob - src/PVE/Firewall/Helpers.pm
bump version to 5.0.7
[pve-firewall.git] / src / PVE / Firewall / Helpers.pm
1 package PVE::Firewall::Helpers;
2
3 use strict;
4 use warnings;
5
6 use Date::Parse qw(str2time);
7 use Errno qw(ENOENT);
8 use File::Basename qw(fileparse);
9 use IO::Zlib;
10 use PVE::Cluster;
11 use PVE::Tools qw(file_get_contents file_set_contents);
12
13 use base 'Exporter';
14 our @EXPORT_OK = qw(
15 lock_vmfw_conf
16 remove_vmfw_conf
17 clone_vmfw_conf
18 collect_refs
19 );
20
21 my $pvefw_conf_dir = "/etc/pve/firewall";
22
23 sub lock_vmfw_conf {
24 my ($vmid, $timeout, $code, @param) = @_;
25
26 die "can't lock VM firewall config for undefined VMID\n"
27 if !defined($vmid);
28
29 my $res = PVE::Cluster::cfs_lock_firewall("vm-$vmid", $timeout, $code, @param);
30 die $@ if $@;
31
32 return $res;
33 }
34
35 sub remove_vmfw_conf {
36 my ($vmid) = @_;
37
38 my $vmfw_conffile = "$pvefw_conf_dir/$vmid.fw";
39
40 unlink $vmfw_conffile;
41 }
42
43 sub clone_vmfw_conf {
44 my ($vmid, $newid) = @_;
45
46 my $sourcevm_conffile = "$pvefw_conf_dir/$vmid.fw";
47 my $clonevm_conffile = "$pvefw_conf_dir/$newid.fw";
48
49 lock_vmfw_conf($newid, 10, sub {
50 if (-f $clonevm_conffile) {
51 unlink $clonevm_conffile;
52 }
53 if (-f $sourcevm_conffile) {
54 my $data = file_get_contents($sourcevm_conffile);
55 file_set_contents($clonevm_conffile, $data);
56 }
57 });
58 }
59
60 sub dump_fw_logfile {
61 my ($filename, $param, $callback) = @_;
62 my ($start, $limit, $since, $until) = $param->@{qw(start limit since until)};
63
64 my $filter = sub {
65 my ($line) = @_;
66
67 if (defined($callback)) {
68 return undef if !$callback->($line);
69 }
70
71 if ($since || $until) {
72 my @words = split / /, $line;
73 my $timestamp = str2time($words[3], $words[4]);
74 return undef if $since && $timestamp < $since;
75 return undef if $until && $timestamp > $until;
76 }
77
78 return $line;
79 };
80
81 if (!defined($since) && !defined($until)) {
82 return PVE::Tools::dump_logfile($filename, $start, $limit, $filter);
83 }
84
85 my %state = (
86 'count' => 0,
87 'lines' => [],
88 'start' => $start,
89 'limit' => $limit,
90 );
91
92 # Take into consideration also rotated logs
93 my ($basename, $logdir, $type) = fileparse($filename);
94 my $regex = qr/^\Q$basename\E(\.[\d]+(\.gz)?)?$/;
95 my @files = ();
96
97 PVE::Tools::dir_glob_foreach($logdir, $regex, sub {
98 my ($file) = @_;
99 push @files, $file;
100 });
101
102 @files = reverse sort @files;
103
104 my $filecount = 0;
105 for my $filename (@files) {
106 $state{'final'} = $filecount == $#files;
107 $filecount++;
108
109 my $fh;
110 if ($filename =~ /\.gz$/) {
111 $fh = IO::Zlib->new($logdir.$filename, "r");
112 } else {
113 $fh = IO::File->new($logdir.$filename, "r");
114 }
115
116 if (!$fh) {
117 # If file vanished since reading dir entries, ignore
118 next if $!{ENOENT};
119
120 my $lines = $state{'lines'};
121 my $count = ++$state{'count'};
122 push @$lines, ($count, { n => $count, t => "unable to open file - $!"});
123 last;
124 }
125
126 PVE::Tools::dump_logfile_by_filehandle($fh, $filter, \%state);
127
128 close($fh);
129 }
130
131 return ($state{'count'}, $state{'lines'});
132 }
133
134 sub collect_refs {
135 my ($conf, $type, $scope) = @_;
136
137
138 my $res = [];
139
140 if (!$type || $type eq 'ipset') {
141 foreach my $name (keys %{$conf->{ipset}}) {
142 my $data = {
143 type => 'ipset',
144 name => $name,
145 ref => "+$name",
146 scope => $scope,
147 };
148 if (my $comment = $conf->{ipset_comments}->{$name}) {
149 $data->{comment} = $comment;
150 }
151 push @$res, $data;
152 }
153 }
154
155 if (!$type || $type eq 'alias') {
156 foreach my $name (keys %{$conf->{aliases}}) {
157 my $e = $conf->{aliases}->{$name};
158 my $data = {
159 type => 'alias',
160 name => $name,
161 ref => $name,
162 scope => $scope,
163 };
164 $data->{comment} = $e->{comment} if $e->{comment};
165 push @$res, $data;
166 }
167 }
168
169 return $res;
170 }
171
172 1;