2011-08-23 07:40:22 +02:00
package PVE::APLInfo ;
use strict ;
use IO::File ;
use PVE::SafeSyslog ;
use LWP::UserAgent ;
use POSIX qw( strftime ) ;
my $ logfile = "/var/log/pveam.log" ;
2012-02-21 11:42:32 +01:00
my $ aplinfodir = "/var/lib/pve-manager/apl-info" ;
2011-08-23 07:40:22 +02:00
# Default list of GPG keys allowed to sign aplinfo
#
#pub 1024D/5CAC72FE 2004-06-24
# Key fingerprint = 9ABD 7E02 AD24 3AD3 C2FB BCCC B0C1 CC22 5CAC 72FE
#uid Proxmox Support Team <support@proxmox.com>
2012-02-21 11:42:32 +01:00
#pub 2048R/A16EB94D 2008-08-15 [expires: 2023-08-12]
# Key fingerprint = 694C FF26 795A 29BA E07B 4EB5 85C2 5E95 A16E B94D
#uid Turnkey Linux Release Key <release@turnkeylinux.com>
2011-08-23 07:40:22 +02:00
my $ valid_keys = {
'9ABD7E02AD243AD3C2FBBCCCB0C1CC225CAC72FE' = > 1 , # fingerprint support@proxmox.com
'25CAC72FE' = > 1 , # keyid support@proxmox.com
2012-02-21 11:42:32 +01:00
'694CFF26795A29BAE07B4EB585C25E95A16EB94D' = > 1 , # fingerprint release@turnkeylinux.com
'A16EB94D' = > 1 , # keyid release@turnkeylinux.com
2011-08-23 07:40:22 +02:00
} ;
sub import_gpg_keys {
2012-02-21 11:42:32 +01:00
my @ keyfiles = ( 'support@proxmox.com.pubkey' , 'release@turnkeylinux.com.pubkey' ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
foreach my $ key ( @ keyfiles ) {
my $ fn = "/usr/share/doc/pve-manager/$key" ;
system ( "/usr/bin/gpg --batch --no-tty --status-fd=1 -q " .
"--logger-fd=1 --import $fn >>$logfile" ) ;
}
2011-08-23 07:40:22 +02:00
}
sub logmsg {
my ( $ logfd , $ msg ) = @ _ ;
chomp $ msg ;
my $ tstr = strftime ( "%b %d %H:%M:%S" , localtime ) ;
foreach my $ line ( split ( /\n/ , $ msg ) ) {
print $ logfd "$tstr $line\n" ;
}
}
2015-06-30 13:43:59 +02:00
sub read_aplinfo_from_fh {
my ( $ fh , $ list , $ source , $ update ) = @ _ ;
2012-02-21 11:42:32 +01:00
local $/ = "" ;
2015-06-30 13:43:59 +02:00
while ( my $ rec = <$fh> ) {
chomp $ rec ;
2012-02-21 11:42:32 +01:00
2015-06-30 13:43:59 +02:00
my $ res = { } ;
while ( $ rec ) {
if ( $ rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si ) {
$ res - > { headline } = $ 1 ;
2015-07-01 13:47:56 +02:00
my $ long = $ 2 || '' ;
2015-06-30 13:43:59 +02:00
$ long =~ s/\n\s+/ /g ;
$ long =~ s/^\s+//g ;
$ long =~ s/\s+$//g ;
$ res - > { description } = $ long ;
} elsif ( $ rec =~ s/^Version:\s*(.*\S)\s*\n//i ) {
my $ version = $ 1 ;
if ( $ version =~ m/^(\d[a-zA-Z0-9\.\+\-\:\~]*)-(\d+)$/ ) {
$ res - > { version } = $ version ;
2012-02-21 11:42:32 +01:00
} else {
2015-06-30 13:43:59 +02:00
my $ msg = "unable to parse appliance record: version = '$version'\n" ;
$ update ? die $ msg : warn $ msg ;
2012-02-21 11:42:32 +01:00
}
2015-06-30 13:43:59 +02:00
} elsif ( $ rec =~ s/^Type:\s*(.*\S)\s*\n//i ) {
my $ type = $ 1 ;
if ( $ type =~ m/^(openvz|lxc)$/ ) {
$ res - > { type } = $ type ;
2012-02-21 11:42:32 +01:00
} else {
2015-06-30 13:43:59 +02:00
my $ msg = "unable to parse appliance record: unknown type '$type'\n" ;
$ update ? die $ msg : warn $ msg ;
2012-02-21 11:42:32 +01:00
}
2015-06-30 13:43:59 +02:00
} elsif ( $ rec =~ s/^([^:]+):\s*(.*\S)\s*\n// ) {
$ res - > { lc $ 1 } = $ 2 ;
2012-02-21 11:42:32 +01:00
} else {
2015-06-30 13:43:59 +02:00
my $ msg = "unable to parse appliance record: $rec\n" ;
2012-02-21 11:42:32 +01:00
$ update ? die $ msg : warn $ msg ;
2015-06-30 13:43:59 +02:00
$ res = { } ;
last ;
}
}
if ( $ res - > { 'package' } eq 'pve-web-news' && $ res - > { description } ) {
$ list - > { 'all' } - > { $ res - > { 'package' } } = $ res ;
next ;
}
$ res - > { section } = 'unknown' if ! $ res - > { section } ;
if ( $ res - > { 'package' } && $ res - > { type } && $ res - > { os } && $ res - > { version } &&
$ res - > { infopage } ) {
my $ template ;
if ( $ res - > { location } ) {
$ template = $ res - > { location } ;
2015-07-02 11:58:24 +02:00
$ template =~ s | . * /([^/ ] + . tar . [ gx ] z ) $| $ 1 | ;
if ( $ res - > { location } !~ m | ^ ( [ a - zA - Z ] + ) \ : // | ) {
# relative localtion (no http:// prefix)
$ res - > { location } = "$source/$res->{location}" ;
}
2015-06-30 13:43:59 +02:00
} else {
2015-07-01 07:44:32 +02:00
my $ arch = $ res - > { architecture } || 'i386' ;
$ template = "$res->{os}-$res->{package}_$res->{version}_$arch.tar.gz" ;
2015-06-30 13:43:59 +02:00
$ template =~ s/$res->{os}-$res->{os}-/$res->{os}-/ ;
2015-07-02 11:24:21 +02:00
$ res - > { location } = "$source/$res->{section}/$template" ;
2012-02-21 11:42:32 +01:00
}
2015-06-30 13:43:59 +02:00
$ res - > { source } = $ source ;
$ res - > { template } = $ template ;
$ list - > { $ res - > { section } } - > { $ template } = $ res ;
$ list - > { 'all' } - > { $ template } = $ res ;
} else {
my $ msg = "found incomplete appliance records\n" ;
$ update ? die $ msg : warn $ msg ;
2012-02-21 11:42:32 +01:00
}
2015-06-30 13:43:59 +02:00
}
}
sub read_aplinfo {
my ( $ filename , $ list , $ source , $ update ) = @ _ ;
my $ fh = IO::File - > new ( "<$filename" ) ||
die "unable to open file '$filename' - $!\n" ;
eval { read_aplinfo_from_fh ( $ fh , $ list , $ source , $ update ) ; } ;
2012-02-21 11:42:32 +01:00
my $ err = $@ ;
close ( $ fh ) ;
die $ err if $ err ;
return $ list ;
}
2011-08-23 07:40:22 +02:00
sub url_get {
my ( $ ua , $ url , $ file , $ logfh ) = @ _ ;
my $ req = HTTP::Request - > new ( GET = > $ url ) ;
logmsg ( $ logfh , "start download $url" ) ;
my $ res = $ ua - > request ( $ req , $ file ) ;
if ( $ res - > is_success ) {
logmsg ( $ logfh , "download finished: " . $ res - > status_line ) ;
return 0 ;
}
logmsg ( $ logfh , "download failed: " . $ res - > status_line ) ;
return 1 ;
}
2012-02-21 11:42:32 +01:00
sub download_aplinfo {
my ( $ ua , $ aplurl , $ host , $ logfd ) = @ _ ;
2011-08-23 07:40:22 +02:00
my $ aplsrcurl = "$aplurl/aplinfo.dat.gz" ;
my $ aplsigurl = "$aplurl/aplinfo.dat.asc" ;
2012-02-21 11:42:32 +01:00
my $ tmp = "$aplinfodir/pveam-${host}.tmp.$$" ;
2011-08-23 07:40:22 +02:00
my $ tmpgz = "$tmp.gz" ;
my $ sigfn = "$tmp.asc" ;
eval {
2012-02-21 11:42:32 +01:00
if ( url_get ( $ ua , $ aplsigurl , $ sigfn , $ logfd ) != 0 ) {
die "update failed - no signature file '$sigfn'\n" ;
2011-08-23 07:40:22 +02:00
}
2012-02-21 11:42:32 +01:00
if ( url_get ( $ ua , $ aplsrcurl , $ tmpgz , $ logfd ) != 0 ) {
die "update failed - no data file '$aplsrcurl'\n" ;
2011-08-23 07:40:22 +02:00
}
2012-02-21 11:42:32 +01:00
if ( system ( "zcat -f $tmpgz >$tmp 2>/dev/null" ) != 0 ) {
2011-08-23 07:40:22 +02:00
die "update failed: unable to unpack '$tmpgz'\n" ;
}
# verify signature
2012-02-21 11:42:32 +01:00
my $ cmd = "/usr/bin/gpg --verify --trust-model always --batch --no-tty --status-fd=1 -q " .
2011-08-23 07:40:22 +02:00
"--logger-fd=1 $sigfn $tmp" ;
2012-02-21 11:42:32 +01:00
open ( CMD , "$cmd|" ) ||
2011-08-23 07:40:22 +02:00
die "unable to execute '$cmd': $!\n" ;
my $ line ;
my $ signer = '' ;
2012-02-21 11:42:32 +01:00
while ( defined ( $ line = <CMD> ) ) {
2011-08-23 07:40:22 +02:00
chomp $ line ;
2012-02-21 11:42:32 +01:00
logmsg ( $ logfd , $ line ) ;
2011-08-23 07:40:22 +02:00
# code borrowed from SA
next if $ line !~ /^\Q[GNUPG:]\E (?:VALID|GOOD)SIG (\S{8,40})/ ;
my $ key = $ 1 ;
# we want either a keyid (8) or a fingerprint (40)
if ( length $ key > 8 && length $ key < 40 ) {
substr ( $ key , 8 ) = '' ;
}
# use the longest match we can find
$ signer = $ key if ( length $ key > length $ signer ) && $ valid_keys - > { $ key } ;
}
2012-02-21 11:42:32 +01:00
close ( CMD ) ;
2011-08-23 07:40:22 +02:00
die "unable to verify signature\n" if ! $ signer ;
2012-02-21 11:42:32 +01:00
logmsg ( $ logfd , "signature valid: $signer" ) ;
2011-08-23 07:40:22 +02:00
# test syntax
eval {
2012-02-21 11:42:32 +01:00
my $ fh = IO::File - > new ( "<$tmp" ) ||
2011-08-23 07:40:22 +02:00
die "unable to open file '$tmp' - $!\n" ;
2012-02-21 11:42:32 +01:00
read_aplinfo ( $ tmp , { } , $ aplurl , 1 ) ;
close ( $ fh ) ;
2011-08-23 07:40:22 +02:00
} ;
die "update failed: $@" if $@ ;
2012-02-21 11:42:32 +01:00
if ( system ( "mv $tmp $aplinfodir/$host 2>/dev/null" ) != 0 ) {
2011-08-23 07:40:22 +02:00
die "update failed: unable to store data\n" ;
}
2012-02-21 11:42:32 +01:00
logmsg ( $ logfd , "update sucessful" ) ;
2011-08-23 07:40:22 +02:00
} ;
my $ err = $@ ;
unlink $ tmp ;
unlink $ tmpgz ;
unlink $ sigfn ;
2012-02-21 11:42:32 +01:00
die $ err if $ err ;
2011-08-23 07:40:22 +02:00
}
2012-02-21 11:42:32 +01:00
sub get_apl_sources {
my $ urls = [] ;
2015-07-01 13:47:56 +02:00
push @$ urls , "http://download.proxmox.com/images" ;
2012-02-21 11:42:32 +01:00
push @$ urls , "http://releases.turnkeylinux.org/pve" ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
return $ urls ;
2011-08-23 07:40:22 +02:00
}
2012-02-21 11:42:32 +01:00
sub update {
my ( $ proxy ) = @ _ ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
my $ size ;
if ( ( $ size = ( - s $ logfile ) || 0 ) > ( 1024 * 50 ) ) {
system ( "mv $logfile $logfile.0" ) ;
}
my $ logfd = IO::File - > new ( ">>$logfile" ) ;
logmsg ( $ logfd , "starting update" ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
import_gpg_keys ( ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
# this code works for ftp and http
# always use passive ftp
local $ ENV { FTP_PASSIVE } = 1 ;
my $ ua = LWP::UserAgent - > new ;
$ ua - > agent ( "PVE/1.0" ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
if ( $ proxy ) {
$ ua - > proxy ( [ 'http' ] , $ proxy ) ;
} else {
$ ua - > env_proxy ;
}
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
my $ urls = get_apl_sources ( ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
mkdir $ aplinfodir ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
my @ dlerr = ( ) ;
foreach my $ aplurl ( @$ urls ) {
eval {
my $ uri = URI - > new ( $ aplurl ) ;
my $ host = $ uri - > host ( ) ;
download_aplinfo ( $ ua , $ aplurl , $ host , $ logfd ) ;
} ;
if ( my $ err = $@ ) {
logmsg ( $ logfd , $ err ) ;
push @ dlerr , $ aplurl ;
}
}
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
close ( $ logfd ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
return 0 if scalar ( @ dlerr ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
return 1 ;
2011-08-23 07:40:22 +02:00
}
2012-02-21 11:42:32 +01:00
sub load_data {
2011-08-23 07:40:22 +02:00
2012-02-21 12:15:10 +01:00
my $ urls = get_apl_sources ( ) ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
my $ list = { } ;
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
foreach my $ aplurl ( @$ urls ) {
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
eval {
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
my $ uri = URI - > new ( $ aplurl ) ;
my $ host = $ uri - > host ( ) ;
read_aplinfo ( "$aplinfodir/$host" , $ list , $ aplurl ) ;
} ;
warn $@ if $@ ;
}
2011-08-23 07:40:22 +02:00
2012-02-21 11:42:32 +01:00
return $ list ;
2011-08-23 07:40:22 +02:00
}
1 ;