]>
Commit | Line | Data |
---|---|---|
aff192e6 DM |
1 | package PVE::APLInfo; |
2 | ||
3 | use strict; | |
609801c5 | 4 | use warnings; |
53b083d5 | 5 | |
aff192e6 | 6 | use IO::File; |
aff192e6 | 7 | use LWP::UserAgent; |
aff192e6 | 8 | use POSIX qw(strftime); |
53b083d5 TL |
9 | |
10 | use PVE::SafeSyslog; | |
81579be0 | 11 | use PVE::Storage; |
53b083d5 | 12 | use PVE::Tools qw(run_command); |
07e5f4f7 | 13 | use PVE::pvecfg; |
aff192e6 DM |
14 | |
15 | my $logfile = "/var/log/pveam.log"; | |
c9164975 | 16 | my $aplinfodir = "/var/lib/pve-manager/apl-info"; |
aff192e6 | 17 | |
aff192e6 DM |
18 | sub 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 |
30 | sub 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 | ||
109 | sub 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 |
125 | sub 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 | 143 | sub 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 | |
c9164975 | 150 | my $tmp = "$aplinfodir/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 | ||
53b083d5 TL |
181 | rename($tmp, "$aplinfodir/$host") or |
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 | 196 | sub 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 |
213 | sub update { |
214 | my ($proxy) = @_; | |
aff192e6 | 215 | |
c9164975 DM |
216 | my $size; |
217 | if (($size = (-s $logfile) || 0) > (1024*50)) { | |
c351eda9 | 218 | rename($logfile, "$logfile.0"); |
c9164975 DM |
219 | } |
220 | my $logfd = IO::File->new (">>$logfile"); | |
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 | |
c9164975 | 235 | mkdir $aplinfodir; |
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 | 255 | sub 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}; | |
263 | read_aplinfo("$aplinfodir/$host", $list, $info->{url}); | |
c9164975 DM |
264 | }; |
265 | warn $@ if $@; | |
266 | } | |
aff192e6 | 267 | |
c9164975 | 268 | return $list; |
aff192e6 DM |
269 | } |
270 | ||
271 | 1; | |
272 |