Annotation of loncom/debugging_tools/modify_config_files.pl, revision 1.15
1.1 matthew 1: #!/usr/bin/perl -w
2: #
3: # The LearningOnline Network
4: #
1.15 ! raeburn 5: # $Id: modify_config_files.pl,v 1.14 2018/10/26 21:39:44 raeburn Exp $
1.1 matthew 6: #
7: # Copyright Michigan State University Board of Trustees
8: #
9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
10: #
11: # LON-CAPA is free software; you can redistribute it and/or modify
12: # it under the terms of the GNU General Public License as published by
13: # the Free Software Foundation; either version 2 of the License, or
14: # (at your option) any later version.
15: #
16: # LON-CAPA is distributed in the hope that it will be useful,
17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19: # GNU General Public License for more details.
20: #
21: # You should have received a copy of the GNU General Public License
22: # along with LON-CAPA; if not, write to the Free Software
23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24: #
25: # /home/httpd/html/adm/gpl.txt
26: #
27: # http://www.lon-capa.org/
28: #
29: ###
30:
31: =pod
32:
33: =head1 NAME
34:
35: B<modify_config_files.pl>
36:
37: =head1 SYNOPSIS
38:
1.9 raeburn 39: This script modifies /etc/my.cnf and one of: /etc/yum.conf
1.13 raeburn 40: (for CentOS/Scientific Linux/RHEL >=5), /etc/apt/sources.list
41: (for Debian/Ubuntu), /etc/sysconfig/rhn/sources (for RHEL4),
42: and /etc/yum.repos.d/loncapa.repo (Fedora >= 21).
1.1 matthew 43:
44: =head1 DESCRIPTION
45:
1.13 raeburn 46: This script modifies /etc/my.cnf, /etc/yum.conf, /etc/yum.repos.d/loncapa.repo,
47: /etc/apt/sources, or /etc/sysconfig/rhn/sources to ensure certain
48: parameters are set properly.
49:
50: The LON-CAPA yum repositories are added to /etc/yum.conf,
51: /etc/yum.repos.d/loncapa.repo, /etc/sysconfig/rhn/sources
52: and the LON-CAPA apt repositories are added to
1.9 raeburn 53: /etc/apt/sources.list.
54:
1.1 matthew 55: The /etc/my.cnf file is modified to set the wait_timeout to 1 year. Backup
1.9 raeburn 56: copies of each file are made in /etc, /etc/apt, and /etc/sysconfig/rhn, as
57: appropriate.
1.1 matthew 58:
59: =cut
60:
61: use strict;
62: use File::Copy;
1.3 matthew 63: use lib '/home/httpd/lib/perl/';
64: use LONCAPA::Configuration;
65: my $loncapa_config=LONCAPA::Configuration::read_conf('loncapa.conf');
1.1 matthew 66:
1.6 raeburn 67: open(DSH,"$$loncapa_config{'lonDaemons'}/distprobe |");
68: my $dist = <DSH>;
69: chomp($dist);
70: close(DSH);
71:
72: my $yum_status;
1.9 raeburn 73: my $loninst = 'http://install.loncapa.org';
74: my $loninst_re = 'http://install\.loncapa\.org';
75: if ($dist =~ /^fedora(\d+)$/) {
1.13 raeburn 76: my $file = '/etc/yum.conf';
1.9 raeburn 77: my $ver = $1;
1.10 raeburn 78: my $gpgchk = '0';
1.9 raeburn 79: my $gpg = "$loninst/versions/fedora/RPM-GPG-KEY-loncapa";
1.13 raeburn 80: my $nobackup;
1.9 raeburn 81: if ($ver > 6) {
1.10 raeburn 82: $gpgchk = '1';
1.9 raeburn 83: }
1.13 raeburn 84: if ($ver >= 21) {
85: $file = '/etc/yum.repos.d/loncapa.repo';
86: $nobackup = 1;
87: }
1.6 raeburn 88: $yum_status =
1.13 raeburn 89: &update_file($file,
1.10 raeburn 90: [{section => 'loncapa-updates-basearch',
1.1 matthew 91: key => 'name=',
1.6 raeburn 92: value => 'Fedora Core $releasever LON-CAPA $basearch Updates',
1.10 raeburn 93: }, {section => 'loncapa-updates-basearch',
1.1 matthew 94: key => 'baseurl=',
1.9 raeburn 95: value => $loninst.'/fedora/linux/loncapa/$releasever/$basearch',
1.10 raeburn 96: }, {section => 'loncapa-updates-basearch',
1.5 matthew 97: key => 'gpgcheck=',
1.9 raeburn 98: value => $gpgchk,
1.10 raeburn 99: }, {section => 'loncapa-updates-basearch',
1.13 raeburn 100: key => 'gpgkey=',
1.9 raeburn 101: value => $gpg,
1.1 matthew 102: }, {section => 'loncapa-updates-noarch',
103: key => 'name=',
104: value => 'Fedora Core $releasever LON-CAPA noarch Updates',
105: }, {section => 'loncapa-updates-noarch',
106: key => 'baseurl=',
1.9 raeburn 107: value => $loninst.'/fedora/linux/loncapa/$releasever/noarch',
1.5 matthew 108: }, {section => 'loncapa-updates-noarch',
109: key => 'gpgcheck=',
1.9 raeburn 110: value => $gpgchk,
111: }, {section => 'loncapa-updates-noarch',
1.13 raeburn 112: key => 'gpgkey=',
1.9 raeburn 113: value => $gpg,
1.13 raeburn 114: }],$nobackup);
1.15 ! raeburn 115: } elsif ($dist =~ /^(rhes|centos|scientific|oracle)(\d+)$/) {
1.9 raeburn 116: my $type = $1;
117: my $ver = $2;
118: my $longver = $ver;
119: if ($type eq 'rhes') {
120: if ($ver == 4) {
121: $longver = '4ES';
122: } elsif ($ver == 5) {
123: $longver = '5Server';
124: }
125: }
126: my %info = (
127: rhes => {
128: title => 'RHEL',
129: path => 'redhat/linux/enterprise/loncapa',
130: gpg => 'versions/redhat/RPM-GPG-KEY-loncapa',
131: gpgchk => 1,
132: },
133: centos => {
134: title => 'CentOS',
135: path => 'centos/loncapa',
136: gpg => 'versions/centos/RPM-GPG-KEY-loncapa',
137: gpgchk => 1,
138: },
139: scientific => {
140: title => 'Scientific Linux',
141: path => 'scientific/loncapa',
142: gpg => 'versions/scientific/RPM-GPG-KEY-loncapa',
143: gpgchk => 1,
144: },
1.15 ! raeburn 145: oracle => {
! 146: title => 'Oracle Linux',
! 147: path => 'oracle/loncapa',
! 148: gpg => 'versions/oracle/RPM-GPG-KEY-loncapa',
! 149: gpgchk => 1,
! 150: },
1.9 raeburn 151: );
152: if (ref($info{$type}) eq 'HASH') {
153: if ($ver > 4) {
154: $yum_status =
155: &update_file('/etc/yum.conf',
156: [{section => 'loncapa-updates-basearch',
157: key => 'name=',
158: value => $info{$type}{title}.' $releasever LON-CAPA $basearch Updates',
159: }, {section => "loncapa-updates-basearch",
160: key => 'baseurl=',
161: value => "$loninst/$info{$type}{path}/".'$releasever/$basearch',
162: }, {section => 'loncapa-updates-basearch',
163: key => 'gpgcheck=',
1.11 raeburn 164: value => $info{$type}{gpgchk},
1.10 raeburn 165: }, {section => 'loncapa-updates-basearch',
1.9 raeburn 166: key => 'gpgkey=',
167: value => "$loninst/$info{$type}{gpg}",
168: }, {section => 'loncapa-updates-noarch',
169: key => 'name=',
170: value => $info{$type}{title}.' $releasever LON-CAPA noarch Updates',
171: }, {section => 'loncapa-updates-noarch',
172: key => 'baseurl=',
173: value => "$loninst/$info{$type}{path}/".'$releasever/noarch',
174: }, {section => 'loncapa-updates-noarch',
175: key => 'gpgcheck=',
1.11 raeburn 176: value => $info{$type}{gpgchk},
1.9 raeburn 177: }, {section => 'loncapa-updates-noarch',
178: key => 'gpgkey=',
179: value => "$loninst/$info{$type}{gpg}",
180: }]);
181: } elsif (($type eq 'rhes') && ($ver == 4)) {
182: my %rhn = (
183: basearch => {
184: regexp => '\s*yum\s+loncapa\-updates\-basearch\s+'.$loninst_re.'/'.$info{$type}{path}.'/'.$longver.'/\$ARCH',
185: text => "yum loncapa-updates-basearch $loninst/$info{$type}{path}/$longver/".'$ARCH',
186: },
187: noarch => {
188: regexp => '\s*yum\s+loncapa\-updates\-noarch\s+'.$loninst_re.'/'.$info{$type}{path}.'/'.$longver.'/noarch',
189: text => "yum loncapa-updates-noarch $loninst/$info{$type}{path}/$longver/noarch",
190: },
191: );
192: $yum_status = &update_rhn_source(\%rhn);
193: }
194: }
195: } elsif ($dist =~ /^(debian|ubuntu)\d+$/) {
196: my %apt_get_source = (
197: debian5 => {
198: regexp => '\s*deb\s+'.$loninst_re.'/debian/\s+lenny\s+main',
199: text => "deb $loninst/debian/ lenny main",
200: },
201: ubuntu6 => {
202: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+dapper\s+main',
203: text => "deb $loninst/ubuntu/ dapper main",
204: },
205: ubuntu8 => {
206: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+hardy\s+main',
207: text => "deb $loninst/ubuntu/ hardy main",
208: },
1.14 raeburn 209: ubuntu10 => {
210: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+lucid\s+main',
211: text => "deb $loninst/ubuntu/ lucid main",
212: },
213: ubuntu12 => {
214: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+precise\s+main',
215: text => "deb $loninst/ubuntu/ precise main",
216: },
217: ubuntu14 => {
218: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+trusty\s+main',
219: text => "deb $loninst/ubuntu/ trusty main",
220: },
221: ubuntu16 => {
222: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+xenial\s+main',
223: text => "deb $loninst/ubuntu/ xenial main",
224: },
225: ubuntu18 => {
226: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/\s+bionic\s+main',
227: text => "deb $loninst/ubuntu/ bionic main",
228: },
1.9 raeburn 229: );
230: my $apt_status;
231: if (defined($apt_get_source{$dist})) {
232: $apt_status = &update_apt_source($apt_get_source{$dist},);
1.6 raeburn 233: }
234: }
1.1 matthew 235:
1.4 matthew 236: my $mysql_global_status =
237: &update_file('/etc/my.cnf',
1.1 matthew 238: [{section =>'mysqld',
1.12 raeburn 239: key =>'wait_timeout=',
1.1 matthew 240: value =>'31536000', }]);
241:
1.3 matthew 242: my $local_my_cnf = '/home/www/.my.cnf';
243: if (! -e $local_my_cnf) {
1.4 matthew 244: # Create a file so we can do something with it...
1.3 matthew 245: system("touch $local_my_cnf");
246: }
1.4 matthew 247: my $mysql_www_status =
248: &update_file($local_my_cnf,
1.3 matthew 249: [{section =>'client',
250: key =>'user=',
251: value =>'www',},
252: {section =>'client',
253: key =>'password=',
254: value =>$loncapa_config->{'lonSqlAccess'}},]);
255:
1.4 matthew 256: my $exitvalue = 0;
257:
258: if ($mysql_global_status) { $exitvalue = 1; }
259:
260: exit $exitvalue;
1.1 matthew 261:
262:
263: sub update_file {
1.13 raeburn 264: my ($file,$newdata,$nobackup) = @_;
1.1 matthew 265: return 1 if (! -e $file);
1.13 raeburn 266: unless ($nobackup) {
267: my $backup = $file.'.backup';
268: if (! copy($file,$backup)) {
269: warn "**** Error: Unable to make backup of $file";
270: return 0;
271: }
1.1 matthew 272: }
273: my ($filedata) = &parse_config_file($file);
1.4 matthew 274: if (! ref($filedata)) { warn "**** Error: $filedata"; return 0;}
275: my $modified = 0;
1.1 matthew 276: foreach my $data (@$newdata) {
277: my $section = $data->{'section'};
278: my $key = $data->{'key'};
279: my $value = $data->{'value'};
1.4 matthew 280: my $result = &modify_config_file($filedata,$section,$key,$value);
281: if ($result) { $modified = 1; }
282: }
283: if ($modified) {
284: my $result = &write_config_file($file,$filedata);
285: if (defined($result)) { warn 'Error:'.$result; return 0; }
1.1 matthew 286: }
1.4 matthew 287: return $modified;
1.1 matthew 288: }
289:
290: #################################################################
291: #################################################################
292:
293: =pod
294:
1.9 raeburn 295: =over 4
296:
297: =item &parse_config_file()
1.1 matthew 298:
299: Read a configuration file in and parse it into an internal data structure.
300:
301: Input: filename
302:
303: Output: array ref $filedata OR scalar error message
304:
1.9 raeburn 305: =back
306:
1.1 matthew 307: =cut
308:
309: #################################################################
310: #################################################################
311: sub parse_config_file {
312: my ($file) = @_;
313: open(INFILE,$file) || return ('Unable to open '.$file.' for reading');
314: my @Input = <INFILE>;
315: close(INFILE);
316: my @Structure;
317: my %Sections;
318: while (my $line = shift(@Input)) {
319: chomp($line);
320: if ($line =~ /^\[([^\]]*)\]/) {
321: my $section_id = $1;
322: push(@Structure,'__section__'.$section_id);
323: while ($line = shift(@Input)) {
1.4 matthew 324: chomp($line);
1.1 matthew 325: if ($line =~ /^\[([^\]]*)\]/) {
326: unshift(@Input,$line);
327: last;
328: } else {
329: push(@{$Sections{$section_id}},$line);
330: }
331: }
332: } else {
333: push(@Structure,$line);
334: }
335: }
336: my $filedata = [\@Structure,\%Sections];
337: return $filedata;
338: }
339:
340: #################################################################
341: #################################################################
342:
343: =pod
344:
1.9 raeburn 345: =over 4
346:
1.1 matthew 347: =item
348:
349: Write a configuration file out based on the internal data structure returned
350: by &parse_config_file
351:
352: Inputs: filename, $filedata (the return value of &parse_config_file
353:
354: Returns: undef on success, scalar error message on failure.
355:
1.9 raeburn 356: =back
357:
1.1 matthew 358: =cut
359:
360: #################################################################
361: #################################################################
362: sub write_config_file {
363: my ($file,$filedata) = @_;
364: my ($structure,$sections) = @$filedata;
365: if (! defined($structure) || ! ref($structure)) {
366: return 'Bad subroutine inputs';
367: }
368: open(OUTPUT,'>'.$file) || return('Unable to open '.$file.' for writing');
369: for (my $i=0;$i<scalar(@$structure);$i++) {
370: my $line = $structure->[$i];
371: chomp($line);
372: if ($line =~ /^__section__(.*)$/) {
373: my $section_id = $1;
374: print OUTPUT ('['.$section_id.']'.$/);
375: foreach my $section_line (@{$sections->{$section_id}}) {
376: chomp($section_line);
377: print OUTPUT $section_line.$/;
378: }
379: # Deal with blank lines
380: if ($sections->{$section_id}->[-1] =~ /^\s*$/) {
381: # No need to output a blank line at the end if there is one
382: # already
383: } else {
384: print OUTPUT $/;
385: }
386: } else {
387: print OUTPUT $line.$/;
388: }
389: }
390: close OUTPUT;
391: return undef;
392: }
393:
394: #################################################################
395: #################################################################
396:
397: =pod
398:
1.9 raeburn 399: =over 4
400:
401: =item &modify_config_file()
1.1 matthew 402:
403: Modifies the internal data structure of a configuration file to include new
404: sections and/or new configuration directives.
405:
406: Inputs: $filedata (see &parse_config_file
407: $section, the [section] the new entry is to reside in. A value of undef will
408: cause the "outer" section (as in yum.conf) to be updated (or have the new
409: value prepended).
410: $newkey: A line which matches this will be replaced with $newkey.$newvalue
411: $newvalue: The new value to be placed with the new key.
412:
1.4 matthew 413: Returns: 0 or 1, indicating if the file was modified(1) or not(0).
414:
1.9 raeburn 415: =back
1.4 matthew 416:
1.1 matthew 417: =cut
418:
419: #################################################################
420: #################################################################
421: sub modify_config_file {
422: my ($filedata,$section,$newkey,$newvalue)=@_;
1.4 matthew 423: my $modified = 0; # returned value - set to true if the file is modified
1.1 matthew 424: my ($structure,$sections) = @$filedata;
425: if (! defined($newvalue)) {
426: $newvalue = '';
427: }
428: my $newline = $newkey.$newvalue;
429: #
430: # Determine which array ref gets the item
431: my $target;
432: if (defined($section)) {
433: if (! exists($sections->{$section})) {
434: push(@$structure,'__section__'.$section);
435: $sections->{$section}=[];
436: }
437: $target = $sections->{$section};
438: } else {
439: $target = $structure;
440: }
441: #
442: # Put the item in or update it.
443: my $key_is_new = 1;
444: for (my $i=0;$i<scalar(@$target);$i++) {
445: if ($target->[$i] =~/^$newkey/) {
1.4 matthew 446: if ($target->[$i] ne $newline) {
447: $target->[$i]=$newline;
448: $modified = 1;
449: }
1.1 matthew 450: $key_is_new = 0;
451: last;
452: }
453: }
454: if ($key_is_new) {
455: if (! defined($section)) {
456: unshift(@$target,$newline);
457: } else {
458: # No need to put things after a blank line.
1.2 matthew 459: if (defined($target->[-1]) && $target->[-1] =~ /^\s*$/) {
1.1 matthew 460: $target->[-1] = $newline;
1.4 matthew 461: $modified = 1;
1.1 matthew 462: } else {
463: push(@$target,$newline);
1.4 matthew 464: $modified = 1;
1.1 matthew 465: }
466: }
467: }
1.4 matthew 468: return $modified;
1.1 matthew 469: }
470:
1.9 raeburn 471: #################################################################
472: #################################################################
473:
474: =pod
475:
476: =over 4
477:
478: =item &update_rhn_source()
479:
480: Modifies the Red Hat 4 sources file which includes repositories used by up2date
481:
482: Inputs:
483: $rhn_items - a reference to hash of a hash containing the regular expression
484: to test for, and the text string to append to the file, if an entry for the
485: LON-CAPA RHEL repository is missing for two cases:
486:
487: (a) basearch
488: (b) noarch
489:
490: Returns: 0 or 1, indicating if the file was modified(1) or not(0).
491:
492: =back
493:
494: =cut
495:
496: #################################################################
497: #################################################################
498: sub update_rhn_source {
499: my ($rhn_items) = @_;
500: return 0 if (ref($rhn_items) ne 'HASH');
501: return 0 if ((ref($rhn_items->{basearch}) ne 'HASH') || (ref($rhn_items->{noarch}) ne 'HASH'));
502: my $file = '/etc/sysconfig/rhn/sources';
503: return 0 if (! -e $file);
504: my $backup = $file.'.backup';
505: if (! copy($file,$backup)) {
506: warn "**** Error: Unable to make backup of $file";
507: return 0;
508: }
509: my $result = 0;
510: my $fh;
511: if (open($fh,"<$file")) {
512: my $total = 0;
513: my %found;
514: foreach my $item (keys(%{$rhn_items})) {
515: $found{$item} = 0;
516: }
517: while(<$fh>) {
518: foreach my $item (keys(%{$rhn_items})) {
519: if (ref($rhn_items->{$item}) eq 'HASH') {
520: my $pattern = $rhn_items->{$item}->{regexp};
521: if ($pattern ne '') {
522: if (m{^$pattern}) {
523: $found{$item} = 1;
524: $total ++;
525: }
526: }
527: }
528: }
529: last if $total == 2;
530: }
531: close($fh);
532: if ($total < 2) {
533: if (open($fh,">>$file")) {
534: foreach my $item (keys(%{$rhn_items})) {
535: unless ($found{$item}) {
536: if (ref($rhn_items->{$item}) eq 'HASH') {
537: if ($rhn_items->{$item}->{text} ne '') {
538: print $fh "\n".$rhn_items->{$item}->{text}."\n";
539: $result = 1;
540: }
541: }
542: }
543: }
544: close($fh);
545: }
546: }
547: }
548: return $result;
549: }
1.1 matthew 550:
551: #################################################################
552: #################################################################
553:
554: =pod
555:
1.9 raeburn 556: =over 4
557:
558: =item &update_apt_source()
559:
560: Modifies the source.list file which includes repositories used by apt-get
561:
562: Inputs:
563: $deb_row - a reference to containing the regular expression
564: to test for, and the text string to append to the file, if an entry for the
565: LON-CAPA Debian/ or Ubuntu repository is missing.
566:
567: Returns: 0 or 1, indicating if the file was modified(1) or not(0).
568:
1.1 matthew 569: =back
570:
571: =cut
572:
573: #################################################################
574: #################################################################
1.9 raeburn 575: sub update_apt_source {
576: my ($deb_row) = @_;
577: return 0 if (ref($deb_row) ne 'HASH');
578: return 0 if (($deb_row->{regexp} eq '') || ($deb_row->{text} eq ''));
579: my $file = '/etc/apt/sources.list';
580: return 0 if (! -e $file);
581: my $backup = $file.'.backup';
582: if (! copy($file,$backup)) {
583: warn "**** Error: Unable to make backup of $file";
584: return 0;
585: }
586: my $result = 0;
587: my $fh;
588: if (open($fh,"<$file")) {
589: my $found = 0;
590: my $pattern = $deb_row->{regexp};
591: while(<$fh>) {
592: if (m{^$pattern}) {
593: $found = 1;
594: last;
595: }
596: }
597: close($fh);
598: if (!$found) {
599: if (open($fh,">>$file")) {
600: print $fh "\n".$deb_row->{text}."\n";
601: close($fh);
602: $result = 1;
603: }
604: }
605: }
606: return $result;
607: }
608:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>