1: #!/usr/bin/perl -w
2: #
3: # The LearningOnline Network
4: #
5: # $Id: modify_config_files.pl,v 1.27 2024/07/15 15:48:52 raeburn Exp $
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:
39: This script modifies local MySQL configuration file(s), which will be:
40: /home/www/.my.cnf and, depending on distro/version also one of:
41: /etc/my.cnf, /etc/mysql/my.cnf, /etc/mysql/mysql.conf.d/mysqld.cnf,
42: or /etc/mysql/mariadb.conf.d/50-server.cnf, and also the file used to
43: store information about the LON-CAPA package repositories located at
44: install.loncapa.org. which provide rpm or deb packages created for the
45: Linux distro/version being used. The file to modify will be one of:
46: /etc/yum.conf (for CentOS/Scientific Linux/RHEL >=5 and <8),
47: /etc/apt/sources.list (for Debian < 10 and Ubuntu < 22), or
48: /etc/apt/sources.list.d/loncapa.list (for Debian >= 10 and Ubuntu > 20),
49: /etc/sysconfig/rhn/sources (for RHEL4), or /etc/yum.repos.d/loncapa.repo
50: (for Fedora >= 21; Oracle Linux; AlmaLinux; RockyLinux; CentOS/RHEL >= 8).
51:
52: The script also modifies /home/www/.inputrc on Linux distros which have
53: readline 8.1 or newer, i.e., CentOS/Scientific Linux/RHEL >= 9,
54: Ubuntu >= 22, Debian >= 12, and Fedora >= 34.
55:
56: =head1 DESCRIPTION
57:
58: This script will modify /etc/my.cnf, /etc/mysql/my.cnf,
59: /etc/mysql/mysql.conf.d/mysqld.cnf, or
60: /etc/mysql/mariadb.conf.d/50-server.cnf,
61: and /etc/yum.conf, /etc/yum.repos.d/loncapa.repo,
62: /etc/apt/sources.list, /etc/apt/sources.list.d/loncapa.list or
63: /etc/sysconfig/rhn/sources and /home/www/.inputrc to ensure
64: certain parameters are set properly.
65:
66: The LON-CAPA yum repositories are added to /etc/yum.conf,
67: /etc/yum.repos.d/loncapa.repo, or /etc/sysconfig/rhn/sources
68: and the LON-CAPA apt repositories are added to
69: /etc/apt/sources.list or /etc/apt/sources.list.d/loncapa.list.
70:
71: The /etc/my.cnf, /etc/mysql/my.cnf /etc/mysql/mysql.conf.d/mysqld.cnf
72: or /etc/mysql/mariadb.conf.d/50-server.cnf file is modified to set the
73: wait_timeout to 1 year. Backup copies of each file are made in
74: /etc, /etc/apt, and /etc/sysconfig/rhn, as appropriate.
75:
76: =cut
77:
78: use strict;
79: use File::Copy;
80: use lib '/home/httpd/lib/perl/';
81: use LONCAPA::Configuration;
82: my $loncapa_config=LONCAPA::Configuration::read_conf('loncapa.conf');
83:
84: open(DSH,"$$loncapa_config{'lonDaemons'}/distprobe |");
85: my $dist = <DSH>;
86: chomp($dist);
87: close(DSH);
88:
89: my $yum_status;
90: my $loninst = 'http://install.loncapa.org';
91: my $loninst_re = 'http://install\.loncapa\.org';
92: if ($dist =~ /^fedora(\d+)$/) {
93: my $file = '/etc/yum.conf';
94: my $ver = $1;
95: my $gpgchk = '0';
96: my $gpg = "$loninst/versions/fedora/RPM-GPG-KEY-loncapa";
97: my $nobackup;
98: if ($ver > 6) {
99: $gpgchk = '1';
100: }
101: if ($ver >= 21) {
102: $file = '/etc/yum.repos.d/loncapa.repo';
103: $nobackup = 1;
104: }
105: $yum_status =
106: &update_file($file,
107: [{section => 'loncapa-updates-basearch',
108: key => 'name=',
109: value => 'Fedora Core $releasever LON-CAPA $basearch Updates',
110: }, {section => 'loncapa-updates-basearch',
111: key => 'baseurl=',
112: value => $loninst.'/fedora/linux/loncapa/$releasever/$basearch',
113: }, {section => 'loncapa-updates-basearch',
114: key => 'gpgcheck=',
115: value => $gpgchk,
116: }, {section => 'loncapa-updates-basearch',
117: key => 'gpgkey=',
118: value => $gpg,
119: }, {section => 'loncapa-updates-noarch',
120: key => 'name=',
121: value => 'Fedora Core $releasever LON-CAPA noarch Updates',
122: }, {section => 'loncapa-updates-noarch',
123: key => 'baseurl=',
124: value => $loninst.'/fedora/linux/loncapa/$releasever/noarch',
125: }, {section => 'loncapa-updates-noarch',
126: key => 'gpgcheck=',
127: value => $gpgchk,
128: }, {section => 'loncapa-updates-noarch',
129: key => 'gpgkey=',
130: value => $gpg,
131: }],$nobackup);
132: } elsif ($dist =~ /^(rhes|centos|scientific|oracle|rocky|alma)(\d+)(|\-stream)$/) {
133: my $type = $1;
134: my $ver = $2;
135: my $stream = $3;
136: my $longver = $ver;
137: my $nobackup;
138: if ($type eq 'rhes') {
139: if ($ver == 4) {
140: $longver = '4ES';
141: } elsif ($ver == 5) {
142: $longver = '5Server';
143: }
144: } elsif ($type eq 'centos') {
145: $type .= $stream;
146: }
147: my %info = (
148: rhes => {
149: title => 'RHEL',
150: path => 'redhat/linux/enterprise/loncapa',
151: gpg => 'versions/redhat/RPM-GPG-KEY-loncapa',
152: gpgchk => 1,
153: },
154: centos => {
155: title => 'CentOS',
156: path => 'centos/loncapa',
157: gpg => 'versions/centos/RPM-GPG-KEY-loncapa',
158: gpgchk => 1,
159: },
160: scientific => {
161: title => 'Scientific Linux',
162: path => 'scientific/loncapa',
163: gpg => 'versions/scientific/RPM-GPG-KEY-loncapa',
164: gpgchk => 1,
165: },
166: oracle => {
167: title => 'Oracle Linux',
168: path => 'oracle/loncapa',
169: gpg => 'versions/oracle/RPM-GPG-KEY-loncapa',
170: gpgchk => 1,
171: },
172: rocky => {
173: title => 'Rocky Linux',
174: path => 'rocky/loncapa',
175: gpg => 'versions/rocky/RPM-GPG-KEY-loncapa',
176: gpgchk => 1,
177: },
178: alma => {
179: title => 'AlmaLinux',
180: path => 'alma/loncapa',
181: gpg => 'versions/alma/RPM-GPG-KEY-loncapa',
182: gpgchk => 1,
183: },
184: 'centos-stream' => {
185: title => 'CentOS Stream',
186: path => 'centos/loncapa',
187: gpg => 'versions/centos/RPM-GPG-KEY-loncapa',
188: gpgchk => 1,
189: },
190: );
191: if (ref($info{$type}) eq 'HASH') {
192: if ($ver > 4) {
193: my $file = '/etc/yum.conf';
194: if (($ver > 7) || ($type eq 'oracle') || ($type eq 'rocky') ||
195: ($type eq 'alma') || ($type eq 'centos-stream')) {
196: $file = '/etc/yum.repos.d/loncapa.repo';
197: $nobackup = 1;
198: }
199: my $release = '$releasever';
200: if ($type eq 'centos-stream') {
201: $release .= '-stream';
202: }
203: $yum_status =
204: &update_file($file,
205: [{section => 'loncapa-updates-basearch',
206: key => 'name=',
207: value => $info{$type}{title}.' $releasever LON-CAPA $basearch Updates',
208: }, {section => "loncapa-updates-basearch",
209: key => 'baseurl=',
210: value => "$loninst/$info{$type}{path}/$release/".'$basearch',
211: }, {section => 'loncapa-updates-basearch',
212: key => 'gpgcheck=',
213: value => $info{$type}{gpgchk},
214: }, {section => 'loncapa-updates-basearch',
215: key => 'gpgkey=',
216: value => "$loninst/$info{$type}{gpg}",
217: }, {section => 'loncapa-updates-noarch',
218: key => 'name=',
219: value => $info{$type}{title}.' $releasever LON-CAPA noarch Updates',
220: }, {section => 'loncapa-updates-noarch',
221: key => 'baseurl=',
222: value => "$loninst/$info{$type}{path}/$release/noarch",
223: }, {section => 'loncapa-updates-noarch',
224: key => 'gpgcheck=',
225: value => $info{$type}{gpgchk},
226: }, {section => 'loncapa-updates-noarch',
227: key => 'gpgkey=',
228: value => "$loninst/$info{$type}{gpg}",
229: }],$nobackup);
230: } elsif (($type eq 'rhes') && ($ver == 4)) {
231: my %rhn = (
232: basearch => {
233: regexp => '\s*yum\s+loncapa\-updates\-basearch\s+'.$loninst_re.'/'.$info{$type}{path}.'/'.$longver.'/\$ARCH',
234: text => "yum loncapa-updates-basearch $loninst/$info{$type}{path}/$longver/".'$ARCH',
235: },
236: noarch => {
237: regexp => '\s*yum\s+loncapa\-updates\-noarch\s+'.$loninst_re.'/'.$info{$type}{path}.'/'.$longver.'/noarch',
238: text => "yum loncapa-updates-noarch $loninst/$info{$type}{path}/$longver/noarch",
239: },
240: );
241: $yum_status = &update_rhn_source(\%rhn);
242: }
243: }
244: } elsif ($dist =~ /^(debian|ubuntu)(\d+)$/) {
245: my ($distname,$distver) = ($1,$2);
246: if ((($distname eq 'ubuntu') && ($distver > 20)) ||
247: (($distname eq 'debian') && ($distver >= 10))) {
248: $loninst = 'https://install.loncapa.org';
249: $loninst_re = 'https://install\.loncapa\.org';
250: }
251: my %apt_get_source = (
252: debian5 => {
253: regexp => '\s*deb\s+'.$loninst_re.'/debian/?\s+lenny\s+main',
254: text => "deb $loninst/debian lenny main",
255: },
256: debian6 => {
257: regexp => '\s*deb\s+'.$loninst_re.'/debian/?\s+squeeze\s+main',
258: text => "deb $loninst/debian squeeze main",
259: },
260: debian10 => {
261: regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/debian/?\s+buster\s+main',
262: text => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/debian buster main",
263: },
264: debian11 => {
265: regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/debian/?\s+bullseye\s+main',
266: text => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/debian bullseye main",
267: },
268: debian12 => {
269: regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/debian/?\s+bookworm\s+main',
270: text => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/debian bookworm main",
271: },
272:
273: ubuntu6 => {
274: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+dapper\s+main',
275: text => "deb $loninst/ubuntu dapper main",
276: },
277: ubuntu8 => {
278: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+hardy\s+main',
279: text => "deb $loninst/ubuntu hardy main",
280: },
281: ubuntu10 => {
282: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+lucid\s+main',
283: text => "deb $loninst/ubuntu lucid main",
284: },
285: ubuntu12 => {
286: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+precise\s+main',
287: text => "deb $loninst/ubuntu precise main",
288: },
289: ubuntu14 => {
290: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+trusty\s+main',
291: text => "deb $loninst/ubuntu trusty main",
292: },
293: ubuntu16 => {
294: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+xenial\s+main',
295: text => "deb $loninst/ubuntu xenial main",
296: },
297: ubuntu18 => {
298: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+bionic\s+main',
299: text => "deb $loninst/ubuntu bionic main",
300: },
301: ubuntu20 => {
302: regexp => '\s*deb\s+'.$loninst_re.'/ubuntu/?\s+focal\s+main',
303: text => "deb $loninst/ubuntu focal main",
304: },
305: ubuntu22 => {
306: regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/ubuntu/?\s+jammy\s+main',
307: text => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/ubuntu jammy main",
308: },
309: ubuntu24 => {
310: regexp => '\s*deb\s+\[signed\-by=/etc/apt/keyrings/loncapa\.gpg\]\s+'.$loninst_re.'/ubuntu/?\s+noble\s+main',
311: text => "deb [signed-by=/etc/apt/keyrings/loncapa.gpg] $loninst/ubuntu noble main",
312: },
313: );
314: my $apt_status;
315: if (defined($apt_get_source{$dist})) {
316: $apt_status = &update_apt_source($distname,$distver,$apt_get_source{$dist});
317: }
318: }
319:
320: my $mysqlfile = '/etc/my.cnf';
321: my $mysqlconf = [{section =>'mysqld',
322: key =>'wait_timeout=',
323: value =>'31536000'}];
324: my $nomysqlbackup;
325: if ($dist =~ /^ubuntu(\d+)$/) {
326: my $version = $1;
327: $mysqlfile = '/etc/mysql/my.cnf';
328: if ($version > 14) {
329: $nomysqlbackup = 1;
330: $mysqlfile = '/etc/mysql/mysql.conf.d/mysqld.cnf';
331: if ($version < 20) {
332: push(@{$mysqlconf},
333: {section =>'mysqld',
334: key =>'sql_mode=',
335: value =>'"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"'});
336: } else {
337: push(@{$mysqlconf},
338: {section =>'mysqld',
339: key =>'sql_mode=',
340: value =>'"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"'});
341: }
342: }
343: } elsif ($dist =~ /^debian(\d+)$/) {
344: my $version = $1;
345: if ($version >= 10) {
346: $mysqlfile = '/etc/mysql/mariadb.conf.d/50-server.cnf';
347: $nomysqlbackup = 1;
348: }
349: }
350:
351: my $mysql_global_status = &update_file($mysqlfile,$mysqlconf,$nomysqlbackup);
352:
353: my $local_my_cnf = '/home/www/.my.cnf';
354: if (! -e $local_my_cnf) {
355: # Create a file so we can do something with it...
356: system("touch $local_my_cnf");
357: }
358: my $mysql_www_status =
359: &update_file($local_my_cnf,
360: [{section =>'client',
361: key =>'user=',
362: value =>'www',},
363: {section =>'client',
364: key =>'password=',
365: value =>$loncapa_config->{'lonSqlAccess'}},]);
366:
367: my $needs_inputrc_check;
368: if ($dist =~ /^debian(\d+)$/) {
369: if ($1 >= 12) {
370: $needs_inputrc_check = 1;
371: }
372: } elsif ($dist =~ /^ubuntu(\d+)$/) {
373: if ($1 >= 22) {
374: $needs_inputrc_check = 1;
375: }
376: } elsif ($dist =~ /^(?:redhat|oracle|alma|rocky|centos-stream)(\d+)$/) {
377: if ($1 >= 9) {
378: $needs_inputrc_check = 1;
379: }
380: } elsif ($dist =~ /^fedora(\d+)$/) {
381: if ($1 >= 34) {
382: $needs_inputrc_check = 1;
383: }
384: }
385:
386: if ($needs_inputrc_check) {
387: my $bash_www_cnf = '/home/www/.inputrc';
388: if (!-e $bash_www_cnf) {
389: system("touch $bash_www_cnf");
390: if (open(my $fh,'>',$bash_www_cnf)) {
391: print $fh <<'END';
392: $if R
393: set enable-bracketed-paste off
394: $endif
395:
396: $if maxima
397: set enable-bracketed-paste off
398: $endif
399: END
400: close($fh);
401: } else {
402: warn "**** Error: could not open $bash_www_cnf to add 'set enable-bracketed-paste to off'";
403: }
404: my $wwwuid = getpwnam('www');
405: my $wwwgid = getgrnam('www');
406: if ($wwwuid!=$<) {
407: chown($wwwuid,$wwwgid,$bash_www_cnf);
408: }
409: } else {
410: my (%bracketed_paste_on,%bracketed_paste_off,@preserve,$condition);
411: $condition = '';
412: if (open(my $fh,'<',$bash_www_cnf)) {
413: while (my $line=<$fh>) {
414: chomp($line);
415: if ($line =~ /^\$if\s+(\w+)\s*$/) {
416: if ($1 eq 'R') {
417: $condition = 'r';
418: } elsif ($1 eq 'maxima') {
419: $condition = 'maxima';
420: } else {
421: $condition = 'other';
422: }
423: } elsif ($line =~ /^\$endif\s*$/) {
424: $condition = '';
425: }
426: if ($line =~ /^\s*set\s+enable\-bracketed\-paste\s+(off|on)\s*$/) {
427: if ($1 eq 'off') {
428: if ($condition ne '') {
429: $bracketed_paste_off{$condition} = 1;
430: } else {
431: $bracketed_paste_off{all} = 1;
432: }
433: push(@preserve,$line);
434: } else {
435: if ($condition ne '') {
436: $bracketed_paste_on{$condition} = 1;
437: if (($condition eq 'r') || ($condition eq 'maxima')) {
438: push(@preserve,' set enable-bracketed-paste off');
439: } else {
440: push(@preserve,$line);
441: }
442: } else {
443: $bracketed_paste_on{all} = 1;
444: push(@preserve,$line);
445: }
446: }
447: } else {
448: push(@preserve,$line);
449: }
450: }
451: close($fh);
452: }
453: if (($bracketed_paste_on{r} || $bracketed_paste_on{maxima}) ||
454: (!exists($bracketed_paste_off{r}) && !exists($bracketed_paste_on{r}) &&
455: !exists($bracketed_paste_off{maxima}) && !exists($bracketed_paste_on{maxima}))) {
456: if (open(my $fh,'>',$bash_www_cnf)) {
457: if (@preserve) {
458: foreach my $entry (@preserve) {
459: print $fh "$entry\n";
460: }
461: if (!exists($bracketed_paste_off{r}) && !exists($bracketed_paste_on{r})) {
462: print $fh <<'END';
463: $if R
464: set enable-bracketed-paste off
465: $endif
466: END
467: }
468: if (!exists($bracketed_paste_off{r}) && !exists($bracketed_paste_on{r})) {
469: print $fh <<'END';
470: $if maxima
471: set enable-bracketed-paste off
472: $endif
473: END
474: }
475: } else {
476: print $fh <<'END';
477: $if R
478: set enable-bracketed-paste off
479: $endif
480:
481: $if maxima
482: set enable-bracketed-paste off
483: $endif
484: END
485: }
486: close($fh);
487: } else {
488: warn "**** Error: could not open $bash_www_cnf to add 'set enable-bracketed-paste to off'";
489: }
490: }
491: }
492: }
493:
494: my $exitvalue = 0;
495:
496: if ($mysql_global_status) { $exitvalue = 1; }
497:
498: exit $exitvalue;
499:
500: #################################################################
501: #################################################################
502:
503: =pod
504:
505: =over 4
506:
507: =item &update_file()
508:
509: Calls &parse_config_file for a file and then sends the
510: retrieved data to &modify_config_file, and checks if
511: modificatione were made.
512:
513: Inputs: filename, newdata (reference to an array of hashed),
514: nobackup 1, if no backup of existing file (.backup
515: appended to filename) should be made.
516:
517: Output: 0 or 1; o if no modifications exist (and hence no file
518: needed to be saved, or 1 if modifications made, and call to
519: &write_config_file to save file, or if filename does not exist.
520:
521: =back
522:
523: =cut
524:
525: #################################################################
526: #################################################################
527:
528:
529: sub update_file {
530: my ($file,$newdata,$nobackup) = @_;
531: return 1 if (! -e $file);
532: unless ($nobackup) {
533: my $backup = $file.'.backup';
534: if (! copy($file,$backup)) {
535: warn "**** Error: Unable to make backup of $file";
536: return 0;
537: }
538: }
539: my ($filedata) = &parse_config_file($file);
540: if (! ref($filedata)) { warn "**** Error: $filedata"; return 0;}
541: my $modified = 0;
542: foreach my $data (@$newdata) {
543: my $section = $data->{'section'};
544: my $key = $data->{'key'};
545: my $value = $data->{'value'};
546: my $result = &modify_config_file($filedata,$section,$key,$value);
547: if ($result) { $modified = 1; }
548: }
549: if ($modified) {
550: my $result = &write_config_file($file,$filedata);
551: if (defined($result)) { warn 'Error:'.$result; return 0; }
552: }
553: return $modified;
554: }
555:
556: #################################################################
557: #################################################################
558:
559: =pod
560:
561: =over 4
562:
563: =item &parse_config_file()
564:
565: Read a configuration file in and parse it into an internal data structure.
566:
567: Input: filename
568:
569: Output: array ref $filedata OR scalar error message
570:
571: =back
572:
573: =cut
574:
575: #################################################################
576: #################################################################
577: sub parse_config_file {
578: my ($file) = @_;
579: open(INFILE,$file) || return ('Unable to open '.$file.' for reading');
580: my @Input = <INFILE>;
581: close(INFILE);
582: my @Structure;
583: my %Sections;
584: while (my $line = shift(@Input)) {
585: chomp($line);
586: if ($line =~ /^\[([^\]]*)\]/) {
587: my $section_id = $1;
588: push(@Structure,'__section__'.$section_id);
589: while ($line = shift(@Input)) {
590: chomp($line);
591: if ($line =~ /^\[([^\]]*)\]/) {
592: unshift(@Input,$line);
593: last;
594: } else {
595: push(@{$Sections{$section_id}},$line);
596: }
597: }
598: } else {
599: push(@Structure,$line);
600: }
601: }
602: my $filedata = [\@Structure,\%Sections];
603: return $filedata;
604: }
605:
606: #################################################################
607: #################################################################
608:
609: =pod
610:
611: =over 4
612:
613: =item &write_config_file()
614:
615: Write a configuration file out based on the internal data structure returned
616: by &parse_config_file
617:
618: Inputs: filename, $filedata (the return value of &parse_config_file
619:
620: Returns: undef on success, scalar error message on failure.
621:
622: =back
623:
624: =cut
625:
626: #################################################################
627: #################################################################
628: sub write_config_file {
629: my ($file,$filedata) = @_;
630: my ($structure,$sections) = @$filedata;
631: if (! defined($structure) || ! ref($structure)) {
632: return 'Bad subroutine inputs';
633: }
634: open(OUTPUT,'>',$file) || return('Unable to open '.$file.' for writing');
635: for (my $i=0;$i<scalar(@$structure);$i++) {
636: my $line = $structure->[$i];
637: chomp($line);
638: if ($line =~ /^__section__(.*)$/) {
639: my $section_id = $1;
640: print OUTPUT ('['.$section_id.']'.$/);
641: foreach my $section_line (@{$sections->{$section_id}}) {
642: chomp($section_line);
643: print OUTPUT $section_line.$/;
644: }
645: # Deal with blank lines
646: if ($sections->{$section_id}->[-1] =~ /^\s*$/) {
647: # No need to output a blank line at the end if there is one
648: # already
649: } else {
650: print OUTPUT $/;
651: }
652: } else {
653: print OUTPUT $line.$/;
654: }
655: }
656: close OUTPUT;
657: return undef;
658: }
659:
660: #################################################################
661: #################################################################
662:
663: =pod
664:
665: =over 4
666:
667: =item &modify_config_file()
668:
669: Modifies the internal data structure of a configuration file to include new
670: sections and/or new configuration directives.
671:
672: Inputs: $filedata (see &parse_config_file
673: $section, the [section] the new entry is to reside in. A value of undef will
674: cause the "outer" section (as in yum.conf) to be updated (or have the new
675: value prepended).
676: $newkey: A line which matches this will be replaced with $newkey.$newvalue
677: $newvalue: The new value to be placed with the new key.
678:
679: Returns: 0 or 1, indicating if the file was modified(1) or not(0).
680:
681: =back
682:
683: =cut
684:
685: #################################################################
686: #################################################################
687: sub modify_config_file {
688: my ($filedata,$section,$newkey,$newvalue)=@_;
689: my $modified = 0; # returned value - set to true if the file is modified
690: my ($structure,$sections) = @$filedata;
691: if (! defined($newvalue)) {
692: $newvalue = '';
693: }
694: my $newline = $newkey.$newvalue;
695: #
696: # Determine which array ref gets the item
697: my $target;
698: if (defined($section)) {
699: if (! exists($sections->{$section})) {
700: push(@$structure,'__section__'.$section);
701: $sections->{$section}=[];
702: }
703: $target = $sections->{$section};
704: } else {
705: $target = $structure;
706: }
707: #
708: # Put the item in or update it.
709: my $key_is_new = 1;
710: for (my $i=0;$i<scalar(@$target);$i++) {
711: if ($target->[$i] =~/^$newkey/) {
712: if ($target->[$i] ne $newline) {
713: $target->[$i]=$newline;
714: $modified = 1;
715: }
716: $key_is_new = 0;
717: last;
718: }
719: }
720: if ($key_is_new) {
721: if (! defined($section)) {
722: unshift(@$target,$newline);
723: } else {
724: # No need to put things after a blank line.
725: if (defined($target->[-1]) && $target->[-1] =~ /^\s*$/) {
726: $target->[-1] = $newline;
727: $modified = 1;
728: } else {
729: push(@$target,$newline);
730: $modified = 1;
731: }
732: }
733: }
734: return $modified;
735: }
736:
737: #################################################################
738: #################################################################
739:
740: =pod
741:
742: =over 4
743:
744: =item &update_rhn_source()
745:
746: Modifies the Red Hat 4 sources file which includes repositories used by up2date
747:
748: Inputs:
749: $rhn_items - a reference to hash of a hash containing the regular expression
750: to test for, and the text string to append to the file, if an entry for the
751: LON-CAPA RHEL repository is missing for two cases:
752:
753: (a) basearch
754: (b) noarch
755:
756: Returns: 0 or 1, indicating if the file was modified(1) or not(0).
757:
758: =back
759:
760: =cut
761:
762: #################################################################
763: #################################################################
764: sub update_rhn_source {
765: my ($rhn_items) = @_;
766: return 0 if (ref($rhn_items) ne 'HASH');
767: return 0 if ((ref($rhn_items->{basearch}) ne 'HASH') || (ref($rhn_items->{noarch}) ne 'HASH'));
768: my $file = '/etc/sysconfig/rhn/sources';
769: return 0 if (! -e $file);
770: my $backup = $file.'.backup';
771: if (! copy($file,$backup)) {
772: warn "**** Error: Unable to make backup of $file";
773: return 0;
774: }
775: my $result = 0;
776: my $fh;
777: if (open($fh,'<',$file)) {
778: my $total = 0;
779: my %found;
780: foreach my $item (keys(%{$rhn_items})) {
781: $found{$item} = 0;
782: }
783: while(<$fh>) {
784: foreach my $item (keys(%{$rhn_items})) {
785: if (ref($rhn_items->{$item}) eq 'HASH') {
786: my $pattern = $rhn_items->{$item}->{regexp};
787: if ($pattern ne '') {
788: if (m{^$pattern}) {
789: $found{$item} = 1;
790: $total ++;
791: }
792: }
793: }
794: }
795: last if $total == 2;
796: }
797: close($fh);
798: if ($total < 2) {
799: if (open($fh,'>>',$file)) {
800: foreach my $item (keys(%{$rhn_items})) {
801: unless ($found{$item}) {
802: if (ref($rhn_items->{$item}) eq 'HASH') {
803: if ($rhn_items->{$item}->{text} ne '') {
804: print $fh "\n".$rhn_items->{$item}->{text}."\n";
805: $result = 1;
806: }
807: }
808: }
809: }
810: close($fh);
811: }
812: }
813: }
814: return $result;
815: }
816:
817: #################################################################
818: #################################################################
819:
820: =pod
821:
822: =over 4
823:
824: =item &update_apt_source()
825:
826: Modifies either the sources.list or sources.list.d/loncapa.list
827: file which include repositories used by apt-get.
828:
829: Inputs:
830: $distname - distro (without version): debian or ubuntu
831: $distver - distro version, e.g., 12 or 24
832: $deb_row - a reference to a hash containing the regular expression
833: to test for, and the text string to append to the file, if an entry
834: for the LON-CAPA Debian/ or Ubuntu repository is missing.
835:
836: Returns: 0 or 1, indicating if the file was modified(1) or not(0).
837:
838: =back
839:
840: =cut
841:
842: #################################################################
843: #################################################################
844: sub update_apt_source {
845: my ($distname,$distver,$deb_row) = @_;
846: return 0 if (ref($deb_row) ne 'HASH');
847: return 0 if (($deb_row->{regexp} eq '') || ($deb_row->{text} eq ''));
848: my $file = '/etc/apt/sources.list';
849: my $nobackup;
850: if ((($distname eq 'ubuntu') && ($distver > 20)) ||
851: (($distname eq 'debian') && ($distver >= 10))) {
852: $file = '/etc/apt/sources.list.d/loncapa.list';
853: $nobackup = 1;
854: }
855: return 0 if (! -e $file);
856: unless ($nobackup) {
857: my $backup = $file.'.backup';
858: if (! copy($file,$backup)) {
859: warn "**** Error: Unable to make backup of $file";
860: return 0;
861: }
862: }
863: my $result = 0;
864: my $fh;
865: if (open($fh,'<',$file)) {
866: my $found = 0;
867: my $pattern = $deb_row->{regexp};
868: while(<$fh>) {
869: if (m{^$pattern}) {
870: $found = 1;
871: last;
872: }
873: }
874: close($fh);
875: if (!$found) {
876: if (open($fh,'>>',$file)) {
877: print $fh "\n".$deb_row->{text}."\n";
878: close($fh);
879: $result = 1;
880: }
881: }
882: }
883: return $result;
884: }
885:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>