File:  [LON-CAPA] / loncom / debugging_tools / modify_config_files.pl
Revision 1.29: download - view: text, annotated - select for diffs
Wed Jul 31 03:39:20 2024 UTC (4 months, 3 weeks ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, version_2_11_X, version_2_11_5_msu, version_2_11_5, HEAD
- distro names for CentOS Stream are centos8-stream and centos9-stream.

#!/usr/bin/perl -w
#
# The LearningOnline Network
#
# $Id: modify_config_files.pl,v 1.29 2024/07/31 03:39:20 raeburn Exp $
#
# 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/
#
###

=pod

=head1 NAME

B<modify_config_files.pl>

=head1 SYNOPSIS

This script modifies local MySQL configuration file(s), which will be:
/home/www/.my.cnf and, depending on distro/version also one of:
/etc/my.cnf, /etc/mysql/my.cnf, /etc/mysql/mysql.conf.d/mysqld.cnf,
or /etc/mysql/mariadb.conf.d/50-server.cnf, and also the file used to 
store information about the LON-CAPA package repositories located at
install.loncapa.org. which provide rpm or deb packages created for the  
Linux distro/version being used.  The file to modify will be one of:
/etc/yum.conf (for CentOS/Scientific Linux/RHEL >=5 and <8), 
/etc/apt/sources.list (for Debian < 10 and Ubuntu < 22), or
/etc/apt/sources.list.d/loncapa.list (for Debian >= 10 and Ubuntu > 20),
/etc/sysconfig/rhn/sources (for RHEL4), or /etc/yum.repos.d/loncapa.repo
(for Fedora >= 21; Oracle Linux; AlmaLinux; RockyLinux; CentOS/RHEL >= 8).

The script also modifies /home/www/.inputrc on Linux distros which have 
readline 8.1 or newer, i.e., CentOS/Scientific Linux/RHEL >= 9,
Ubuntu >= 22, Debian >= 12, and Fedora >= 34.

=head1 DESCRIPTION

This script will modify /etc/my.cnf, /etc/mysql/my.cnf, 
/etc/mysql/mysql.conf.d/mysqld.cnf, or
/etc/mysql/mariadb.conf.d/50-server.cnf,
and /etc/yum.conf, /etc/yum.repos.d/loncapa.repo,
/etc/apt/sources.list, /etc/apt/sources.list.d/loncapa.list or 
/etc/sysconfig/rhn/sources and /home/www/.inputrc to ensure 
certain parameters are set properly.

The LON-CAPA yum repositories are added to /etc/yum.conf, 
/etc/yum.repos.d/loncapa.repo, or /etc/sysconfig/rhn/sources
and the LON-CAPA apt repositories are added to 
/etc/apt/sources.list or /etc/apt/sources.list.d/loncapa.list.

The /etc/my.cnf, /etc/mysql/my.cnf /etc/mysql/mysql.conf.d/mysqld.cnf
or /etc/mysql/mariadb.conf.d/50-server.cnf file is modified to set the 
wait_timeout to 1 year.  Backup copies of each file are made in
/etc, /etc/apt, and /etc/sysconfig/rhn, as appropriate.

=cut

use strict;
use File::Copy;
use lib '/home/httpd/lib/perl/';
use LONCAPA::Configuration;
my $loncapa_config=LONCAPA::Configuration::read_conf('loncapa.conf');

open(DSH,"$$loncapa_config{'lonDaemons'}/distprobe |");
my $dist = <DSH>;
chomp($dist);
close(DSH);

my $yum_status;
my $loninst = 'http://install.loncapa.org';
my $loninst_re = 'http://install\.loncapa\.org';
if ($dist =~ /^fedora(\d+)$/) {
    my $file = '/etc/yum.conf';
    my $ver = $1;
    my $gpgchk = '0';
    my $gpg = "$loninst/versions/fedora/RPM-GPG-KEY-loncapa"; 
    my $nobackup;
    if ($ver > 6) {
        $gpgchk = '1';
    }
    if ($ver >= 21) {
        $file = '/etc/yum.repos.d/loncapa.repo';
        $nobackup = 1;
    }
    $yum_status =  
        &update_file($file,
             [{section => 'loncapa-updates-basearch',
               key     => 'name=',
               value   => 'Fedora Core $releasever LON-CAPA $basearch Updates',
           }, {section => 'loncapa-updates-basearch',
               key     => 'baseurl=',
               value   => $loninst.'/fedora/linux/loncapa/$releasever/$basearch',
           }, {section => 'loncapa-updates-basearch',
               key     => 'gpgcheck=',
               value   =>  $gpgchk,
           }, {section => 'loncapa-updates-basearch',
               key     => 'gpgkey=',
               value   => $gpg,
           }, {section => 'loncapa-updates-noarch',
               key     => 'name=',
               value   => 'Fedora Core $releasever LON-CAPA noarch Updates',
           }, {section => 'loncapa-updates-noarch',
               key     => 'baseurl=',
               value   => $loninst.'/fedora/linux/loncapa/$releasever/noarch',
           }, {section => 'loncapa-updates-noarch',
               key     => 'gpgcheck=',
               value   => $gpgchk,
           }, {section => 'loncapa-updates-noarch',
               key     => 'gpgkey=',
               value   => $gpg,
           }],$nobackup);
} elsif ($dist =~ /^(rhes|centos|scientific|oracle|rocky|alma)(\d+)(|\-stream)$/) {
    my $type = $1;
    my $ver = $2;
    my $stream = $3;
    my $longver = $ver;
    my $nobackup;
    if ($type eq 'rhes') {
        if ($ver == 4) {
            $longver = '4ES';
        } elsif ($ver == 5) {
            $longver = '5Server';
        }
    } elsif ($type eq 'centos') {
        $type .= $stream;
    }
    my %info = (
                 rhes => {
                           title => 'RHEL',
                           path => 'redhat/linux/enterprise/loncapa',
                           gpg => 'versions/redhat/RPM-GPG-KEY-loncapa',
                           gpgchk => 1,
                         },
                 centos => {
                             title => 'CentOS',
                             path => 'centos/loncapa',
                             gpg => 'versions/centos/RPM-GPG-KEY-loncapa',
                             gpgchk => 1,
                           },
                 scientific => {
                                 title => 'Scientific Linux',
                                 path => 'scientific/loncapa',
                                 gpg => 'versions/scientific/RPM-GPG-KEY-loncapa',
                                 gpgchk => 1,
                               },
                 oracle => {
                             title => 'Oracle Linux',
                             path => 'oracle/loncapa',
                             gpg => 'versions/oracle/RPM-GPG-KEY-loncapa',
                             gpgchk => 1,
                           },
                 rocky => {
                             title => 'Rocky Linux',
                             path => 'rocky/loncapa',
                             gpg => 'versions/rocky/RPM-GPG-KEY-loncapa',
                             gpgchk => 1,
                          },
                 alma => {
                             title => 'AlmaLinux',
                             path => 'alma/loncapa',
                             gpg => 'versions/alma/RPM-GPG-KEY-loncapa',
                             gpgchk => 1,
                          },
                 'centos-stream' => {
                                      title => 'CentOS Stream',
                                      path  => 'centos/loncapa',
                                      gpg => 'versions/centos/RPM-GPG-KEY-loncapa',
                                      gpgchk => 1, 
                                    },
               );
    if (ref($info{$type}) eq 'HASH') {
        if ($ver > 4) {
            my $file = '/etc/yum.conf';
            if (($ver > 7) || ($type eq 'oracle') || ($type eq 'rocky') || 
                ($type eq 'alma') || ($type eq 'centos-stream')) {
                $file = '/etc/yum.repos.d/loncapa.repo';
                $nobackup = 1;
            }
            my $release = '$releasever';
            if ($type eq 'centos-stream') {
                $release .= '-stream';
            }
            $yum_status =
                &update_file($file,
                     [{section => 'loncapa-updates-basearch',
                       key     => 'name=',
                       value   => $info{$type}{title}.' $releasever LON-CAPA $basearch Updates',
                      }, {section => "loncapa-updates-basearch",
                          key     => 'baseurl=',
                          value   => "$loninst/$info{$type}{path}/$release/".'$basearch',
                      }, {section => 'loncapa-updates-basearch',
                          key     => 'gpgcheck=',
                          value   => $info{$type}{gpgchk},
                      }, {section => 'loncapa-updates-basearch',
                          key     => 'gpgkey=',
                          value   => "$loninst/$info{$type}{gpg}",
                      }, {section => 'loncapa-updates-noarch',
                          key     => 'name=',
                          value   => $info{$type}{title}.' $releasever LON-CAPA noarch Updates',
                      }, {section => 'loncapa-updates-noarch',
                          key     => 'baseurl=',
                          value   => "$loninst/$info{$type}{path}/$release/noarch",
                      }, {section => 'loncapa-updates-noarch',
                          key     => 'gpgcheck=',
                          value   => $info{$type}{gpgchk},
                      }, {section => 'loncapa-updates-noarch',
                          key     => 'gpgkey=',
                          value   => "$loninst/$info{$type}{gpg}",
                      }],$nobackup);
        } elsif (($type eq 'rhes') && ($ver == 4)) {
            my %rhn = (
                        basearch => { 
                            regexp => '\s*yum\s+loncapa\-updates\-basearch\s+'.$loninst_re.'/'.$info{$type}{path}.'/'.$longver.'/\$ARCH',
                            text => "yum loncapa-updates-basearch $loninst/$info{$type}{path}/$longver/".'$ARCH',
                                    },
                        noarch =>  {
                            regexp => '\s*yum\s+loncapa\-updates\-noarch\s+'.$loninst_re.'/'.$info{$type}{path}.'/'.$longver.'/noarch',
                            text => "yum loncapa-updates-noarch $loninst/$info{$type}{path}/$longver/noarch",
                                   },
                      );
            $yum_status = &update_rhn_source(\%rhn); 
        }
    }
} elsif ($dist =~ /^(debian|ubuntu)(\d+)$/) {
    my ($distname,$distver) = ($1,$2);
    if ((($distname eq 'ubuntu') && ($distver > 20)) ||
        (($distname eq 'debian') && ($distver >= 10))) {
        $loninst = 'https://install.loncapa.org';
        $loninst_re = 'https://install\.loncapa\.org';
    }
    my %apt_get_source = (
                           debian5 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/debian/?\s+lenny\s+main',
                                        text   => "deb $loninst/debian lenny main",
                                      },
                           debian6 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/debian/?\s+squeeze\s+main',
                                        text   => "deb $loninst/debian squeeze main",
                                      },
                           debian10 => {
                                        regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/debian/?\s+buster\s+main',
                                        text   => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/debian buster main",
                                      },
                           debian11 => {
                                        regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/debian/?\s+bullseye\s+main',
                                        text   => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/debian bullseye main",
                                      },
                           debian12 => {
                                        regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/debian/?\s+bookworm\s+main',
                                        text   => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/debian bookworm main",
                                      },

                           ubuntu6 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+dapper\s+main',
                                        text   => "deb $loninst/ubuntu dapper main",
                                      },
                           ubuntu8 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+hardy\s+main',
                                        text   => "deb $loninst/ubuntu hardy main",
                                      },
                           ubuntu10 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+lucid\s+main',
                                        text   => "deb $loninst/ubuntu lucid main",
                                       },
                           ubuntu12 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+precise\s+main',
                                        text   => "deb $loninst/ubuntu precise main",
                                       },
                           ubuntu14 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+trusty\s+main',
                                        text   => "deb $loninst/ubuntu trusty main",
                                       },
                           ubuntu16 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+xenial\s+main',
                                        text   => "deb $loninst/ubuntu xenial main",
                                       },
                           ubuntu18 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+bionic\s+main',
                                        text   => "deb $loninst/ubuntu bionic main",
                                       },
                           ubuntu20 => {
                                        regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+focal\s+main',
                                        text   => "deb $loninst/ubuntu focal main",
                                       },
                           ubuntu22 => {
                                        regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/ubuntu/?\s+jammy\s+main',
                                        text   => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/ubuntu jammy main",
                                       },
                           ubuntu24 => {
                                        regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/ubuntu/?\s+noble\s+main',
                                        text   => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/ubuntu noble main",
                                       },
                         );
    my $apt_status;
    if (defined($apt_get_source{$dist})) {
        $apt_status = &update_apt_source($distname,$distver,$apt_get_source{$dist});
    }
}

my $mysqlfile = '/etc/my.cnf';
my $mysqlconf = [{section =>'mysqld',
                  key     =>'wait_timeout=',
                  value   =>'31536000'}];
my $nomysqlbackup;
if ($dist =~ /^ubuntu(\d+)$/) {
    my $version = $1;
    $mysqlfile = '/etc/mysql/my.cnf';
    if ($version > 14) {
        $nomysqlbackup = 1;
        $mysqlfile = '/etc/mysql/mysql.conf.d/mysqld.cnf';
        if ($version < 20) {
            push(@{$mysqlconf},
                 {section =>'mysqld',
                  key     =>'sql_mode=',
                  value   =>'"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"'});
        } else {
            push(@{$mysqlconf},
                 {section =>'mysqld',
                  key     =>'sql_mode=',
                  value   =>'"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"'});
        }
    }
} elsif ($dist =~ /^debian(\d+)$/) {
    my $version = $1;
    if ($version >= 10) {
        $mysqlfile = '/etc/mysql/mariadb.conf.d/50-server.cnf';
        $nomysqlbackup = 1;
    }
}

my $mysql_global_status = &update_file($mysqlfile,$mysqlconf,$nomysqlbackup);

my $local_my_cnf = '/home/www/.my.cnf';
if (! -e $local_my_cnf) {
    # Create a file so we can do something with it...
    system("touch $local_my_cnf");
}
my $mysql_www_status =
    &update_file($local_my_cnf,
             [{section =>'client',
               key     =>'user=',
               value   =>'www',},
              {section =>'client',
               key     =>'password=',
               value   =>$loncapa_config->{'lonSqlAccess'}},]);

my $needs_inputrc_check;
if ($dist  =~ /^debian(\d+)$/) {
    if ($1 >= 12) {
        $needs_inputrc_check = 1;
    }
} elsif ($dist =~ /^ubuntu(\d+)$/) {
    if ($1 >= 22) {
        $needs_inputrc_check = 1;
    }
} elsif ($dist =~ /^(?:rhes|oracle|alma|rocky|centos)(\d+)(?:|\-stream)$/) {
    if ($1 >= 9) {
        $needs_inputrc_check = 1;
    }
} elsif ($dist =~ /^fedora(\d+)$/) {
    if ($1 >= 34) {
        $needs_inputrc_check = 1;
    }
}

if ($needs_inputrc_check) {
    my $bash_www_cnf = '/home/www/.inputrc';
    if (!-e $bash_www_cnf) {
        system("touch $bash_www_cnf");
        if (open(my $fh,'>',$bash_www_cnf)) {
            print $fh <<'END';
$if R
    set enable-bracketed-paste off
$endif

$if maxima
    set enable-bracketed-paste off
$endif
END
            close($fh);
        } else {
            warn "**** Error: could not open $bash_www_cnf to add 'set enable-bracketed-paste to off'";
        }
        my $wwwuid = getpwnam('www');
        my $wwwgid = getgrnam('www');
        if ($wwwuid!=$<) {
            chown($wwwuid,$wwwgid,$bash_www_cnf);
        }
    } else {
        my (%bracketed_paste_on,%bracketed_paste_off,@preserve,$condition);
        $condition = '';
        if (open(my $fh,'<',$bash_www_cnf)) {
            while (my $line=<$fh>) {
                chomp($line);
                if ($line =~ /^\$if\s+(\w+)\s*$/) {
                    if ($1 eq 'R') { 
                        $condition = 'r';
                    } elsif ($1 eq 'maxima') {
                        $condition = 'maxima';
                    } else {
                        $condition = 'other';
                    }
                } elsif ($line =~ /^\$endif\s*$/) {
                    $condition = '';
                }
                if ($line =~ /^\s*set\s+enable\-bracketed\-paste\s+(off|on)\s*$/) {
                    if ($1 eq 'off') {
                        if ($condition ne '') {
                            $bracketed_paste_off{$condition} = 1;
                        } else {
                            $bracketed_paste_off{all} = 1;
                        }
                        push(@preserve,$line);
                    } else {
                        if ($condition ne '') {
                            $bracketed_paste_on{$condition} = 1;
                            if (($condition eq 'r') || ($condition eq 'maxima')) {
                                push(@preserve,'    set enable-bracketed-paste off');
                            } else {
                                push(@preserve,$line); 
                            }
                        } else {
                            $bracketed_paste_on{all} = 1;
                            push(@preserve,$line);
                        }
                    }
                } else {
                    push(@preserve,$line);
                }
            }
            close($fh);
        }
        if (($bracketed_paste_on{r} || $bracketed_paste_on{maxima}) ||
            (!exists($bracketed_paste_off{r}) && !exists($bracketed_paste_on{r}) &&
             !exists($bracketed_paste_off{maxima}) && !exists($bracketed_paste_on{maxima}))) {
            if (open(my $fh,'>',$bash_www_cnf)) {
                if (@preserve) {
                    foreach my $entry (@preserve) {
                        print $fh "$entry\n";
                    }
                    if (!exists($bracketed_paste_off{r}) && !exists($bracketed_paste_on{r})) {
print $fh <<'END';
$if R
    set enable-bracketed-paste off
$endif
END
                    }
                    if (!exists($bracketed_paste_off{r}) && !exists($bracketed_paste_on{r})) {
print $fh <<'END';
$if maxima
    set enable-bracketed-paste off
$endif
END
                    }
                } else {
print $fh <<'END';
$if R
    set enable-bracketed-paste off
$endif

$if maxima
    set enable-bracketed-paste off
$endif
END
                }
                close($fh);
            } else {
                warn "**** Error: could not open $bash_www_cnf to add 'set enable-bracketed-paste to off'";
            }
        }
    }
}

my $exitvalue = 0;

if ($mysql_global_status) { $exitvalue = 1; }

exit $exitvalue;

#################################################################
#################################################################

=pod

=over 4

=item &update_file()

Calls &parse_config_file for a file and then sends the 
retrieved data to &modify_config_file, and checks if
modificatione were made.

Inputs: filename, newdata (reference to an array of hashed),
        nobackup 1, if no backup of existing file (.backup
        appended to filename) should be made.

Output: 0 or 1; o if no modifications exist (and hence no file
needed to be saved, or 1 if modifications made, and call to
&write_config_file to save file, or if filename does not exist.

=back

=cut

#################################################################
#################################################################


sub update_file {
    my ($file,$newdata,$nobackup) = @_;
    return 1 if (! -e $file);
    unless ($nobackup) {
        my $backup = $file.'.backup';
        if (! copy($file,$backup)) {
            warn "**** Error: Unable to make backup of $file";
            return 0;
        }
    }
    my ($filedata) = &parse_config_file($file);
    if (! ref($filedata)) { warn "**** Error: $filedata"; return 0;}
    my $modified = 0;
    foreach my $data (@$newdata) {
        my $section = $data->{'section'};
        my $key = $data->{'key'};
        my $value = $data->{'value'};
        my $result = &modify_config_file($filedata,$section,$key,$value);
        if ($result) { $modified = 1; }
    }
    if ($modified) {
        my $result = &write_config_file($file,$filedata);
        if (defined($result)) { warn 'Error:'.$result; return 0; }
    }
    return $modified;
}

#################################################################
#################################################################

=pod

=over 4

=item &parse_config_file()

Read a configuration file in and parse it into an internal data structure.

Input: filename

Output: array ref $filedata  OR  scalar error message

=back 

=cut

#################################################################
#################################################################
sub parse_config_file {
    my ($file) = @_;
    open(INFILE,$file) || return ('Unable to open '.$file.' for reading');
    my @Input = <INFILE>;
    close(INFILE);
    my @Structure;
    my %Sections;
    while (my $line = shift(@Input)) {
        chomp($line);
        if ($line =~ /^\[([^\]]*)\]/) {
            my $section_id = $1;
            push(@Structure,'__section__'.$section_id);
            while ($line = shift(@Input)) {
                chomp($line);
                if ($line =~ /^\[([^\]]*)\]/) {
                    unshift(@Input,$line);
                    last;
                } else {
                    push(@{$Sections{$section_id}},$line);
                }
            }
        } else {
            push(@Structure,$line);
        }
    }
    my $filedata = [\@Structure,\%Sections];
    return $filedata;
}

#################################################################
#################################################################

=pod

=over 4

=item &write_config_file()

Write a configuration file out based on the internal data structure returned
by &parse_config_file

Inputs: filename, $filedata (the return value of &parse_config_file

Returns: undef on success, scalar error message on failure.

=back

=cut

#################################################################
#################################################################
sub write_config_file {
    my ($file,$filedata) = @_;
    my ($structure,$sections) = @$filedata;
    if (! defined($structure) || ! ref($structure)) {
        return 'Bad subroutine inputs';
    }
    open(OUTPUT,'>',$file) || return('Unable to open '.$file.' for writing');
    for (my $i=0;$i<scalar(@$structure);$i++) {
        my $line = $structure->[$i];
        chomp($line);
        if ($line =~ /^__section__(.*)$/) {
            my $section_id = $1;
            print OUTPUT ('['.$section_id.']'.$/);
            foreach my $section_line (@{$sections->{$section_id}}) {
                chomp($section_line);
                print OUTPUT $section_line.$/;
            }
            # Deal with blank lines
            if ($sections->{$section_id}->[-1] =~ /^\s*$/) {
                # No need to output a blank line at the end if there is one 
                # already
            } else {
                print OUTPUT $/;
            }
        } else {
            print OUTPUT $line.$/;
        }
    }
    close OUTPUT;
    return undef;
}

#################################################################
#################################################################

=pod

=over 4

=item &modify_config_file()

Modifies the internal data structure of a configuration file to include new
sections and/or new configuration directives.

Inputs: $filedata (see &parse_config_file
$section, the [section] the new entry is to reside in.  A value of undef will
cause the "outer" section (as in yum.conf) to be updated (or have the new
value prepended).
$newkey: A line which matches this will be replaced with $newkey.$newvalue
$newvalue: The new value to be placed with the new key.

Returns: 0 or 1, indicating if the file was modified(1) or not(0).

=back 

=cut

#################################################################
#################################################################
sub modify_config_file {
    my ($filedata,$section,$newkey,$newvalue)=@_;
    my $modified = 0;    # returned value - set to true if the file is modified
    my ($structure,$sections) = @$filedata;
    if (! defined($newvalue)) {
        $newvalue = '';
    }
    my $newline = $newkey.$newvalue;
    #
    # Determine which array ref gets the item
    my $target;
    if (defined($section)) {
        if (! exists($sections->{$section})) {
            push(@$structure,'__section__'.$section);
            $sections->{$section}=[];
        }
        $target = $sections->{$section};
    } else {
        $target = $structure;
    }
    #
    # Put the item in or update it.
    my $key_is_new = 1;
    for (my $i=0;$i<scalar(@$target);$i++) {
        if ($target->[$i] =~/^$newkey/) {
            if ($target->[$i] ne $newline) {
                $target->[$i]=$newline;
                $modified = 1;
            }
            $key_is_new = 0;
            last;
        }
    }
    if ($key_is_new) {
        if (! defined($section)) {
            unshift(@$target,$newline);
        } else {
            # No need to put things after a blank line.
            if (defined($target->[-1]) && $target->[-1] =~ /^\s*$/) {
                $target->[-1] = $newline;
                $modified = 1;
            } else {
                push(@$target,$newline);
                $modified = 1;
            }
        }
    }
    return $modified;
}

#################################################################
#################################################################

=pod

=over 4

=item &update_rhn_source()

Modifies the Red Hat 4 sources file which includes repositories used by up2date 

Inputs: 
$rhn_items - a reference to hash of a hash containing the regular expression
to test for, and the text string to append to the file, if an entry for the 
LON-CAPA RHEL repository is missing for two cases:

(a) basearch
(b) noarch 

Returns: 0 or 1, indicating if the file was modified(1) or not(0).

=back

=cut

#################################################################
#################################################################
sub update_rhn_source {
    my ($rhn_items) = @_;
    return 0 if (ref($rhn_items) ne 'HASH');
    return 0 if ((ref($rhn_items->{basearch}) ne 'HASH') || (ref($rhn_items->{noarch}) ne 'HASH'));
    my $file = '/etc/sysconfig/rhn/sources';
    return 0 if (! -e $file);
    my $backup = $file.'.backup';
    if (! copy($file,$backup)) {
        warn "**** Error: Unable to make backup of $file";
        return 0;
    }
    my $result = 0;
    my $fh;
    if (open($fh,'<',$file)) {
        my $total = 0;
        my %found;
        foreach my $item (keys(%{$rhn_items})) {
            $found{$item} = 0;
        }
        while(<$fh>) {
            foreach my $item (keys(%{$rhn_items})) {
                if (ref($rhn_items->{$item}) eq 'HASH') {
                    my $pattern = $rhn_items->{$item}->{regexp};
                    if ($pattern ne '') { 
                        if (m{^$pattern}) {
                            $found{$item} = 1;
                            $total ++;
                        }
                    }
                }
            }
            last if $total == 2;
        }
        close($fh);
        if ($total < 2) {
            if (open($fh,'>>',$file)) {
                foreach my $item (keys(%{$rhn_items})) {
                    unless ($found{$item}) {
                        if (ref($rhn_items->{$item}) eq 'HASH') {
                            if ($rhn_items->{$item}->{text} ne '') {
                                print $fh "\n".$rhn_items->{$item}->{text}."\n";
                                $result = 1;
                            }
                        }
                    }
                }
                close($fh);
            }
        }
    }
    return $result;
}

#################################################################
#################################################################

=pod

=over 4

=item &update_apt_source()

Modifies either the sources.list or sources.list.d/loncapa.list 
file which include repositories used by apt-get.

Inputs:
$distname - distro (without version): debian or ubuntu
$distver - distro version, e.g., 12 or 24
$deb_row - a reference to a hash containing the regular expression
to test for, and the text string to append to the file, if an entry 
for the LON-CAPA Debian/ or Ubuntu repository is missing.

Returns: 0 or 1, indicating if the file was modified(1) or not(0).

=back

=cut

#################################################################
#################################################################
sub update_apt_source {
    my ($distname,$distver,$deb_row) = @_;
    return 0 if (ref($deb_row) ne 'HASH');
    return 0 if (($deb_row->{regexp} eq '') || ($deb_row->{text} eq ''));
    my $file = '/etc/apt/sources.list';
    my $nobackup;
    if ((($distname eq 'ubuntu') && ($distver > 20)) ||
        (($distname eq 'debian') && ($distver >= 10))) {
        $file = '/etc/apt/sources.list.d/loncapa.list';
        $nobackup = 1;
    }
    return 0 if (! -e $file);
    unless ($nobackup) {
        my $backup = $file.'.backup';
        if (! copy($file,$backup)) {
            warn "**** Error: Unable to make backup of $file";
            return 0;
        }
    }
    my $result = 0;
    my $fh;
    if (open($fh,'<',$file)) {
        my $found = 0;
        my $pattern = $deb_row->{regexp};
        while(<$fh>) {
            if (m{^$pattern}) {
                $found = 1;
                last;
            }
        }
        close($fh);
        if (!$found) {
            if (open($fh,'>>',$file)) {
                print $fh "\n".$deb_row->{text}."\n";
                close($fh);
                $result = 1;
            }
        }
    }
    return $result;
}


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