]> git.proxmox.com Git - pve-manager.git/blame - PVE/APLInfo.pm
update shipped appliance info index
[pve-manager.git] / PVE / APLInfo.pm
CommitLineData
aff192e6
DM
1package PVE::APLInfo;
2
3use strict;
609801c5 4use warnings;
53b083d5 5
aff192e6 6use IO::File;
aff192e6 7use LWP::UserAgent;
aff192e6 8use POSIX qw(strftime);
53b083d5
TL
9
10use PVE::SafeSyslog;
81579be0 11use PVE::Storage;
53b083d5 12use PVE::Tools qw(run_command);
07e5f4f7 13use PVE::pvecfg;
aff192e6 14
33b0c265
TL
15my $LOGFILE = "/var/log/pveam.log";
16my $APL_INFO_DIRECTORY = "/var/lib/pve-manager/apl-info";
aff192e6 17
aff192e6
DM
18sub logmsg {
19 my ($logfd, $msg) = @_;
20
21 chomp $msg;
22
8a0cae0e 23 my $tstr = strftime ("%F %H:%M:%S", localtime);
aff192e6
DM
24
25 foreach my $line (split (/\n/, $msg)) {
26 print $logfd "$tstr $line\n";
27 }
28}
29
6de794eb
DM
30sub read_aplinfo_from_fh {
31 my ($fh, $list, $source, $update) = @_;
c9164975
DM
32
33 local $/ = "";
34
6de794eb
DM
35 while (my $rec = <$fh>) {
36 chomp $rec;
295d8c43 37
6de794eb
DM
38 my $res = {};
39
40 while ($rec) {
41
42 if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) {
43 $res->{headline} = $1;
648af5ab 44 my $long = $2 || '';
6de794eb
DM
45 $long =~ s/\n\s+/ /g;
46 $long =~ s/^\s+//g;
47 $long =~ s/\s+$//g;
48 $res->{description} = $long;
49 } elsif ($rec =~ s/^Version:\s*(.*\S)\s*\n//i) {
50 my $version = $1;
f6b20cf9 51 if ($version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)(-(\d+))?$/) {
6de794eb 52 $res->{version} = $version;
c9164975 53 } else {
6de794eb
DM
54 my $msg = "unable to parse appliance record: version = '$version'\n";
55 $update ? die $msg : warn $msg;
c9164975 56 }
6de794eb
DM
57 } elsif ($rec =~ s/^Type:\s*(.*\S)\s*\n//i) {
58 my $type = $1;
59 if ($type =~ m/^(openvz|lxc)$/) {
60 $res->{type} = $type;
c9164975 61 } else {
6de794eb
DM
62 my $msg = "unable to parse appliance record: unknown type '$type'\n";
63 $update ? die $msg : warn $msg;
c9164975 64 }
6de794eb
DM
65 } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) {
66 $res->{lc $1} = $2;
c9164975 67 } else {
6de794eb 68 my $msg = "unable to parse appliance record: $rec\n";
295d8c43 69 $update ? die $msg : warn $msg;
6de794eb
DM
70 $res = {};
71 last;
72 }
73 }
295d8c43 74
6de794eb 75 if ($res->{'package'} eq 'pve-web-news' && $res->{description}) {
295d8c43 76 $list->{'all'}->{$res->{'package'}} = $res;
6de794eb
DM
77 next;
78 }
79
80 $res->{section} = 'unknown' if !$res->{section};
295d8c43 81
6de794eb
DM
82 if ($res->{'package'} && $res->{type} && $res->{os} && $res->{version} &&
83 $res->{infopage}) {
84 my $template;
85 if ($res->{location}) {
86 $template = $res->{location};
81579be0 87 $template =~ s|.*/([^/]+$PVE::Storage::vztmpl_extension_re)$|$1|;
7a07d675
DM
88 if ($res->{location} !~ m|^([a-zA-Z]+)\://|) {
89 # relative localtion (no http:// prefix)
90 $res->{location} = "$source/$res->{location}";
91 }
6de794eb 92 } else {
07c1e6b0
DM
93 my $arch = $res->{architecture} || 'i386';
94 $template = "$res->{os}-$res->{package}_$res->{version}_$arch.tar.gz";
6de794eb 95 $template =~ s/$res->{os}-$res->{os}-/$res->{os}-/;
4886fe07 96 $res->{location} = "$source/$res->{section}/$template";
c9164975 97 }
6de794eb
DM
98 $res->{source} = $source;
99 $res->{template} = $template;
100 $list->{$res->{section}}->{$template} = $res;
101 $list->{'all'}->{$template} = $res;
102 } else {
103 my $msg = "found incomplete appliance records\n";
295d8c43 104 $update ? die $msg : warn $msg;
c9164975 105 }
6de794eb
DM
106 }
107}
108
109sub read_aplinfo {
110 my ($filename, $list, $source, $update) = @_;
111
112 my $fh = IO::File->new("<$filename") ||
113 die "unable to open file '$filename' - $!\n";
114
115 eval { read_aplinfo_from_fh($fh, $list, $source, $update); };
c9164975
DM
116 my $err = $@;
117
118 close($fh);
119
120 die $err if $err;
295d8c43 121
c9164975
DM
122 return $list;
123}
124
aff192e6
DM
125sub url_get {
126 my ($ua, $url, $file, $logfh) = @_;
127
128 my $req = HTTP::Request->new(GET => $url);
129
130 logmsg ($logfh, "start download $url");
131 my $res = $ua->request($req, $file);
132
133 if ($res->is_success) {
134 logmsg ($logfh, "download finished: " . $res->status_line);
135 return 0;
136 }
137
138 logmsg ($logfh, "download failed: " . $res->status_line);
139
140 return 1;
141}
142
c9164975 143sub download_aplinfo {
8e96e296 144 my ($ua, $aplinfo, $logfd) = @_;
aff192e6 145
8e96e296
TL
146 my $aplsrcurl = "$aplinfo->{url}/$aplinfo->{file}.gz";
147 my $aplsigurl = "$aplinfo->{url}/$aplinfo->{file}.asc";
148 my $host = $aplinfo->{host};
aff192e6 149
33b0c265 150 my $tmp = "$APL_INFO_DIRECTORY/pveam-${host}.tmp.$$";
aff192e6
DM
151 my $tmpgz = "$tmp.gz";
152 my $sigfn = "$tmp.asc";
153
aff192e6 154 eval {
c9164975
DM
155
156 if (url_get($ua, $aplsigurl, $sigfn, $logfd) != 0) {
157 die "update failed - no signature file '$sigfn'\n";
aff192e6
DM
158 }
159
c9164975
DM
160 if (url_get($ua, $aplsrcurl, $tmpgz, $logfd) != 0) {
161 die "update failed - no data file '$aplsrcurl'\n";
aff192e6 162 }
295d8c43 163
53b083d5 164 eval { run_command(["gunzip", "-f", $tmpgz]) };
c351eda9 165 die "update failed: unable to unpack '$tmpgz'\n" if $@;
aff192e6 166
868801cb
FG
167 # verify signature
168 my $trustedkeyring = "/usr/share/doc/pve-manager/trustedkeys.gpg";
169 my $cmd = "/usr/bin/gpgv -q --keyring $trustedkeyring $sigfn $tmp";
170
53b083d5 171 my $logfunc = sub { logmsg($logfd, "signature verification: $_[0]"); };
868801cb 172 eval {
53b083d5 173 run_command($cmd, outfunc => $logfunc, errfunc => $logfunc);
868801cb
FG
174 };
175 die "unable to verify signature - $@\n" if $@;
aff192e6
DM
176
177 # test syntax
8e96e296 178 eval { read_aplinfo($tmp, {}, $aplinfo->{url}, 1) };
aff192e6
DM
179 die "update failed: $@" if $@;
180
33b0c265 181 rename($tmp, "$APL_INFO_DIRECTORY/$host") or
53b083d5 182 die "update failed: unable to store data: $!\n";
aff192e6 183
76189130 184 logmsg($logfd, "update successful");
aff192e6
DM
185 };
186
187 my $err = $@;
188
189 unlink $tmp;
190 unlink $tmpgz;
191 unlink $sigfn;
192
c9164975 193 die $err if $err;
aff192e6
DM
194}
195
c9164975 196sub get_apl_sources {
8e96e296
TL
197 my $sources = [
198 {
199 host => "download.proxmox.com",
200 url => "http://download.proxmox.com/images",
63c8b371 201 file => 'aplinfo-pve-8.dat',
8e96e296
TL
202 },
203 {
204 host => "releases.turnkeylinux.org",
205 url => "https://releases.turnkeylinux.org/pve",
206 file => 'aplinfo.dat',
207 },
208 ];
209
210 return $sources;
aff192e6
DM
211}
212
c9164975
DM
213sub update {
214 my ($proxy) = @_;
aff192e6 215
33b0c265 216 my $logfile_size = -s $LOGFILE || 0;
c6611893 217 if ($logfile_size > 1024 * 256) {
33b0c265 218 rename($LOGFILE, "$LOGFILE.0") or warn "failed to rotate log file $LOGFILE - $!\n";
c9164975 219 }
33b0c265 220 my $logfd = IO::File->new (">>$LOGFILE");
c9164975 221 logmsg($logfd, "starting update");
aff192e6 222
c9164975 223 my $ua = LWP::UserAgent->new;
180a86d3
TL
224 my $release = PVE::pvecfg::release();
225 $ua->agent("PVE/$release");
aff192e6 226
c9164975 227 if ($proxy) {
49ee2d1a 228 $ua->proxy(['http', 'https'], $proxy);
c9164975
DM
229 } else {
230 $ua->env_proxy;
231 }
aff192e6 232
8e96e296 233 my $sources = get_apl_sources();
aff192e6 234
33b0c265 235 mkdir $APL_INFO_DIRECTORY;
aff192e6 236
c9164975 237 my @dlerr = ();
8e96e296
TL
238 foreach my $info (@$sources) {
239 eval {
240 download_aplinfo($ua, $info, $logfd);
c9164975
DM
241 };
242 if (my $err = $@) {
243 logmsg ($logfd, $err);
8e96e296 244 push @dlerr, $info->{url};
c9164975 245 }
295d8c43 246 }
aff192e6 247
c9164975 248 close($logfd);
aff192e6 249
c9164975 250 return 0 if scalar(@dlerr);
aff192e6 251
c9164975 252 return 1;
aff192e6
DM
253}
254
c9164975 255sub load_data {
aff192e6 256
8e96e296 257 my $sources = get_apl_sources();
aff192e6 258
c9164975 259 my $list = {};
8e96e296
TL
260 foreach my $info (@$sources) {
261 eval {
262 my $host = $info->{host};
33b0c265 263 read_aplinfo("$APL_INFO_DIRECTORY/$host", $list, $info->{url});
c9164975
DM
264 };
265 warn $@ if $@;
266 }
aff192e6 267
c9164975 268 return $list;
aff192e6
DM
269}
270
2711;
272