From 1cd8d7f483aa9bd93000136601dfa5d6b597cb46 Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmeggins@redhat.com>
Date: Mon, 23 Jan 2012 13:12:05 -0700
Subject: [PATCH] Ticket #264 - upgrade needs better check for "server is running"

https://fedorahosted.org/389/ticket/264
Resolves: Ticket #264
Bug Description: upgrade needs better check for "server is running"
Reviewed by: ???
Branch: master
Fix Description: The database upgrade tasks need the server to be offline,
and they need for the upgrade mode to be offline.  These upgrade scripts
check for the presence of the .pid file.  If the file is present, upgrade
assumes the server is running, and skips tasks that require the server to
be offline.  However, under certain conditions (systemd) the .pid files
are left over even after the server is shutdown.  This adds an explicit test
to see if the pid in the pidfile is running.  Note that when pids are
recycled, we could still get a false positive - there could be another
process with the same pid.  In this case, the user will have to manually
remove the .pid files and re-run setup-ds.pl -u.
Platforms tested: RHEL6 x86_64
Flag Day: no
Doc impact: no
---
 ldap/admin/src/scripts/70upgradefromldif.pl |    8 +-----
 ldap/admin/src/scripts/80upgradednformat.pl |   10 ++++----
 ldap/admin/src/scripts/81changelog.pl       |    7 +++++-
 ldap/admin/src/scripts/90subtreerename.pl   |   15 ++++++------
 ldap/admin/src/scripts/91subtreereindex.pl  |   11 ++++-----
 ldap/admin/src/scripts/DSUpdate.pm.in       |   28 +++++++++++++++++++++-
 ldap/admin/src/scripts/DSUtil.pm.in         |   32 +++++++++++++++++++++++++-
 ldap/admin/src/scripts/setup-ds.res.in      |    1 +
 8 files changed, 82 insertions(+), 30 deletions(-)

diff --git a/ldap/admin/src/scripts/70upgradefromldif.pl b/ldap/admin/src/scripts/70upgradefromldif.pl
index e8b968d..daeed66 100644
--- a/ldap/admin/src/scripts/70upgradefromldif.pl
+++ b/ldap/admin/src/scripts/70upgradefromldif.pl
@@ -4,7 +4,7 @@ use Mozilla::LDAP::Utils qw(normalizeDN);
 use Mozilla::LDAP::API qw(:constant ldap_url_parse ldap_explode_dn);
 use File::Basename;
 use File::Copy;
-use DSUtil qw(debug);
+use DSUtil qw(debug serverIsRunning);
 
 # Used to upgrade from an older version whose database might not be
 # compatible - also for an upgrade from a machine of a different
@@ -32,12 +32,8 @@ sub runinst {
     }
     my $rundir = $config_entry->getValues('nsslapd-rundir');
     my $instdir = $config_entry->getValues('nsslapd-instancedir');
-    my $isrunning = 0;
     # Check if the server is up or not
-    my $pidfile = $rundir . "/" . $inst . ".pid";
-    if (-e $pidfile) {
-        $isrunning = 1;
-    }
+    my $isrunning = serverIsRunning($rundir, $inst);
 
     for my $file (glob("$ldifdir/*.upgrade.ldif")) {
         # assumes file name is backendname.upgrade.ldif
diff --git a/ldap/admin/src/scripts/80upgradednformat.pl b/ldap/admin/src/scripts/80upgradednformat.pl
index 1bcebba..092b7cd 100644
--- a/ldap/admin/src/scripts/80upgradednformat.pl
+++ b/ldap/admin/src/scripts/80upgradednformat.pl
@@ -3,6 +3,7 @@ use Mozilla::LDAP::Utils qw(normalizeDN);
 use Mozilla::LDAP::API qw(:constant ldap_url_parse ldap_explode_dn);
 use File::Basename;
 use File::Copy;
+use DSUpdate qw(isOffline);
 
 # Upgrade DN format if needed.
 # For each backend instance, 
@@ -22,13 +23,12 @@ sub runinst {
     if (!$config_entry) {
         return ("error_no_configuration_entry", $!);
     }
-    # First, check if the server is up or down.
-    my $rundir = $config_entry->getValues('nsslapd-rundir');
 
     # Check if the server is up or not
-    my $pidfile = $rundir . "/" . $inst . ".pid";
-    if (-e $pidfile) {
-        return (); # server is running; do nothing.
+    my $rc;
+    ($rc, @errs) = isOffline($inf, $inst, $conn);
+    if (!$rc) {
+        return @errs;
     }
     my $mappingtree = "cn=mapping tree,cn=config";
     my $ldbmbase = "cn=ldbm database,cn=plugins,cn=config";
diff --git a/ldap/admin/src/scripts/81changelog.pl b/ldap/admin/src/scripts/81changelog.pl
index 205700a..b1e399a 100644
--- a/ldap/admin/src/scripts/81changelog.pl
+++ b/ldap/admin/src/scripts/81changelog.pl
@@ -1,4 +1,5 @@
 use Mozilla::LDAP::Conn;
+use DSUpdate qw(isOffline);
 
 # Cleanup local changelog db
 # If changelog db exists, run db_checkpoint to flush the transaction logs.
@@ -6,7 +7,7 @@ use Mozilla::LDAP::Conn;
 sub runinst {
     my ($inf, $inst, $dseldif, $conn) = @_;
 
-    my @errs;
+    my @errs, $rc;
 
     my $config = "cn=changelog5,cn=config";
     my $config_entry = $conn->search($config, "base", "(cn=*)");
@@ -15,6 +16,10 @@ sub runinst {
         return ();
     }
     # First, check if the server is up or down.
+    ($rc, @errs) = isOffline($inf, $inst, $conn);
+    if (!$rc) {
+        return @errs;
+    }
     my $changelogdir = $config_entry->getValues('nsslapd-changelogdir');
 
     # Run db_checkpoint
diff --git a/ldap/admin/src/scripts/90subtreerename.pl b/ldap/admin/src/scripts/90subtreerename.pl
index d9e1d37..6c90f0d 100644
--- a/ldap/admin/src/scripts/90subtreerename.pl
+++ b/ldap/admin/src/scripts/90subtreerename.pl
@@ -1,24 +1,23 @@
 use Mozilla::LDAP::Conn;
 use Mozilla::LDAP::Utils qw(normalizeDN);
 use Mozilla::LDAP::API qw(:constant ldap_url_parse ldap_explode_dn);
+use DSUpdate qw(isOffline);
 
 sub runinst {
     my ($inf, $inst, $dseldif, $conn) = @_;
 
-    my @errs;
+    my $rc, @errs;
+
+    ($rc, @errs) = isOffline($inf, $inst, $conn);
+    if (!$rc) {
+        return @errs;
+    }
 
     my $ent0 = $conn->search("cn=config", "base", "(objectclass=*)");
     if (!$ent0) {
         return ('error_finding_config_entry', 'cn=config',
                 $conn->getErrorString());
     }
-    my $rundir = $ent0->getValues('nsslapd-rundir');
-
-    # Check if the server is up or not
-    my $pidfile = $rundir . "/" . $inst . ".pid";
-    if (-e $pidfile) {
-        return (); # server is running; do nothing.
-    }
 
     my $ent1 = $conn->search("cn=config,cn=ldbm database,cn=plugins,cn=config",
                             "base", "(objectclass=*)");
diff --git a/ldap/admin/src/scripts/91subtreereindex.pl b/ldap/admin/src/scripts/91subtreereindex.pl
index 1e0e30d..f5aee9f 100644
--- a/ldap/admin/src/scripts/91subtreereindex.pl
+++ b/ldap/admin/src/scripts/91subtreereindex.pl
@@ -1,11 +1,12 @@
 use Mozilla::LDAP::Conn;
 use Mozilla::LDAP::Utils qw(normalizeDN);
 use Mozilla::LDAP::API qw(:constant ldap_url_parse ldap_explode_dn);
+use DSUpdate qw(isOffline);
 
 sub runinst {
     my ($inf, $inst, $dseldif, $conn) = @_;
 
-    my @errs = ();
+    my $rc, @errs;
 
     my $config = $conn->search("cn=config", "base", "(objectclass=*)");
     if (!$config) {
@@ -13,12 +14,10 @@ sub runinst {
                      $conn->getErrorString()];
         return @errs;
     }
-    my $rundir = $config->getValues('nsslapd-rundir');
 
-    # Check if the server is up or not
-    my $pidfile = $rundir . "/" . $inst . ".pid";
-    if (-e $pidfile) {
-        return (); # server is running; do nothing.
+    ($rc, @errs) = isOffline($inf, $inst, $conn);
+    if (!$rc) {
+        return @errs;
     }
 
     my $dbconf = $conn->search("cn=config,cn=ldbm database,cn=plugins,cn=config", "base", "(objectclass=*)");
diff --git a/ldap/admin/src/scripts/DSUpdate.pm.in b/ldap/admin/src/scripts/DSUpdate.pm.in
index f1ab1b1..ecdfeb7 100644
--- a/ldap/admin/src/scripts/DSUpdate.pm.in
+++ b/ldap/admin/src/scripts/DSUpdate.pm.in
@@ -60,8 +60,8 @@ use Mozilla::LDAP::LDIF;
 
 use Exporter;
 @ISA       = qw(Exporter);
-@EXPORT    = qw(updateDS);
-@EXPORT_OK = qw(updateDS);
+@EXPORT    = qw(updateDS isOffline);
+@EXPORT_OK = qw(updateDS isOffline);
 
 use strict;
 
@@ -513,6 +513,30 @@ sub initInfFromInst {
     return setDefaults($inf);
 }
 
+# check to see if the user has chosen offline mode and the server is really offline
+sub isOffline {
+    my ($inf, $inst, $conn) = @_;
+
+    if ($inf->{General}->{UpdateMode} !~ /offline/i) {
+        debug(3, "UpdateMode " . $inf->{General}->{UpdateMode} . " is not offline\n");
+        return 0;
+    }
+
+    # mode is offline - see if server is really offline
+    my $config = $conn->search("cn=config", "base", "(objectclass=*)");
+    if (!$config) {
+        return 0, ['error_finding_config_entry', 'cn=config',
+                   $conn->getErrorString()];
+    }
+    my $rundir = $config->getValues('nsslapd-rundir');
+
+    if (serverIsRunning($rundir, $inst)) {
+        return 0, ['error_update_not_offline', $inst];
+    }
+
+    return 1; # server is offline
+}
+
 1;
 
 # emacs settings
diff --git a/ldap/admin/src/scripts/DSUtil.pm.in b/ldap/admin/src/scripts/DSUtil.pm.in
index 16f46b6..4ee6af0 100644
--- a/ldap/admin/src/scripts/DSUtil.pm.in
+++ b/ldap/admin/src/scripts/DSUtil.pm.in
@@ -49,12 +49,12 @@ require Exporter;
                 process_maptbl check_and_add_entry getMappedEntries addErr
                 getHashedPassword debug createInfFromConfig shellEscape
                 isValidServerID isValidUser isValidGroup makePaths getLogin getGroup
-                remove_tree remove_pidfile setDebugLog checkHostname);
+                remove_tree remove_pidfile setDebugLog checkHostname serverIsRunning);
 @EXPORT_OK = qw(portAvailable getAvailablePort isValidDN addSuffix getMappedEntries
                 process_maptbl check_and_add_entry getMappedEntries addErr
                 getHashedPassword debug createInfFromConfig shellEscape
                 isValidServerID isValidUser isValidGroup makePaths getLogin getGroup
-                remove_tree remove_pidfile setDebugLog checkHostname);
+                remove_tree remove_pidfile setDebugLog checkHostname serverIsRunning);
 
 use strict;
 
@@ -1055,6 +1055,34 @@ sub remove_pidfile
     }
 }
 
+sub serverIsRunning
+{
+    my ($run_dir, $inst) = @_;
+    my $pidfile = $run_dir . "/" . $inst . ".pid";
+    if ( -e $pidfile ) {
+        if (!open(PIDFILE, $pidfile)) {
+            debug(3, "Could not open pidfile $pidfile - $! - assume server is not running\n");
+            return 0; # could not open pid file - assume server is not running
+        }
+        my $pid = <PIDFILE>;
+        chomp($pid);
+        close(PIDFILE);
+        if (!$pid) {
+            debug(3, "Bogus pid $pid found in pidfile $pidfile - assume server is not running\n");
+            return 0; # could not open pid file - assume server is not running
+        }
+        if (kill(0, $pid)) {
+            debug(3, "pid $pid from file $pidfile is running\n");
+            return 1; # server is running
+        }
+        debug(3, "pid $pid from file $pidfile is not running - could not kill 0 - $!\n");
+    } else {
+        debug(3, "No such file pidfile $pidfile - $! - assume server is not running\n");
+    }
+
+    return 0; # no pid file - assume not running
+}
+
 1;
 
 # emacs settings
diff --git a/ldap/admin/src/scripts/setup-ds.res.in b/ldap/admin/src/scripts/setup-ds.res.in
index 2747703..a8a0c00 100644
--- a/ldap/admin/src/scripts/setup-ds.res.in
+++ b/ldap/admin/src/scripts/setup-ds.res.in
@@ -201,3 +201,4 @@ error_linking_file = Error: could not link '%s' to '%s': %s
 error_running_command = Error: command '%s' failed - output [%s] error [%s]
 error_opening_file = Opening file '%s' failed.  Error: %s\n
 error_format_error = '%s' has invalid format.\n
+error_update_not_offline = Error: offline mode selected but the server [%s] is still running.\n
-- 
1.7.1

