]>
Commit | Line | Data |
---|---|---|
5bdc31fb AD |
1 | package PVE::Firewall::Helpers; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
8bd9b3e4 CE |
6 | use Date::Parse qw(str2time); |
7 | use Errno qw(ENOENT); | |
8 | use File::Basename qw(fileparse); | |
9 | use IO::Zlib; | |
21d5ba9c | 10 | use PVE::Cluster; |
5bdc31fb AD |
11 | use PVE::Tools qw(file_get_contents file_set_contents); |
12 | ||
13 | use base 'Exporter'; | |
14 | our @EXPORT_OK = qw( | |
21d5ba9c | 15 | lock_vmfw_conf |
5bdc31fb AD |
16 | remove_vmfw_conf |
17 | clone_vmfw_conf | |
b06a8c2d | 18 | collect_refs |
5bdc31fb AD |
19 | ); |
20 | ||
21 | my $pvefw_conf_dir = "/etc/pve/firewall"; | |
22 | ||
21d5ba9c TL |
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 | ||
5bdc31fb AD |
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 | ||
8bd9b3e4 CE |
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 | ||
b06a8c2d LN |
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 | ||
21d5ba9c | 172 | 1; |