From 46440b97bc505077200ce0c2cf1281dfdbda1d71 Mon Sep 17 00:00:00 2001
From: Ondrej Kos <okos@redhat.com>
Date: Thu, 8 Nov 2012 14:34:36 +0100
Subject: [PATCH] Display more information on DB version crash

https://fedorahosted.org/sssd/ticket/1589

Added check for determining, whether database version is higher or
lower than expected. To distinguish it from other errors it uses
following retun values (further used for appropriate error message):
EMEDIUMTYPE for lower version than expected
EUCLEAN for higher version than expected

When SSSD or one of it's tools fails on DB version mismatch, new error
message is showed suggesting how to proceed.
---
 src/db/sysdb.c                          |  4 +-
 src/monitor/monitor.c                   |  1 +
 src/responder/common/responder_common.c |  1 +
 src/tools/sss_cache.c                   |  1 +
 src/tools/sss_seed.c                    |  1 +
 src/tools/tools_util.c                  |  1 +
 src/util/util.c                         | 82 +++++++++++++++++++++++++++++++++
 src/util/util.h                         | 13 ++++++
 8 files changed, 102 insertions(+), 2 deletions(-)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index 9685163b3f00cc2a45617072efa353589de111ce..ee359a01447f974b54e6d748ae5b191dba9441fa 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -1037,7 +1037,7 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
             if (!allow_upgrade) {
                 DEBUG(0, ("Wrong DB version (got %s expected %s)\n",
                           version, SYSDB_VERSION));
-                ret = EINVAL;
+                ret = sss_db_version_check(SYSDB_VERSION, version);
                 goto done;
             }
 
@@ -1136,7 +1136,7 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
 
         DEBUG(0,("Unknown DB version [%s], expected [%s] for domain %s!\n",
                  version?version:"not found", SYSDB_VERSION, domain->name));
-        ret = EINVAL;
+        ret = sss_db_version_check(SYSDB_VERSION, version);
         goto done;
     }
 
diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 956efb13ceb29a9361e60526e934883e0ecb5a16..29d4222d2eb6403c0018fa2c0180cc1dacbbf14a 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -2113,6 +2113,7 @@ int monitor_process_init(struct mt_ctx *ctx,
     }
     ret = sysdb_init(tmp_ctx, ctx->cdb, NULL, true, &db_list);
     if (ret != EOK) {
+        SSS_DB_MIS_CHECK(ret, false);
         return ret;
     }
     talloc_zfree(tmp_ctx);
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index d9f73fe2698cd7eacfb1a589c159e5cb037dab64..eb528d2d854ffb0159ef5a8f9f628ca4be0614b5 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -839,6 +839,7 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
 
     ret = sysdb_init(rctx, cdb, NULL, false, &rctx->db_list);
     if (ret != EOK) {
+        SSS_DB_MIS_CHECK(ret, false);
         DEBUG(0, ("fatal error initializing resp_ctx\n"));
         return ret;
     }
diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index 06b0099dc642812752949aa6cb8781ac1349ad90..bd5edbc89a06cd5dbab814fdbc7cceb541b52382 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -339,6 +339,7 @@ errno_t init_domains(struct cache_tool_ctx *ctx, const char *domain)
         ret = sysdb_init_domain_and_sysdb(ctx, ctx->confdb, domain, DB_PATH,
                                           &ctx->domains, &db_ctx);
         if (ret != EOK) {
+            SSS_DB_MIS_CHECK(ret, true);
             DEBUG(1, ("Could not initialize connection to the sysdb\n"));
             goto fail;
         }
diff --git a/src/tools/sss_seed.c b/src/tools/sss_seed.c
index cd1b263863e44411e7aac8772c3922178ad9dca4..ae80f43f75ce1e5a8ab5bf3c30d54fef15ccdf91 100644
--- a/src/tools/sss_seed.c
+++ b/src/tools/sss_seed.c
@@ -631,6 +631,7 @@ static int seed_init_db(TALLOC_CTX *mem_ctx,
     ret = sysdb_init_domain_and_sysdb(tmp_ctx, confdb, domain_name,
                                       DB_PATH, &domain, &sysdb);
     if (ret != EOK) {
+        SSS_DB_MIS_CHECK(ret, true);
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Could not initialize connection to domain '%s' in sysdb.%s\n",
                domain_name, ret == ENOENT ? " Domain not found." : ""));
diff --git a/src/tools/tools_util.c b/src/tools/tools_util.c
index 99b79f171d82cde5f1c58988949422981e4d5ee3..649b8eb9f96963d575662cb8b62dadb607a8b3b1 100644
--- a/src/tools/tools_util.c
+++ b/src/tools/tools_util.c
@@ -57,6 +57,7 @@ static int setup_db(struct tools_ctx *ctx)
     ret = sysdb_init_domain_and_sysdb(ctx, ctx->confdb, "local", DB_PATH,
                                       &ctx->local, &ctx->sysdb);
     if (ret != EOK) {
+        SSS_DB_MIS_CHECK(ret, true);
         DEBUG(1, ("Could not initialize connection to the sysdb\n"));
         return ret;
     }
diff --git a/src/util/util.c b/src/util/util.c
index b812ef1b118803129caae25247efcbc1194b22f5..9f6b95314943b40f8be1facd4e32fc2c4651f3b5 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -634,3 +634,85 @@ remove_ipv6_brackets(char *ipv6addr)
 
     return EOK;
 }
+
+double
+sss_db_version_str_to_num(char *str)
+{
+    int i;
+    char *c;
+    bool sep_occured = false;
+
+    for (i = 0, c = str; c[i]; i++) {
+        if (c[i] == ',' || c[i] == '.') {
+            if (sep_occured) {
+                return NAN;
+            }
+            c[i] = '.';
+            sep_occured = true;
+        } else if (!isdigit(c[i])) {
+            return NAN;
+        }
+    }
+    return atof(str);
+}
+
+errno_t
+sss_db_version_check(const char *expected,
+                     const char *received)
+{
+
+    double expected_n, received_n;
+    char *expected_s, *received_s;
+
+    expected_s = talloc_strdup(NULL, expected);
+    if (!expected_s) {
+        return ENOMEM;
+    }
+    expected_n = sss_db_version_str_to_num(expected_s);
+    talloc_free(expected_s);
+    if (expected_n == NAN) {
+        return EINVAL;
+    }
+
+    received_s = talloc_strdup(NULL, received);
+    if (!received_s) {
+        return ENOMEM;
+    }
+    received_n = sss_db_version_str_to_num(received_s);
+    talloc_free(received_s);
+    if (received_n == NAN) {
+        return EINVAL;
+    }
+
+    if (received_n > expected_n) {
+        return EUCLEAN;
+    } else {
+        return EMEDIUMTYPE;
+    }
+
+    return EOK;
+}
+
+/* Print out database version mismatch error message
+ * according to expected one and environment */
+void
+sss_db_version_mismatch(errno_t equ_type,
+                        bool in_tool)
+{
+    ERROR("DB version mismatch!\n");
+    switch (equ_type) {
+        case EUCLEAN:
+            ERROR("Lower version of database is expected.\n");
+            break;
+        case EMEDIUMTYPE:
+            ERROR("Higher version of database is expected. %s\n",
+                    in_tool ? "You could try running SSSD, which should "
+                    "handle the upgrade itself." : "");
+            break;
+        default:
+            break;
+    }
+    ERROR("Removing cached files in /var/run/sss/db/ should fix the issue, "
+            "but be aware, that deleting cache files also removes all of your "
+            "cached credentials.\n");
+}
diff --git a/src/util/util.h b/src/util/util.h
index b6ecfc2c952bd6a37f49194d961b3f762c9546e7..e29c30ae364b714160b83ae74595d9218aafc0d4 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -216,6 +216,10 @@ errno_t set_debug_file_from_fd(const int fd);
 #define NULL 0
 #endif
 
+#ifndef NAN
+#define NAN (0.0)/(0.0)
+#endif /*NAN*/
+
 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
 
 #define EOK 0
@@ -360,6 +364,15 @@ void talloc_log_fn(const char *msg);
 
 void sss_log(int priority, const char *format, ...);
 
+errno_t sss_db_version_check(const char *expected, const char *received);
+
+void sss_db_version_mismatch(errno_t equ_type, bool in_tool);
+
+#define SSS_DB_MIS_CHECK(ret, in_tool) \
+    if (ret == EMEDIUMTYPE || ret == EUCLEAN) { \
+        sss_db_version_mismatch(ret, in_tool); \
+    }
+
 /* from server.c */
 struct main_context {
     struct tevent_context *event_ctx;
-- 
1.7.11.7

