# The LearningOnline Network with CAPA # Checksum installed LON-CAPA modules and some configuration files # # $Id: Checksumming.pm,v 1.7 2016/02/20 20:58:59 raeburn Exp $ # # The LearningOnline Network with CAPA # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # package LONCAPA::Checksumming; use strict; use lib '/home/httpd/lib/perl/'; use Apache::lonlocal(); use Apache::loncommon(); use Digest::SHA; sub get_checksums { my ($distro,$londaemons,$lonlib,$lonincludes,$lontabdir) = @_; my $output; my (%versions,%chksums); my $dirh; my $revtag = '$Id:'; my @paths; if ($londaemons) { push(@paths,($londaemons.'/*', $londaemons.'/debug/*.pl')); } if ($lonlib) { push(@paths,($lonlib.'/perl/Apache/*.pm', $lonlib.'/perl/Apache/localize/*.pm', $lonlib.'/perl/LONCAPA/*.pm', $lonlib.'/perl/AlgParser.pm', $lonlib.'/perl/LONCAPA.pm', $lonlib.'/perl/Safe.pm', $lonlib.'/perl/*-std.pm', $lonlib.'/perl/HTML/*.pm', $lonlib.'/perl/Safe.pm')); } if ($lonincludes) { push(@paths,$lonincludes.'/*.lcpm'); } push(@paths,('/home/httpd/cgi-bin/*.pl','/home/httpd/cgi-bin/*.png')); my $confdir = '/etc/httpd/conf'; if ($distro =~ /^(ubuntu|debian)(\d+)$/) { $confdir = '/etc/apache2'; } elsif ($distro =~ /^sles(\d+)$/) { if ($1 >= 10) { $confdir = '/etc/apache2'; } } elsif ($distro =~ /^suse(\d+\.\d+)$/) { if ($1 >= 10.0) { $confdir = '/etc/apache2'; } } push(@paths,("$confdir/loncapa_apache.conf","$confdir/startup.pl")); if (@paths) { my $pathstr = join (' ',@paths); if (open($dirh,"grep '$revtag' $pathstr 2>&1 |")) { while (my $line=<$dirh>) { if ($line =~ m{^([^#]+):#\s\$Id:\s[\w.]+,v\s([\d.]+)\s}) { $versions{$1} = $2; } } close($dirh); } foreach my $key (sort(keys(%versions))) { next if ($key =~ /\.lpmlsave$/); my $sum; if (open(my $fh,"<$key")) { binmode $fh; my $sha_obj = Digest::SHA->new(); $sha_obj->addfile($fh); $sum = $sha_obj->hexdigest; close($fh); $chksums{$key} = $sum; } $output .= "$key,$versions{$key},$sum\n"; } } if ($lontabdir ne '') { if (open(my $tabfh,">$lontabdir/lonchksums.tab")) { print $tabfh '# Last written: '.localtime(time)."\n"; print $tabfh "$output\n"; close($tabfh); } } return (\%chksums,\%versions); } sub compare_checksums { my ($target,$lonhost,$version,$serversums,$serverversions) = @_; my ($message,$numchg,$linefeed); if ($target eq 'web') { $linefeed = '
'; } else { $linefeed = "\n"; } if (!$Apache::lonlocal::lh) { &Apache::lonlocal::get_language_handle(); } if ((ref($serversums) eq 'HASH') && (keys(%{$serversums}))) { my $checksums = &Apache::lonnet::fetch_dns_checksums(); my (%extra,%missing,%diffs,%stdsums,%stdversions); if (ref($checksums) eq 'HASH') { if (ref($checksums->{'sums'}) eq 'HASH') { %stdsums = %{$checksums->{'sums'}}; } if (ref($checksums->{'versions'}) eq 'HASH') { %stdversions = %{$checksums->{'versions'}}; } if (keys(%stdsums)) { foreach my $key (keys(%stdsums)) { if (exists($serversums->{$key})) { if ($serversums->{$key} ne $stdsums{$key}) { $diffs{$key} = 1; $numchg ++; } } else { unless ((-e $key) && (-B $key)) { $missing{$key} = 1; $numchg ++; } } } foreach my $key (keys(%{$serversums})) { unless (exists($stdsums{$key})) { $extra{$key} = 1; $numchg ++; } } } if ($numchg) { $message = &Apache::lonlocal::mt('[quant,_1,difference was,differences were] found'. ' between LON-CAPA modules installed on your server [_2]'. ' and those expected for the LON-CAPA version you are'. ' currently running.',$numchg,"($lonhost)$linefeed"); if ($target eq 'web') { $message = '

'.$message.'

'; } else { $message .= "\n\n"; } my (@diffversion,@modified); if (keys(%diffs) > 0) { foreach my $file (sort(keys(%diffs))) { if ($serverversions->{$file} ne $stdversions{$file}) { push(@diffversion,$file); } else { push(@modified,$file); } } if (@diffversion > 0) { my $text = &Apache::lonlocal::mt('The following [quant,_1,file is a,files are]'. ' different version(s) from that expected for LON-CAPA [_2]:', scalar(@diffversion),$version); if ($target eq 'web') { $message .= '

'.$text.'

'. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row()."\n". ''.&Apache::lonlocal::mt('File').''."\n". ''.&Apache::lonlocal::mt('Server version').''."\n". ''.&Apache::lonlocal::mt('Expected version').''."\n". &Apache::loncommon::end_data_table_header_row()."\n"; } else { $message .= "$text\n"; } foreach my $file (sort(@diffversion)) { my $revnum = $stdversions{$file}; if ($target eq 'web') { $message .= &Apache::loncommon::start_data_table_row(). ''.$file.''."\n". ''.$serverversions->{$file}.''."\n". ''.$revnum.''."\n". &Apache::loncommon::end_data_table_row()."\n"; } else { $message .= $file.' '. &Apache::lonlocal::mt('(local rev: [_1])', $serverversions->{$file}); if ($revnum) { $message .= '; '. &Apache::lonlocal::mt('(expected rev: [_1])', $revnum); } $message .= "\n"; } } if ($target eq 'web') { $message .= &Apache::loncommon::end_data_table().'
'; } else { $message .= "\n"; } } if (@modified > 0) { my $text = &Apache::lonlocal::mt('The following [quant,_1,file appears,files appear]'. ' to have been modified locally:',scalar(@modified)); if ($target eq 'web') { $message .= '

'.$text.'

'. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row()."\n". ''.&Apache::lonlocal::mt('File').''."\n". ''.&Apache::lonlocal::mt('Version').''."\n". &Apache::loncommon::end_data_table_header_row()."\n"; } else { $message .= "$text\n"; } foreach my $file (sort(@modified)) { if ($target eq 'web') { $message .= &Apache::loncommon::start_data_table_row()."\n". ''.$file.''."\n". ''.$serverversions->{$file}.''."\n". &Apache::loncommon::end_data_table_row()."\n"; } else { $message .= $file.' '. &Apache::lonlocal::mt('(local rev: [_1])', $serverversions->{$file}). "\n"; } } if ($target eq 'web') { $message .= &Apache::loncommon::end_data_table().'
'; } else { $message .= "\n"; } } } if (keys(%missing) > 0) { my $text = &Apache::lonlocal::mt('The following [quant,_1,local file appears,local files appear]'. ' to be missing:',scalar(keys(%missing))); if ($target eq 'web') { $message .= '

'.$text.'

'. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row()."\n". ''.&Apache::lonlocal::mt('File').''."\n". ''.&Apache::lonlocal::mt('Expected version').''."\n". &Apache::loncommon::end_data_table_header_row()."\n"; } else { $message .= "$text\n"; } foreach my $file (sort(keys(%missing))) { my $revnum = $stdversions{$file}; if ($target eq 'web') { $message .= &Apache::loncommon::start_data_table_row()."\n". ''.$file.''."\n". ''.$revnum.''."\n". &Apache::loncommon::end_data_table_row()."\n"; } else { $message .= $file; if ($revnum) { $message .= ' '. &Apache::lonlocal::mt('(expected rev: [_1])', $revnum); } $message .= "\n"; } } if ($target eq 'web') { $message .= &Apache::loncommon::end_data_table(); } else { $message .= "\n"; } } if (keys(%extra) > 0) { my $text = &Apache::lonlocal::mt('The following [quant,_1,file is,files are]'. ' not required by the release you have installed:', scalar(keys(%extra))); if ($target eq 'web') { $message .= '

'.$text.'

'. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row()."\n". ''.&Apache::lonlocal::mt('File').''."\n". ''.&Apache::lonlocal::mt('Version').''."\n". &Apache::loncommon::end_data_table_header_row()."\n"; } else { $message .= "$text\n"; } foreach my $file (sort(keys(%extra))) { if ($target eq 'web') { $message .= &Apache::loncommon::start_data_table_row()."\n". ''.$file.''."\n". ''.$serverversions->{$file}.''."\n". &Apache::loncommon::end_data_table_row()."\n"; } else { $message .= $file.' '. &Apache::lonlocal::mt('(local rev: [_1])', $serverversions->{$file}). "\n"; } } if ($target eq 'web') { $message .= &Apache::loncommon::end_data_table().'
'; } else { $message .= "\n"; } } } else { $message = &Apache::lonlocal::mt('No differences detected between installed files and files expected for LON-CAPA [_1]',$version); } } else { $message = &Apache::lonlocal::mt('No comparison attempted - failed to retrieve checksums for installed files.'); } } else { $message = &Apache::lonlocal::mt('No comparison attempted - failed to retrieve checksums for installed files.'); } unless ($message) { $message = &Apache::lonlocal::mt('LON-CAPA module check found no file changes.')."\n"; } return ($message,$numchg); } 1;