[LON-CAPA-cvs] cvs: doc /install/linux install.pl

raeburn raeburn at source.lon-capa.org
Sat Aug 8 18:44:55 EDT 2026


raeburn		Sat Aug  8 22:44:55 2026 EDT

  Modified files:              
    /doc/install/linux	install.pl 
  Log:
  - Support Ubuntu 26 for which MySQL 8.4 is the default, and for which use of
    the old mysql_native_password authentication method is disabled by default.
  
  
-------------- next part --------------
Index: doc/install/linux/install.pl
diff -u doc/install/linux/install.pl:1.102 doc/install/linux/install.pl:1.103
--- doc/install/linux/install.pl:1.102	Sat Aug  8 22:04:56 2026
+++ doc/install/linux/install.pl	Sat Aug  8 22:44:55 2026
@@ -78,7 +78,7 @@
           &mt('Stopping execution.')."\n";
     exit;
 } else {
-    print LOG '$Id: install.pl,v 1.102 2026/08/08 22:04:56 raeburn Exp $'."\n";
+    print LOG '$Id: install.pl,v 1.103 2026/08/08 22:44:55 raeburn Exp $'."\n";
 }
 
 #
@@ -611,7 +611,7 @@
     }
     my ($mysqlon,$mysqlsetup,$mysqlrestart,$dbh,$has_pass,$mysql_unix_socket,$has_lcdb,
         %recommended,$downloadstatus,$filetouse,$production,$testing,$apachefw,
-        $tostop,$uses_systemctl,$mysql_has_wwwuser,$hostname,$hostip);
+        $tostop,$uses_systemctl,$mysql_has_wwwuser,$hostname,$hostip,$need_mysql_native);
     my $wwwuid = &uid_of_www();
     my $wwwgid = getgrnam('www');
     if (($wwwuid eq '') || ($wwwgid eq '')) {
@@ -641,7 +641,7 @@
     print_and_log("\n".&mt('Hostname is [_1] and IP address is [_2]',$hostname,$hostip)."\n");
     $mysqlon = &check_mysql_running($distro);
     if ($mysqlon) {
-        ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser,$mysql_unix_socket) =
+        ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser,$mysql_unix_socket,$need_mysql_native) =
             &check_mysql_setup($instdir,$dsn,$distro);
         if ($mysqlsetup eq 'needsrestart') {
             $mysqlrestart = '';
@@ -649,7 +649,8 @@
                 $mysqlrestart = 'sudo ';
             }
             $mysqlrestart .= 'service mysql restart';
-            return ($distro,$gotprereqs,$localecmd,$langcmd,$packagecmd,$updatecmd,$installnow,$mysqlrestart);
+            return ($distro,$gotprereqs,$localecmd,$langcmd,$packagecmd,$updatecmd,$installnow,$mysqlrestart,
+                    $need_mysql_native);
         } else {
             if ($mysqlsetup eq 'noroot') {
                 $recommended{'mysqlperms'} = 1;
@@ -664,6 +665,9 @@
             unless ($has_lcdb) {
                 $recommended{'mysql'} = 1;
             }
+            if ($need_mysql_native) {
+                $recommended{'mysqlperms'} = 1;
+            }
         }
     }
     my ($sslhostsfilesref,$has_std,$has_int,$rewritenum,$nochgstd,$nochgint);
@@ -679,7 +683,7 @@
     ($recommended{'download'},$downloadstatus,$filetouse,$production,$testing)
         = &need_download($distro,$instdir);
     return ($distro,$gotprereqs,$localecmd,$langcmd,$packagecmd,$updatecmd,$installnow,
-            $mysqlrestart,\%recommended,$dbh,$has_pass,$mysql_unix_socket,
+            $mysqlrestart,$need_mysql_native,\%recommended,$dbh,$has_pass,$mysql_unix_socket,
             $has_lcdb,$downloadstatus,$filetouse,$production,$testing,$apachefw,
             $uses_systemctl,$hostname,$hostip,$sslhostsfilesref,$has_std,$has_int,
             $rewritenum,$nochgstd,$nochgint);
@@ -1691,7 +1695,7 @@
 
 sub check_mysql_setup {
     my ($instdir,$dsn,$distro) = @_;
-    my ($mysqlsetup,$has_pass,$mysql_unix_socket,$mysql_has_wwwuser);
+    my ($mysqlsetup,$has_pass,$mysql_unix_socket,$mysql_has_wwwuser,$need_mysql_native);
     my $dbh = DBI->connect($dsn,'root','',{'PrintError'=>0});
     my ($mysqlversion,$mysqlminorversion,$mysqlsubver,$mysqlname) = &get_mysql_version($distro);
     if (($mysqlname =~ /^MariaDB/i) && (($mysqlversion == 10 && $mysqlminorversion >= 4) || ($mysqlversion >= 11))) {
@@ -1725,11 +1729,21 @@
                 }
             }
             $sth->finish();
+            if (($mysqlversion == 8 && $mysqlminorversion >= 4)) {
+                $need_mysql_native = &check_mysql_native_pw($dbh);
+            }
             if ($mysql_unix_socket) {
                 print_and_log(&mt('MySQL using unix_socket for root access from localhost.')."\n");
                 $mysqlsetup = 'rootok';
                 $mysql_has_wwwuser = &check_mysql_wwwuser($dbh);
-                return ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser,$mysql_unix_socket);
+                if ($need_mysql_native) {
+                    print_and_log(&mt('[_1] needs mysql_native_password to be activated',
+                                      "$mysqlname $mysqlversion.$mysqlminorversion")."\n");
+                }
+                return ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser,$mysql_unix_socket,$need_mysql_native);
+            } elsif ($need_mysql_native) {
+                print_and_log(&mt('[_1] needs mysql_native_password to be activated.',
+                                  "$mysqlname $mysqlversion.$mysqlminorversion")."\n"); 
             }
         }
     } elsif ($DBI::err =~ /1045/) {
@@ -1737,7 +1751,7 @@
     } elsif ($distro =~ /^ubuntu(\d+)$/) {
         my $version = $1;
         if ($1 > 12) {
-            print_and_log(&mt('Restarting mysql, please be patient')."\n");
+            print_and_log(&mt('Restarting mysql, please be patient.')."\n");
             if (open (PIPE, "service mysql restart 2>&1 |")) {
                 while (<PIPE>) {
                     print $_;
@@ -1748,12 +1762,15 @@
             if ($dbh) {
                 $mysqlsetup = 'noroot';
                 $mysql_has_wwwuser = &check_mysql_wwwuser($dbh);
+                if (($mysqlversion == 8) && ($mysqlminorversion >= 4)) {
+                    $need_mysql_native = &check_mysql_native_pw($dbh);
+                }
             } elsif ($DBI::err =~ /1045/) {
                 $has_pass = 1;
             } else {
                 $mysqlsetup = 'needsrestart';
                 $mysql_has_wwwuser = &check_mysql_wwwuser();
-                return ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser);
+                return ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser,'',$need_mysql_native);
             }
         }
     }
@@ -1777,6 +1794,7 @@
                     $mysqlsetup = 'rootok';
                     print_and_log(&mt('Password accepted.')."\n");
                     $mysql_has_wwwuser = &check_mysql_wwwuser($dbh);
+
                 } else {
                     if ($DBI::err =~ /1045/) {
                         print_and_log(&mt('Incorrect password.')."\n");
@@ -1790,7 +1808,18 @@
         $mysqlsetup = 'rootfail';
         $mysql_has_wwwuser = &check_mysql_wwwuser();
     }
-    return ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser);
+    unless ($mysqlsetup eq 'rootfail') {
+        if ($dbh) {
+            if (($mysqlversion == 8) && ($mysqlminorversion >= 4)) {
+                $need_mysql_native = &check_mysql_native_pw($dbh);
+                if ($need_mysql_native) {
+                    print_and_log(&mt('[_1] needs mysql_native_password to be activated',
+                                      "$mysqlname $mysqlversion.$mysqlminorversion")."\n");
+                }
+            }
+        }
+    }
+    return ($mysqlsetup,$has_pass,$dbh,$mysql_has_wwwuser,'',$need_mysql_native);
 }
 
 sub check_mysql_wwwuser {
@@ -1826,6 +1855,21 @@
     return $has_lcdb;
 }
 
+sub check_mysql_native_pw {
+    my ($dbh) = @_;
+    my $needed;
+    if (ref($dbh)) { 
+        my $sth = $dbh->prepare("SELECT PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'mysql_native_password'");
+        $sth->execute();
+        my $status = $sth->fetchrow_array();
+        $sth->finish();
+        unless ($status eq 'ACTIVE') {
+            $needed = 1;
+        }
+    }
+    return $needed;
+}
+
 sub get_pathto_iptables {
     my $iptables;
     if (-e '/sbin/iptables') {
@@ -2080,7 +2124,7 @@
 
 my $dsn = "DBI:mysql:database=mysql";
 my ($distro,$gotprereqs,$localecmd,$langcmd,$packagecmd,$updatecmd,$installnow,$mysqlrestart,
-    $recommended,$dbh,$has_pass,$mysql_unix_socket,$has_lcdb,$downloadstatus,
+    $need_mysql_native,$recommended,$dbh,$has_pass,$mysql_unix_socket,$has_lcdb,$downloadstatus,
     $filetouse,$production,$testing,$apachefw,$uses_systemctl,$hostname,$hostip,
     $sslhostsfiles,$has_std,$has_int,$rewritenum,$nochgstd,$nochgint) =
     &check_required($instdir,$dsn);
@@ -2099,6 +2143,28 @@
           &mt('Please run the install.pl script again, once you have restarted mysql.')."\n";
     exit;
 }
+if ($need_mysql_native) {
+    print "\n".&mt('Your MySQL configuration needs updating.')."\n";
+    my $updated = &update_mysql_config($distro);
+    if ($updated) {
+        print_and_log("\n".&mt('Your MySQL configuration was successfully updated.')."\n");
+        print_and_log(&mt('Restarting mysql, please be patient.')."\n");
+        if (open (PIPE, "systemctl restart mysql 2>&1 |")) {
+            while (<PIPE>) {
+                print $_;
+            }
+            close(PIPE);
+        } else {
+            print "\n".&mt('Restarting mysql failed.')."\n".
+                   &mt('Stopping execution of install.pl script.')."\n"; 
+            exit;
+        }
+    } else {
+        print "\n".&mt('Your MySQL configuration needed updating, but the attempt to do so failed.')."\n".
+              &mt('Stopping execution of install.pl script.')."\n";
+        exit;
+    }
+}
 if ($localecmd ne '') {
     print "\n".&mt('Although the LON-CAPA application itself is localized for a number of different languages,[_1]the default locale language for the Linux OS on which it runs should be US English.',"\n")."\n\n";
     if ($langcmd ne '') {
@@ -2836,7 +2902,7 @@
 sub setup_mysql_permissions {
     my ($dbh,$has_pass,$mysql_unix_socket,$distro, at mysql_lc_commands) = @_;
     my ($mysqlversion,$mysqlminorversion,$mysqlsubver,$mysqlname) = &get_mysql_version($distro);
-    my ($usescreate,$usesauth,$is_mariadb,$hasauthcol, at mysql_commands);
+    my ($usescreate,$usesauth,$is_mariadb,$hasauthcol,$check_native_pw, at mysql_commands);
     if ($mysqlname =~ /^MariaDB/i) {
         $is_mariadb = 1;
         if ((($mysqlversion == 10) && ($mysqlminorversion >= 4)) || ($mysqlversion >= 11)) {
@@ -2854,6 +2920,9 @@
                  (($mysqlminorversion >= 6) || (($mysqlminorversion == 5) && ($mysqlsubver >= 7)))) {
             $hasauthcol = 1;
         }
+        if (($mysqlversion == 8) && ($mysqlminorversion >= 4)) {
+            $check_native_pw = 1;
+        }
     }
     if ($usescreate) {
         @mysql_commands = ("CREATE USER 'www'\@'localhost' IDENTIFIED BY 'localhostkey'");
@@ -2880,6 +2949,18 @@
     push(@mysql_commands,"DELETE FROM user WHERE host<>'localhost'");
     if (($has_pass) || ($mysql_unix_socket)) {
         if ($dbh) {
+            my $need_mysql_native;
+            if ($check_native_pw) {
+                $need_mysql_native = &check_mysql_native_pw($dbh);
+            }
+            if ($need_mysql_native) {
+                my $result = &set_native_perm($distro,$dbh);
+                if ($result eq 'fail') {
+                    print_and_log(&mt('Problem configuring MySQL.')."\n".
+                                  &mt('Permissions not set.')."\n");
+                    return;
+                }
+            }
             push(@mysql_commands,"FLUSH PRIVILEGES");
             if (@mysql_commands) {
                 foreach my $cmd (@mysql_commands) {
@@ -2925,6 +3006,18 @@
             print_and_log(&mt('Failed to get MySQL root password from user input.')."\n");
         }
         if ($dbh) {
+            my $need_mysql_native;
+            if ($check_native_pw) {
+                $need_mysql_native = &check_mysql_native_pw($dbh);
+            }
+            if ($need_mysql_native) {
+                my $result = &set_native_perm($distro,$dbh);
+                if ($result eq 'fail') {
+                    print_and_log(&mt('Problem configuring MySQL.')."\n".
+                                  &mt('Permissions not set.')."\n");
+                    return;
+                }
+            }
             if (@mysql_commands) {
                 foreach my $cmd (@mysql_commands) {
                     $dbh->do($cmd) || print $dbh->errstr."\n";
@@ -2978,6 +3071,9 @@
         chomp($info);
         close(PIPE);
         ($version,$minorversion,$subversion,$name) = ($info =~ /(\d+)\.(\d+)\.(\d+)(?:\-?(\w*),|)/);
+        if ($name eq '') {
+            $name = 'MySQL';
+        }
     } else {
         print &mt('Could not determine which version of MySQL is installed.').
               "\n";
@@ -2985,6 +3081,218 @@
     return ($version,$minorversion,$subversion,$name);
 }
 
+sub set_native_perm {
+    my ($distro,$dbh) = @_;
+    my ($disconnected,$status);
+    if (ref($dbh)) {
+        if (!$dbh->disconnect) {
+            &print_and_log(&mt('Failed to disconnect from MySQL:')."\n".
+                           $dbh->errstr);
+        } else {
+            $disconnected = 1; 
+        }
+    }
+    my $result = &update_mysql_config($distro);
+    if ($result) {
+        if (open (PIPE, "systemctl restart mysql 2>&1 |")) {
+            while (<PIPE>) {
+                print $_;
+            }
+            close(PIPE);
+        } else {
+            print "\n".&mt('Restarting mysql failed.')."\n".
+                   &mt('Stopping execution of install.pl script.')."\n";
+            $status = 'fail';
+        }
+    } else {
+        $status = 'fail';
+    }
+    if ($disconnected) {
+        my $dsn = "DBI:mysql:database=mysql";
+        my $currpass = &get_mysql_password(&mt('Please enter the password now'));
+        $dbh = DBI->connect($dsn,'root','',{'PrintError'=>0});
+        if ($DBI::err =~ /1045/) {
+            print &mt('You have already set a root password for the MySQL database.')."\n";
+            my $currpass = &get_mysql_password(&mt('Please enter the password now'));
+            $dbh = DBI->connect($dsn,'root',$currpass,{'PrintError'=>0});
+            if ($dbh) {
+                print &mt('Password accepted.')."\n";
+            } else {
+                print &mt('Problem accessing MySQL.')."\n";
+                if ($DBI::err =~ /1045/) {
+                    print &mt('Perhaps the password was incorrect?')."\n";
+                    print &mt('Try again?').' ';
+                    $currpass = &get_mysql_password(&mt('Re-enter password now'));
+                    $dbh = DBI->connect($dsn,'root',$currpass,{'PrintError'=>0});
+                    if ($dbh) {
+                        print &mt('Password accepted.')."\n";
+                    } else {
+                        if ($DBI::err =~ /1045/) {
+                           print &mt('Incorrect password.')."\n";
+                           $status = 'fail';
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return $status;
+}
+
+sub update_mysql_config {
+    my ($distro) = @_;
+    my $mysqlfile = '/etc/my.cnf';
+    my $mysqlconf = [{section =>'mysqld',
+                      key     =>'mysql_native_password=',
+                      value   =>'ON'}];
+    if ($distro =~ /^ubuntu(\d+)$/) {
+        my $version = $1;
+        $mysqlfile = '/etc/mysql/my.cnf';
+        if ($version > 14) {
+            $mysqlfile = '/etc/mysql/mysql.conf.d/mysqld.cnf';
+        }
+    } elsif ($distro =~ /^debian(\d+)$/) {
+        my $version = $1;
+        if ($version >= 10) {
+            $mysqlfile = '/etc/mysql/mariadb.conf.d/50-server.cnf';
+        }
+    }
+    if (-e $mysqlfile) { 
+        my ($filedata) = &parse_config_file($mysqlfile);
+        if (ref($filedata)) {
+            my $modified = 0;
+            foreach my $data (@$mysqlconf) {
+                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($mysqlfile,$filedata);
+                if (defined($result)) { print "**** Error when writing to $mysqlfile : $result\n"; return 0; }
+            }
+            return $modified;
+        } else {
+            print "**** Error: existing data could not be retrived from $mysqlfile\n";
+            return 0;
+        }
+    } else {
+        print "**** Error: $mysqlfile does not exist\n";
+    }
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
 sub check_systemd_update {
     my ($distro) = @_;
     my ($use_systemctl,$service);


More information about the LON-CAPA-cvs mailing list