]>
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 | ||
c9164975 DM |
51 | sub read_aplinfo { |
52 | my ($filename, $list, $source, $update) = @_; | |
53 | ||
54 | my $fh = IO::File->new("<$filename") || | |
55 | die "unable to open file '$filename' - $!\n"; | |
56 | ||
57 | local $/ = ""; | |
58 | ||
59 | eval { | |
60 | while (my $rec = <$fh>) { | |
61 | chomp $rec; | |
62 | ||
63 | my $res = {}; | |
64 | ||
65 | while ($rec) { | |
66 | ||
67 | if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) { | |
68 | $res->{headline} = $1; | |
69 | my $long = $2; | |
70 | $long =~ s/\n\s+/ /g; | |
71 | $long =~ s/^\s+//g; | |
72 | $long =~ s/\s+$//g; | |
73 | $res->{description} = $long; | |
74 | } elsif ($rec =~ s/^Version:\s*(.*\S)\s*\n//i) { | |
75 | my $version = $1; | |
76 | if ($version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)-(\d+)$/) { | |
77 | $res->{version} = $version; | |
78 | } else { | |
79 | my $msg = "unable to parse appliance record: version = '$version'\n"; | |
80 | $update ? die $msg : warn $msg; | |
81 | } | |
82 | } elsif ($rec =~ s/^Type:\s*(.*\S)\s*\n//i) { | |
83 | my $type = $1; | |
84 | if ($type =~ m/^(openvz)$/) { | |
85 | $res->{type} = $type; | |
86 | } else { | |
87 | my $msg = "unable to parse appliance record: unknown type '$type'\n"; | |
88 | $update ? die $msg : warn $msg; | |
89 | } | |
90 | } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) { | |
91 | $res->{lc $1} = $2; | |
92 | } else { | |
93 | my $msg = "unable to parse appliance record: $rec\n"; | |
94 | $update ? die $msg : warn $msg; | |
95 | $res = {}; | |
96 | last; | |
97 | } | |
98 | } | |
99 | ||
100 | if ($res->{'package'} eq 'pve-web-news' && $res->{description}) { | |
101 | $list->{'all'}->{$res->{'package'}} = $res; | |
102 | next; | |
103 | } | |
104 | ||
105 | $res->{section} = 'unknown' if !$res->{section}; | |
106 | ||
107 | if ($res->{'package'} && $res->{type} && $res->{os} && $res->{version} && | |
108 | $res->{infopage}) { | |
109 | my $template; | |
110 | if ($res->{location}) { | |
111 | $template = $res->{location}; | |
112 | $template =~ s|.*/([^/]+.tar.gz)|$1|; | |
113 | } else { | |
114 | $template = "$res->{os}-$res->{package}_$res->{version}_i386.tar.gz"; | |
115 | $template =~ s/$res->{os}-$res->{os}-/$res->{os}-/; | |
116 | } | |
117 | $res->{source} = $source; | |
118 | $res->{template} = $template; | |
119 | $list->{$res->{section}}->{$template} = $res; | |
120 | $list->{'all'}->{$template} = $res; | |
121 | } else { | |
122 | my $msg = "found incomplete appliance records\n"; | |
123 | $update ? die $msg : warn $msg; | |
124 | } | |
125 | } | |
126 | }; | |
127 | my $err = $@; | |
128 | ||
129 | close($fh); | |
130 | ||
131 | die $err if $err; | |
132 | ||
133 | return $list; | |
134 | } | |
135 | ||
aff192e6 DM |
136 | sub url_get { |
137 | my ($ua, $url, $file, $logfh) = @_; | |
138 | ||
139 | my $req = HTTP::Request->new(GET => $url); | |
140 | ||
141 | logmsg ($logfh, "start download $url"); | |
142 | my $res = $ua->request($req, $file); | |
143 | ||
144 | if ($res->is_success) { | |
145 | logmsg ($logfh, "download finished: " . $res->status_line); | |
146 | return 0; | |
147 | } | |
148 | ||
149 | logmsg ($logfh, "download failed: " . $res->status_line); | |
150 | ||
151 | return 1; | |
152 | } | |
153 | ||
c9164975 DM |
154 | sub download_aplinfo { |
155 | my ($ua, $aplurl, $host, $logfd) = @_; | |
aff192e6 | 156 | |
aff192e6 DM |
157 | my $aplsrcurl = "$aplurl/aplinfo.dat.gz"; |
158 | my $aplsigurl = "$aplurl/aplinfo.dat.asc"; | |
159 | ||
c9164975 | 160 | my $tmp = "$aplinfodir/pveam-${host}.tmp.$$"; |
aff192e6 DM |
161 | my $tmpgz = "$tmp.gz"; |
162 | my $sigfn = "$tmp.asc"; | |
163 | ||
aff192e6 | 164 | eval { |
c9164975 DM |
165 | |
166 | if (url_get($ua, $aplsigurl, $sigfn, $logfd) != 0) { | |
167 | die "update failed - no signature file '$sigfn'\n"; | |
aff192e6 DM |
168 | } |
169 | ||
c9164975 DM |
170 | if (url_get($ua, $aplsrcurl, $tmpgz, $logfd) != 0) { |
171 | die "update failed - no data file '$aplsrcurl'\n"; | |
aff192e6 DM |
172 | } |
173 | ||
c9164975 | 174 | if (system("zcat -f $tmpgz >$tmp 2>/dev/null") != 0) { |
aff192e6 DM |
175 | die "update failed: unable to unpack '$tmpgz'\n"; |
176 | } | |
177 | ||
178 | # verify signature | |
179 | ||
c9164975 | 180 | my $cmd = "/usr/bin/gpg --verify --trust-model always --batch --no-tty --status-fd=1 -q " . |
aff192e6 DM |
181 | "--logger-fd=1 $sigfn $tmp"; |
182 | ||
c9164975 | 183 | open(CMD, "$cmd|") || |
aff192e6 DM |
184 | die "unable to execute '$cmd': $!\n"; |
185 | ||
186 | my $line; | |
187 | my $signer = ''; | |
c9164975 | 188 | while (defined($line = <CMD>)) { |
aff192e6 | 189 | chomp $line; |
c9164975 | 190 | logmsg($logfd, $line); |
aff192e6 DM |
191 | |
192 | # code borrowed from SA | |
193 | next if $line !~ /^\Q[GNUPG:]\E (?:VALID|GOOD)SIG (\S{8,40})/; | |
194 | my $key = $1; | |
195 | ||
196 | # we want either a keyid (8) or a fingerprint (40) | |
197 | if (length $key > 8 && length $key < 40) { | |
198 | substr($key, 8) = ''; | |
199 | } | |
200 | # use the longest match we can find | |
201 | $signer = $key if (length $key > length $signer) && $valid_keys->{$key}; | |
202 | } | |
203 | ||
c9164975 | 204 | close(CMD); |
aff192e6 DM |
205 | |
206 | die "unable to verify signature\n" if !$signer; | |
207 | ||
c9164975 | 208 | logmsg($logfd, "signature valid: $signer"); |
aff192e6 DM |
209 | |
210 | # test syntax | |
211 | eval { | |
c9164975 | 212 | my $fh = IO::File->new("<$tmp") || |
aff192e6 | 213 | die "unable to open file '$tmp' - $!\n"; |
c9164975 DM |
214 | read_aplinfo($tmp, {}, $aplurl, 1); |
215 | close($fh); | |
aff192e6 DM |
216 | }; |
217 | die "update failed: $@" if $@; | |
218 | ||
c9164975 | 219 | if (system("mv $tmp $aplinfodir/$host 2>/dev/null") != 0) { |
aff192e6 DM |
220 | die "update failed: unable to store data\n"; |
221 | } | |
222 | ||
c9164975 | 223 | logmsg($logfd, "update sucessful"); |
aff192e6 DM |
224 | }; |
225 | ||
226 | my $err = $@; | |
227 | ||
228 | unlink $tmp; | |
229 | unlink $tmpgz; | |
230 | unlink $sigfn; | |
231 | ||
c9164975 | 232 | die $err if $err; |
aff192e6 DM |
233 | } |
234 | ||
c9164975 DM |
235 | sub get_apl_sources { |
236 | ||
237 | my $urls = []; | |
238 | push @$urls, "http://download.proxmox.com/appliances"; | |
239 | push @$urls, "http://releases.turnkeylinux.org/pve"; | |
aff192e6 | 240 | |
c9164975 | 241 | return $urls; |
aff192e6 DM |
242 | } |
243 | ||
c9164975 DM |
244 | sub update { |
245 | my ($proxy) = @_; | |
aff192e6 | 246 | |
c9164975 DM |
247 | my $size; |
248 | if (($size = (-s $logfile) || 0) > (1024*50)) { | |
249 | system ("mv $logfile $logfile.0"); | |
250 | } | |
251 | my $logfd = IO::File->new (">>$logfile"); | |
252 | logmsg($logfd, "starting update"); | |
aff192e6 | 253 | |
c9164975 | 254 | import_gpg_keys(); |
aff192e6 | 255 | |
c9164975 DM |
256 | # this code works for ftp and http |
257 | # always use passive ftp | |
258 | local $ENV{FTP_PASSIVE} = 1; | |
259 | my $ua = LWP::UserAgent->new; | |
260 | $ua->agent("PVE/1.0"); | |
aff192e6 | 261 | |
c9164975 DM |
262 | if ($proxy) { |
263 | $ua->proxy(['http'], $proxy); | |
264 | } else { | |
265 | $ua->env_proxy; | |
266 | } | |
aff192e6 | 267 | |
c9164975 | 268 | my $urls = get_apl_sources(); |
aff192e6 | 269 | |
c9164975 | 270 | mkdir $aplinfodir; |
aff192e6 | 271 | |
c9164975 DM |
272 | my @dlerr = (); |
273 | foreach my $aplurl (@$urls) { | |
274 | eval { | |
275 | my $uri = URI->new($aplurl); | |
276 | my $host = $uri->host(); | |
277 | download_aplinfo($ua, $aplurl, $host, $logfd); | |
278 | }; | |
279 | if (my $err = $@) { | |
280 | logmsg ($logfd, $err); | |
281 | push @dlerr, $aplurl; | |
282 | } | |
283 | } | |
aff192e6 | 284 | |
c9164975 | 285 | close($logfd); |
aff192e6 | 286 | |
c9164975 | 287 | return 0 if scalar(@dlerr); |
aff192e6 | 288 | |
c9164975 | 289 | return 1; |
aff192e6 DM |
290 | } |
291 | ||
c9164975 | 292 | sub load_data { |
aff192e6 | 293 | |
75a6a7f5 | 294 | my $urls = get_apl_sources(); |
aff192e6 | 295 | |
c9164975 | 296 | my $list = {}; |
aff192e6 | 297 | |
c9164975 | 298 | foreach my $aplurl (@$urls) { |
aff192e6 | 299 | |
c9164975 | 300 | eval { |
aff192e6 | 301 | |
c9164975 DM |
302 | my $uri = URI->new($aplurl); |
303 | my $host = $uri->host(); | |
304 | read_aplinfo("$aplinfodir/$host", $list, $aplurl); | |
305 | }; | |
306 | warn $@ if $@; | |
307 | } | |
aff192e6 | 308 | |
c9164975 | 309 | return $list; |
aff192e6 DM |
310 | } |
311 | ||
312 | 1; | |
313 |