]> git.proxmox.com Git - pve-manager.git/blame - PVE/APLInfo.pm
change turnkey URL to https version to permit a redirection.
[pve-manager.git] / PVE / APLInfo.pm
CommitLineData
aff192e6
DM
1package PVE::APLInfo;
2
3use strict;
4use IO::File;
5use PVE::SafeSyslog;
aff192e6 6use LWP::UserAgent;
aff192e6
DM
7use POSIX qw(strftime);
8
9my $logfile = "/var/log/pveam.log";
c9164975 10my $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
21my $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
28sub 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
39sub 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
51sub 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;
648af5ab 65 my $long = $2 || '';
6de794eb
DM
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;
f6b20cf9 72 if ($version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)(-(\d+))?$/) {
6de794eb 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};
7a07d675
DM
108 $template =~ s|.*/([^/]+.tar.[gx]z)$|$1|;
109 if ($res->{location} !~ m|^([a-zA-Z]+)\://|) {
110 # relative localtion (no http:// prefix)
111 $res->{location} = "$source/$res->{location}";
112 }
6de794eb 113 } else {
07c1e6b0
DM
114 my $arch = $res->{architecture} || 'i386';
115 $template = "$res->{os}-$res->{package}_$res->{version}_$arch.tar.gz";
6de794eb 116 $template =~ s/$res->{os}-$res->{os}-/$res->{os}-/;
4886fe07 117 $res->{location} = "$source/$res->{section}/$template";
c9164975 118 }
6de794eb
DM
119 $res->{source} = $source;
120 $res->{template} = $template;
121 $list->{$res->{section}}->{$template} = $res;
122 $list->{'all'}->{$template} = $res;
123 } else {
124 my $msg = "found incomplete appliance records\n";
125 $update ? die $msg : warn $msg;
c9164975 126 }
6de794eb
DM
127 }
128}
129
130sub read_aplinfo {
131 my ($filename, $list, $source, $update) = @_;
132
133 my $fh = IO::File->new("<$filename") ||
134 die "unable to open file '$filename' - $!\n";
135
136 eval { read_aplinfo_from_fh($fh, $list, $source, $update); };
c9164975
DM
137 my $err = $@;
138
139 close($fh);
140
141 die $err if $err;
142
143 return $list;
144}
145
aff192e6
DM
146sub url_get {
147 my ($ua, $url, $file, $logfh) = @_;
148
149 my $req = HTTP::Request->new(GET => $url);
150
151 logmsg ($logfh, "start download $url");
152 my $res = $ua->request($req, $file);
153
154 if ($res->is_success) {
155 logmsg ($logfh, "download finished: " . $res->status_line);
156 return 0;
157 }
158
159 logmsg ($logfh, "download failed: " . $res->status_line);
160
161 return 1;
162}
163
c9164975
DM
164sub download_aplinfo {
165 my ($ua, $aplurl, $host, $logfd) = @_;
aff192e6 166
aff192e6
DM
167 my $aplsrcurl = "$aplurl/aplinfo.dat.gz";
168 my $aplsigurl = "$aplurl/aplinfo.dat.asc";
169
c9164975 170 my $tmp = "$aplinfodir/pveam-${host}.tmp.$$";
aff192e6
DM
171 my $tmpgz = "$tmp.gz";
172 my $sigfn = "$tmp.asc";
173
aff192e6 174 eval {
c9164975
DM
175
176 if (url_get($ua, $aplsigurl, $sigfn, $logfd) != 0) {
177 die "update failed - no signature file '$sigfn'\n";
aff192e6
DM
178 }
179
c9164975
DM
180 if (url_get($ua, $aplsrcurl, $tmpgz, $logfd) != 0) {
181 die "update failed - no data file '$aplsrcurl'\n";
aff192e6
DM
182 }
183
c9164975 184 if (system("zcat -f $tmpgz >$tmp 2>/dev/null") != 0) {
aff192e6
DM
185 die "update failed: unable to unpack '$tmpgz'\n";
186 }
187
188 # verify signature
189
c9164975 190 my $cmd = "/usr/bin/gpg --verify --trust-model always --batch --no-tty --status-fd=1 -q " .
aff192e6
DM
191 "--logger-fd=1 $sigfn $tmp";
192
c9164975 193 open(CMD, "$cmd|") ||
aff192e6
DM
194 die "unable to execute '$cmd': $!\n";
195
196 my $line;
197 my $signer = '';
c9164975 198 while (defined($line = <CMD>)) {
aff192e6 199 chomp $line;
c9164975 200 logmsg($logfd, $line);
aff192e6
DM
201
202 # code borrowed from SA
203 next if $line !~ /^\Q[GNUPG:]\E (?:VALID|GOOD)SIG (\S{8,40})/;
204 my $key = $1;
205
206 # we want either a keyid (8) or a fingerprint (40)
207 if (length $key > 8 && length $key < 40) {
208 substr($key, 8) = '';
209 }
210 # use the longest match we can find
211 $signer = $key if (length $key > length $signer) && $valid_keys->{$key};
212 }
213
c9164975 214 close(CMD);
aff192e6
DM
215
216 die "unable to verify signature\n" if !$signer;
217
c9164975 218 logmsg($logfd, "signature valid: $signer");
aff192e6
DM
219
220 # test syntax
221 eval {
c9164975 222 my $fh = IO::File->new("<$tmp") ||
aff192e6 223 die "unable to open file '$tmp' - $!\n";
c9164975
DM
224 read_aplinfo($tmp, {}, $aplurl, 1);
225 close($fh);
aff192e6
DM
226 };
227 die "update failed: $@" if $@;
228
c9164975 229 if (system("mv $tmp $aplinfodir/$host 2>/dev/null") != 0) {
aff192e6
DM
230 die "update failed: unable to store data\n";
231 }
232
c9164975 233 logmsg($logfd, "update sucessful");
aff192e6
DM
234 };
235
236 my $err = $@;
237
238 unlink $tmp;
239 unlink $tmpgz;
240 unlink $sigfn;
241
c9164975 242 die $err if $err;
aff192e6
DM
243}
244
c9164975
DM
245sub get_apl_sources {
246
247 my $urls = [];
648af5ab 248 push @$urls, "http://download.proxmox.com/images";
06399fd7 249 push @$urls, "https://releases.turnkeylinux.org/pve";
aff192e6 250
c9164975 251 return $urls;
aff192e6
DM
252}
253
c9164975
DM
254sub update {
255 my ($proxy) = @_;
aff192e6 256
c9164975
DM
257 my $size;
258 if (($size = (-s $logfile) || 0) > (1024*50)) {
259 system ("mv $logfile $logfile.0");
260 }
261 my $logfd = IO::File->new (">>$logfile");
262 logmsg($logfd, "starting update");
aff192e6 263
c9164975 264 import_gpg_keys();
aff192e6 265
c9164975
DM
266 my $ua = LWP::UserAgent->new;
267 $ua->agent("PVE/1.0");
aff192e6 268
c9164975 269 if ($proxy) {
49ee2d1a 270 $ua->proxy(['http', 'https'], $proxy);
c9164975
DM
271 } else {
272 $ua->env_proxy;
273 }
aff192e6 274
c9164975 275 my $urls = get_apl_sources();
aff192e6 276
c9164975 277 mkdir $aplinfodir;
aff192e6 278
c9164975
DM
279 my @dlerr = ();
280 foreach my $aplurl (@$urls) {
281 eval {
282 my $uri = URI->new($aplurl);
283 my $host = $uri->host();
284 download_aplinfo($ua, $aplurl, $host, $logfd);
285 };
286 if (my $err = $@) {
287 logmsg ($logfd, $err);
288 push @dlerr, $aplurl;
289 }
290 }
aff192e6 291
c9164975 292 close($logfd);
aff192e6 293
c9164975 294 return 0 if scalar(@dlerr);
aff192e6 295
c9164975 296 return 1;
aff192e6
DM
297}
298
c9164975 299sub load_data {
aff192e6 300
75a6a7f5 301 my $urls = get_apl_sources();
aff192e6 302
c9164975 303 my $list = {};
aff192e6 304
c9164975 305 foreach my $aplurl (@$urls) {
aff192e6 306
c9164975 307 eval {
aff192e6 308
c9164975
DM
309 my $uri = URI->new($aplurl);
310 my $host = $uri->host();
311 read_aplinfo("$aplinfodir/$host", $list, $aplurl);
312 };
313 warn $@ if $@;
314 }
aff192e6 315
c9164975 316 return $list;
aff192e6
DM
317}
318
3191;
320