]>
Commit | Line | Data |
---|---|---|
aff192e6 DM |
1 | package PVE::APLInfo; |
2 | ||
3 | use strict; | |
4 | use IO::File; | |
5 | use PVE::SafeSyslog; | |
aff192e6 | 6 | use LWP::UserAgent; |
aff192e6 DM |
7 | use POSIX qw(strftime); |
8 | ||
9 | my $logfile = "/var/log/pveam.log"; | |
c9164975 | 10 | my $aplinfodir = "/var/lib/pve-manager/apl-info"; |
aff192e6 DM |
11 | |
12 | # Default list of GPG keys allowed to sign aplinfo | |
13 | # | |
14 | #pub 1024D/5CAC72FE 2004-06-24 | |
15 | # Key fingerprint = 9ABD 7E02 AD24 3AD3 C2FB BCCC B0C1 CC22 5CAC 72FE | |
16 | #uid Proxmox Support Team <support@proxmox.com> | |
c9164975 DM |
17 | #pub 2048R/A16EB94D 2008-08-15 [expires: 2023-08-12] |
18 | # Key fingerprint = 694C FF26 795A 29BA E07B 4EB5 85C2 5E95 A16E B94D | |
19 | #uid Turnkey Linux Release Key <release@turnkeylinux.com> | |
aff192e6 DM |
20 | |
21 | my $valid_keys = { | |
22 | '9ABD7E02AD243AD3C2FBBCCCB0C1CC225CAC72FE' => 1, # fingerprint support@proxmox.com | |
23 | '25CAC72FE' => 1, # keyid support@proxmox.com | |
c9164975 DM |
24 | '694CFF26795A29BAE07B4EB585C25E95A16EB94D' => 1, # fingerprint release@turnkeylinux.com |
25 | 'A16EB94D' => 1, # keyid release@turnkeylinux.com | |
aff192e6 DM |
26 | }; |
27 | ||
28 | sub import_gpg_keys { | |
29 | ||
c9164975 | 30 | my @keyfiles = ('support@proxmox.com.pubkey', 'release@turnkeylinux.com.pubkey'); |
aff192e6 | 31 | |
c9164975 DM |
32 | foreach my $key (@keyfiles) { |
33 | my $fn = "/usr/share/doc/pve-manager/$key"; | |
34 | system ("/usr/bin/gpg --batch --no-tty --status-fd=1 -q " . | |
35 | "--logger-fd=1 --import $fn >>$logfile"); | |
36 | } | |
aff192e6 DM |
37 | } |
38 | ||
39 | sub logmsg { | |
40 | my ($logfd, $msg) = @_; | |
41 | ||
42 | chomp $msg; | |
43 | ||
44 | my $tstr = strftime ("%b %d %H:%M:%S", localtime); | |
45 | ||
46 | foreach my $line (split (/\n/, $msg)) { | |
47 | print $logfd "$tstr $line\n"; | |
48 | } | |
49 | } | |
50 | ||
6de794eb DM |
51 | sub read_aplinfo_from_fh { |
52 | my ($fh, $list, $source, $update) = @_; | |
c9164975 DM |
53 | |
54 | local $/ = ""; | |
55 | ||
6de794eb DM |
56 | while (my $rec = <$fh>) { |
57 | chomp $rec; | |
c9164975 | 58 | |
6de794eb DM |
59 | my $res = {}; |
60 | ||
61 | while ($rec) { | |
62 | ||
63 | if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) { | |
64 | $res->{headline} = $1; | |
65 | my $long = $2; | |
66 | $long =~ s/\n\s+/ /g; | |
67 | $long =~ s/^\s+//g; | |
68 | $long =~ s/\s+$//g; | |
69 | $res->{description} = $long; | |
70 | } elsif ($rec =~ s/^Version:\s*(.*\S)\s*\n//i) { | |
71 | my $version = $1; | |
72 | if ($version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)-(\d+)$/) { | |
73 | $res->{version} = $version; | |
c9164975 | 74 | } else { |
6de794eb DM |
75 | my $msg = "unable to parse appliance record: version = '$version'\n"; |
76 | $update ? die $msg : warn $msg; | |
c9164975 | 77 | } |
6de794eb DM |
78 | } elsif ($rec =~ s/^Type:\s*(.*\S)\s*\n//i) { |
79 | my $type = $1; | |
80 | if ($type =~ m/^(openvz|lxc)$/) { | |
81 | $res->{type} = $type; | |
c9164975 | 82 | } else { |
6de794eb DM |
83 | my $msg = "unable to parse appliance record: unknown type '$type'\n"; |
84 | $update ? die $msg : warn $msg; | |
c9164975 | 85 | } |
6de794eb DM |
86 | } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) { |
87 | $res->{lc $1} = $2; | |
c9164975 | 88 | } else { |
6de794eb | 89 | my $msg = "unable to parse appliance record: $rec\n"; |
c9164975 | 90 | $update ? die $msg : warn $msg; |
6de794eb DM |
91 | $res = {}; |
92 | last; | |
93 | } | |
94 | } | |
95 | ||
96 | if ($res->{'package'} eq 'pve-web-news' && $res->{description}) { | |
97 | $list->{'all'}->{$res->{'package'}} = $res; | |
98 | next; | |
99 | } | |
100 | ||
101 | $res->{section} = 'unknown' if !$res->{section}; | |
102 | ||
103 | if ($res->{'package'} && $res->{type} && $res->{os} && $res->{version} && | |
104 | $res->{infopage}) { | |
105 | my $template; | |
106 | if ($res->{location}) { | |
107 | $template = $res->{location}; | |
108 | $template =~ s|.*/([^/]+.tar.gz)|$1|; | |
109 | } else { | |
07c1e6b0 DM |
110 | my $arch = $res->{architecture} || 'i386'; |
111 | $template = "$res->{os}-$res->{package}_$res->{version}_$arch.tar.gz"; | |
6de794eb | 112 | $template =~ s/$res->{os}-$res->{os}-/$res->{os}-/; |
c9164975 | 113 | } |
6de794eb DM |
114 | $res->{source} = $source; |
115 | $res->{template} = $template; | |
116 | $list->{$res->{section}}->{$template} = $res; | |
117 | $list->{'all'}->{$template} = $res; | |
118 | } else { | |
119 | my $msg = "found incomplete appliance records\n"; | |
120 | $update ? die $msg : warn $msg; | |
c9164975 | 121 | } |
6de794eb DM |
122 | } |
123 | } | |
124 | ||
125 | sub read_aplinfo { | |
126 | my ($filename, $list, $source, $update) = @_; | |
127 | ||
128 | my $fh = IO::File->new("<$filename") || | |
129 | die "unable to open file '$filename' - $!\n"; | |
130 | ||
131 | eval { read_aplinfo_from_fh($fh, $list, $source, $update); }; | |
c9164975 DM |
132 | my $err = $@; |
133 | ||
134 | close($fh); | |
135 | ||
136 | die $err if $err; | |
137 | ||
138 | return $list; | |
139 | } | |
140 | ||
aff192e6 DM |
141 | sub url_get { |
142 | my ($ua, $url, $file, $logfh) = @_; | |
143 | ||
144 | my $req = HTTP::Request->new(GET => $url); | |
145 | ||
146 | logmsg ($logfh, "start download $url"); | |
147 | my $res = $ua->request($req, $file); | |
148 | ||
149 | if ($res->is_success) { | |
150 | logmsg ($logfh, "download finished: " . $res->status_line); | |
151 | return 0; | |
152 | } | |
153 | ||
154 | logmsg ($logfh, "download failed: " . $res->status_line); | |
155 | ||
156 | return 1; | |
157 | } | |
158 | ||
c9164975 DM |
159 | sub download_aplinfo { |
160 | my ($ua, $aplurl, $host, $logfd) = @_; | |
aff192e6 | 161 | |
aff192e6 DM |
162 | my $aplsrcurl = "$aplurl/aplinfo.dat.gz"; |
163 | my $aplsigurl = "$aplurl/aplinfo.dat.asc"; | |
164 | ||
c9164975 | 165 | my $tmp = "$aplinfodir/pveam-${host}.tmp.$$"; |
aff192e6 DM |
166 | my $tmpgz = "$tmp.gz"; |
167 | my $sigfn = "$tmp.asc"; | |
168 | ||
aff192e6 | 169 | eval { |
c9164975 DM |
170 | |
171 | if (url_get($ua, $aplsigurl, $sigfn, $logfd) != 0) { | |
172 | die "update failed - no signature file '$sigfn'\n"; | |
aff192e6 DM |
173 | } |
174 | ||
c9164975 DM |
175 | if (url_get($ua, $aplsrcurl, $tmpgz, $logfd) != 0) { |
176 | die "update failed - no data file '$aplsrcurl'\n"; | |
aff192e6 DM |
177 | } |
178 | ||
c9164975 | 179 | if (system("zcat -f $tmpgz >$tmp 2>/dev/null") != 0) { |
aff192e6 DM |
180 | die "update failed: unable to unpack '$tmpgz'\n"; |
181 | } | |
182 | ||
183 | # verify signature | |
184 | ||
c9164975 | 185 | my $cmd = "/usr/bin/gpg --verify --trust-model always --batch --no-tty --status-fd=1 -q " . |
aff192e6 DM |
186 | "--logger-fd=1 $sigfn $tmp"; |
187 | ||
c9164975 | 188 | open(CMD, "$cmd|") || |
aff192e6 DM |
189 | die "unable to execute '$cmd': $!\n"; |
190 | ||
191 | my $line; | |
192 | my $signer = ''; | |
c9164975 | 193 | while (defined($line = <CMD>)) { |
aff192e6 | 194 | chomp $line; |
c9164975 | 195 | logmsg($logfd, $line); |
aff192e6 DM |
196 | |
197 | # code borrowed from SA | |
198 | next if $line !~ /^\Q[GNUPG:]\E (?:VALID|GOOD)SIG (\S{8,40})/; | |
199 | my $key = $1; | |
200 | ||
201 | # we want either a keyid (8) or a fingerprint (40) | |
202 | if (length $key > 8 && length $key < 40) { | |
203 | substr($key, 8) = ''; | |
204 | } | |
205 | # use the longest match we can find | |
206 | $signer = $key if (length $key > length $signer) && $valid_keys->{$key}; | |
207 | } | |
208 | ||
c9164975 | 209 | close(CMD); |
aff192e6 DM |
210 | |
211 | die "unable to verify signature\n" if !$signer; | |
212 | ||
c9164975 | 213 | logmsg($logfd, "signature valid: $signer"); |
aff192e6 DM |
214 | |
215 | # test syntax | |
216 | eval { | |
c9164975 | 217 | my $fh = IO::File->new("<$tmp") || |
aff192e6 | 218 | die "unable to open file '$tmp' - $!\n"; |
c9164975 DM |
219 | read_aplinfo($tmp, {}, $aplurl, 1); |
220 | close($fh); | |
aff192e6 DM |
221 | }; |
222 | die "update failed: $@" if $@; | |
223 | ||
c9164975 | 224 | if (system("mv $tmp $aplinfodir/$host 2>/dev/null") != 0) { |
aff192e6 DM |
225 | die "update failed: unable to store data\n"; |
226 | } | |
227 | ||
c9164975 | 228 | logmsg($logfd, "update sucessful"); |
aff192e6 DM |
229 | }; |
230 | ||
231 | my $err = $@; | |
232 | ||
233 | unlink $tmp; | |
234 | unlink $tmpgz; | |
235 | unlink $sigfn; | |
236 | ||
c9164975 | 237 | die $err if $err; |
aff192e6 DM |
238 | } |
239 | ||
c9164975 DM |
240 | sub get_apl_sources { |
241 | ||
242 | my $urls = []; | |
243 | push @$urls, "http://download.proxmox.com/appliances"; | |
244 | push @$urls, "http://releases.turnkeylinux.org/pve"; | |
aff192e6 | 245 | |
c9164975 | 246 | return $urls; |
aff192e6 DM |
247 | } |
248 | ||
c9164975 DM |
249 | sub update { |
250 | my ($proxy) = @_; | |
aff192e6 | 251 | |
c9164975 DM |
252 | my $size; |
253 | if (($size = (-s $logfile) || 0) > (1024*50)) { | |
254 | system ("mv $logfile $logfile.0"); | |
255 | } | |
256 | my $logfd = IO::File->new (">>$logfile"); | |
257 | logmsg($logfd, "starting update"); | |
aff192e6 | 258 | |
c9164975 | 259 | import_gpg_keys(); |
aff192e6 | 260 | |
c9164975 DM |
261 | # this code works for ftp and http |
262 | # always use passive ftp | |
263 | local $ENV{FTP_PASSIVE} = 1; | |
264 | my $ua = LWP::UserAgent->new; | |
265 | $ua->agent("PVE/1.0"); | |
aff192e6 | 266 | |
c9164975 DM |
267 | if ($proxy) { |
268 | $ua->proxy(['http'], $proxy); | |
269 | } else { | |
270 | $ua->env_proxy; | |
271 | } | |
aff192e6 | 272 | |
c9164975 | 273 | my $urls = get_apl_sources(); |
aff192e6 | 274 | |
c9164975 | 275 | mkdir $aplinfodir; |
aff192e6 | 276 | |
c9164975 DM |
277 | my @dlerr = (); |
278 | foreach my $aplurl (@$urls) { | |
279 | eval { | |
280 | my $uri = URI->new($aplurl); | |
281 | my $host = $uri->host(); | |
282 | download_aplinfo($ua, $aplurl, $host, $logfd); | |
283 | }; | |
284 | if (my $err = $@) { | |
285 | logmsg ($logfd, $err); | |
286 | push @dlerr, $aplurl; | |
287 | } | |
288 | } | |
aff192e6 | 289 | |
c9164975 | 290 | close($logfd); |
aff192e6 | 291 | |
c9164975 | 292 | return 0 if scalar(@dlerr); |
aff192e6 | 293 | |
c9164975 | 294 | return 1; |
aff192e6 DM |
295 | } |
296 | ||
c9164975 | 297 | sub load_data { |
aff192e6 | 298 | |
75a6a7f5 | 299 | my $urls = get_apl_sources(); |
aff192e6 | 300 | |
c9164975 | 301 | my $list = {}; |
aff192e6 | 302 | |
c9164975 | 303 | foreach my $aplurl (@$urls) { |
aff192e6 | 304 | |
c9164975 | 305 | eval { |
aff192e6 | 306 | |
c9164975 DM |
307 | my $uri = URI->new($aplurl); |
308 | my $host = $uri->host(); | |
309 | read_aplinfo("$aplinfodir/$host", $list, $aplurl); | |
310 | }; | |
311 | warn $@ if $@; | |
312 | } | |
aff192e6 | 313 | |
c9164975 | 314 | return $list; |
aff192e6 DM |
315 | } |
316 | ||
317 | 1; | |
318 |