diff -u ocsinventory-agent-2.0/debian/changelog ocsinventory-agent-2.0/debian/changelog --- ocsinventory-agent-2.0/debian/changelog +++ ocsinventory-agent-2.0/debian/changelog @@ -1,8 +1,38 @@ -ocsinventory-agent (2:2.0-0ubuntu1) maverick; urgency=low +ocsinventory-agent (2:2.0-1ubuntu1) oneiric; urgency=low - * Packaged OCS Unified Unix Agent 2.0 + * merged OCS 2.0-1 debian with ubuntu - -- Richard Sellam Fri, 24 Jun 2011 16:34:44 +0200 + -- Richard Sellam Fri, 09 Sep 2011 16:34:44 +0200 + + +ocsinventory-agent (2:2.0-1) unstable; urgency=low + + * Imported Upstream version 2.0 (Closes: #629047) + * Removed patch no-ExtUtils-Installed.diff, applied upstream + * Bump Standards Version to 3.9.2 + * Add build-arch and build-indep targets (fix lintian warnings) + * Remove temporary file in postinst (Closes: #602407) + * Add hdparm to recommended packages (Closes: #609879) + + -- Pierre Chifflier Fri, 29 Jul 2011 23:51:45 +0200 + +ocsinventory-agent (2:1.1.1-2.3) unstable; urgency=low + + * Non-maintainer upload. + * Fix encoding of Danish debconf translation. + + -- Christian Perrier Tue, 11 Jan 2011 22:07:19 +0100 + +ocsinventory-agent (2:1.1.1-2.2) unstable; urgency=low + + * Non-maintainer upload. + * Do not use ExtUtils::Installed to find module (Closes: #590879) + ExtUtils::Installed looks all the @INC directory for .pack files, + including '.'. When the agent was launched by a cron task or in + daemon mode, the current directory is / and so the whole system was + scanned, thank you Remi COLLET for pointing this issue. + + -- Gonéri Le Bouder Fri, 24 Sep 2010 22:08:09 +0200 ocsinventory-agent (2:1.1.1-2.1) unstable; urgency=low diff -u ocsinventory-agent-2.0/debian/watch ocsinventory-agent-2.0/debian/watch --- ocsinventory-agent-2.0/debian/watch +++ ocsinventory-agent-2.0/debian/watch @@ -2 +2 @@ -http://launchpad.net/ocsinventory-unix-agent/stable-2.0/2.0/+download/ Ocsinventory-Agent-([[:digit:]].*)\.tar\.gz +http://search.cpan.org/dist/Ocsinventory-Agent/ .*/Ocsinventory-Agent-([[:digit:]].*)\.tar\.gz diff -u ocsinventory-agent-2.0/debian/rules ocsinventory-agent-2.0/debian/rules --- ocsinventory-agent-2.0/debian/rules +++ ocsinventory-agent-2.0/debian/rules @@ -6,8 +6,6 @@ DEB_DH_INSTALL_CRON_ARGS=--name=ocsinventory-agent -DESTDIR=debian/ocsinventory-agent - configure: configure-stamp configure-stamp: dh_testdir @@ -17,12 +15,14 @@ touch configure-stamp -build: build-stamp +build: build-arch build-indep +build-arch: build-stamp +build-indep: build-stamp build-stamp: configure-stamp dh_testdir - PERL_AUTOINSTALL=1 perl Makefile.PL INSTALLDIRS=vendor + PERL_AUTOINSTALL=--skip perl Makefile.PL INSTALLDIRS=vendor touch $@ @@ -37,6 +37,7 @@ QUILT_PATCHES=debian/patches quilt pop -a -R || test $$? = 2 rm -rf .pc + [ ! -f ipdiscover ] || rm -f ipdiscover dh_clean @@ -46,9 +47,12 @@ dh_clean -k dh_installdirs - $(MAKE) DESTDIR=$(CURDIR)/debian/ocsinventory-agent install + $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install + + install -d -m 0755 $(CURDIR)/debian/tmp/etc/ocsinventory + install -m 0644 etc/ocsinventory-agent/modules.conf $(CURDIR)/debian/tmp/etc/ocsinventory/ - install -m 0644 etc/ocsinventory-agent/modules.conf $(DESTDIR)/etc/ocsinventory/ + dh_install --list-missing --sourcedir=debian/tmp diff -u ocsinventory-agent-2.0/debian/ocsinventory-agent.postinst ocsinventory-agent-2.0/debian/ocsinventory-agent.postinst --- ocsinventory-agent-2.0/debian/ocsinventory-agent.postinst +++ ocsinventory-agent-2.0/debian/ocsinventory-agent.postinst @@ -62,6 +62,7 @@ ucf --debconf-ok $TMPFILE /etc/ocsinventory/ocsinventory-agent.cfg db_stop + rm -f $TMPFILE ;; diff -u ocsinventory-agent-2.0/debian/ocsinventory-agent.dirs ocsinventory-agent-2.0/debian/ocsinventory-agent.dirs --- ocsinventory-agent-2.0/debian/ocsinventory-agent.dirs +++ ocsinventory-agent-2.0/debian/ocsinventory-agent.dirs @@ -4,7 +4,4 @@ -etc/ocsinventory-agent usr/bin var/cache/ocsinventory-agent var/lib/ocsinventory-agent var/log/ocsinventory-client -var/run - diff -u ocsinventory-agent-2.0/debian/copyright ocsinventory-agent-2.0/debian/copyright --- ocsinventory-agent-2.0/debian/copyright +++ ocsinventory-agent-2.0/debian/copyright @@ -1,10 +1,7 @@ -This package was packaged by Richard Sellam -on Fri, 24 Jun 2011 16:34:44 +0200. +This package was debianized by Pierre Chifflier on +Mon, 15 Jan 2007 12:28:59 +0100. -It was downloaded from http://launchpad.net/ocsinventory-unix-agent/ - -This package was first debianized by Pierre Chifflier -on Mon, 15 Jan 2007 12:28:59 +0100. +It was downloaded from http://search.cpan.org/dist/Ocsinventory-Agent/ Upstream maintainer: OCS Inventory Team Copyright (C) 2006-2009 Gonéri LE BOUDER @@ -36,8 +33,6 @@ On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. -The repackaging is (C) 2011, Richard Sellam -and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. The Debian packaging is (C) 2007, Pierre Chifflier and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. diff -u ocsinventory-agent-2.0/debian/control ocsinventory-agent-2.0/debian/control --- ocsinventory-agent-2.0/debian/control +++ ocsinventory-agent-2.0/debian/control @@ -2,23 +2,29 @@ Section: net Priority: extra Maintainer: Richard Sellam -XSBC-Original-Maintainer: Pierre Chifflier +XSBC-Original-Maintainer: Pierre Chifflier Build-Depends: debhelper (>= 5), libxml-simple-perl, libnet-ip-perl, libcompress-zlib-perl, - libwww-perl, + libwww-perl, libnet-ssleay-perl, po-debconf, perl, quilt -Standards-Version: 3.9.1 +Standards-Version: 3.9.2 Homepage: http://www.ocsinventory-ng.org/ Package: ocsinventory-agent Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, debconf (>= 0.5) | debconf-2.0, po-debconf, - ucf, libdigest-md5-file-perl, - libxml-simple-perl, libnet-ip-perl, - libwww-perl, dmidecode, pciutils -Suggests: libproc-daemon-perl, libproc-pid-file-perl, ipmitool, libcompress-zlib-perl, read-edid, libnet-cups-perl, libnet-snmp-perl, nmap, libnmap-parser-perl + ucf, + libxml-simple-perl, libcompress-zlib-perl, + libnet-ip-perl, libwww-perl, libdigest-md5-file-perl, + libnet-ssleay-perl, dmidecode +Recommends: hdparm, pciutils +Suggests: libcrypt-ssleay-perl, libnet-snmp-perl, + libproc-pid-file-perl, libproc-daemon-perl, + net-tools, libsys-syslog-perl, + ipmitool, read-edid, libnet-cups-perl, + nmap, libnmap-parser-perl Description: Hardware and software inventory tool (client) Open Computer and Software Inventory Next Generation is an application designed to help a network or system administrator to diff -u ocsinventory-agent-2.0/debian/po/da.po ocsinventory-agent-2.0/debian/po/da.po --- ocsinventory-agent-2.0/debian/po/da.po +++ ocsinventory-agent-2.0/debian/po/da.po @@ -11,6 +11,7 @@ "PO-Revision-Date: 2010-05-10 17:30+01:00\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -37,31 +38,31 @@ #. Description #: ../ocsinventory-agent.templates:2002 msgid "Choose the 'local' method if you do not have a network connection." -msgstr "V??lg metoden lokal hvis du ikke har en netv??rksforbindelse." +msgstr "Vælg metoden lokal hvis du ikke har en netværksforbindelse." #. Type: select #. Description #: ../ocsinventory-agent.templates:2002 msgid "Choose the 'http' method if an OCS Inventory server is set up." -msgstr "V??lg metoden http hvis en OCS-lagerserver er sat op." +msgstr "Vælg metoden http hvis en OCS-lagerserver er sat op." #. Type: string #. Description #: ../ocsinventory-agent.templates:3001 msgid "OCS Inventory server host name:" -msgstr "Serverv??rtsnavn for OCS-lager:" +msgstr "Serverværtsnavn for OCS-lager:" #. Type: string #. Description #: ../ocsinventory-agent.templates:3001 msgid "Please enter the host name of the OCS inventory server." -msgstr "Indtast venligst v??rtsnavnet p?? OCS-lagerserveren." +msgstr "Indtast venligst værtsnavnet på OCS-lagerserveren." #. Type: string #. Description #: ../ocsinventory-agent.templates:4001 msgid "Tag for the generated inventory:" -msgstr "M??rke for det oprettede lager:" +msgstr "Mærke for det oprettede lager:" #. Type: string #. Description @@ -70,8 +71,8 @@ "Each inventory can have an associated tag. Please enter the tag you would " "like for the new inventory." msgstr "" -"Hvert lager kan have et associeret m??rke. Indtast venligst det m??rke du ??" -"nsker for dette nye lager." +"Hvert lager kan have et associeret mærke. Indtast venligst det mærke du " +"ønsker for dette nye lager." #. Type: string #. Description @@ -82,3 +83,3 @@ msgstr "" -"Dette felt kan efterlades uudfyldt for at forts??tte uden et nyt m??rke for " +"Dette felt kan efterlades uudfyldt for at fortsætte uden et nyt mærke for " "lageret." only in patch2: unchanged: --- ocsinventory-agent-2.0.orig/debian/ocsinventory-agent.files +++ ocsinventory-agent-2.0/debian/ocsinventory-agent.files @@ -0,0 +1 @@ +ocsinventory-agent_1.0~rc3-1_i386.deb net extra only in patch2: unchanged: --- ocsinventory-agent-2.0.orig/debian/ocsinventory-agent.install +++ ocsinventory-agent-2.0/debian/ocsinventory-agent.install @@ -0,0 +1,4 @@ +etc/ocsinventory +usr/bin/ocsinventory-agent +usr/share/man +usr/share/perl5 only in patch2: unchanged: --- ocsinventory-agent-2.0.orig/debian/patches/no-ExtUtils-Installed.diff +++ ocsinventory-agent-2.0/debian/patches/no-ExtUtils-Installed.diff @@ -0,0 +1,26 @@ +--- ocsinventory-agent-1.1.1.orig/lib/Ocsinventory/Agent/Backend.pm ++++ ocsinventory-agent-1.1.1/lib/Ocsinventory/Agent/Backend.pm +@@ -4,8 +4,6 @@ + no strict 'refs'; + use warnings; + +-use ExtUtils::Installed; +- + sub new { + my (undef, $params) = @_; + +@@ -88,14 +86,6 @@ + # devlib enable, I only search for backend module in ./lib + push (@dirToScan, './lib'); + } else { +- my ($inst) = ExtUtils::Installed->new(); +- +- eval {@installed_files = +- $inst->files('Ocsinventory')}; +- +-# ExtUtils::Installed is nice but it needs properly installed package with +-# .packlist +-# This is a workaround for 'invalide' installations... + foreach (@INC) { + next if ! -d || (-l && -d readlink) || /^(\.|lib)$/; + push @dirToScan, $_; only in patch2: unchanged: --- ocsinventory-agent-2.0.orig/lib/Ocsinventory/Agent/Option/Download.pm +++ ocsinventory-agent-2.0/lib/Ocsinventory/Agent/Option/Download.pm @@ -0,0 +1,918 @@ +############################################################################### +## OCSINVENTORY-NG +## Copyleft Pascal DANEK 2005 +## Web : http://ocsinventory.sourceforge.net +## +## This code is open source and may be copied and modified as long as the source +## code is always made freely available. +## Please refer to the General Public Licence http://www.gnu.org/ or Licence.txt +################################################################################ +# Function by hook: +# -download_prolog_reader, download_message, download +# -download_inventory_handler +# -download_end_handler, begin, done, clean, finish, period, download, execute, +# check_signature and build_package +package Ocsinventory::Agent::Option::Download; + +use strict; + +require Exporter; + +our @ISA = qw /Exporter/; + +our @EXPORT = qw/ + download_inventory_handler + download_prolog_reader + download_end_handler +/; + +use Fcntl qw/:flock/; +use XML::Simple; +use LWP::UserAgent; +use Compress::Zlib; +use Ocsinventory::Agent::Common qw/_uncompress _get_path _already_in_array/; +use Digest::MD5; +use File::Path; +use Socket; +use Net::SSLeay qw(die_now die_if_ssl_error); + +# Can be missing. By default, we use MD5 +# You have to install it if you want to use SHA1 digest +eval{ require Digest::SHA1 }; +use constant HTTPS_PORT => '443'; +# Time to wait between scheduler periods, scheduling cycles and fragments downloads +use constant FRAG_LATENCY_DEFAULT => 10; +use constant PERIOD_LATENCY_DEFAULT => 0; +use constant CYCLE_LATENCY_DEFAULT => 10; +use constant MAX_ERROR_COUNT => 30; +# Number of loops for one period +use constant PERIOD_LENGTH_DEFAULT => 10; +# Errors +use constant CODE_SUCCESS => 'SUCCESS'; +use constant ERR_BAD_ID => 'ERR_BAD_ID'; +use constant ERR_DOWNLOAD_INFO => 'ERR_DOWNLOAD_INFO'; +use constant ERR_BAD_DIGEST => 'ERR_BAD_DIGEST'; +use constant ERR_DOWNLOAD_PACK => 'ERR_DOWNLOAD_PACK'; +use constant ERR_BUILD => 'ERR_BUILD'; +use constant ERR_EXECUTE => 'ERR_EXECUTE'; +use constant ERR_CLEAN => 'ERR_CLEAN'; +use constant ERR_TIMEOUT => 'ERR_TIMEOUT'; +use constant ERR_ALREADY_SETUP => 'ERR_ALREADY_SETUP'; + +my @packages; +my $current_context; +my $ua; +my $config; +my $error; +my $debug; + +# Read prolog response +sub download_prolog_reader{ + $current_context = shift; + my $prolog = shift; + + $debug = $::debug; + + &log($prolog); + + $prolog = XML::Simple::XMLin( $prolog, ForceArray => ['OPTION', 'PARAM']); + my $option; + # Create working directory + my $opt_dir = $current_context->{'OCS_AGENT_INSTALL_PATH'}.'/download'; + mkdir($opt_dir) unless -d $opt_dir; + + # We create a file to tell to download process that we are running + open SUSPEND, ">$opt_dir/suspend"; + close(SUSPEND); + + # Create history file if needed + unless(-e "$opt_dir/history"){ + open HISTORY, ">$opt_dir/history" or die("Cannot create history file: $!"); + close(HISTORY); + } + + # Create lock file if needed + unless(-e "$opt_dir/lock"){ + open LOCK, ">$opt_dir/lock" or die("Cannot create lock file: $!"); + close(LOCK); + } + + # Retrieve our options + for $option (@{$prolog->{OPTION}}){ + if( $option->{NAME} =~/download/i){ + for ( @{ $option->{PARAM} } ) { + # Type of param + if($_->{'TYPE'} eq 'CONF'){ + # Writing configuration + open FH, ">$opt_dir/config" or die("Cannot open/create + config file ($opt_dir/config)"); + if(flock(FH, LOCK_EX)){ + &log("Writing config file."); + print FH XMLout($_, RootName => 'CONF'); + close(FH); + $config = $_; + }else{ + &log("Cannot lock config file !!"); + close(FH); + return 0; + } + + # Apply config + # ON ? + if($_->{'ON'} == '0'){ + &log("Download is off."); + open LOCK, "$opt_dir/lock" or die("Cannot open lock file: $!"); + if(flock(LOCK, LOCK_EX|LOCK_NB)){ + close(LOCK); + unlink("$opt_dir/suspend"); + return 0; + }else{ + &log("Try to kill current download process..."); + my $pid = ; + close(LOCK); + &log("Sending USR1 to $pid..."); + if(kill("USR1", $pid)){ + &log("Success."); + }else{ + &log("Failed."); + } + return 0; + } + } + # Maybe a new package to download + }elsif($_->{'TYPE'} eq 'PACK'){ + push @packages, { + 'PACK_LOC' => $_->{'PACK_LOC'}, + 'INFO_LOC' => $_->{'INFO_LOC'}, + 'ID' => $_->{'ID'}, + 'CERT_PATH' => $_->{'CERT_PATH'}, + 'CERT_FILE' => $_->{'CERT_FILE'} + }; + } + } + } + } + + # We are now in download child + # Connect to server + $ua = LWP::UserAgent->new(); + $ua->agent('OCS-NG_linux_client_v'.$current_context->{'OCS_AGENT_VERSION'}); + $ua->credentials( $current_context->{'OCS_AGENT_SERVER_NAME'}, + $current_context->{'OCS_AGENT_AUTH_REALM'}, + $current_context->{'OCS_AGENT_AUTH_USER'} => $current_context->{'OCS_AGENT_AUTH_PWD'} + ); + + # Check history file + unless(open HISTORY, "$opt_dir/history") { + flock(HISTORY, LOCK_EX); + unlink("$opt_dir/suspend"); + &log("Cannot read history file: $!"); + return 1; + } + + chomp(my @done = ); + close(HISTORY); + + # Package is maybe already handled + for(@packages){ + my $dir = $opt_dir."/".$_->{'ID'}; + my $fileid = $_->{'ID'}; + my $infofile = 'info'; + my $location = $_->{'INFO_LOC'}; + + if(_already_in_array($fileid, @done)){ + &log("Will not download $fileid. (already in history file)"); + &download_message({ 'ID' => $fileid }, ERR_ALREADY_SETUP); + next; + } + + # Looking for packages status + unless(-d $dir){ + &log("Making working directory for $fileid."); + mkdir($dir) or die("Cannot create $fileid directory: $!"); + open FH, ">$dir/since" or die("Cannot create $fileid since file: $!");; + print FH time(); + close(FH); + } + + # Retrieve and writing info file if needed + unless(-f "$dir/$infofile"){ + # Special value INSTALL_PATH + $_->{CERT_PATH} =~ s/INSTALL_PATH/$current_context->{OCS_AGENT_INSTALL_PATH}/; + $_->{CERT_FILE} =~ s/INSTALL_PATH/$current_context->{OCS_AGENT_INSTALL_PATH}/; + + if (!-f $_->{CERT_FILE}) { + &log("No certificat found in ".$_->{CERT_FILE}); + } + + # Getting info file + &log("Retrieving info file for $fileid"); + + my ($ctx, $ssl, $ra); + eval { + $| = 1; + &log('Initialize ssl layer...'); + + # Initialize openssl + if ( -e '/dev/urandom') { + $Net::SSLeay::random_device = '/dev/urandom'; + $Net::SSLeay::how_random = 512; + } + else { + srand (time ^ $$ ^ unpack "%L*", `ps wwaxl | gzip`); + $ENV{RND_SEED} = rand 4294967296; + } + + Net::SSLeay::randomize(); + Net::SSLeay::load_error_strings(); + Net::SSLeay::ERR_load_crypto_strings(); + Net::SSLeay::SSLeay_add_ssl_algorithms(); + + #Create ctx object + $ctx = Net::SSLeay::CTX_new() or die_now("Failed to create SSL_CTX $!"); + Net::SSLeay::CTX_load_verify_locations( $ctx, $_->{CERT_FILE}, $_->{CERT_PATH} ) + or die_now("CTX load verify loc: $!"); + # Tell to SSLeay where to find AC file (or dir) + Net::SSLeay::CTX_set_verify($ctx, &Net::SSLeay::VERIFY_PEER, \&ssl_verify_callback); + die_if_ssl_error('callback: ctx set verify'); + + my($server_name,$server_port,$server_dir); + + if($_->{INFO_LOC}=~ /^([^:]+):(\d{1,5})(.*)$/){ + $server_name = $1; + $server_port = $2; + $server_dir = $3; + }elsif($_->{INFO_LOC}=~ /^([^\/]+)(.*)$/){ + $server_name = $1; + $server_dir = $2; + $server_port = HTTPS_PORT; + } + $server_dir .= '/' unless $server_dir=~/\/$/; + + $server_name = gethostbyname ($server_name) or die; + my $dest_serv_params = pack ('S n a4 x8', &AF_INET, $server_port, $server_name ); + + # Connect to server + &log("Connect to server: $_->{INFO_LOC}..."); + socket (S, &AF_INET, &SOCK_STREAM, 0) or die "socket: $!"; + connect (S, $dest_serv_params) or die "connect: $!"; + + # Flush socket + select (S); $| = 1; select (STDOUT); + $ssl = Net::SSLeay::new($ctx) or die_now("Failed to create SSL $!"); + Net::SSLeay::set_fd($ssl, fileno(S)); + + # SSL handshake + &log('Starting SSL connection...'); + Net::SSLeay::connect($ssl); + die_if_ssl_error('callback: ssl connect!'); + + # Get info file + my $http_request = "GET /$server_dir".$fileid."/info HTTP/1.0\n\n"; + Net::SSLeay::ssl_write_all($ssl, $http_request); + shutdown S, 1; + + $ra = Net::SSLeay::ssl_read_all($ssl); + $ra = (split("\r\n\r\n", $ra))[1] or die; + &log("Info file: $ra"); + + my $xml = XML::Simple::XMLin( $ra ) or die; + + $xml->{PACK_LOC} = $_->{PACK_LOC}; + + $ra = XML::Simple::XMLout( $xml ) or die; + + open FH, ">$dir/info" or die("Cannot open info file: $!"); + print FH $ra; + close FH; + }; + if($@){ + download_message({ 'ID' => $fileid }, ERR_DOWNLOAD_INFO); + &log("Error: SSL hanshake has failed"); + next; + } + else { + &log("Success. :-)"); + } + Net::SSLeay::free ($ssl); + Net::SSLeay::CTX_free ($ctx); + close S; + sleep(1); + } + } + unless(unlink("$opt_dir/suspend")){ + &log("Cannot delete suspend file: $!"); + return 1; + } + return 0; +} + +sub ssl_verify_callback { + my ($ok, $x509_store_ctx) = @_; + return $ok; +} + +sub download_inventory_handler{ + # Adding the ocs package ids to softwares + my $current_context = shift; + my $inventory = shift; + my @history; + # Read download history file + if( open PACKAGES, "$current_context->{OCS_AGENT_INSTALL_PATH}/download/history" ){ + flock(PACKAGES, LOCK_SH); + while(){ + chomp( $_ ); + push @history, { ID => $_ }; + } + } + close(PACKAGES); + # Add it to inventory (will be handled by Download.pm server module + push @{ $inventory->{'CONTENT'}->{'DOWNLOAD'}->{'HISTORY'} },{ + 'PACKAGE'=> \@history + }; +} + +sub download_end_handler{ + # Get global structure + $current_context = shift; + my $dir = $current_context->{'OCS_AGENT_INSTALL_PATH'}."/download"; + my $pidfile = $dir."/lock"; + + return 0 unless -d $dir; + + # We have jobs, we do it alone + my $fork = fork(); + if($fork>0){ + return 0; + }elsif($fork<0){ + return 1; + }else{ + $SIG{'USR1'} = sub { + print "Exiting on signal...\n"; + &finish(); + }; + # Go into working directory + chdir($dir) or die("Cannot chdir to working directory...Abort\n"); + } + + unless($debug){ + open STDOUT, '>/dev/null' or die("Cannot redirect STDOUT"); + open STDERR, '>/dev/null' or die("Cannot redirect STDERR"); + } + + # Maybe an other process is running + exit(0) if begin($pidfile); + # Retrieve the packages to download + opendir DIR, $dir or die("Cannot read working directory: $!"); + + my $end; + + while(1){ + # If agent is running, we wait + if (-e "suspend") { + &log('Found a suspend file... Will wait 10 seconds before retry'); + sleep(10); + next; + } + + $end = 1; + undef @packages; + # Reading configuration + open FH, "$dir/config" or die("Cannot read config file: $!"); + if(flock(FH, LOCK_SH)){ + $config = XMLin("$dir/config"); + close(FH); + # If Frag latency is null, download is off + if($config->{'ON'} eq '0'){ + &log("Option turned off. Exiting."); + finish(); + } + }else{ + &log("Cannot read config file :-( . Exiting."); + close(FH); + finish(); + } + + # Retrieving packages to download and their priority + while(my $entry = readdir(DIR)){ + next if $entry !~ /^\d+$/; + + next unless(-d $entry); + + # Clean package if info file does not still exist + unless(-e "$entry/info"){ + &log("No info file found for $entry!!"); + clean( { 'ID' => $entry } ); + next; + } + my $info = XML::Simple::XMLin( "$entry/info" ) or next; + + # Check that fileid == directory name + if($info->{'ID'} ne $entry){ + &log("ID in info file does not correspond!!"); + clean( { 'ID' => $entry } ); + download_message({ 'ID' => $entry }, ERR_BAD_ID); + next; + } + + # Manage package timeout + # Clean package if since timestamp is not present + unless(-e "$entry/since"){ + &log("No since file found!!"); + clean( { 'ID' => $entry } ); + next; + }else{ + my $time = time(); + if(open SINCE, "$entry/since"){ + my $since = ; + if($since=~/\d+/){ + if( (($time-$since)/86400) > $config->{TIMEOUT}){ + &log("Timeout Reached for $entry."); + clean( { 'ID' => $entry } ); + &download_message('ID' => $entry, ERR_TIMEOUT); + close(SINCE); + next; + }else{ + &log("Checking timeout for $entry... OK"); + } + }else{ + &log("Since data for $entry is incorrect."); + clean( { 'ID' => $entry } ); + &download_message('ID' => $entry, ERR_TIMEOUT); + close(SINCE); + next; + } + close(SINCE); + }else{ + &log("Cannot find since data for $entry."); + clean( { 'ID' => $entry } ); + &download_message('ID' => $entry, ERR_TIMEOUT); + next; + } + } + + # Building task file if needed + unless( -f "$entry/task" and -f "$entry/task_done" ){ + open FH, ">$entry/task" or die("Cannot create task file for $entry: $!"); + + my $i; + my $frags = $info->{'FRAGS'}; + # There are no frags if there is only a command + if($frags){ + for($i=1;$i<=$frags;$i++){ + print FH "$entry-$i\n"; + } + }; + close FH; + # To be sure that task file is fully created + open FLAG, ">$entry/task_done" or die ("Cannot create task flag file for $entry: $!"); + close(FLAG); + } + # Push package XML description + push @packages, $info; + $end = 0; + } + # Rewind directory + rewinddir(DIR); + # Call packages scheduler + if($end){ + last; + }else{ + period(\@packages); + } + } + &log("No more package to download."); + finish(); +} + +# Schedule the packages +sub period{ + my $packages = shift; + my @rt; + my $i; + + @rt = grep {$_->{'PRI'} eq "0"} @$packages; + + &log("New period. Nb of cycles: ". + (defined($config->{'PERIOD_LENGTH'})?$config->{'PERIOD_LENGTH'}:PERIOD_LENGTH_DEFAULT)); + + for($i=1;$i<=( defined($config->{'PERIOD_LENGTH'})?$config->{'PERIOD_LENGTH'}:PERIOD_LENGTH_DEFAULT);$i++){ + # Highest priority + if(@rt){ + &log("Managing ".scalar(@rt)." package(s) with absolute priority."); + for(@rt){ + # If done file found, clean package + if(-e "$_->{'ID'}/done"){ + &log("done file found!!"); + done($_); + next; + } + download($_); + &log("Now pausing for a cycle latency => ".( + defined($config->{'FRAG_LATENCY'})?$config->{'FRAG_LATENCY'}:FRAG_LATENCY_DEFAULT) + ." seconds"); + sleep( defined($config->{'FRAG_LATENCY'})?$config->{'FRAG_LATENCY'}:FRAG_LATENCY_DEFAULT ); + } + next; + } + # Normal priority + for(@$packages){ + # If done file found, clean package + if(-e "$_->{'ID'}/done"){ + &log("done file found!!"); + done($_); + next; + } + next if $i % $_->{'PRI'} != 0; + download($_); + + &log("Now pausing for a fragment latency => ". + (defined( $config->{'FRAG_LATENCY'} )?$config->{'FRAG_LATENCY'}:FRAG_LATENCY_DEFAULT) + ." seconds"); + + sleep(defined($config->{'FRAG_LATENCY'})?$config->{'FRAG_LATENCY'}:FRAG_LATENCY_DEFAULT); + } + + &log("Now pausing for a cycle latency => ".( + defined($config->{'CYCLE_LATENCY'})?$config->{'CYCLE_LATENCY'}:CYCLE_LATENCY_DEFAULT) + ." seconds"); + + sleep(defined($config->{'CYCLE_LATENCY'})?$config->{'CYCLE_LATENCY'}:CYCLE_LATENCY_DEFAULT); + } + sleep($config->{'PERIOD_LATENCY'}?$config->{'PERIOD_LATENCY'}:PERIOD_LATENCY_DEFAULT); +} + +# Download a fragment of the specified package +sub download{ + my $p = shift; + my $proto = $p->{'PROTO'}; + my $location = $p->{'PACK_LOC'}; + my $id = $p->{'ID'}; + my $URI = "$proto://$location/$id/"; + + # If we find a temp file, we know that the update of the task file has failed for any reason. So we retrieve it from this file + if(-e "$id/task.temp") { + unlink("$id/task.temp"); + rename("$id/task.temp","$id/task") or return 1; + } + + # Retrieve fragments already downloaded + unless(open TASK, "$id/task"){ + &log("Download: Cannot open $id/task."); + return 1; + } + my @task = ; + + # Done + if(!@task){ + &log("Download of $p->{'ID'}... Finished."); + close(TASK); + execute($p); + return 0; + } + + my $fragment = shift(@task); + my $request = HTTP::Request->new(GET => $URI.$fragment); + + &log("Downloading $fragment..."); + + # Using proxy if possible + $ua->env_proxy; + my $res = $ua->request($request); + + # Checking if connected + if($res->is_success) { + &log("Success :-)"); + $error = 0; + open FRAGMENT, ">$id/$fragment" or return 1; + print FRAGMENT $res->content; + close(FRAGMENT); + + # Updating task file + rename(">$id/task", ">$id/task.temp"); + open TASK, ">$id/task" or return 1; + print TASK @task; + close(TASK); + unlink(">$id/task.temp"); + + } + else { + #download_message($p, ERR_DOWNLOAD_PACK); + close(TASK); + &log("Error :-( ".$res->code); + $error++; + if($error > MAX_ERROR_COUNT){ + &log("Error : Max errors count reached"); + finish(); + } + return 1; + } + return 0; +} + +# Assemble and handle downloaded package +sub execute{ + my $p = shift; + my $tmp = $p->{'ID'}."/tmp"; + my $exit_code; + + &log("Execute orders for package $p->{'ID'}."); + + if(build_package($p)){ + clean($p); + return 1; + }else{ + # First, we get in temp directory + unless( chdir($tmp) ){ + &log("Cannot chdir to working directory: $!"); + download_message($p, ERR_EXECUTE); + clean($p); + return 1; + } + + # Executing preorders (notify user, auto launch, etc.... + # $p->{NOTIFY_USER} + # $p->{NOTIFY_TEXT} + # $p->{NOTIFY_COUNTDOWN} + # $p->{NOTIFY_CAN_ABORT} + # TODO: notification to send through DBUS to the user + + + eval{ + # Execute instructions + if($p->{'ACT'} eq 'LAUNCH'){ + my $exe_line = $p->{'NAME'}; + $p->{'NAME'} =~ s/^([^ -]+).*/$1/; + # Exec specified file (LAUNCH => NAME) + if(-e $p->{'NAME'}){ + &log("Launching $p->{'NAME'}..."); + chmod(0755, $p->{'NAME'}) or die("Cannot chmod: $!"); + $exit_code = system( "./".$exe_line ); + }else{ + die(); + } + + }elsif($p->{'ACT'} eq 'EXECUTE'){ + # Exec specified command EXECUTE => COMMAND + &log("Execute $p->{'COMMAND'}..."); + system( $p->{'COMMAND'} ) and die(); + + }elsif($p->{'ACT'} eq 'STORE'){ + # Store files in specified path STORE => PATH + $p->{'PATH'} =~ s/INSTALL_PATH/$current_context->{OCS_AGENT_INSTALL_PATH}/; + + # Build it if needed + my @dir = split('/', $p->{'PATH'}); + my $dir; + + for(@dir){ + $dir .= "$_/"; + unless(-e $dir){ + mkdir($dir); + &log("Create $dir..."); + } + } + + &log("Storing package to $p->{'PATH'}..."); + # Stefano Brandimarte => Stevenson! + system(&_get_path('cp')." -dpr * ".$p->{'PATH'}) and die(); + } + }; + if($@){ + # Notify success to ocs server + download_message($p, ERR_EXECUTE); + chdir("../..") or die("Cannot go back to download directory: $!"); + clean($p); + return 1; + }else{ + chdir("../..") or die("Cannot go back to download directory: $!"); + done($p, (defined($exit_code)?$exit_code:'_NONE_')); + return 0; + } + } +} + +# Check package integrity +sub build_package{ + my $p = shift; + my $id = $p->{'ID'}; + my $count = $p->{'FRAGS'}; + my $i; + my $tmp = "./$id/tmp"; + + unless(-d $tmp){ + mkdir("$tmp"); + } + # No job if no files + return 0 unless $count; + + # Assemble package + &log("Building package for $p->{'ID'}."); + + for($i=1;$i<=$count;$i++){ + if(-f "./$id/$id-$i"){ + # We make a tmp working directory + if($i==1){ + open PACKAGE, ">$tmp/build.tar.gz" or return 1; + } + # We write each fragment in the final package + open FRAGMENT, "./$id/$id-$i" or return 1; + my $row; + while($row = ){ + print PACKAGE $row; + } + close(FRAGMENT); + }else{ + return 1; + } + } + close(PACKAGE); + # + if(check_signature($p->{'DIGEST'}, "$tmp/build.tar.gz", $p->{'DIGEST_ALGO'}, $p->{'DIGEST_ENCODE'})){ + download_message($p, ERR_BAD_DIGEST); + return 1; + } + + if( system( &_get_path("tar")." -xvzf $tmp/build.tar.gz -C $tmp") ){ + &log("Cannot extract $p->{'ID'}."); + download_message($p, ERR_BUILD); + return 1; + } + &log("Building of $p->{'ID'}... Success."); + unlink("$tmp/build.tar.gz") or die ("Cannot remove build file: $!\n"); + return 0; +} + +sub check_signature{ + my ($checksum, $file, $digest, $encode) = @_; + + &log("Checking signature for $file."); + + my $base64; + + # Open file + unless(open FILE, $file){ + &log("cannot open $file: $!"); + return 1; + } + + binmode(FILE); + # Retrieving encoding form + if($encode =~ /base64/i){ + $base64 = 1; + &log('Digest format: Base 64'); + }elsif($encode =~ /hexa/i){ + &log('Digest format: Hexadecimal'); + }else{ + &log('Digest format: Not supported'); + return 1; + } + + eval{ + # Check it + if($digest eq 'MD5'){ + &log('Digest algo: MD5'); + if($base64){ + die unless Digest::MD5->new->addfile(*FILE)->b64digest eq $checksum; + } + else{ + die unless Digest::MD5->new->addfile(*FILE)->hexdigest eq $checksum; + } + }elsif($digest eq 'SHA1'){ + &log('Digest algo: SHA1'); + if($base64){ + die unless Digest::SHA1->new->addfile(*FILE)->b64digest eq $checksum; + } + else{ + die unless Digest::SHA1->new->addfile(*FILE)->hexdigest eq $checksum; + } + }else{ + &log('Digest algo unknown: '.$digest); + die; + } + }; + if($@){ + &log("Digest checking error !!"); + close(FILE); + return 1; + }else{ + close(FILE); + &log("Digest OK..."); + return 0; + } +} + +# Launch a download error to ocs server +sub download_message{ + my ($p, $code) = @_; + + &log("Sending message for $p->{'ID'}, code=$code."); + + my $xml = { + 'DEVICEID' => $current_context->{'OCS_AGENT_DEVICEID'}, + 'QUERY' => 'DOWNLOAD', + 'ID' => $p->{'ID'}, + 'ERR' => $code + }; + + # Generate xml + $xml = XMLout($xml, RootName => 'REQUEST'); + + # Compress data + $xml = Compress::Zlib::compress( $xml ); + + my $URI = $current_context->{'OCS_AGENT_SERVER_NAME'}; + + # Send request + my $request = HTTP::Request->new(POST => $URI); + $request->header('Pragma' => 'no-cache', 'Content-type', 'application/x-compress'); + $request->content($xml); + my $res = $ua->request($request); + + # Checking result + if($res->is_success) { + return 0; + }else{ + return 1; + } +} + +# At the beginning of end handler +sub begin{ + my $pidfile = shift; + open LOCK_R, "$pidfile" or die("Cannot open pid file: $!"); + if(flock(LOCK_R,LOCK_EX|LOCK_NB)){ + open LOCK_W, ">$pidfile" or die("Cannot open pid file: $!"); + select(LOCK_W) and $|=1; + select(STDOUT) and $|=1; + print LOCK_W $$; + &log("Beginning work. I am $$."); + return 0; + }else{ + close(LOCK_R); + &log("$pidfile locked. Cannot begin work... :-("); + return 1; + } +} + +sub done{ + my $p = shift; + my $suffix = shift; + &log("Package $p->{'ID'}... Done. Sending message..."); + # Trace installed package + open DONE, ">$p->{'ID'}/done"; + close(DONE); + # Put it in history file + open DONE, "history" or warn("Cannot open history file: $!"); + flock(DONE, LOCK_EX); + my @historyIds = ; + if( &_already_in_array($p->{'ID'}, @historyIds) ){ + &log("Warning: id $p->{'ID'} has been found in the history file!!"); + } + else { + print DONE $p->{'ID'},"\n"; + } + close(DONE); + + # Notify success to ocs server + my $code; + if($suffix ne '_NONE_'){ + $code = CODE_SUCCESS."_$suffix"; + } + else{ + $code = CODE_SUCCESS; + } + unless(download_message($p, $code)){ + # Clean package + clean($p); + }else{ + sleep( defined($config->{'FRAG_LATENCY'})?$config->{'FRAG_LATENCY'}:FRAG_LATENCY_DEFAULT ); + } + return 0; +} + +sub clean{ + my $p = shift; + &log("Cleaning $p->{'ID'} package."); + unless(File::Path::rmtree($p->{'ID'}, $debug, 0)){ + &log("Cannot clean $p->{'ID'}!! Abort..."); + download_message($p, ERR_CLEAN); + die(); + } + return 0; +} + +# At the end +sub finish{ + open LOCK, '>'.$current_context->{'OCS_AGENT_INSTALL_PATH'}.'/download/lock'; + &log("End of work...\n"); + exit(0); +} + +sub log{ + return 0 unless $debug; + my $message = shift; + print "DOWNLOAD: $message\n"; +} + +1;