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