From c14e2ba478436dbf39d1194d51ba6b3cb084261a Mon Sep 17 00:00:00 2001
From: Ludwig Krispenz <lkrispen@redhat.com>
Date: Wed, 17 Feb 2016 14:04:09 +0100
Subject: [PATCH] Ticket 48402 - v2 allow plugins to detect a restore or import

        Bug Description: RFE

        Fix Description: see: http://www.port389.org/docs/389ds/design/detect-startup-after-import-or-restore.html

        https://fedorahosted.org/389/ticket/48402

        Reviewed by: ?
---
 ldap/servers/slapd/back-ldbm/archive.c         |   9 ++
 ldap/servers/slapd/back-ldbm/dblayer.c         | 166 +++++++++++++++++++++++++
 ldap/servers/slapd/back-ldbm/instance.c        |  12 +-
 ldap/servers/slapd/back-ldbm/ldif2ldbm.c       |  14 ++-
 ldap/servers/slapd/back-ldbm/proto-back-ldbm.h |   9 ++
 ldap/servers/slapd/slapi-plugin.h              |   2 +
 6 files changed, 210 insertions(+), 2 deletions(-)

diff --git a/ldap/servers/slapd/back-ldbm/archive.c b/ldap/servers/slapd/back-ldbm/archive.c
index 6b56937..64d558c 100644
--- a/ldap/servers/slapd/back-ldbm/archive.c
+++ b/ldap/servers/slapd/back-ldbm/archive.c
@@ -69,6 +69,12 @@ int ldbm_back_archive2ldbm( Slapi_PBlock *pb )
     if (run_from_cmdline) {
         mapping_tree_init();
         ldbm_config_load_dse_info(li);
+
+        /* initialize a restore file to be able to detect a startup after restore */
+        if (dblayer_restore_file_init(li)) {
+            LDAPDebug0Args(LDAP_DEBUG_ANY, "archive2db: failed to write restore file.\n");
+            return -1;
+        }
     } 
     if (backendname) {
         inst = ldbm_instance_find_by_name(li, backendname);
@@ -249,6 +255,9 @@ int ldbm_back_archive2ldbm( Slapi_PBlock *pb )
         }
     }
 out:
+    if (run_from_cmdline && ( 0 == return_value )) {
+        dblayer_restore_file_update(li, directory);
+    }
     slapi_ch_free_string(&directory);
     return return_value;
 }
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c
index e65f3cf..d782265 100644
--- a/ldap/servers/slapd/back-ldbm/dblayer.c
+++ b/ldap/servers/slapd/back-ldbm/dblayer.c
@@ -204,6 +204,8 @@ static void dblayer_push_pvt_txn(back_txn *txn);
 static back_txn *dblayer_get_pvt_txn();
 static void dblayer_pop_pvt_txn();
 
+static int dblayer_post_restore = 0;
+
 #define MEGABYTE (1024 * 1024)
 #define GIGABYTE (1024 * MEGABYTE)
 
@@ -1589,6 +1591,9 @@ dblayer_start(struct ldbminfo *li, int dbmode)
             /* The error message was output by read_metadata() */
             return -1;
         }
+        if (dblayer_restore_file_check(li)) {
+            dblayer_set_restored();
+        }
     }
 
     dblayer_free_env(&priv->dblayer_env);
@@ -7208,6 +7213,167 @@ error_out:
     return return_value;
 }
 
+static char *
+dblayer_import_file_name(ldbm_instance *inst)
+{
+    char *fname = slapi_ch_smprintf("%s/.import_%s",
+                                    inst->inst_parent_dir_name,
+                                    inst->inst_dir_name);
+    return fname;
+}
+
+static char *
+dblayer_restore_file_name(struct ldbminfo *li)
+{
+    char *fname = slapi_ch_smprintf("%s/../.restore", li->li_directory);
+
+    return fname;
+}
+
+static int
+dblayer_file_open(char *fname, int flags, int mode, PRFileDesc **prfd)
+{
+    int rc = 0;
+    *prfd = PR_Open(fname, flags, mode );
+
+    if (NULL == *prfd) rc = PR_GetError();
+    if (rc && rc != PR_FILE_NOT_FOUND_ERROR ) {
+        LDAPDebug(LDAP_DEBUG_ANY,
+                       "Failed to open file: %s, error: (%d) %s\n",
+                       fname, rc, slapd_pr_strerror(rc));
+    }
+    return rc;
+}
+
+int
+dblayer_import_file_init(ldbm_instance *inst)
+{
+    int rc = -1;
+    PRFileDesc *prfd = NULL;
+    char *fname = dblayer_import_file_name(inst);
+    rc = dblayer_file_open(fname, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, inst->inst_li->li_mode, &prfd);
+    if (prfd) {
+        PR_Close(prfd);
+        rc = 0;
+    }
+    slapi_ch_free_string(&fname);
+    return rc;
+}
+
+int
+dblayer_restore_file_init(struct ldbminfo *li)
+{
+    int rc = -1;
+    PRFileDesc *prfd;
+    char *fname = dblayer_restore_file_name(li);
+    rc = dblayer_file_open(fname, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, li->li_mode, &prfd);
+    if (prfd) {
+        PR_Close(prfd);
+        rc = 0;
+    }
+    slapi_ch_free_string(&fname);
+    return rc;
+
+}
+void
+dblayer_import_file_update(ldbm_instance *inst)
+{
+    int rc;
+    PRFileDesc *prfd;
+    char *fname = dblayer_import_file_name(inst);
+    dblayer_file_open(fname, PR_RDWR, inst->inst_li->li_mode, &prfd);
+
+    if (prfd) {
+        char *line = slapi_ch_smprintf("import of %s succeeded",inst->inst_dir_name);
+        slapi_write_buffer(prfd, line, strlen(line));
+        slapi_ch_free_string(&line);
+        PR_Close(prfd);
+    }
+    slapi_ch_free_string(&fname);
+}
+
+int
+dblayer_file_check(char *fname, int mode)
+{
+    int rc = 0;
+    int err;
+    PRFileDesc *prfd;
+    err = dblayer_file_open(fname, PR_RDWR, mode, &prfd);
+
+    if (prfd) {
+        /* file exists, additional check on size */
+        PRFileInfo64 prfinfo;
+        rc = 1;
+        /* read it */
+        err = PR_GetOpenFileInfo64(prfd, &prfinfo);
+        if (err == PR_SUCCESS && 0 == prfinfo.size) {
+            /* it is empty restore or import has failed */
+            LDAPDebug1Arg(LDAP_DEBUG_ANY,
+                       "Previous import or restore failed, file: %s is empty\n", fname);
+        }
+        PR_Close(prfd);
+        PR_Delete(fname);
+    } else {
+        if (PR_FILE_NOT_FOUND_ERROR == err) {
+            rc = 0;
+        } else {
+            /* file exists, but we cannot open it */
+            rc = 1;
+            /* error is already looged try to delete it*/
+            PR_Delete(fname);
+        }
+    }
+
+    return rc;
+
+}
+int
+dblayer_import_file_check(ldbm_instance *inst)
+{
+    int rc;
+    char *fname = dblayer_import_file_name(inst);
+    rc = dblayer_file_check(fname, inst->inst_li->li_mode);
+    slapi_ch_free_string(&fname);
+    return rc;
+}
+
+int
+dblayer_restore_file_check(struct ldbminfo *li)
+{
+    int rc;
+    char *fname = dblayer_restore_file_name(li);
+    rc = dblayer_file_check(fname, li->li_mode);
+    slapi_ch_free_string(&fname);
+    return rc;
+}
+
+void
+dblayer_restore_file_update(struct ldbminfo *li, char *directory)
+{
+    int rc;
+    PRFileDesc *prfd;
+    char *fname = dblayer_restore_file_name(li);
+    dblayer_file_open(fname, PR_RDWR, li->li_mode, &prfd);
+    if (prfd) {
+        char *line = slapi_ch_smprintf("restore of %s succeeded", directory);
+        slapi_write_buffer(prfd, line, strlen(line));
+        slapi_ch_free_string(&line);
+        PR_Close(prfd);
+    }
+}
+
+int
+dblayer_is_restored(void)
+{
+    return dblayer_post_restore;
+}
+
+void
+dblayer_set_restored(void)
+{
+    dblayer_post_restore = 1;
+}
+
 /*
  * inst_dir_name is a relative path  (from 6.21)
  *     ==> txn log stores relative paths and becomes relocatable
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
index 53d922e..f059e8a 100644
--- a/ldap/servers/slapd/back-ldbm/instance.c
+++ b/ldap/servers/slapd/back-ldbm/instance.c
@@ -299,7 +299,16 @@ ldbm_instance_stop(backend *be)
 
     return rc;
 }
-
+static void
+ldbm_instance_set_flags(ldbm_instance *inst)
+{
+    if (dblayer_is_restored()) {
+        slapi_be_set_flag(inst->inst_be, SLAPI_BE_FLAG_POST_RESTORE);
+    }
+    if (dblayer_import_file_check(inst)) {
+        slapi_be_set_flag(inst->inst_be, SLAPI_BE_FLAG_POST_IMPORT);
+    }
+}
 
 /* Walks down the set of instances, starting each one. */
 int 
@@ -313,6 +322,7 @@ ldbm_instance_startall(struct ldbminfo *li)
     while (inst_obj != NULL)  {
         int rc1;
         inst = (ldbm_instance *) object_get_data(inst_obj);
+        ldbm_instance_set_flags(inst);
         rc1 = ldbm_instance_start(inst->inst_be);
     if (rc1 != 0) {
         rc = rc1;
diff --git a/ldap/servers/slapd/back-ldbm/ldif2ldbm.c b/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
index 150a008..8830d03 100644
--- a/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
+++ b/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
@@ -690,6 +690,14 @@ int ldbm_back_ldif2ldbm( Slapi_PBlock *pb )
         return -1;
     }
 
+    if ((task_flags & SLAPI_TASK_RUNNING_FROM_COMMANDLINE)) {
+        if (dblayer_import_file_init(inst)) {
+            LDAPDebug0Args(LDAP_DEBUG_ANY,
+                    "ldbm_back_ldif2ldbm: failed to write import file\n");
+            return -1;
+        }
+    }
+
     /***** prepare & init libdb and dblayer *****/
 
     if (! (task_flags & SLAPI_TASK_RUNNING_FROM_COMMANDLINE)) {
@@ -766,7 +774,11 @@ int ldbm_back_ldif2ldbm( Slapi_PBlock *pb )
 
     /* always use "new" import code now */
     slapi_pblock_set(pb, SLAPI_BACKEND, inst->inst_be);
-    return ldbm_back_ldif2ldbm_deluxe(pb);
+    ret = ldbm_back_ldif2ldbm_deluxe(pb);
+    if ((ret == 0) && (task_flags & SLAPI_TASK_RUNNING_FROM_COMMANDLINE)) {
+        dblayer_import_file_update(inst);
+    }
+    return ret;
 
 fail:
     /* DON'T enable the backend -- leave it offline */
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
index 37f9f20..c366297 100644
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
@@ -170,6 +170,15 @@ int ldbm_back_get_info(Slapi_Backend *be, int cmd, void **info);
 int ldbm_back_set_info(Slapi_Backend *be, int cmd, void *info);
 int ldbm_back_ctrl_info(Slapi_Backend *be, int cmd, void *info);
 
+int dblayer_is_restored(void);
+void dblayer_set_restored(void);
+int dblayer_restore_file_check(struct ldbminfo *li);
+int dblayer_restore_file_init(struct ldbminfo *li);
+void dblayer_restore_file_update(struct ldbminfo *li, char *directory);
+int dblayer_import_file_init(ldbm_instance *inst);
+void dblayer_import_file_update(ldbm_instance *inst);
+int dblayer_import_file_check(ldbm_instance *inst);
+
 /*
  * dn2entry.c
  */
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index d2b076b..0d37741 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -6416,6 +6416,8 @@ void slapi_be_set_flag(Slapi_Backend * be, int flag);
 void slapi_be_unset_flag(Slapi_Backend * be, int flag);
 #define SLAPI_BE_FLAG_REMOTE_DATA   0x1  /* entries held by backend are remote */
 #define SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST   0x10  /* force to call filter_test (search only) */
+#define SLAPI_BE_FLAG_POST_IMPORT   0x100  /* backend was imported */
+#define SLAPI_BE_FLAG_POST_RESTORE   0x200  /* startup after restore */
 
 
 /* These functions allow a plugin to register for callback when
-- 
2.4.3

