URL: https://github.com/SSSD/sssd/pull/812
Author: jhrozek
 Title: #812: Implement background refresh for IPA and AD domains and subdomains
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/812/head:pr812
git checkout pr812
From fda2b29f6cfbcd890d47bd1b5716184c0c3acfb3 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 28 May 2019 14:56:05 +0200
Subject: [PATCH 01/12] SYSDB: Add sysdb_search_with_ts_attr

Adds a new public sysdb call sysdb_search_with_ts_attr() that allows to
search on the timestamp cache attributes, but merge back persistent
cache attributes. The converse also works, when searching the persistent
cache the timestamp attributes or even entries matches only in the
timestamp cache are merged.

What does not work is AND-ed complex filter that contains both
attributes from the timestamp cache and the persistent cache because
the searches use the same filter, which doesn't match. We would need to
decompose the filter ourselves.

Because matching and merging the results can be time-consuming, two
flags are provided:
    SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER that only searches the timestamp
    cache, but merges back the corresponding entries from the persistent
    cache
    SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER that only searches the
    persistent cache but merges back the attributes from the timestamp
    cache

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/db/sysdb.h                         |  12 ++
 src/db/sysdb_ops.c                     |  16 +-
 src/db/sysdb_private.h                 |  10 ++
 src/db/sysdb_search.c                  | 231 +++++++++++++++++++++++--
 src/tests/cmocka/test_sysdb_ts_cache.c | 198 +++++++++++++++++++++
 5 files changed, 446 insertions(+), 21 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 89b0d95715..28801e0302 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -1181,6 +1181,18 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx,
                        size_t *msgs_count,
                        struct ldb_message ***msgs);
 
+#define SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER     0x0001
+#define SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER  0x0002
+
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *domain,
+                                  struct ldb_dn *base_dn,
+                                  enum ldb_scope scope,
+                                  int optflags,
+                                  const char *filter,
+                                  const char *attrs[],
+                                  struct ldb_result **_result);
+
 int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx,
                                     struct sss_domain_info *domain,
                                     const char *sub_filter,
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 59fb227a41..55ba62140a 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -261,14 +261,14 @@ int sysdb_delete_recursive(struct sysdb_ctx *sysdb,
 
 /* =Search-Entry========================================================== */
 
-static int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
-                                    struct ldb_context *ldb,
-                                    struct ldb_dn *base_dn,
-                                    enum ldb_scope scope,
-                                    const char *filter,
-                                    const char **attrs,
-                                    size_t *_msgs_count,
-                                    struct ldb_message ***_msgs)
+int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
+                             struct ldb_context *ldb,
+                             struct ldb_dn *base_dn,
+                             enum ldb_scope scope,
+                             const char *filter,
+                             const char **attrs,
+                             size_t *_msgs_count,
+                             struct ldb_message ***_msgs)
 {
     TALLOC_CTX *tmp_ctx;
     struct ldb_result *res;
diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
index 58544d8260..53603b30e2 100644
--- a/src/db/sysdb_private.h
+++ b/src/db/sysdb_private.h
@@ -252,6 +252,16 @@ errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
 struct ldb_result *sss_merge_ldb_results(struct ldb_result *res,
                                          struct ldb_result *subres);
 
+/* Search Entry in an ldb cache */
+int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
+                             struct ldb_context *ldb,
+                             struct ldb_dn *base_dn,
+                             enum ldb_scope scope,
+                             const char *filter,
+                             const char **attrs,
+                             size_t *_msgs_count,
+                             struct ldb_message ***_msgs);
+
 /* Search Entry in the timestamp cache */
 int sysdb_search_ts_entry(TALLOC_CTX *mem_ctx,
                           struct sysdb_ctx *sysdb,
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index f0918bf9a3..a71c43112e 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -68,6 +68,29 @@ static errno_t merge_ts_attr(struct ldb_message *ts_msg,
     return EOK;
 }
 
+static errno_t merge_all_ts_attrs(struct ldb_message *ts_msg,
+                                  struct ldb_message *sysdb_msg,
+                                  const char *want_attrs[])
+{
+    int ret;
+
+    /* Deliberately start from 2 in order to not merge
+     * objectclass/objectcategory and avoid breaking MPGs where the OC might
+     * be made up
+     */
+    for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
+        ret = merge_ts_attr(ts_msg, sysdb_msg,
+                            sysdb_ts_cache_attrs[c], want_attrs);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Cannot merge ts attr %s\n", sysdb_ts_cache_attrs[c]);
+            return ret;
+        }
+    }
+
+    return EOK;
+}
+
 static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
                                   struct ldb_message *sysdb_msg,
                                   const char *attrs[])
@@ -114,21 +137,46 @@ static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
         return EIO;
     }
 
-    /* Deliberately start from 2 in order to not merge
-     * objectclass/objectcategory and avoid breaking MPGs where the OC might
-     * be made up
-     */
-    for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
-        ret = merge_ts_attr(ts_msgs[0], sysdb_msg,
-                            sysdb_ts_cache_attrs[c], attrs);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "Cannot merge ts attr %s\n", sysdb_ts_cache_attrs[c]);
-            goto done;
-        }
+    ret = merge_all_ts_attrs(ts_msgs[0], sysdb_msg, attrs);
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+static errno_t merge_msg_sysdb_attrs(TALLOC_CTX *mem_ctx,
+                                     struct sysdb_ctx *sysdb,
+                                     struct ldb_message *ts_msg,
+                                     struct ldb_message **_sysdb_msg,
+                                     const char *attrs[])
+{
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+    size_t msgs_count;
+    struct ldb_message **sysdb_msgs;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
     }
 
-    ret = EOK;
+    ret = sysdb_cache_search_entry(tmp_ctx, sysdb->ldb, ts_msg->dn, LDB_SCOPE_BASE,
+                                   NULL, attrs, &msgs_count, &sysdb_msgs);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (msgs_count != 1) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Expected 1 result for base search, got %zu\n", msgs_count);
+        goto done;
+    }
+
+    ret = merge_all_ts_attrs(ts_msg, sysdb_msgs[0], attrs);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    *_sysdb_msg = talloc_steal(mem_ctx, sysdb_msgs[0]);
 done:
     talloc_zfree(tmp_ctx);
     return ret;
@@ -166,6 +214,50 @@ errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx,
     return EOK;
 }
 
+static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx,
+                                     struct sysdb_ctx *ctx,
+                                     struct ldb_result *ts_res,
+                                     struct ldb_result **_ts_cache_res,
+                                     const char *attrs[])
+{
+    errno_t ret;
+    struct ldb_result *ts_cache_res = NULL;
+
+    if (ts_res == NULL || ctx->ldb_ts == NULL) {
+        return EOK;
+    }
+
+    ts_cache_res = talloc_zero(mem_ctx, struct ldb_result);
+    if (ts_cache_res == NULL) {
+        return ENOMEM;
+    }
+    ts_cache_res->count = ts_res->count;
+    ts_cache_res->msgs = talloc_zero_array(ts_cache_res,
+                                           struct ldb_message *,
+                                           ts_res->count);
+    if (ts_cache_res->msgs == NULL) {
+        talloc_free(ts_cache_res);
+        return ENOMEM;
+    }
+
+    for (size_t c = 0; c < ts_res->count; c++) {
+        ret = merge_msg_sysdb_attrs(ts_cache_res->msgs,
+                                    ctx,
+                                    ts_res->msgs[c],
+                                    &ts_cache_res->msgs[c], attrs);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Cannot merge sysdb cache values for %s\n",
+                  ldb_dn_get_linearized(ts_res->msgs[c]->dn));
+            /* non-fatal, we just get only the non-timestamp attrs */
+            continue;
+        }
+    }
+
+    *_ts_cache_res = ts_cache_res;
+    return EOK;
+}
+
 errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
                                       size_t msgs_count,
                                       struct ldb_message **msgs,
@@ -543,6 +635,119 @@ errno_t sysdb_search_ts_matches(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *domain,
+                                  struct ldb_dn *base_dn,
+                                  enum ldb_scope scope,
+                                  int optflags,
+                                  const char *filter,
+                                  const char *attrs[],
+                                  struct ldb_result **_res)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct ldb_result *res;
+    errno_t ret;
+    struct ldb_message **ts_msgs = NULL;
+    struct ldb_result *ts_cache_res = NULL;
+    size_t ts_count;
+
+    if (filter == NULL) {
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    res = talloc_zero(tmp_ctx, struct ldb_result);
+    if (res == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (optflags & SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER) {
+        /* We only care about searching the persistent db */
+        ts_cache_res = talloc_zero(tmp_ctx, struct ldb_result);
+        if (ts_cache_res == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        ts_cache_res->count = 0;
+        ts_cache_res->msgs = NULL;
+    } else {
+        /* Because the timestamp database does not contain all the
+         * attributes, we need to search the persistent db for each
+         * of the entries found and merge the results
+         */
+        struct ldb_result ts_res;
+
+        /* We assume that some of the attributes are more up-to-date in
+         * timestamps db and we're supposed to search by them, so let's
+         * first search the timestamp db
+         */
+        ret = sysdb_search_ts_entry(tmp_ctx, domain->sysdb, base_dn,
+                                    scope, filter, attrs,
+                                    &ts_count, &ts_msgs);
+        if (ret == ENOENT) {
+            ts_count = 0;
+        } else if (ret != EOK) {
+            goto done;
+        }
+
+        memset(&ts_res, 0, sizeof(struct ldb_result));
+        ts_res.count = ts_count;
+        ts_res.msgs = ts_msgs;
+
+        /* Overlay the results from the main cache with the ts attrs */
+        ret = merge_res_sysdb_attrs(tmp_ctx,
+                                    domain->sysdb,
+                                    &ts_res,
+                                    &ts_cache_res,
+                                    attrs);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    if (optflags & SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER) {
+        /* The filter only contains timestamp attrs, no need to search the
+         * persistent db
+         */
+        if (ts_cache_res) {
+            res->count = ts_cache_res->count;
+            res->msgs = talloc_steal(res, ts_cache_res->msgs);
+        }
+    } else {
+        /* Because some of the attributes being searched might exist in the persistent
+         * database only, we also search the persistent db
+         */
+        size_t count;
+
+        ret = sysdb_search_entry(res, domain->sysdb, base_dn, scope,
+                                 filter, attrs, &count, &res->msgs);
+        if (ret == ENOENT) {
+            res->count = 0;
+        } else if (ret != EOK) {
+            goto done;
+        }
+        res->count = count; /* Just to cleanly assign size_t to unsigned */
+
+        res = sss_merge_ldb_results(res, ts_cache_res);
+        if (res == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    *_res = talloc_steal(mem_ctx, res);
+    ret = EOK;
+
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
 static errno_t sysdb_enum_dn_filter(TALLOC_CTX *mem_ctx,
                                     struct ldb_result *ts_res,
                                     const char *name_filter,
diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c
index fdf9935da2..d2296d1b80 100644
--- a/src/tests/cmocka/test_sysdb_ts_cache.c
+++ b/src/tests/cmocka/test_sysdb_ts_cache.c
@@ -1411,6 +1411,201 @@ static void test_sysdb_zero_now(void **state)
     assert_true(cache_expire_ts > TEST_CACHE_TIMEOUT);
 }
 
+static void test_sysdb_search_with_ts(void **state)
+{
+    int ret;
+    struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                     struct sysdb_ts_test_ctx);
+    struct ldb_result *res = NULL;
+    struct ldb_dn *base_dn;
+    const char *attrs[] = { SYSDB_NAME,
+                            SYSDB_OBJECTCATEGORY,
+                            SYSDB_GIDNUM,
+                            SYSDB_CACHE_EXPIRE,
+                            NULL };
+    struct sysdb_attrs *group_attrs = NULL;
+    char *filter;
+    uint64_t cache_expire_sysdb;
+    uint64_t cache_expire_ts;
+    size_t count;
+    struct ldb_message **msgs;
+
+    base_dn = sysdb_base_dn(test_ctx->tctx->dom->sysdb, test_ctx);
+    assert_non_null(base_dn);
+
+    /* Nothing must be stored in either cache at the beginning of the test */
+    ret = sysdb_search_with_ts_attr(test_ctx,
+                                    test_ctx->tctx->dom,
+                                    base_dn,
+                                    LDB_SCOPE_SUBTREE,
+                                    0,
+                                    SYSDB_NAME"=*",
+                                    attrs,
+                                    &res);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 0);
+    talloc_free(res);
+
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
+    assert_non_null(group_attrs);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom,
+                            TEST_GROUP_NAME,
+                            TEST_GROUP_GID,
+                            group_attrs,
+                            TEST_CACHE_TIMEOUT,
+                            TEST_NOW_1);
+    assert_int_equal(ret, EOK);
+    talloc_zfree(group_attrs);
+
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
+    assert_non_null(group_attrs);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom,
+                            TEST_GROUP_NAME_2,
+                            TEST_GROUP_GID_2,
+                            group_attrs,
+                            TEST_CACHE_TIMEOUT,
+                            TEST_NOW_2);
+    assert_int_equal(ret, EOK);
+    talloc_zfree(group_attrs);
+
+    /* Bump the timestamps in the cache so that the ts cache
+     * and sysdb differ
+     */
+
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
+    assert_non_null(group_attrs);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom,
+                            TEST_GROUP_NAME,
+                            TEST_GROUP_GID,
+                            group_attrs,
+                            TEST_CACHE_TIMEOUT,
+                            TEST_NOW_3);
+    assert_int_equal(ret, EOK);
+
+    talloc_zfree(group_attrs);
+
+
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
+    assert_non_null(group_attrs);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom,
+                            TEST_GROUP_NAME_2,
+                            TEST_GROUP_GID_2,
+                            group_attrs,
+                            TEST_CACHE_TIMEOUT,
+                            TEST_NOW_4);
+    assert_int_equal(ret, EOK);
+
+    talloc_zfree(group_attrs);
+
+    get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME,
+                           &cache_expire_sysdb, &cache_expire_ts);
+    assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1);
+    assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_3);
+
+    get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME_2,
+                           &cache_expire_sysdb, &cache_expire_ts);
+    assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2);
+    assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_4);
+
+    /* Search for groups that don't expire until TEST_NOW_4 */
+    filter = talloc_asprintf(test_ctx, SYSDB_CACHE_EXPIRE">=%d", TEST_NOW_4);
+    assert_non_null(filter);
+
+    /* This search should yield only one group (so, it needs to search the ts
+     * cache to hit the TEST_NOW_4), but should return attributes merged from
+     * both caches
+     */
+    ret = sysdb_search_with_ts_attr(test_ctx,
+                                    test_ctx->tctx->dom,
+                                    base_dn,
+                                    LDB_SCOPE_SUBTREE,
+                                    0,
+                                    filter,
+                                    attrs,
+                                    &res);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 1);
+    assert_int_equal(TEST_GROUP_GID_2, ldb_msg_find_attr_as_uint64(res->msgs[0],
+                                                                   SYSDB_GIDNUM, 0));
+    talloc_free(res);
+
+    /*
+     * In contrast, sysdb_search_entry merges the timestamp attributes, but does
+     * not search the timestamp cache
+     */
+    ret = sysdb_search_entry(test_ctx,
+                             test_ctx->tctx->dom->sysdb,
+                             base_dn,
+                             LDB_SCOPE_SUBTREE,
+                             filter,
+                             attrs,
+                             &count,
+                             &msgs);
+    assert_int_equal(ret, ENOENT);
+
+    /* Should get the same result when searching by ts attrs only */
+    ret = sysdb_search_with_ts_attr(test_ctx,
+                                    test_ctx->tctx->dom,
+                                    base_dn,
+                                    LDB_SCOPE_SUBTREE,
+                                    SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER,
+                                    filter,
+                                    attrs,
+                                    &res);
+    talloc_zfree(filter);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 1);
+    assert_int_equal(TEST_GROUP_GID_2, ldb_msg_find_attr_as_uint64(res->msgs[0],
+                                                                   SYSDB_GIDNUM, 0));
+    talloc_free(res);
+
+    /* We can also search in sysdb only as well, we should get back ts attrs */
+    filter = talloc_asprintf(test_ctx, SYSDB_GIDNUM"=%d", TEST_GROUP_GID);
+    assert_non_null(filter);
+
+    ret = sysdb_search_with_ts_attr(test_ctx,
+                                    test_ctx->tctx->dom,
+                                    base_dn,
+                                    LDB_SCOPE_SUBTREE,
+                                    SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER,
+                                    filter,
+                                    attrs,
+                                    &res);
+    talloc_zfree(filter);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 1);
+    assert_int_equal(TEST_GROUP_GID, ldb_msg_find_attr_as_uint64(res->msgs[0],
+                                                                 SYSDB_GIDNUM, 0));
+    assert_int_equal(TEST_CACHE_TIMEOUT + TEST_NOW_3,
+                     ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_CACHE_EXPIRE, 0));
+    talloc_free(res);
+
+    /* We can also search in both using an OR-filter. Note that an AND-filter is not possible
+     * unless we deconstruct the filter..
+     */
+    filter = talloc_asprintf(test_ctx, "(|("SYSDB_GIDNUM"=%d)"
+                                         "("SYSDB_CACHE_EXPIRE">=%d))",
+                                         TEST_GROUP_GID, TEST_NOW_4);
+    assert_non_null(filter);
+
+    ret = sysdb_search_with_ts_attr(test_ctx,
+                                    test_ctx->tctx->dom,
+                                    base_dn,
+                                    LDB_SCOPE_SUBTREE,
+                                    0,
+                                    filter,
+                                    attrs,
+                                    &res);
+    talloc_zfree(filter);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(res->count, 2);
+    talloc_free(res);
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -1462,6 +1657,9 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_sysdb_zero_now,
                                         test_sysdb_ts_setup,
                                         test_sysdb_ts_teardown),
+        cmocka_unit_test_setup_teardown(test_sysdb_search_with_ts,
+                                        test_sysdb_ts_setup,
+                                        test_sysdb_ts_teardown),
     };
 
     /* Set debug level to invalid value so we can decide if -d 0 was used. */

From ecc7a2c8bd227840d0d9af14a23586d6ad908b33 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 28 May 2019 14:56:15 +0200
Subject: [PATCH 02/12] BE: search with sysdb_search_with_ts_attr

Previously, the background refresh code had used sysdb_search_entry()
which does not run the search on the timestamp cache. Instead, this
patch changes to using sysdb_search_with_ts_attr with the
SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER optimization because currently only
the dataExpireTimestamp attribute is included in the filter.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index e8cf5da758..c6bb66b68c 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -40,9 +40,8 @@ static errno_t be_refresh_get_values_ex(TALLOC_CTX *mem_ctx,
     const char *attrs[] = {attr, NULL};
     const char *filter = NULL;
     char **values = NULL;
-    struct ldb_message **msgs = NULL;
     struct sysdb_attrs **records = NULL;
-    size_t count;
+    struct ldb_result *res;
     time_t now = time(NULL);
     errno_t ret;
 
@@ -58,23 +57,23 @@ static errno_t be_refresh_get_values_ex(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = sysdb_search_entry(tmp_ctx, domain->sysdb, base_dn,
-                             LDB_SCOPE_SUBTREE, filter, attrs,
-                             &count, &msgs);
-    if (ret == ENOENT) {
-        count = 0;
-    } else if (ret != EOK) {
+    ret = sysdb_search_with_ts_attr(tmp_ctx, domain, base_dn,
+                                    LDB_SCOPE_SUBTREE,
+                                    SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER,
+                                    filter, attrs,
+                                    &res);
+    if (ret != EOK) {
         goto done;
     }
 
-    ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &records);
+    ret = sysdb_msg2attrs(tmp_ctx, res->count, res->msgs, &records);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Could not convert ldb message to sysdb_attrs\n");
         goto done;
     }
 
-    ret = sysdb_attrs_to_list(tmp_ctx, records, count, attr, &values);
+    ret = sysdb_attrs_to_list(tmp_ctx, records, res->count, attr, &values);
     if (ret != EOK) {
         goto done;
     }

From d26159ae00c1678ac32f96c204dba65d1d04bb8f Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 24 Apr 2019 21:09:53 +0200
Subject: [PATCH 03/12] BE: Enable refresh for multiple domains

Descend into subdomains on back end refresh and make sure to start from
users again.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index c6bb66b68c..02e478c95b 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -255,7 +255,9 @@ static errno_t be_refresh_step(struct tevent_req *req)
 
         /* if not found than continue with next domain */
         if (state->index == BE_REFRESH_TYPE_SENTINEL) {
-            state->domain = get_next_domain(state->domain, 0);
+            state->domain = get_next_domain(state->domain,
+                                            SSS_GND_DESCEND);
+            state->index = 0;
             continue;
         }
 

From 3f6d3544d216d051e70f61c39772818ab49c63b6 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Mon, 20 May 2019 22:32:13 +0200
Subject: [PATCH 04/12] BE: Make be_refresh_ctx_init set up the periodical
 task, too

This is mostly a preparatory patch that rolls in setting up the ptask
into be_refresh_ctx_init. Since in later patches we will call
be_refresh_ctx_init from several different places, this will prevent
code duplication.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c       | 21 +++++++++++++++++++--
 src/providers/be_refresh.h       |  2 +-
 src/providers/data_provider_be.c | 14 --------------
 3 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index 02e478c95b..c7b048a951 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -134,11 +134,13 @@ struct be_refresh_ctx {
     struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL];
 };
 
-struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx)
+struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx)
 {
     struct be_refresh_ctx *ctx = NULL;
+    uint32_t refresh_interval;
+    errno_t ret;
 
-    ctx = talloc_zero(mem_ctx, struct be_refresh_ctx);
+    ctx = talloc_zero(be_ctx, struct be_refresh_ctx);
     if (ctx == NULL) {
         return NULL;
     }
@@ -147,6 +149,21 @@ struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx)
     ctx->callbacks[BE_REFRESH_TYPE_GROUPS].name = "groups";
     ctx->callbacks[BE_REFRESH_TYPE_NETGROUPS].name = "netgroups";
 
+    refresh_interval = be_ctx->domain->refresh_expired_interval;
+    if (refresh_interval > 0) {
+        ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0,
+                              refresh_interval, BE_PTASK_OFFLINE_SKIP, 0,
+                              be_refresh_send, be_refresh_recv,
+                              be_ctx->refresh_ctx, "Refresh Records", NULL);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "Unable to initialize refresh periodic task [%d]: %s\n",
+                  ret, sss_strerror(ret));
+            talloc_free(ctx);
+            return NULL;
+        }
+    }
+
     return ctx;
 }
 
diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h
index 927fa4a33b..664f018163 100644
--- a/src/providers/be_refresh.h
+++ b/src/providers/be_refresh.h
@@ -52,7 +52,7 @@ enum be_refresh_type {
 
 struct be_refresh_ctx;
 
-struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx);
+struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx);
 
 errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
                           enum be_refresh_type type,
diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 147627b798..5e6235786b 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -454,7 +454,6 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct confdb_ctx *cdb)
 {
-    uint32_t refresh_interval;
     struct tevent_req *req;
     struct be_ctx *be_ctx;
     char *str = NULL;
@@ -545,19 +544,6 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    refresh_interval = be_ctx->domain->refresh_expired_interval;
-    if (refresh_interval > 0) {
-        ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0,
-                              refresh_interval, BE_PTASK_OFFLINE_SKIP, 0,
-                              be_refresh_send, be_refresh_recv,
-                              be_ctx->refresh_ctx, "Refresh Records", NULL);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_FATAL_FAILURE,
-                  "Unable to initialize refresh periodic task\n");
-            goto done;
-        }
-    }
-
     req = dp_init_send(be_ctx, be_ctx->ev, be_ctx, be_ctx->uid, be_ctx->gid);
     if (req == NULL) {
         ret = ENOMEM;

From bd485e7404751b4dc249fa55592c7781ab049c77 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Mon, 20 May 2019 22:42:47 +0200
Subject: [PATCH 05/12] BE/LDAP: Call be_refresh_ctx_init() in the provider
 libraries, not in back end

Since later patches will pass different parameters to
be_refresh_ctx_init(), let's call the init function in the provider
libraries not directly in the back end.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/ad/ad_init.c        |  2 +-
 src/providers/data_provider_be.c  |  8 --------
 src/providers/ipa/ipa_init.c      |  2 +-
 src/providers/ldap/ldap_common.h  |  2 +-
 src/providers/ldap/ldap_init.c    |  2 +-
 src/providers/ldap/sdap_refresh.c | 17 +++++++++++++----
 6 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index 423071dcd2..b8ebaea2fd 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -408,7 +408,7 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx,
         return ret;
     }
 
-    ret = sdap_refresh_init(be_ctx->refresh_ctx, sdap_id_ctx);
+    ret = sdap_refresh_init(be_ctx, sdap_id_ctx);
     if (ret != EOK && ret != EEXIST) {
         DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh "
               "will not work [%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 5e6235786b..e573f69a5f 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -536,14 +536,6 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    /* Initialize be_refresh periodic task. */
-    be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx);
-    if (be_ctx->refresh_ctx == NULL) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n");
-        ret = ENOMEM;
-        goto done;
-    }
-
     req = dp_init_send(be_ctx, be_ctx->ev, be_ctx, be_ctx->uid, be_ctx->gid);
     if (req == NULL) {
         ret = ENOMEM;
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 6818e21714..b3060e228c 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -594,7 +594,7 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx,
         }
     }
 
-    ret = sdap_refresh_init(be_ctx->refresh_ctx, sdap_id_ctx);
+    ret = sdap_refresh_init(be_ctx, sdap_id_ctx);
     if (ret != EOK && ret != EEXIST) {
         DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh "
               "will not work [%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 5d6302dcd1..60e3ef2974 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -365,7 +365,7 @@ struct sdap_id_ctx *
 sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
                 struct sdap_service *sdap_service);
 
-errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx,
+errno_t sdap_refresh_init(struct be_ctx *be_ctx,
                           struct sdap_id_ctx *id_ctx);
 
 errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx);
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index 057e173adf..3ce574e28c 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -432,7 +432,7 @@ static errno_t ldap_init_misc(struct be_ctx *be_ctx,
     }
 
     /* Setup periodical refresh of expired records */
-    ret = sdap_refresh_init(be_ctx->refresh_ctx, id_ctx);
+    ret = sdap_refresh_init(be_ctx, id_ctx);
     if (ret != EOK && ret != EEXIST) {
         DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh will not work "
               "[%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c
index 6d6c43e20c..457df8be23 100644
--- a/src/providers/ldap/sdap_refresh.c
+++ b/src/providers/ldap/sdap_refresh.c
@@ -255,12 +255,19 @@ static errno_t sdap_refresh_netgroups_recv(struct tevent_req *req)
     return sdap_refresh_recv(req);
 }
 
-errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx,
+errno_t sdap_refresh_init(struct be_ctx *be_ctx,
                           struct sdap_id_ctx *id_ctx)
 {
     errno_t ret;
 
-    ret = be_refresh_add_cb(refresh_ctx, BE_REFRESH_TYPE_USERS,
+    be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx);
+    if (be_ctx->refresh_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n");
+        return ENOMEM;
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_USERS,
                             sdap_refresh_users_send,
                             sdap_refresh_users_recv,
                             id_ctx);
@@ -269,7 +276,8 @@ errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx,
               "will not work [%d]: %s\n", ret, strerror(ret));
     }
 
-    ret = be_refresh_add_cb(refresh_ctx, BE_REFRESH_TYPE_GROUPS,
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_USERS,
                             sdap_refresh_groups_send,
                             sdap_refresh_groups_recv,
                             id_ctx);
@@ -278,7 +286,8 @@ errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx,
               "will not work [%d]: %s\n", ret, strerror(ret));
     }
 
-    ret = be_refresh_add_cb(refresh_ctx, BE_REFRESH_TYPE_NETGROUPS,
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_USERS,
                             sdap_refresh_netgroups_send,
                             sdap_refresh_netgroups_recv,
                             id_ctx);

From e31b864b386eae0102920434c47b009a7070ba3f Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 21 May 2019 12:09:24 +0200
Subject: [PATCH 06/12] BE: Pass in attribute to look up with instead of
 hardcoding SYSDB_NAME

In later patches, we will implement refreshes for AD or IPA which might
refresh objects that do not have a name yet, but always do have a different
attribute, like a SID or a uniqueID. In this case, it's better to use that
different attribute instead of name.

This patch allows the caller to tell the refresh module which attribute
to use.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c        | 12 ++++++++----
 src/providers/be_refresh.h        |  3 ++-
 src/providers/ldap/sdap_refresh.c |  2 +-
 3 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index c7b048a951..66cc4cf985 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -89,6 +89,7 @@ static errno_t be_refresh_get_values_ex(TALLOC_CTX *mem_ctx,
 
 static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx,
                                      enum be_refresh_type type,
+                                     const char *attr_name,
                                      struct sss_domain_info *domain,
                                      time_t period,
                                      char ***_values)
@@ -116,7 +117,7 @@ static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx,
     }
 
     ret = be_refresh_get_values_ex(mem_ctx, domain, period,
-                                   base_dn, SYSDB_NAME, _values);
+                                   base_dn, attr_name, _values);
 
     talloc_free(base_dn);
     return ret;
@@ -131,10 +132,12 @@ struct be_refresh_cb {
 };
 
 struct be_refresh_ctx {
+    const char *attr_name;
     struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL];
 };
 
-struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx)
+struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx,
+                                           const char *attr_name)
 {
     struct be_refresh_ctx *ctx = NULL;
     uint32_t refresh_interval;
@@ -145,6 +148,7 @@ struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx)
         return NULL;
     }
 
+    ctx->attr_name = attr_name;
     ctx->callbacks[BE_REFRESH_TYPE_USERS].name = "users";
     ctx->callbacks[BE_REFRESH_TYPE_GROUPS].name = "groups";
     ctx->callbacks[BE_REFRESH_TYPE_NETGROUPS].name = "netgroups";
@@ -284,8 +288,8 @@ static errno_t be_refresh_step(struct tevent_req *req)
             goto done;
         }
 
-        ret = be_refresh_get_values(state, state->index, state->domain,
-                                    state->period, &values);
+        ret = be_refresh_get_values(state, state->index, state->ctx->attr_name,
+                                    state->domain, state->period, &values);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain DN list [%d]: %s\n",
                                         ret, sss_strerror(ret));
diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h
index 664f018163..8c7b1d0ba8 100644
--- a/src/providers/be_refresh.h
+++ b/src/providers/be_refresh.h
@@ -52,7 +52,8 @@ enum be_refresh_type {
 
 struct be_refresh_ctx;
 
-struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx);
+struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx,
+                                           const char *attr_name);
 
 errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
                           enum be_refresh_type type,
diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c
index 457df8be23..ed04da36a0 100644
--- a/src/providers/ldap/sdap_refresh.c
+++ b/src/providers/ldap/sdap_refresh.c
@@ -260,7 +260,7 @@ errno_t sdap_refresh_init(struct be_ctx *be_ctx,
 {
     errno_t ret;
 
-    be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx);
+    be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx, SYSDB_NAME);
     if (be_ctx->refresh_ctx == NULL) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n");
         return ENOMEM;

From 9622a29f107ea0a9dc6cab402cc8226ec8088890 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 21 May 2019 12:07:34 +0200
Subject: [PATCH 07/12] BE: Change be_refresh_ctx_init to return errno and set
 be_ctx->refresh_ctx

It is a bit odd that a caller to a be_ function would set a property of
be_ctx. IMO it is cleaner if the function has a side-effect and sets the
property internally and rather returns errno.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c        | 13 +++++++------
 src/providers/be_refresh.h        |  4 ++--
 src/providers/ldap/sdap_refresh.c |  4 ++--
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index 66cc4cf985..8a6e1ba586 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -136,8 +136,8 @@ struct be_refresh_ctx {
     struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL];
 };
 
-struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx,
-                                           const char *attr_name)
+errno_t be_refresh_ctx_init(struct be_ctx *be_ctx,
+                            const char *attr_name)
 {
     struct be_refresh_ctx *ctx = NULL;
     uint32_t refresh_interval;
@@ -145,7 +145,7 @@ struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx,
 
     ctx = talloc_zero(be_ctx, struct be_refresh_ctx);
     if (ctx == NULL) {
-        return NULL;
+        return ENOMEM;
     }
 
     ctx->attr_name = attr_name;
@@ -158,17 +158,18 @@ struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx,
         ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0,
                               refresh_interval, BE_PTASK_OFFLINE_SKIP, 0,
                               be_refresh_send, be_refresh_recv,
-                              be_ctx->refresh_ctx, "Refresh Records", NULL);
+                              ctx, "Refresh Records", NULL);
         if (ret != EOK) {
             DEBUG(SSSDBG_FATAL_FAILURE,
                   "Unable to initialize refresh periodic task [%d]: %s\n",
                   ret, sss_strerror(ret));
             talloc_free(ctx);
-            return NULL;
+            return ret;
         }
     }
 
-    return ctx;
+    be_ctx->refresh_ctx = ctx;
+    return EOK;
 }
 
 errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h
index 8c7b1d0ba8..980ac7d060 100644
--- a/src/providers/be_refresh.h
+++ b/src/providers/be_refresh.h
@@ -52,8 +52,8 @@ enum be_refresh_type {
 
 struct be_refresh_ctx;
 
-struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx,
-                                           const char *attr_name);
+errno_t be_refresh_ctx_init(struct be_ctx *be_ctx,
+                            const char *attr_name);
 
 errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
                           enum be_refresh_type type,
diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c
index ed04da36a0..baa7fa59fb 100644
--- a/src/providers/ldap/sdap_refresh.c
+++ b/src/providers/ldap/sdap_refresh.c
@@ -260,8 +260,8 @@ errno_t sdap_refresh_init(struct be_ctx *be_ctx,
 {
     errno_t ret;
 
-    be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx, SYSDB_NAME);
-    if (be_ctx->refresh_ctx == NULL) {
+    ret = be_refresh_ctx_init(be_ctx, SYSDB_NAME);
+    if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n");
         return ENOMEM;
     }

From 2ee68de5959022c40b48ce7f5c98687ede082e1b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 8 May 2019 14:38:44 +0200
Subject: [PATCH 08/12] BE/LDAP: Split out a helper function from sdap_refresh
 for later reuse

Every refresh request will send a similar account_req. Let's split out
the function that creates the account_req into a reusable one.

Also removes the type string as it was only used in DEBUG messages and
there is already a function in the back end API that provides the same
functionality.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c        | 18 ++++++++++++++++++
 src/providers/be_refresh.h        |  4 ++++
 src/providers/ldap/sdap_refresh.c | 29 +++++------------------------
 3 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index 8a6e1ba586..c49229e717 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -362,3 +362,21 @@ errno_t be_refresh_recv(struct tevent_req *req)
 
     return EOK;
 }
+
+struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx,
+                                       uint32_t entry_type,
+                                       struct sss_domain_info *domain)
+{
+    struct dp_id_data *account_req;
+
+    account_req = talloc_zero(mem_ctx, struct dp_id_data);
+    if (account_req == NULL) {
+        return NULL;
+    }
+
+    account_req->entry_type = entry_type;
+    account_req->filter_type = BE_FILTER_NAME;
+    account_req->extra_value = NULL;
+    account_req->domain = domain->name;
+    return account_req;
+}
diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h
index 980ac7d060..b7ba5d4c23 100644
--- a/src/providers/be_refresh.h
+++ b/src/providers/be_refresh.h
@@ -69,4 +69,8 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx,
 
 errno_t be_refresh_recv(struct tevent_req *req);
 
+struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx,
+                                       uint32_t entry_type,
+                                       struct sss_domain_info *domain);
+
 #endif /* _DP_REFRESH_H_ */
diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c
index baa7fa59fb..af39d86861 100644
--- a/src/providers/ldap/sdap_refresh.c
+++ b/src/providers/ldap/sdap_refresh.c
@@ -30,7 +30,6 @@ struct sdap_refresh_state {
     struct dp_id_data *account_req;
     struct sdap_id_ctx *id_ctx;
     struct sdap_domain *sdom;
-    const char *type;
     char **names;
     size_t index;
 };
@@ -74,32 +73,12 @@ static struct tevent_req *sdap_refresh_send(TALLOC_CTX *mem_ctx,
         goto immediately;
     }
 
-    switch (entry_type) {
-    case BE_REQ_USER:
-        state->type = "user";
-        break;
-    case BE_REQ_GROUP:
-        state->type = "group";
-        break;
-    case BE_REQ_NETGROUP:
-        state->type = "netgroup";
-        break;
-    default:
-        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid entry type [%d]!\n", entry_type);
-    }
-
-    state->account_req = talloc_zero(state, struct dp_id_data);
+    state->account_req = be_refresh_acct_req(state, entry_type, domain);
     if (state->account_req == NULL) {
         ret = ENOMEM;
         goto immediately;
     }
 
-    state->account_req->entry_type = entry_type;
-    state->account_req->filter_type = BE_FILTER_NAME;
-    state->account_req->extra_value = NULL;
-    state->account_req->domain = domain->name;
-    /* filter will be filled later */
-
     ret = sdap_refresh_step(req);
     if (ret == EOK) {
         DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n");
@@ -143,7 +122,8 @@ static errno_t sdap_refresh_step(struct tevent_req *req)
     }
 
     DEBUG(SSSDBG_TRACE_FUNC, "Issuing refresh of %s %s\n",
-          state->type, state->account_req->filter_value);
+          be_req2str(state->account_req->entry_type),
+          state->account_req->filter_value);
 
     subreq = sdap_handle_acct_req_send(state, state->be_ctx,
                                        state->account_req, state->id_ctx,
@@ -178,7 +158,8 @@ static void sdap_refresh_done(struct tevent_req *subreq)
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, "
-              "sdap_ret: %d, errno: %d]: %s\n", state->type,
+              "sdap_ret: %d, errno: %d]: %s\n",
+               be_req2str(state->account_req->entry_type),
               dp_error, sdap_ret, ret, err_msg);
         goto done;
     }

From 11783fcd0cf88543c23089db503da069f0d996ab Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 21 May 2019 12:07:59 +0200
Subject: [PATCH 09/12] BE: Pass in filter_type when creating the refresh
 account request

For refreshing AD users and groups, we'll want to create a request by
SID, for all other requests we'll want to create a request by name. This
patch allows parametrizing the request creation by the caller.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c        | 3 ++-
 src/providers/be_refresh.h        | 1 +
 src/providers/ldap/sdap_refresh.c | 3 ++-
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index c49229e717..c4ff71e1ff 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -365,6 +365,7 @@ errno_t be_refresh_recv(struct tevent_req *req)
 
 struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx,
                                        uint32_t entry_type,
+                                       uint32_t filter_type,
                                        struct sss_domain_info *domain)
 {
     struct dp_id_data *account_req;
@@ -375,7 +376,7 @@ struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx,
     }
 
     account_req->entry_type = entry_type;
-    account_req->filter_type = BE_FILTER_NAME;
+    account_req->filter_type = filter_type;
     account_req->extra_value = NULL;
     account_req->domain = domain->name;
     return account_req;
diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h
index b7ba5d4c23..c7b4872df1 100644
--- a/src/providers/be_refresh.h
+++ b/src/providers/be_refresh.h
@@ -71,6 +71,7 @@ errno_t be_refresh_recv(struct tevent_req *req);
 
 struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx,
                                        uint32_t entry_type,
+                                       uint32_t filter_type,
                                        struct sss_domain_info *domain);
 
 #endif /* _DP_REFRESH_H_ */
diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c
index af39d86861..2206d6670a 100644
--- a/src/providers/ldap/sdap_refresh.c
+++ b/src/providers/ldap/sdap_refresh.c
@@ -73,7 +73,8 @@ static struct tevent_req *sdap_refresh_send(TALLOC_CTX *mem_ctx,
         goto immediately;
     }
 
-    state->account_req = be_refresh_acct_req(state, entry_type, domain);
+    state->account_req = be_refresh_acct_req(state, entry_type,
+                                             BE_FILTER_NAME, domain);
     if (state->account_req == NULL) {
         ret = ENOMEM;
         goto immediately;

From fb26da69b091693d35c3baf8e62527c5e95ba8e5 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 8 May 2019 23:16:07 +0200
Subject: [PATCH 10/12] BE: Send refresh requests in batches

As we extend the background refresh into larger domains, the amount of
data that SSSD refreshes on the background might be larger. And
refreshing all expired entries in a single request might block sssd_be
for a long time, either triggering the watchdog or starving other
legitimate requests.

Therefore the background refresh will be done in batches of 200 entries.
The first batch of every type (up to 200 users, up to 200 groups, ...)
will be scheduled imediatelly and subsequent batches with a 0.5 second
delay.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 src/providers/be_refresh.c            | 131 ++++++++++++++++++++++----
 src/tests/cmocka/test_expire_common.c |   6 +-
 src/tests/sss_idmap-tests.c           |   8 +-
 src/util/util.h                       |   8 ++
 4 files changed, 128 insertions(+), 25 deletions(-)

diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c
index c4ff71e1ff..5d86509bbd 100644
--- a/src/providers/be_refresh.c
+++ b/src/providers/be_refresh.c
@@ -204,8 +204,21 @@ struct be_refresh_state {
     struct sss_domain_info *domain;
     enum be_refresh_type index;
     time_t period;
+
+    char **refresh_values;
+    size_t refresh_val_size;
+    size_t refresh_index;
+
+    size_t batch_size;
+    char **refresh_batch;
 };
 
+static errno_t be_refresh_batch_step(struct tevent_req *req,
+                                     uint32_t msec_delay);
+static void be_refresh_batch_step_wakeup(struct tevent_context *ev,
+                                         struct tevent_timer *tt,
+                                         struct timeval tv,
+                                         void *pvt);
 static errno_t be_refresh_step(struct tevent_req *req);
 static void be_refresh_done(struct tevent_req *subreq);
 
@@ -236,6 +249,13 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx,
         goto immediately;
     }
 
+    state->batch_size = 200;
+    state->refresh_batch = talloc_zero_array(state, char *, state->batch_size+1);
+    if (state->refresh_batch == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
     ret = be_refresh_step(req);
     if (ret == EOK) {
         goto immediately;
@@ -261,8 +281,6 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx,
 static errno_t be_refresh_step(struct tevent_req *req)
 {
     struct be_refresh_state *state = NULL;
-    struct tevent_req *subreq = NULL;
-    char **values = NULL;
     errno_t ret;
 
     state = tevent_req_data(req, struct be_refresh_state);
@@ -289,42 +307,103 @@ static errno_t be_refresh_step(struct tevent_req *req)
             goto done;
         }
 
+        talloc_zfree(state->refresh_values);
         ret = be_refresh_get_values(state, state->index, state->ctx->attr_name,
-                                    state->domain, state->period, &values);
+                                    state->domain, state->period,
+                                    &state->refresh_values);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain DN list [%d]: %s\n",
                                         ret, sss_strerror(ret));
             goto done;
         }
 
-        DEBUG(SSSDBG_TRACE_FUNC, "Refreshing %s in domain %s\n",
-              state->cb->name, state->domain->name);
+        for (state->refresh_val_size = 0;
+             state->refresh_values[state->refresh_val_size] != NULL;
+             state->refresh_val_size++);
+
+        DEBUG(SSSDBG_TRACE_FUNC, "Refreshing %zu %s in domain %s\n",
+              state->refresh_val_size, state->cb->name, state->domain->name);
 
-        subreq = state->cb->send_fn(state, state->ev, state->be_ctx,
-                                    state->domain, values, state->cb->pvt);
-        if (subreq == NULL) {
-            ret = ENOMEM;
+        ret = be_refresh_batch_step(req, 0);
+        if (ret == EOK) {
+            state->index++;
+            continue;
+        } else if (ret != EAGAIN) {
             goto done;
         }
-
-        /* make the list disappear with subreq */
-        talloc_steal(subreq, values);
-
-        tevent_req_set_callback(subreq, be_refresh_done, req);
+        /* EAGAIN only, refreshing something.. */
 
         state->index++;
-        ret = EAGAIN;
         goto done;
     }
 
     ret = EOK;
 
 done:
-    if (ret != EOK && ret != EAGAIN) {
-        talloc_free(values);
+    return ret;
+}
+
+static errno_t be_refresh_batch_step(struct tevent_req *req,
+                                     uint32_t msec_delay)
+{
+    struct be_refresh_state *state = tevent_req_data(req, struct be_refresh_state);
+    struct timeval tv;
+    struct tevent_timer *timeout = NULL;
+
+    size_t remaining;
+    size_t batch_size;
+
+    memset(state->refresh_batch, 0, sizeof(char *) * state->batch_size);
+
+    if (state->refresh_index >= state->refresh_val_size) {
+        DEBUG(SSSDBG_FUNC_DATA, "The batch is done\n");
+        state->refresh_index = 0;
+        return EOK;
     }
 
-    return ret;
+    remaining = state->refresh_val_size - state->refresh_index;
+    batch_size = MIN(remaining, state->batch_size);
+    DEBUG(SSSDBG_FUNC_DATA,
+          "This batch will refresh %zu entries (so far %zu/%zu)\n",
+          batch_size, state->refresh_index, state->refresh_val_size);
+
+    for (size_t i = 0; i < batch_size; i++) {
+        state->refresh_batch[i] = state->refresh_values[state->refresh_index];
+        state->refresh_index++;
+    }
+
+    tv = tevent_timeval_current_ofs(0, msec_delay * 1000);
+    timeout = tevent_add_timer(state->be_ctx->ev, req, tv,
+                               be_refresh_batch_step_wakeup, req);
+    if (timeout == NULL) {
+        return ENOMEM;
+    }
+
+    return EAGAIN;
+}
+
+static void be_refresh_batch_step_wakeup(struct tevent_context *ev,
+                                         struct tevent_timer *tt,
+                                         struct timeval tv,
+                                         void *pvt)
+{
+    struct tevent_req *req;
+    struct tevent_req *subreq = NULL;
+    struct be_refresh_state *state = NULL;
+
+    req = talloc_get_type(pvt, struct tevent_req);
+    state = tevent_req_data(req, struct be_refresh_state);
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Issuing refresh\n");
+    subreq = state->cb->send_fn(state, state->ev, state->be_ctx,
+                                state->domain,
+                                state->refresh_batch,
+                                state->cb->pvt);
+    if (subreq == NULL) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, be_refresh_done, req);
 }
 
 static void be_refresh_done(struct tevent_req *subreq)
@@ -342,8 +421,24 @@ static void be_refresh_done(struct tevent_req *subreq)
         goto done;
     }
 
+    ret = be_refresh_batch_step(req, 500);
+    if (ret == EAGAIN) {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "Another batch in this step in progress\n");
+        return;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "be_refresh_batch_step failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_INTERNAL, "All batches in this step refreshed\n");
+
+    /* Proceed to the next step */
     ret = be_refresh_step(req);
     if (ret == EAGAIN) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Another step in progress\n");
         return;
     }
 
diff --git a/src/tests/cmocka/test_expire_common.c b/src/tests/cmocka/test_expire_common.c
index 5d3ea02f3e..4f61681904 100644
--- a/src/tests/cmocka/test_expire_common.c
+++ b/src/tests/cmocka/test_expire_common.c
@@ -32,7 +32,7 @@
 #include "tests/common_check.h"
 #include "tests/cmocka/test_expire_common.h"
 
-#define MAX 100
+#define MAX_VAL 100
 
 static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s)
 {
@@ -41,10 +41,10 @@ static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s)
     size_t len;
     char *timestr;
 
-    timestr = talloc_array(mem_ctx, char, MAX);
+    timestr = talloc_array(mem_ctx, char, MAX_VAL);
 
     tm = gmtime(&t);
-    len = strftime(timestr, MAX, format, tm);
+    len = strftime(timestr, MAX_VAL, format, tm);
     if (len == 0) {
         return NULL;
     }
diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c
index 96f0861ac6..e5f3f70416 100644
--- a/src/tests/sss_idmap-tests.c
+++ b/src/tests/sss_idmap-tests.c
@@ -140,8 +140,8 @@ void idmap_add_domain_with_sec_slices_setup_cb_fail(void)
 }
 
 
-#define MAX 1000
-char data[MAX];
+#define DATA_MAX 1000
+char data[DATA_MAX];
 
 enum idmap_error_code cb2(const char *dom_name,
                           const char *dom_sid,
@@ -154,10 +154,10 @@ enum idmap_error_code cb2(const char *dom_name,
     char *p = (char*)pvt;
     size_t len;
 
-    len = snprintf(p, MAX, "%s, %s %s, %"PRIu32", %"PRIu32", %" PRIu32,
+    len = snprintf(p, DATA_MAX, "%s, %s %s, %"PRIu32", %"PRIu32", %" PRIu32,
                    dom_name, dom_sid, range_id, min_id, max_id, first_rid);
 
-    if (len >= MAX) {
+    if (len >= DATA_MAX) {
         return IDMAP_OUT_OF_MEMORY;
     }
     return IDMAP_SUCCESS;
diff --git a/src/util/util.h b/src/util/util.h
index c5680d89ae..13e434b627 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -67,6 +67,14 @@
 #define NULL 0
 #endif
 
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#endif
+
 #define SSSD_MAIN_OPTS SSSD_DEBUG_OPTS
 
 #define SSSD_SERVER_OPTS(uid, gid) \

From fe243193c928c1d9ca30f2d0fa2b40f8d9194edd Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 24 Apr 2019 20:52:11 +0200
Subject: [PATCH 11/12] AD: Implement background refresh for AD domains

Split out the actual useful functionality from the AD account handler
into a tevent request. This tevent request is then subsequently used by
a new ad_refresh module.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 Makefile.am                   |   5 +-
 src/providers/ad/ad_common.h  |   4 +
 src/providers/ad/ad_id.c      | 140 +++++++++++++----
 src/providers/ad/ad_id.h      |  10 ++
 src/providers/ad/ad_init.c    |   2 +-
 src/providers/ad/ad_refresh.c | 283 ++++++++++++++++++++++++++++++++++
 6 files changed, 412 insertions(+), 32 deletions(-)
 create mode 100644 src/providers/ad/ad_refresh.c

diff --git a/Makefile.am b/Makefile.am
index 043a7ebb44..f9f17904e7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4502,7 +4502,10 @@ libsss_ad_la_SOURCES = \
     src/providers/ad/ad_gpo_ndr.c \
     src/providers/ad/ad_srv.c \
     src/providers/ad/ad_subdomains.c \
-    src/providers/ad/ad_domain_info.c
+    src/providers/ad/ad_domain_info.c \
+    src/providers/ad/ad_refresh.c \
+    $(NULL)
+
 
 if BUILD_SUDO
 libsss_ad_la_SOURCES += \
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index e224254e15..75f11de2e4 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -224,4 +224,8 @@ errno_t ad_inherit_opts_if_needed(struct dp_option *parent_opts,
                                   struct confdb_ctx *cdb,
                                   const char *subdom_conf_path,
                                   int opt_id);
+
+errno_t ad_refresh_init(struct be_ctx *be_ctx,
+                        struct ad_id_ctx *id_ctx);
+
 #endif /* AD_COMMON_H_ */
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index c3bda16626..eb6e36824a 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -360,44 +360,36 @@ get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
     return clist;
 }
 
-struct ad_account_info_handler_state {
-    struct sss_domain_info *domain;
-    struct dp_reply_std reply;
+struct ad_account_info_state {
+    const char *err_msg;
+    int dp_error;
 };
 
-static void ad_account_info_handler_done(struct tevent_req *subreq);
+static void ad_account_info_done(struct tevent_req *subreq);
 
 struct tevent_req *
-ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
-                              struct ad_id_ctx *id_ctx,
-                              struct dp_id_data *data,
-                              struct dp_req_params *params)
+ad_account_info_send(TALLOC_CTX *mem_ctx,
+                     struct be_ctx *be_ctx,
+                     struct ad_id_ctx *id_ctx,
+                     struct dp_id_data *data)
 {
-    struct ad_account_info_handler_state *state;
-    struct sdap_id_conn_ctx **clist;
-    struct sdap_id_ctx *sdap_id_ctx;
-    struct sss_domain_info *domain;
+    struct sss_domain_info *domain = NULL;
+    struct ad_account_info_state *state = NULL;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct sdap_id_conn_ctx **clist = NULL;
+    struct sdap_id_ctx *sdap_id_ctx = NULL;
     struct sdap_domain *sdom;
-    struct tevent_req *subreq;
-    struct tevent_req *req;
-    struct be_ctx *be_ctx;
     errno_t ret;
 
-    sdap_id_ctx = id_ctx->sdap_id_ctx;
-    be_ctx = params->be_ctx;
-
     req = tevent_req_create(mem_ctx, &state,
-                            struct ad_account_info_handler_state);
+                            struct ad_account_info_state);
     if (req == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
         return NULL;
     }
 
-    if (sdap_is_enum_request(data)) {
-        DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n");
-        ret = EOK;
-        goto immediately;
-    }
+    sdap_id_ctx = id_ctx->sdap_id_ctx;
 
     domain = be_ctx->domain;
     if (strcasecmp(data->domain, be_ctx->domain->name) != 0) {
@@ -406,6 +398,7 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
     }
 
     if (domain == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain\n");
         ret = EINVAL;
         goto immediately;
     }
@@ -413,6 +406,7 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
     /* Determine whether to connect to GC, LDAP or try both. */
     clist = get_conn_list(state, id_ctx, domain, data);
     if (clist == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create conn list\n");
         ret = EIO;
         goto immediately;
     }
@@ -423,14 +417,100 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
         goto immediately;
     }
 
-    state->domain = sdom->dom;
-
     subreq = ad_handle_acct_info_send(state, data, sdap_id_ctx,
                                       id_ctx->ad_options, sdom, clist);
     if (subreq == NULL) {
         ret = ENOMEM;
         goto immediately;
     }
+    tevent_req_set_callback(subreq, ad_account_info_done, req);
+    return req;
+
+immediately:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, be_ctx->ev);
+    return req;
+}
+
+static void ad_account_info_done(struct tevent_req *subreq)
+{
+    struct ad_account_info_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_account_info_state);
+
+    ret = ad_handle_acct_info_recv(subreq, &state->dp_error, &state->err_msg);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "ad_handle_acct_info_recv failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        /* The caller wouldn't fail either, just report the error up */
+    }
+    talloc_zfree(subreq);
+    tevent_req_done(req);
+}
+
+errno_t ad_account_info_recv(struct tevent_req *req,
+                             int *_dp_error,
+                             const char **_err_msg)
+{
+    struct ad_account_info_state *state = NULL;
+
+    state = tevent_req_data(req, struct ad_account_info_state);
+
+    if (_err_msg != NULL) {
+        *_err_msg = state->err_msg;
+    }
+
+    if (_dp_error) {
+        *_dp_error = state->dp_error;
+    }
+
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+struct ad_account_info_handler_state {
+    struct sss_domain_info *domain;
+    struct dp_reply_std reply;
+};
+
+static void ad_account_info_handler_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ad_account_info_handler_send(TALLOC_CTX *mem_ctx,
+                              struct ad_id_ctx *id_ctx,
+                              struct dp_id_data *data,
+                              struct dp_req_params *params)
+{
+    struct ad_account_info_handler_state *state;
+    struct tevent_req *subreq;
+    struct tevent_req *req;
+    errno_t ret;
+
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ad_account_info_handler_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    if (sdap_is_enum_request(data)) {
+        DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n");
+        ret = EOK;
+        goto immediately;
+    }
+
+    subreq = ad_account_info_send(state, params->be_ctx, id_ctx, data);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
 
     tevent_req_set_callback(subreq, ad_account_info_handler_done, req);
 
@@ -451,13 +531,13 @@ static void ad_account_info_handler_done(struct tevent_req *subreq)
     struct ad_account_info_handler_state *state;
     struct tevent_req *req;
     const char *err_msg;
-    int dp_error;
+    int dp_error = DP_ERR_FATAL;
     errno_t ret;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_account_info_handler_state);
 
-    ret = ad_handle_acct_info_recv(subreq, &dp_error, &err_msg);
+    ret = ad_account_info_recv(subreq, &dp_error, &err_msg);
     talloc_zfree(subreq);
 
     /* TODO For backward compatibility we always return EOK to DP now. */
@@ -466,8 +546,8 @@ static void ad_account_info_handler_done(struct tevent_req *subreq)
 }
 
 errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx,
-                                      struct tevent_req *req,
-                                      struct dp_reply_std *data)
+                                     struct tevent_req *req,
+                                     struct dp_reply_std *data)
 {
     struct ad_account_info_handler_state *state = NULL;
 
diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h
index 5154393c5f..19cc54eec9 100644
--- a/src/providers/ad/ad_id.h
+++ b/src/providers/ad/ad_id.h
@@ -33,6 +33,16 @@ errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx,
                                       struct tevent_req *req,
                                       struct dp_reply_std *data);
 
+struct tevent_req *
+ad_account_info_send(TALLOC_CTX *mem_ctx,
+                     struct be_ctx *be_ctx,
+                     struct ad_id_ctx *id_ctx,
+                     struct dp_id_data *data);
+
+errno_t ad_account_info_recv(struct tevent_req *req,
+                             int *_dp_error,
+                             const char **_err_msg);
+
 struct tevent_req *
 ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
                          struct dp_id_data *ar,
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index b8ebaea2fd..42c17de00f 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -408,7 +408,7 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx,
         return ret;
     }
 
-    ret = sdap_refresh_init(be_ctx, sdap_id_ctx);
+    ret = ad_refresh_init(be_ctx, ad_id_ctx);
     if (ret != EOK && ret != EEXIST) {
         DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh "
               "will not work [%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/providers/ad/ad_refresh.c b/src/providers/ad/ad_refresh.c
new file mode 100644
index 0000000000..ee541056fe
--- /dev/null
+++ b/src/providers/ad/ad_refresh.c
@@ -0,0 +1,283 @@
+/*
+    Copyright (C) 2019 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/ad/ad_common.h"
+#include "providers/ad/ad_id.h"
+
+struct ad_refresh_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct dp_id_data *account_req;
+    struct ad_id_ctx *id_ctx;
+    char **names;
+    size_t index;
+};
+
+static errno_t ad_refresh_step(struct tevent_req *req);
+static void ad_refresh_done(struct tevent_req *subreq);
+
+static struct tevent_req *ad_refresh_send(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct be_ctx *be_ctx,
+                                            struct sss_domain_info *domain,
+                                            int entry_type,
+                                            char **names,
+                                            void *pvt)
+{
+    struct ad_refresh_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+    uint32_t filter_type;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ad_refresh_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    if (names == NULL) {
+        ret = EOK;
+        goto immediately;
+    }
+
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->id_ctx = talloc_get_type(pvt, struct ad_id_ctx);
+    state->names = names;
+    state->index = 0;
+
+    switch (entry_type) {
+    case BE_REQ_NETGROUP:
+        filter_type = BE_FILTER_NAME;
+        break;
+    case BE_REQ_USER:
+    case BE_REQ_GROUP:
+        filter_type = BE_FILTER_SECID;
+        break;
+    default:
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    state->account_req = be_refresh_acct_req(state, entry_type,
+                                             filter_type, domain);
+    if (state->account_req == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ret = ad_refresh_step(req);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n");
+        goto immediately;
+    } else if (ret != EAGAIN) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "ad_refresh_step() failed "
+                                   "[%d]: %s\n", ret, sss_strerror(ret));
+        goto immediately;
+    }
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t ad_refresh_step(struct tevent_req *req)
+{
+    struct ad_refresh_state *state = NULL;
+    struct tevent_req *subreq = NULL;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct ad_refresh_state);
+
+    if (state->names == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    state->account_req->filter_value = state->names[state->index];
+    if (state->account_req->filter_value == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Issuing refresh of %s %s\n",
+          be_req2str(state->account_req->entry_type),
+          state->account_req->filter_value);
+
+    subreq = ad_account_info_send(state, state->be_ctx, state->id_ctx,
+                                  state->account_req);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, ad_refresh_done, req);
+
+    state->index++;
+    ret = EAGAIN;
+
+done:
+    return ret;
+}
+
+static void ad_refresh_done(struct tevent_req *subreq)
+{
+    struct ad_refresh_state *state = NULL;
+    struct tevent_req *req = NULL;
+    const char *err_msg = NULL;
+    errno_t dp_error;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_refresh_state);
+
+    ret = ad_account_info_recv(subreq, &dp_error, &err_msg);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, "
+              "errno: %d]: %s\n", be_req2str(state->account_req->entry_type),
+              dp_error, ret, err_msg);
+        goto done;
+    }
+
+    ret = ad_refresh_step(req);
+    if (ret == EAGAIN) {
+        return;
+    }
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static errno_t ad_refresh_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+static struct tevent_req *
+ad_refresh_users_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct be_ctx *be_ctx,
+                      struct sss_domain_info *domain,
+                      char **names,
+                      void *pvt)
+{
+    return ad_refresh_send(mem_ctx, ev, be_ctx, domain,
+                           BE_REQ_USER, names, pvt);
+}
+
+static errno_t ad_refresh_users_recv(struct tevent_req *req)
+{
+    return ad_refresh_recv(req);
+}
+
+static struct tevent_req *
+ad_refresh_groups_send(TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct be_ctx *be_ctx,
+                       struct sss_domain_info *domain,
+                       char **names,
+                       void *pvt)
+{
+    return ad_refresh_send(mem_ctx, ev, be_ctx, domain,
+                           BE_REQ_GROUP, names, pvt);
+}
+
+static errno_t ad_refresh_groups_recv(struct tevent_req *req)
+{
+    return ad_refresh_recv(req);
+}
+
+static struct tevent_req *
+ad_refresh_netgroups_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct be_ctx *be_ctx,
+                          struct sss_domain_info *domain,
+                          char **names,
+                          void *pvt)
+{
+    return ad_refresh_send(mem_ctx, ev, be_ctx, domain,
+                           BE_REQ_NETGROUP, names, pvt);
+}
+
+static errno_t ad_refresh_netgroups_recv(struct tevent_req *req)
+{
+    return ad_refresh_recv(req);
+}
+
+errno_t ad_refresh_init(struct be_ctx *be_ctx,
+                        struct ad_id_ctx *id_ctx)
+{
+    errno_t ret;
+
+    ret = be_refresh_ctx_init(be_ctx, SYSDB_SID_STR);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n");
+        return ret;
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_USERS,
+                            ad_refresh_users_send,
+                            ad_refresh_users_recv,
+                            id_ctx);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of users "
+              "will not work [%d]: %s\n", ret, strerror(ret));
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_GROUPS,
+                            ad_refresh_groups_send,
+                            ad_refresh_groups_recv,
+                            id_ctx);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of groups "
+              "will not work [%d]: %s\n", ret, strerror(ret));
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_NETGROUPS,
+                            ad_refresh_netgroups_send,
+                            ad_refresh_netgroups_recv,
+                            id_ctx);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of netgroups "
+              "will not work [%d]: %s\n", ret, strerror(ret));
+    }
+
+    return ret;
+}

From 53ce227b63a2b4c8f2c57477f7192149ad68d46b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 8 May 2019 14:39:23 +0200
Subject: [PATCH 12/12] IPA: Implement background refresh for IPA domains

Split out the actual useful functionality from the IPA account lookup
handler into a tevent request. This tevent request is then used in a new
ipa_refresh module.

Related:
https://pagure.io/SSSD/sssd/issue/4012
---
 Makefile.am                     |   1 +
 src/providers/ipa/ipa_common.h  |   3 +
 src/providers/ipa/ipa_id.c      | 140 +++++++++++++----
 src/providers/ipa/ipa_id.h      |   8 +
 src/providers/ipa/ipa_init.c    |   2 +-
 src/providers/ipa/ipa_refresh.c | 264 ++++++++++++++++++++++++++++++++
 6 files changed, 386 insertions(+), 32 deletions(-)
 create mode 100644 src/providers/ipa/ipa_refresh.c

diff --git a/Makefile.am b/Makefile.am
index f9f17904e7..cbd6bbfdb5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4430,6 +4430,7 @@ libsss_ipa_la_SOURCES = \
     src/providers/ipa/ipa_srv.c \
     src/providers/ipa/ipa_idmap.c \
     src/providers/ipa/ipa_dn.c \
+    src/providers/ipa/ipa_refresh.c \
     src/providers/ad/ad_opts.c \
     src/providers/ad/ad_common.c \
     src/providers/ad/ad_dyndns.c \
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 31e671eb50..6bb1739efb 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -301,4 +301,7 @@ errno_t ipa_get_host_attrs(struct dp_option *ipa_options,
                            struct sysdb_attrs **hosts,
                            struct sysdb_attrs **_ipa_host);
 
+errno_t ipa_refresh_init(struct be_ctx *be_ctx,
+                         struct ipa_id_ctx *id_ctx);
+
 #endif /* _IPA_COMMON_H_ */
diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
index e644af5ffc..9abee34cb4 100644
--- a/src/providers/ipa/ipa_id.c
+++ b/src/providers/ipa/ipa_id.c
@@ -1344,43 +1344,39 @@ ipa_decide_account_info_type(struct dp_id_data *data, struct be_ctx *be_ctx)
     return IPA_ACCOUNT_INFO_OTHER;
 }
 
-struct ipa_account_info_handler_state {
+struct ipa_account_info_state {
     enum ipa_account_info_type type;
-    struct dp_reply_std reply;
+
+    const char *err_msg;
+    int dp_error;
 };
 
-static void ipa_account_info_handler_done(struct tevent_req *subreq);
+static void ipa_account_info_done(struct tevent_req *subreq);
 
 struct tevent_req *
-ipa_account_info_handler_send(TALLOC_CTX *mem_ctx,
-                              struct ipa_id_ctx *id_ctx,
-                              struct dp_id_data *data,
-                              struct dp_req_params *params)
+ipa_account_info_send(TALLOC_CTX *mem_ctx,
+                      struct be_ctx *be_ctx,
+                      struct ipa_id_ctx *id_ctx,
+                      struct dp_id_data *data)
 {
-    struct ipa_account_info_handler_state *state;
+    struct ipa_account_info_state *state = NULL;
+    struct tevent_req *req = NULL;
     struct tevent_req *subreq = NULL;
-    struct tevent_req *req;
     errno_t ret;
 
     req = tevent_req_create(mem_ctx, &state,
-                            struct ipa_account_info_handler_state);
+                            struct ipa_account_info_state);
     if (req == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
         return NULL;
     }
 
-    state->type = ipa_decide_account_info_type(data, params->be_ctx);
-
-    if (sdap_is_enum_request(data)) {
-        DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n");
-        ret = EOK;
-        goto immediately;
-    }
+    state->type = ipa_decide_account_info_type(data, be_ctx);
 
     switch (state->type) {
     case IPA_ACCOUNT_INFO_SUBDOMAIN:
         /* Subdomain lookups are handled differently on server and client. */
-        subreq = ipa_subdomain_account_send(state, params->ev, id_ctx, data);
+        subreq = ipa_subdomain_account_send(state, be_ctx->ev, id_ctx, data);
         break;
     case IPA_ACCOUNT_INFO_NETGROUP:
         if (data->filter_type != BE_FILTER_NAME) {
@@ -1388,11 +1384,11 @@ ipa_account_info_handler_send(TALLOC_CTX *mem_ctx,
             goto immediately;
         }
 
-        subreq = ipa_id_get_netgroup_send(state, params->ev, id_ctx,
+        subreq = ipa_id_get_netgroup_send(state, be_ctx->ev, id_ctx,
                                           data->filter_value);
         break;
     case IPA_ACCOUNT_INFO_OTHER:
-        subreq = ipa_id_get_account_info_send(state, params->ev, id_ctx, data);
+        subreq = ipa_id_get_account_info_send(state, be_ctx->ev, id_ctx, data);
         break;
     }
 
@@ -1400,7 +1396,99 @@ ipa_account_info_handler_send(TALLOC_CTX *mem_ctx,
         ret = ENOMEM;
         goto immediately;
     }
+    tevent_req_set_callback(subreq, ipa_account_info_done, req);
+    return req;
+
+immediately:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, be_ctx->ev);
+    return req;
+}
+
+static void ipa_account_info_done(struct tevent_req *subreq)
+{
+    struct ipa_account_info_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_account_info_state);
+
+    switch (state->type) {
+    case IPA_ACCOUNT_INFO_SUBDOMAIN:
+        ret = ipa_subdomain_account_recv(subreq, &state->dp_error);
+        break;
+    case IPA_ACCOUNT_INFO_NETGROUP:
+        ret = ipa_id_get_netgroup_recv(subreq, &state->dp_error);
+        break;
+    case IPA_ACCOUNT_INFO_OTHER:
+        ret = ipa_id_get_account_info_recv(subreq, &state->dp_error);
+        break;
+    default:
+        ret = EINVAL;
+        break;
+    }
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
 
+errno_t ipa_account_info_recv(struct tevent_req *req,
+                              int *_dp_error)
+{
+    struct ipa_account_info_state *state = NULL;
+
+    state = tevent_req_data(req, struct ipa_account_info_state);
+
+    /* Fail the request after collecting the dp_error */
+    if (_dp_error) {
+        *_dp_error = state->dp_error;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+struct ipa_account_info_handler_state {
+    struct dp_reply_std reply;
+};
+
+static void ipa_account_info_handler_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_account_info_handler_send(TALLOC_CTX *mem_ctx,
+                              struct ipa_id_ctx *id_ctx,
+                              struct dp_id_data *data,
+                              struct dp_req_params *params)
+{
+    struct ipa_account_info_handler_state *state;
+    struct tevent_req *subreq = NULL;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_account_info_handler_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    if (sdap_is_enum_request(data)) {
+        DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n");
+        ret = EOK;
+        goto immediately;
+    }
+
+    subreq = ipa_account_info_send(state, params->be_ctx, id_ctx, data);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
     tevent_req_set_callback(subreq, ipa_account_info_handler_done, req);
 
     return req;
@@ -1425,17 +1513,7 @@ static void ipa_account_info_handler_done(struct tevent_req *subreq)
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ipa_account_info_handler_state);
 
-    switch (state->type) {
-    case IPA_ACCOUNT_INFO_SUBDOMAIN:
-        ret = ipa_subdomain_account_recv(subreq, &dp_error);
-        break;
-    case IPA_ACCOUNT_INFO_NETGROUP:
-        ret = ipa_id_get_netgroup_recv(subreq, &dp_error);
-        break;
-    case IPA_ACCOUNT_INFO_OTHER:
-        ret = ipa_id_get_account_info_recv(subreq, &dp_error);
-        break;
-    }
+    ret = ipa_account_info_recv(subreq, &dp_error);
     talloc_zfree(subreq);
 
     /* TODO For backward compatibility we always return EOK to DP now. */
diff --git a/src/providers/ipa/ipa_id.h b/src/providers/ipa/ipa_id.h
index 4b25498821..fe9acfeef0 100644
--- a/src/providers/ipa/ipa_id.h
+++ b/src/providers/ipa/ipa_id.h
@@ -33,6 +33,14 @@
 
 #define IPA_DEFAULT_VIEW_NAME "Default Trust View"
 
+struct tevent_req *
+ipa_account_info_send(TALLOC_CTX *mem_ctx,
+                      struct be_ctx *be_ctx,
+                      struct ipa_id_ctx *id_ctx,
+                      struct dp_id_data *data);
+errno_t ipa_account_info_recv(struct tevent_req *req,
+                              int *_dp_error);
+
 struct tevent_req *
 ipa_account_info_handler_send(TALLOC_CTX *mem_ctx,
                               struct ipa_id_ctx *id_ctx,
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index b3060e228c..cdfd11d7ae 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -594,7 +594,7 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx,
         }
     }
 
-    ret = sdap_refresh_init(be_ctx, sdap_id_ctx);
+    ret = ipa_refresh_init(be_ctx, ipa_id_ctx);
     if (ret != EOK && ret != EEXIST) {
         DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh "
               "will not work [%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/providers/ipa/ipa_refresh.c b/src/providers/ipa/ipa_refresh.c
new file mode 100644
index 0000000000..72051cfdd9
--- /dev/null
+++ b/src/providers/ipa/ipa_refresh.c
@@ -0,0 +1,264 @@
+/*
+    Copyright (C) 2019 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_id.h"
+
+struct ipa_refresh_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct dp_id_data *account_req;
+    struct ipa_id_ctx *id_ctx;
+    char **names;
+    size_t index;
+};
+
+static errno_t ipa_refresh_step(struct tevent_req *req);
+static void ipa_refresh_done(struct tevent_req *subreq);
+
+static struct tevent_req *ipa_refresh_send(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct be_ctx *be_ctx,
+                                            struct sss_domain_info *domain,
+                                            int entry_type,
+                                            char **names,
+                                            void *pvt)
+{
+    struct ipa_refresh_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_refresh_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    if (names == NULL) {
+        ret = EOK;
+        goto immediately;
+    }
+
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->id_ctx = talloc_get_type(pvt, struct ipa_id_ctx);
+    state->names = names;
+    state->index = 0;
+
+    state->account_req = be_refresh_acct_req(state, entry_type,
+                                             BE_FILTER_NAME, domain);
+    if (state->account_req == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ret = ipa_refresh_step(req);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n");
+        goto immediately;
+    } else if (ret != EAGAIN) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "ipa_refresh_step() failed "
+                                   "[%d]: %s\n", ret, sss_strerror(ret));
+        goto immediately;
+    }
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t ipa_refresh_step(struct tevent_req *req)
+{
+    struct ipa_refresh_state *state = NULL;
+    struct tevent_req *subreq = NULL;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct ipa_refresh_state);
+
+    if (state->names == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    state->account_req->filter_value = state->names[state->index];
+    if (state->account_req->filter_value == NULL) {
+        ret = EOK;
+        goto done;
+    }
+
+    subreq = ipa_account_info_send(state, state->be_ctx, state->id_ctx,
+                                  state->account_req);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, ipa_refresh_done, req);
+
+    state->index++;
+    ret = EAGAIN;
+
+done:
+    return ret;
+}
+
+static void ipa_refresh_done(struct tevent_req *subreq)
+{
+    struct ipa_refresh_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t dp_error;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ipa_refresh_state);
+
+    ret = ipa_account_info_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, "
+              "errno: %d]\n", be_req2str(state->account_req->entry_type),
+              dp_error, ret);
+        goto done;
+    }
+
+    ret = ipa_refresh_step(req);
+    if (ret == EAGAIN) {
+        return;
+    }
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static errno_t ipa_refresh_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+static struct tevent_req *
+ipa_refresh_users_send(TALLOC_CTX *mem_ctx,
+                        struct tevent_context *ev,
+                        struct be_ctx *be_ctx,
+                        struct sss_domain_info *domain,
+                        char **names,
+                        void *pvt)
+{
+    return ipa_refresh_send(mem_ctx, ev, be_ctx, domain,
+                           BE_REQ_USER, names, pvt);
+}
+
+static errno_t ipa_refresh_users_recv(struct tevent_req *req)
+{
+    return ipa_refresh_recv(req);
+}
+
+static struct tevent_req *
+ipa_refresh_groups_send(TALLOC_CTX *mem_ctx,
+                         struct tevent_context *ev,
+                         struct be_ctx *be_ctx,
+                         struct sss_domain_info *domain,
+                         char **names,
+                         void *pvt)
+{
+    return ipa_refresh_send(mem_ctx, ev, be_ctx, domain,
+                           BE_REQ_GROUP, names, pvt);
+}
+
+static errno_t ipa_refresh_groups_recv(struct tevent_req *req)
+{
+    return ipa_refresh_recv(req);
+}
+
+static struct tevent_req *
+ipa_refresh_netgroups_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct be_ctx *be_ctx,
+                            struct sss_domain_info *domain,
+                            char **names,
+                            void *pvt)
+{
+    return ipa_refresh_send(mem_ctx, ev, be_ctx, domain,
+                           BE_REQ_NETGROUP, names, pvt);
+}
+
+static errno_t ipa_refresh_netgroups_recv(struct tevent_req *req)
+{
+    return ipa_refresh_recv(req);
+}
+
+errno_t ipa_refresh_init(struct be_ctx *be_ctx,
+                         struct ipa_id_ctx *id_ctx)
+{
+    errno_t ret;
+
+    ret = be_refresh_ctx_init(be_ctx, SYSDB_NAME);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n");
+        return ENOMEM;
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_USERS,
+                            ipa_refresh_users_send,
+                            ipa_refresh_users_recv,
+                            id_ctx);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of users "
+              "will not work [%d]: %s\n", ret, strerror(ret));
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_GROUPS,
+                            ipa_refresh_groups_send,
+                            ipa_refresh_groups_recv,
+                            id_ctx);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of groups "
+              "will not work [%d]: %s\n", ret, strerror(ret));
+    }
+
+    ret = be_refresh_add_cb(be_ctx->refresh_ctx,
+                            BE_REFRESH_TYPE_NETGROUPS,
+                            ipa_refresh_netgroups_send,
+                            ipa_refresh_netgroups_recv,
+                            id_ctx);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of netgroups "
+              "will not work [%d]: %s\n", ret, strerror(ret));
+    }
+
+    return ret;
+}
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to