File:  [LON-CAPA] / loncom / configuration / Checksumming.pm
Revision 1.11: download - view: text, annotated - select for diffs
Mon May 15 02:34:51 2023 UTC (19 months, 1 week ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, version_2_11_5_msu, version_2_11_4_msu, HEAD
- Typo

# The LearningOnline Network with CAPA
# Checksum installed LON-CAPA modules and some configuration files
#
# $Id: Checksumming.pm,v 1.11 2023/05/15 02:34:51 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/math_parser/*.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 ($lontabdir) {
        push(@paths,$lontabdir.'/mydesk.tab');
    }
    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);
        }
        if ($londaemons) {
            my @binaries = qw (apachereload lchttpdlogs lcinstallfile lciptables lcnfsoff lcnfson lcpasswd lcuserdel pwchange);
            foreach my $file (@binaries) {
                next if ($versions{"$londaemons/$file"});
                if (-B "$londaemons/$file") {
                    if (-T $londaemons.'/.'.$file) {
                        if (open(my $fh,'<',$londaemons.'/.'.$file)) {
                            while (my $line=<$fh>) {
                                if ($line =~ m{^#\s\$Id:\s[\w.]+,v\s([\d.]+)\s}) {
                                    $versions{"$londaemons/$file"} = $1;
                                    last;
                                }
                            }
                            close($fh);
                        }
                        if ($versions{"$londaemons/$file"}) {
                            if (open(my $fh,'<',$londaemons.'/.'.$file)) {
                                binmode $fh;
                                my $sha_obj = Digest::SHA->new();
                                $sha_obj->addfile($fh);
                                $chksums{"$londaemons/$file"} = $sha_obj->hexdigest;
                                close($fh);
                            }
                        }
                    }
                }
            }
        }
        foreach my $key (sort(keys(%versions))) {
            next if ($key =~ /\.lpmlsave$/);
            unless (exists($chksums{$key})) {
                if (open(my $fh,"<$key")) {
                    binmode $fh;
                    my $sha_obj = Digest::SHA->new();
                    $sha_obj->addfile($fh);
                    $chksums{$key} = $sha_obj->hexdigest;
                    close($fh);
                }
            }
            $output .= "$key,$versions{$key},$chksums{$key}\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 = '<br />';
    } 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 {
                        $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 = '<p>'.$message.'</p>';
                } 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 .= '<p>'.$text.'</p>'.
                                        &Apache::loncommon::start_data_table().
                                        &Apache::loncommon::start_data_table_header_row()."\n".
                                        '<th>'.&Apache::lonlocal::mt('File').'</th>'."\n".
                                        '<th>'.&Apache::lonlocal::mt('Server version').'</th>'."\n".
                                        '<th>'.&Apache::lonlocal::mt('Expected version').'</th>'."\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().
                                             '<td>'.$file.'</td>'."\n".
                                             '<td>'.$serverversions->{$file}.'</td>'."\n".
                                             '<td>'.$revnum.'</td>'."\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().'<br />';
                        } 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 .= '<p>'.$text.'</p>'.
                                        &Apache::loncommon::start_data_table().
                                        &Apache::loncommon::start_data_table_header_row()."\n".
                                        '<th>'.&Apache::lonlocal::mt('File').'</th>'."\n".
                                        '<th>'.&Apache::lonlocal::mt('Version').'</th>'."\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".
                                            '<td>'.$file.'</td>'."\n".
                                            '<td>'.$serverversions->{$file}.'</td>'."\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().'<br />';
                        } 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 .= '<p>'.$text.'</p>'.
                                    &Apache::loncommon::start_data_table().
                                    &Apache::loncommon::start_data_table_header_row()."\n".
                                    '<th>'.&Apache::lonlocal::mt('File').'</th>'."\n".
                                    '<th>'.&Apache::lonlocal::mt('Expected version').'</th>'."\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".
                                        '<td>'.$file.'</td>'."\n".
                                        '<td>'.$revnum.'</td>'."\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 .= '<p>'.$text.'</p>'.
                                    &Apache::loncommon::start_data_table().
                                    &Apache::loncommon::start_data_table_header_row()."\n".
                                    '<th>'.&Apache::lonlocal::mt('File').'</th>'."\n".
                                    '<th>'.&Apache::lonlocal::mt('Version').'</th>'."\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".
                                        '<td>'.$file.'</td>'."\n".
                                        '<td>'.$serverversions->{$file}.'</td>'."\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().'<br />';
                    } 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;


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>