[SSSD] [PATCHES] Enable enumeration and cleanup tasks for subdomains

Jakub Hrozek jhrozek at redhat.com
Sat Aug 24 16:42:21 UTC 2013


On Thu, Aug 22, 2013 at 12:25:28PM +0200, Jakub Hrozek wrote:
> On Thu, Aug 22, 2013 at 12:06:33PM +0200, Jakub Hrozek wrote:
> > Hi,
> > 
> > the attached patch implements enumeration and cleanup for the IPA server
> > mode and also makes it possible to support enumeration and cleanup for
> > other subdomains in general (we already have a request from one of our
> > users to enumerate trusted AD domains).
> > 
> > Some of the changes can also be leveraged to special-case enumeration
> > requests in AD or IPA providers to e.g. download the master domain data
> > before enumerating the domain for the first time.
> > 
> > I hope the patches are split well to make it possible to review them
> > easily. The bigger patches usually just move code around.
> 
> I forgot to note two important things:
> 
> 1) the subdomain enumeration setting is inherited from the master domain
> enumeration. Is this OK or do we need to enumerate the AD trusted domain
> automatically? I think that only a minority of the legacy clients
> actually need enumeration, so as long as we document how enumeration
> works in the server mode, we should be fine.
> 
> 2) These patches currently do not optimize the enumeration which is what
> the ticket initially talked about. The reason is that just enabling the
> enumeration properly took a long time and also performance is only a
> problem for the initial enumeration. The subsequent ones can leverage
> lastUSN to only download deltas. Because the IPA server would mostly
> stay online and running, I think the initial enumeration can be further
> optimized in 1.11.1. Sumit came up with some idea when he visited Brno,
> so I'll work on that next week.

I found a couple of bugs (tevent_req_error not terminated with return,
uninitialized variable etc). New patches are attached.

Please note that these patches depend on the patch with subject "DB:
Update sss_domain_info with new updated data".
-------------- next part --------------
>From 2bdf9b0b06a000efcfbf22d404b68372eef584ad Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 20 Aug 2013 16:12:13 +0200
Subject: [PATCH 01/11] LDAP: Add enum_{users,groups} to follow the tevent_req
 style

The enum code was quite old and predated the tevent_req style. In
particular, the enum code was checking tevent state direcly and not
using _recv functions or the helper macros we added later.
As a consequence, it was not easy to read. This patch adds the standard
_recv functions to read the status of the enum requests.
---
 src/providers/ldap/ldap_id_enum.c | 41 +++++++++++++++++----------------------
 1 file changed, 18 insertions(+), 23 deletions(-)

diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index 06d6e8772c066169a38dc9b37080d07cf710a8d8..f9507a82a513c7d890e86e4d2dc6b975ed2d7875 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -192,6 +192,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct sdap_domain *sdom,
                                           struct sdap_id_op *op,
                                           bool purge);
+static errno_t enum_users_recv(struct tevent_req *req);
 static void ldap_id_enum_users_done(struct tevent_req *subreq);
 static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
@@ -199,6 +200,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct sdap_domain *sdom,
                                           struct sdap_id_op *op,
                                           bool purge);
+static errno_t enum_groups_recv(struct tevent_req *req);
 static void ldap_id_enum_groups_done(struct tevent_req *subreq);
 static void ldap_id_enum_services_done(struct tevent_req *subreq);
 static void ldap_id_enum_cleanup_done(struct tevent_req *subreq);
@@ -296,22 +298,12 @@ static void ldap_id_enum_users_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct global_enum_state *state = tevent_req_data(req,
                                                  struct global_enum_state);
-    enum tevent_req_state tstate;
     uint64_t err = 0;
     int ret, dp_error = DP_ERR_FATAL;
 
-    if (tevent_req_is_error(subreq, &tstate, &err)) {
-        if (tstate != TEVENT_REQ_USER_ERROR) {
-            err = EIO;
-        }
-
-        if (err == ENOENT) {
-            err = EOK;
-        }
-    }
+    err = enum_users_recv(subreq);
     talloc_zfree(subreq);
-
-    if (err != EOK) {
+    if (err != EOK && err != ENOENT) {
         /* We call sdap_id_op_done only on error
          * as the connection is reused by groups enumeration */
         ret = sdap_id_op_done(state->op, (int)err, &dp_error);
@@ -351,21 +343,11 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct global_enum_state *state = tevent_req_data(req,
                                                  struct global_enum_state);
-    enum tevent_req_state tstate;
     uint64_t err = 0;
     int ret, dp_error = DP_ERR_FATAL;
 
-    if (tevent_req_is_error(subreq, &tstate, &err)) {
-        if (tstate != TEVENT_REQ_USER_ERROR) {
-            err = EIO;
-        }
-
-        if (err == ENOENT) {
-            err = EOK;
-        }
-    }
+    err = enum_groups_recv(subreq);
     talloc_zfree(subreq);
-
     if (err != EOK) {
         /* We call sdap_id_op_done only on error
          * as the connection is reused by services enumeration */
@@ -632,6 +614,13 @@ static void enum_users_op_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
+static errno_t enum_users_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
 /* =Group-Enumeration===================================================== */
 
 struct enum_groups_state {
@@ -794,3 +783,9 @@ static void enum_groups_op_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
+static errno_t enum_groups_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
-- 
1.8.3.1

-------------- next part --------------
>From d3b30cbbd55484fa25cafb467859189a4eb4475a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 20 Aug 2013 16:12:32 +0200
Subject: [PATCH 02/11] LDAP: Remove unused constant

The constant was not used since Euegene came up with his reconnection
logic.
---
 src/providers/ldap/ldap_id_enum.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index f9507a82a513c7d890e86e4d2dc6b975ed2d7875..86076d9ad20f1c09b497a6815fc7fe9991ce2a71 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -176,8 +176,6 @@ int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
     return EOK;
 }
 
-#define MAX_ENUM_RESTARTS 3
-
 struct global_enum_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
-- 
1.8.3.1

-------------- next part --------------
>From 8d0e18d2e6cf780a82b46c3a2d4c776112c78027 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 20 Aug 2013 16:13:56 +0200
Subject: [PATCH 03/11] LDAP: Move the ldap enum request to its own reusable
 module

The LDAP enumeration was too closely tied to the LDAP identity provider.
Because some providers might need special handling such as refresh the
master domain record before proceeding with the enumeration itself, this
patch splits the request itself to a separate async request and lets the
ldap_id_enum.c module only configure this new request.

Also move the enum timestamp to sdap_domain to make the enum tracking
per sdap domain. The cleanup timestamp will be moved in another patch.
---
 Makefile.am                                        |   2 +
 src/providers/ldap/ldap_common.h                   |   5 -
 src/providers/ldap/ldap_id_enum.c                  | 637 +--------------------
 src/providers/ldap/sdap.h                          |   3 +
 .../ldap/{ldap_id_enum.c => sdap_async_enum.c}     | 358 +++++-------
 src/providers/ldap/sdap_async_enum.h               |  38 ++
 src/providers/ldap/sdap_reinit.c                   |  13 +-
 7 files changed, 184 insertions(+), 872 deletions(-)
 copy src/providers/ldap/{ldap_id_enum.c => sdap_async_enum.c} (64%)
 create mode 100644 src/providers/ldap/sdap_async_enum.h

diff --git a/Makefile.am b/Makefile.am
index 4dd8ed352179211dcbbc19a61936d1041ab08408..8de2e0fbb01a8abe2739d0985daec9a97a2c586d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -460,6 +460,7 @@ dist_noinst_HEADERS = \
     src/providers/ldap/sdap_range.h \
     src/providers/ldap/sdap_users.h \
     src/providers/ldap/sdap_dyndns.h \
+    src/providers/ldap/sdap_async_enum.h \
     src/providers/ipa/ipa_common.h \
     src/providers/ipa/ipa_config.h \
     src/providers/ipa/ipa_access.h \
@@ -1428,6 +1429,7 @@ pkglib_LTLIBRARIES += libsss_ldap_common.la
 libsss_ldap_common_la_SOURCES = \
     src/providers/ldap/ldap_id.c \
     src/providers/ldap/ldap_id_enum.c \
+    src/providers/ldap/sdap_async_enum.c \
     src/providers/ldap/ldap_id_cleanup.c \
     src/providers/ldap/ldap_id_netgroup.c \
     src/providers/ldap/ldap_id_services.c \
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index db2466ad832d7867d9244a835cde1adab39d347d..c9b2f663b72658c9cdcd4d54ec32b1dd74f31326 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -63,8 +63,6 @@ struct sdap_id_ctx {
     /* connection to a server */
     struct sdap_id_conn_ctx *conn;
 
-    /* enumeration loop timer */
-    struct timeval last_enum;
     /* cleanup loop timer */
     struct timeval last_purge;
 
@@ -170,9 +168,6 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
 int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
 int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
 
-struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx);
-
 void sdap_mark_offline(struct sdap_id_ctx *ctx);
 
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index 86076d9ad20f1c09b497a6815fc7fe9991ce2a71..43eb4c9972bb7383435e2e130f01646af46c0c38 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -31,6 +31,7 @@
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap_async.h"
 #include "providers/ldap/sdap_idmap.h"
+#include "providers/ldap/sdap_async_enum.h"
 
 extern struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
                                                struct tevent_context *ev,
@@ -38,9 +39,6 @@ extern struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
 
 /* ==Enumeration-Task===================================================== */
 
-static int ldap_id_enumerate_retry(struct tevent_req *req);
-static void ldap_id_enumerate_connect_done(struct tevent_req *req);
-
 static void ldap_id_enumerate_reschedule(struct tevent_req *req);
 
 static void ldap_id_enumerate_timeout(struct tevent_context *ev,
@@ -66,7 +64,7 @@ static void ldap_id_enumerate_timer(struct tevent_context *ev,
         return;
     }
 
-    req = ldap_id_enumerate_send(ev, ctx);
+    req = sdap_dom_enum_send(ctx, ev, ctx, ctx->opts->sdom, ctx->conn);
     if (!req) {
         DEBUG(1, ("Failed to schedule enumeration, retrying later!\n"));
         /* schedule starting from now, not the last run */
@@ -129,30 +127,19 @@ static void ldap_id_enumerate_reschedule(struct tevent_req *req)
 {
     struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
                                                        struct sdap_id_ctx);
-    enum tevent_req_state tstate;
-    uint64_t err;
     struct timeval tv;
     int delay;
     errno_t ret;
 
-    if (tevent_req_is_error(req, &tstate, &err)) {
+    ret = sdap_dom_enum_recv(req);
+    talloc_zfree(req);
+    if (ret != EOK) {
         /* On error schedule starting from now, not the last run */
         tv = tevent_timeval_current();
     } else {
-        tv = ctx->last_enum;
+        tv = ctx->opts->sdom->last_enum;
 
-        /* Ok, we've completed an enumeration. Save this to the
-         * sysdb so we can postpone starting up the enumeration
-         * process on the next SSSD service restart (to avoid
-         * slowing down system boot-up
-         */
-        ret = sysdb_set_enumerated(ctx->be->domain->sysdb, ctx->be->domain, true);
-        if (ret != EOK) {
-            DEBUG(1, ("Could not mark domain as having enumerated.\n"));
-            /* This error is non-fatal, so continue */
-        }
     }
-    talloc_zfree(req);
 
     delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
     tv = tevent_timeval_add(&tv, delay, 0);
@@ -175,615 +162,3 @@ int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
 
     return EOK;
 }
-
-struct global_enum_state {
-    struct tevent_context *ev;
-    struct sdap_id_ctx *ctx;
-    struct sdap_id_op *op;
-
-    bool purge;
-};
-
-static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
-                                          struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx,
-                                          struct sdap_domain *sdom,
-                                          struct sdap_id_op *op,
-                                          bool purge);
-static errno_t enum_users_recv(struct tevent_req *req);
-static void ldap_id_enum_users_done(struct tevent_req *subreq);
-static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
-                                          struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx,
-                                          struct sdap_domain *sdom,
-                                          struct sdap_id_op *op,
-                                          bool purge);
-static errno_t enum_groups_recv(struct tevent_req *req);
-static void ldap_id_enum_groups_done(struct tevent_req *subreq);
-static void ldap_id_enum_services_done(struct tevent_req *subreq);
-static void ldap_id_enum_cleanup_done(struct tevent_req *subreq);
-
-struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx)
-{
-    struct global_enum_state *state;
-    struct tevent_req *req;
-    int t;
-
-    req = tevent_req_create(ctx, &state, struct global_enum_state);
-    if (!req) return NULL;
-
-    state->ev = ev;
-    state->ctx = ctx;
-    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
-    if (!state->op) {
-        DEBUG(2, ("sdap_id_op_create failed\n"));
-        talloc_zfree(req);
-        return NULL;
-    }
-
-    ctx->last_enum = tevent_timeval_current();
-
-    t = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-    if ((ctx->last_purge.tv_sec + t) < ctx->last_enum.tv_sec) {
-        state->purge = true;
-    } else {
-        state->purge = false;
-    }
-
-    int ret = ldap_id_enumerate_retry(req);
-    if (ret != EOK) {
-        DEBUG(2, ("ldap_id_enumerate_retry failed\n"));
-        talloc_zfree(req);
-        return NULL;
-    }
-
-    return req;
-}
-
-static int ldap_id_enumerate_retry(struct tevent_req *req)
-{
-    struct global_enum_state *state = tevent_req_data(req,
-                                                      struct global_enum_state);
-    struct tevent_req *subreq;
-    int ret;
-
-    subreq = sdap_id_op_connect_send(state->op, state, &ret);
-    if (!subreq) {
-        return ret;
-    }
-
-    tevent_req_set_callback(subreq, ldap_id_enumerate_connect_done, req);
-    return EOK;
-}
-
-static void ldap_id_enumerate_connect_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
-    int ret, dp_error;
-
-    ret = sdap_id_op_connect_recv(subreq, &dp_error);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        if (dp_error == DP_ERR_OFFLINE) {
-            tevent_req_done(req);
-        } else {
-            DEBUG(9, ("User enumeration failed to connect to LDAP server: (%d)[%s]\n",
-                      ret, strerror(ret)));
-            tevent_req_error(req, ret);
-        }
-
-        return;
-    }
-
-    subreq = enum_users_send(state, state->ev,
-                             state->ctx, state->ctx->opts->sdom,
-                             state->op, state->purge);
-    if(!subreq) {
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
-
-    tevent_req_set_callback(subreq, ldap_id_enum_users_done, req);
-}
-
-static void ldap_id_enum_users_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
-    uint64_t err = 0;
-    int ret, dp_error = DP_ERR_FATAL;
-
-    err = enum_users_recv(subreq);
-    talloc_zfree(subreq);
-    if (err != EOK && err != ENOENT) {
-        /* We call sdap_id_op_done only on error
-         * as the connection is reused by groups enumeration */
-        ret = sdap_id_op_done(state->op, (int)err, &dp_error);
-        if (dp_error == DP_ERR_OK) {
-            /* retry */
-            ret = ldap_id_enumerate_retry(req);
-            if (ret == EOK) {
-                return;
-            }
-
-            dp_error = DP_ERR_FATAL;
-        }
-
-        if (dp_error == DP_ERR_OFFLINE) {
-            tevent_req_done(req);
-        } else {
-            DEBUG(9, ("User enumeration failed with: (%d)[%s]\n",
-                      ret, strerror(ret)));
-            tevent_req_error(req, ret);
-        }
-        return;
-    }
-
-    subreq = enum_groups_send(state, state->ev, state->ctx,
-                              state->ctx->opts->sdom,
-                              state->op, state->purge);
-    if (!subreq) {
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
-    tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req);
-}
-
-static void ldap_id_enum_groups_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
-    uint64_t err = 0;
-    int ret, dp_error = DP_ERR_FATAL;
-
-    err = enum_groups_recv(subreq);
-    talloc_zfree(subreq);
-    if (err != EOK) {
-        /* We call sdap_id_op_done only on error
-         * as the connection is reused by services enumeration */
-        ret = sdap_id_op_done(state->op, (int)err, &dp_error);
-        if (dp_error == DP_ERR_OK && ret != EOK) {
-            /* retry */
-            ret = ldap_id_enumerate_retry(req);
-            if (ret == EOK) {
-                return;
-            }
-
-            dp_error = DP_ERR_FATAL;
-        }
-
-        if (ret != EOK) {
-            if (dp_error == DP_ERR_OFFLINE) {
-                tevent_req_done(req);
-            } else {
-                DEBUG(9, ("Group enumeration failed with: (%d)[%s]\n",
-                          ret, strerror(ret)));
-                tevent_req_error(req, ret);
-            }
-
-            return;
-        }
-    }
-
-    subreq = enum_services_send(state, state->ev, state->ctx,
-                                state->op, state->purge);
-    if (!subreq) {
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
-    tevent_req_set_callback(subreq, ldap_id_enum_services_done, req);
-}
-
-static void ldap_id_enum_services_done(struct tevent_req *subreq)
-{
-    errno_t ret;
-    int dp_error = DP_ERR_FATAL;
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
-
-    ret = enum_services_recv(subreq);
-    talloc_zfree(subreq);
-    if (ret == ENOENT) ret = EOK;
-
-    /* All enumerations are complete, so conclude the
-     * id_op
-     */
-    ret = sdap_id_op_done(state->op, ret, &dp_error);
-    if (dp_error == DP_ERR_OK && ret != EOK) {
-        /* retry */
-        ret = ldap_id_enumerate_retry(req);
-        if (ret == EOK) {
-            return;
-        }
-
-        dp_error = DP_ERR_FATAL;
-    }
-
-    if (ret != EOK) {
-        if (dp_error == DP_ERR_OFFLINE) {
-            tevent_req_done(req);
-        } else {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("Service enumeration failed with: (%d)[%s]\n",
-                   ret, strerror(ret)));
-            tevent_req_error(req, ret);
-        }
-
-        return;
-    }
-
-    if (state->purge) {
-
-        subreq = ldap_id_cleanup_send(state, state->ev, state->ctx);
-        if (!subreq) {
-            tevent_req_error(req, ENOMEM);
-            return;
-        }
-
-        tevent_req_set_callback(subreq, ldap_id_enum_cleanup_done, req);
-        return;
-    }
-
-    tevent_req_done(req);
-}
-
-static void ldap_id_enum_cleanup_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    talloc_zfree(subreq);
-    tevent_req_done(req);
-}
-
-/* ==User-Enumeration===================================================== */
-
-struct enum_users_state {
-    struct tevent_context *ev;
-    struct sdap_id_ctx *ctx;
-    struct sdap_domain *sdom;
-    struct sdap_id_op *op;
-
-    char *filter;
-    const char **attrs;
-};
-
-static void enum_users_op_done(struct tevent_req *subreq);
-
-static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
-                                          struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx,
-                                          struct sdap_domain *sdom,
-                                          struct sdap_id_op *op,
-                                          bool purge)
-{
-    struct tevent_req *req, *subreq;
-    struct enum_users_state *state;
-    int ret;
-    bool use_mapping;
-
-    req = tevent_req_create(memctx, &state, struct enum_users_state);
-    if (!req) return NULL;
-
-    state->ev = ev;
-    state->sdom = sdom;
-    state->ctx = ctx;
-    state->op = op;
-
-    use_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                          ctx->opts->idmap_ctx,
-                                                          sdom->dom->domain_id);
-
-    /* We always want to filter on objectclass and an available name */
-    state->filter = talloc_asprintf(state,
-                                    "(&(objectclass=%s)(%s=*)",
-                                    ctx->opts->user_map[SDAP_OC_USER].name,
-                                    ctx->opts->user_map[SDAP_AT_USER_NAME].name);
-    if (!state->filter) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("Failed to build base filter\n"));
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    if (use_mapping) {
-        /* If we're ID-mapping, check for the objectSID as well */
-        state->filter = talloc_asprintf_append_buffer(
-                state->filter, "(%s=*)",
-                ctx->opts->user_map[SDAP_AT_USER_OBJECTSID].name);
-    } else {
-        /* We're not ID-mapping, so make sure to only get entries
-         * that have UID and GID
-         */
-        state->filter = talloc_asprintf_append_buffer(
-                state->filter, "(%s=*)(%s=*)",
-                ctx->opts->user_map[SDAP_AT_USER_UID].name,
-                ctx->opts->user_map[SDAP_AT_USER_GID].name);
-    }
-    if (!state->filter) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("Failed to build base filter\n"));
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    if (ctx->srv_opts && ctx->srv_opts->max_user_value && !purge) {
-        /* If we have lastUSN available and we're not doing a full
-         * refresh, limit to changes with a higher entryUSN value.
-         */
-        state->filter = talloc_asprintf_append_buffer(
-                state->filter,
-                "(%s>=%s)(!(%s=%s))",
-                ctx->opts->user_map[SDAP_AT_USER_USN].name,
-                ctx->srv_opts->max_user_value,
-                ctx->opts->user_map[SDAP_AT_USER_USN].name,
-                ctx->srv_opts->max_user_value);
-
-        if (!state->filter) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("Failed to build base filter\n"));
-            ret = ENOMEM;
-            goto fail;
-        }
-    }
-
-    /* Terminate the search filter */
-    state->filter = talloc_asprintf_append_buffer(state->filter, ")");
-    if (!state->filter) {
-        DEBUG(2, ("Failed to build base filter\n"));
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    /* TODO: handle attrs_type */
-    ret = build_attrs_from_map(state, ctx->opts->user_map, SDAP_OPTS_USER,
-                               NULL, &state->attrs, NULL);
-    if (ret != EOK) goto fail;
-
-    /* TODO: restrict the enumerations to using a single
-     * search base at a time.
-     */
-
-    subreq = sdap_get_users_send(state, state->ev,
-                                 state->sdom->dom,
-                                 state->sdom->dom->sysdb,
-                                 state->ctx->opts,
-                                 state->sdom->user_search_bases,
-                                 sdap_id_op_handle(state->op),
-                                 state->attrs, state->filter,
-                                 dp_opt_get_int(state->ctx->opts->basic,
-                                                SDAP_ENUM_SEARCH_TIMEOUT),
-                                 true);
-    if (!subreq) {
-        ret = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(subreq, enum_users_op_done, req);
-
-    return req;
-
-fail:
-    tevent_req_error(req, ret);
-    tevent_req_post(req, ev);
-    return req;
-}
-
-static void enum_users_op_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    struct enum_users_state *state = tevent_req_data(req,
-                                                     struct enum_users_state);
-    char *usn_value;
-    char *endptr = NULL;
-    unsigned usn_number;
-    int ret;
-
-    ret = sdap_get_users_recv(subreq, state, &usn_value);
-    talloc_zfree(subreq);
-    if (ret) {
-        tevent_req_error(req, ret);
-        return;
-    }
-
-    if (usn_value) {
-        talloc_zfree(state->ctx->srv_opts->max_user_value);
-        state->ctx->srv_opts->max_user_value = talloc_steal(state->ctx, usn_value);
-
-        usn_number = strtoul(usn_value, &endptr, 10);
-        if ((endptr == NULL || (*endptr == '\0' && endptr != usn_value))
-            && (usn_number > state->ctx->srv_opts->last_usn)) {
-            state->ctx->srv_opts->last_usn = usn_number;
-        }
-    }
-
-    DEBUG(4, ("Users higher USN value: [%s]\n",
-              state->ctx->srv_opts->max_user_value));
-
-    tevent_req_done(req);
-}
-
-static errno_t enum_users_recv(struct tevent_req *req)
-{
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    return EOK;
-}
-
-/* =Group-Enumeration===================================================== */
-
-struct enum_groups_state {
-    struct tevent_context *ev;
-    struct sdap_id_ctx *ctx;
-    struct sdap_domain *sdom;
-    struct sdap_id_op *op;
-
-    char *filter;
-    const char **attrs;
-};
-
-static void enum_groups_op_done(struct tevent_req *subreq);
-
-static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
-                                          struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx,
-                                          struct sdap_domain *sdom,
-                                          struct sdap_id_op *op,
-                                          bool purge)
-{
-    struct tevent_req *req, *subreq;
-    struct enum_groups_state *state;
-    int ret;
-    bool use_mapping;
-
-    req = tevent_req_create(memctx, &state, struct enum_groups_state);
-    if (!req) return NULL;
-
-    state->ev = ev;
-    state->sdom = sdom;
-    state->ctx = ctx;
-    state->op = op;
-
-    use_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                          ctx->opts->idmap_ctx,
-                                                          sdom->dom->domain_id);
-
-    /* We always want to filter on objectclass and an available name */
-    state->filter = talloc_asprintf(state,
-                                    "(&(objectclass=%s)(%s=*)",
-                                    ctx->opts->group_map[SDAP_OC_GROUP].name,
-                                    ctx->opts->group_map[SDAP_AT_GROUP_NAME].name);
-    if (!state->filter) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("Failed to build base filter\n"));
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    if (use_mapping) {
-        /* If we're ID-mapping, check for the objectSID as well */
-        state->filter = talloc_asprintf_append_buffer(
-                state->filter, "(%s=*)",
-                ctx->opts->group_map[SDAP_AT_GROUP_OBJECTSID].name);
-    } else {
-        /* We're not ID-mapping, so make sure to only get entries
-         * that have a non-zero GID.
-         */
-        state->filter = talloc_asprintf_append_buffer(
-                state->filter, "(&(%s=*)(!(%s=0)))",
-                ctx->opts->group_map[SDAP_AT_GROUP_GID].name,
-                ctx->opts->group_map[SDAP_AT_GROUP_GID].name);
-    }
-    if (!state->filter) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("Failed to build base filter\n"));
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    if (ctx->srv_opts && ctx->srv_opts->max_group_value && !purge) {
-        state->filter = talloc_asprintf_append_buffer(
-                state->filter,
-                "(%s>=%s)(!(%s=%s))",
-                ctx->opts->group_map[SDAP_AT_GROUP_USN].name,
-                ctx->srv_opts->max_group_value,
-                ctx->opts->group_map[SDAP_AT_GROUP_USN].name,
-                ctx->srv_opts->max_group_value);
-        if (!state->filter) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("Failed to build base filter\n"));
-            ret = ENOMEM;
-            goto fail;
-        }
-    }
-
-    /* Terminate the search filter */
-    state->filter = talloc_asprintf_append_buffer(state->filter, ")");
-    if (!state->filter) {
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("Failed to build base filter\n"));
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    /* TODO: handle attrs_type */
-    ret = build_attrs_from_map(state, ctx->opts->group_map, SDAP_OPTS_GROUP,
-                               NULL, &state->attrs, NULL);
-    if (ret != EOK) goto fail;
-
-    /* TODO: restrict the enumerations to using a single
-     * search base at a time.
-     */
-
-    subreq = sdap_get_groups_send(state, state->ev,
-                                  state->sdom,
-                                  state->ctx->opts,
-                                  sdap_id_op_handle(state->op),
-                                  state->attrs, state->filter,
-                                  dp_opt_get_int(state->ctx->opts->basic,
-                                                 SDAP_ENUM_SEARCH_TIMEOUT),
-                                  true);
-    if (!subreq) {
-        ret = ENOMEM;
-        goto fail;
-    }
-    tevent_req_set_callback(subreq, enum_groups_op_done, req);
-
-    return req;
-
-fail:
-    tevent_req_error(req, ret);
-    tevent_req_post(req, ev);
-    return req;
-}
-
-static void enum_groups_op_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    struct enum_groups_state *state = tevent_req_data(req,
-                                                 struct enum_groups_state);
-    char *usn_value;
-    char *endptr = NULL;
-    unsigned usn_number;
-    int ret;
-
-    ret = sdap_get_groups_recv(subreq, state, &usn_value);
-    talloc_zfree(subreq);
-    if (ret) {
-        tevent_req_error(req, ret);
-        return;
-    }
-
-    if (usn_value) {
-        talloc_zfree(state->ctx->srv_opts->max_group_value);
-        state->ctx->srv_opts->max_group_value =
-                                        talloc_steal(state->ctx, usn_value);
-        usn_number = strtoul(usn_value, &endptr, 10);
-        if ((endptr == NULL || (*endptr == '\0' && endptr != usn_value))
-            && (usn_number > state->ctx->srv_opts->last_usn)) {
-            state->ctx->srv_opts->last_usn = usn_number;
-        }
-    }
-
-    DEBUG(4, ("Groups higher USN value: [%s]\n",
-              state->ctx->srv_opts->max_group_value));
-
-    tevent_req_done(req);
-}
-
-static errno_t enum_groups_recv(struct tevent_req *req)
-{
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    return EOK;
-}
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 6d24982b7759f7d4679d1be3030d0de0a3b9a607..f5f6d90aa075b5e7cffa4445cda87b1757dd3ca3 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -383,6 +383,9 @@ struct sdap_domain {
     struct sdap_domain *next, *prev;
     /* Need to modify the list from a talloc destructor */
     struct sdap_domain **head;
+
+    /* enumeration loop timer */
+    struct timeval last_enum;
 };
 
 struct sdap_options {
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/sdap_async_enum.c
similarity index 64%
copy from src/providers/ldap/ldap_id_enum.c
copy to src/providers/ldap/sdap_async_enum.c
index 86076d9ad20f1c09b497a6815fc7fe9991ce2a71..afd19f10e91c7673e852b4077170510831865ebd 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/sdap_async_enum.c
@@ -1,12 +1,13 @@
 /*
     SSSD
 
-    LDAP Identity Enumeration
+    LDAP Enumeration Module
 
     Authors:
         Simo Sorce <ssorce at redhat.com>
+        Jakub Hrozek <jhrozek at redhat.com>
 
-    Copyright (C) 2009 Red Hat
+    Copyright (C) 2013 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
@@ -23,8 +24,6 @@
 */
 
 #include <errno.h>
-#include <time.h>
-#include <sys/time.h>
 
 #include "util/util.h"
 #include "db/sysdb.h"
@@ -36,154 +35,6 @@ extern struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
                                                struct tevent_context *ev,
                                                struct sdap_id_ctx *ctx);
 
-/* ==Enumeration-Task===================================================== */
-
-static int ldap_id_enumerate_retry(struct tevent_req *req);
-static void ldap_id_enumerate_connect_done(struct tevent_req *req);
-
-static void ldap_id_enumerate_reschedule(struct tevent_req *req);
-
-static void ldap_id_enumerate_timeout(struct tevent_context *ev,
-                                      struct tevent_timer *te,
-                                      struct timeval tv, void *pvt);
-
-static void ldap_id_enumerate_timer(struct tevent_context *ev,
-                                    struct tevent_timer *tt,
-                                    struct timeval tv, void *pvt)
-{
-    struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
-    struct tevent_timer *timeout;
-    struct tevent_req *req;
-    int delay;
-    errno_t ret;
-
-    if (be_is_offline(ctx->be)) {
-        DEBUG(4, ("Backend is marked offline, retry later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ldap_id_enumerate_set_timer(ctx, tv);
-        return;
-    }
-
-    req = ldap_id_enumerate_send(ev, ctx);
-    if (!req) {
-        DEBUG(1, ("Failed to schedule enumeration, retrying later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ret = ldap_id_enumerate_set_timer(ctx, tv);
-        if (ret != EOK) {
-            DEBUG(1, ("Error setting up enumerate timer\n"));
-        }
-        return;
-    }
-    tevent_req_set_callback(req, ldap_id_enumerate_reschedule, ctx);
-
-    /* if enumeration takes so long, either we try to enumerate too
-     * frequently, or something went seriously wrong */
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-    tv = tevent_timeval_current_ofs(delay, 0);
-    timeout = tevent_add_timer(ctx->be->ev, req, tv,
-                               ldap_id_enumerate_timeout, req);
-    if (timeout == NULL) {
-        /* If we can't guarantee a timeout, we
-         * need to cancel the request, to avoid
-         * the possibility of starting another
-         * concurrently
-         */
-        talloc_zfree(req);
-
-        DEBUG(1, ("Failed to schedule enumeration, retrying later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ret = ldap_id_enumerate_set_timer(ctx, tv);
-        if (ret != EOK) {
-            DEBUG(1, ("Error setting up enumerate timer\n"));
-        }
-        return;
-    }
-    return;
-}
-
-static void ldap_id_enumerate_timeout(struct tevent_context *ev,
-                                      struct tevent_timer *te,
-                                      struct timeval tv, void *pvt)
-{
-    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
-    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
-                                                       struct sdap_id_ctx);
-    int delay;
-
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-    DEBUG(1, ("Enumeration timed out! Timeout too small? (%ds)!\n", delay));
-
-    tv = tevent_timeval_current_ofs(delay, 0);
-    ldap_id_enumerate_set_timer(ctx, tv);
-
-    talloc_zfree(req);
-}
-
-static void ldap_id_enumerate_reschedule(struct tevent_req *req)
-{
-    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
-                                                       struct sdap_id_ctx);
-    enum tevent_req_state tstate;
-    uint64_t err;
-    struct timeval tv;
-    int delay;
-    errno_t ret;
-
-    if (tevent_req_is_error(req, &tstate, &err)) {
-        /* On error schedule starting from now, not the last run */
-        tv = tevent_timeval_current();
-    } else {
-        tv = ctx->last_enum;
-
-        /* Ok, we've completed an enumeration. Save this to the
-         * sysdb so we can postpone starting up the enumeration
-         * process on the next SSSD service restart (to avoid
-         * slowing down system boot-up
-         */
-        ret = sysdb_set_enumerated(ctx->be->domain->sysdb, ctx->be->domain, true);
-        if (ret != EOK) {
-            DEBUG(1, ("Could not mark domain as having enumerated.\n"));
-            /* This error is non-fatal, so continue */
-        }
-    }
-    talloc_zfree(req);
-
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-    tv = tevent_timeval_add(&tv, delay, 0);
-    ldap_id_enumerate_set_timer(ctx, tv);
-}
-
-int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
-{
-    struct tevent_timer *enum_task;
-
-    DEBUG(6, ("Scheduling next enumeration at %ld.%ld\n",
-              (long)tv.tv_sec, (long)tv.tv_usec));
-
-    enum_task = tevent_add_timer(ctx->be->ev, ctx,
-                                 tv, ldap_id_enumerate_timer, ctx);
-    if (!enum_task) {
-        DEBUG(0, ("FATAL: failed to setup enumeration task!\n"));
-        return EFAULT;
-    }
-
-    return EOK;
-}
-
-struct global_enum_state {
-    struct tevent_context *ev;
-    struct sdap_id_ctx *ctx;
-    struct sdap_id_op *op;
-
-    bool purge;
-};
-
 static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
@@ -191,7 +42,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct sdap_id_op *op,
                                           bool purge);
 static errno_t enum_users_recv(struct tevent_req *req);
-static void ldap_id_enum_users_done(struct tevent_req *subreq);
+
 static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
@@ -199,115 +50,142 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct sdap_id_op *op,
                                           bool purge);
 static errno_t enum_groups_recv(struct tevent_req *req);
-static void ldap_id_enum_groups_done(struct tevent_req *subreq);
-static void ldap_id_enum_services_done(struct tevent_req *subreq);
-static void ldap_id_enum_cleanup_done(struct tevent_req *subreq);
 
-struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
-                                          struct sdap_id_ctx *ctx)
+/* ==Enumeration-Request==================================================== */
+struct sdap_dom_enum_state {
+    struct tevent_context *ev;
+    struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
+    struct sdap_id_conn_ctx *conn;
+    struct sdap_id_op *op;
+
+    bool purge;
+};
+
+static errno_t sdap_dom_enum_retry(struct tevent_req *req);
+static void sdap_dom_enum_conn_done(struct tevent_req *subreq);
+static void sdap_dom_enum_users_done(struct tevent_req *subreq);
+static void sdap_dom_enum_groups_done(struct tevent_req *subreq);
+static void sdap_dom_enum_services_done(struct tevent_req *subreq);
+static void sdap_dom_enum_cleanup_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sdap_dom_enum_send(TALLOC_CTX *memctx,
+                   struct tevent_context *ev,
+                   struct sdap_id_ctx *ctx,
+                   struct sdap_domain *sdom,
+                   struct sdap_id_conn_ctx *conn)
 {
-    struct global_enum_state *state;
     struct tevent_req *req;
+    struct sdap_dom_enum_state *state;
     int t;
+    errno_t ret;
 
-    req = tevent_req_create(ctx, &state, struct global_enum_state);
+    req = tevent_req_create(ctx, &state, struct sdap_dom_enum_state);
     if (!req) return NULL;
 
     state->ev = ev;
     state->ctx = ctx;
+    state->sdom = sdom;
+    state->conn = conn;
     state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
-        DEBUG(2, ("sdap_id_op_create failed\n"));
-        talloc_zfree(req);
-        return NULL;
+        DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_id_op_create failed\n"));
+        ret = EIO;
+        goto fail;
     }
 
-    ctx->last_enum = tevent_timeval_current();
+    sdom->last_enum = tevent_timeval_current();
 
     t = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-    if ((ctx->last_purge.tv_sec + t) < ctx->last_enum.tv_sec) {
+    if ((ctx->last_purge.tv_sec + t) < sdom->last_enum.tv_sec) {
         state->purge = true;
-    } else {
-        state->purge = false;
     }
 
-    int ret = ldap_id_enumerate_retry(req);
+    ret = sdap_dom_enum_retry(req);
     if (ret != EOK) {
-        DEBUG(2, ("ldap_id_enumerate_retry failed\n"));
-        talloc_zfree(req);
-        return NULL;
+        DEBUG(SSSDBG_OP_FAILURE, ("ldap_id_enumerate_retry failed\n"));
+        goto fail;
     }
 
     return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
 }
 
-static int ldap_id_enumerate_retry(struct tevent_req *req)
+static errno_t sdap_dom_enum_retry(struct tevent_req *req)
 {
-    struct global_enum_state *state = tevent_req_data(req,
-                                                      struct global_enum_state);
+    struct sdap_dom_enum_state *state = tevent_req_data(req,
+                                                   struct sdap_dom_enum_state);
     struct tevent_req *subreq;
-    int ret;
+    errno_t ret;
 
     subreq = sdap_id_op_connect_send(state->op, state, &ret);
-    if (!subreq) {
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("sdap_id_op_connect_send failed: %d\n", ret));
         return ret;
     }
 
-    tevent_req_set_callback(subreq, ldap_id_enumerate_connect_done, req);
+    tevent_req_set_callback(subreq, sdap_dom_enum_conn_done, req);
     return EOK;
 }
 
-static void ldap_id_enumerate_connect_done(struct tevent_req *subreq)
+static void sdap_dom_enum_conn_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
+    struct sdap_dom_enum_state *state = tevent_req_data(req,
+                                                   struct sdap_dom_enum_state);
     int ret, dp_error;
 
     ret = sdap_id_op_connect_recv(subreq, &dp_error);
     talloc_zfree(subreq);
     if (ret != EOK) {
         if (dp_error == DP_ERR_OFFLINE) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  ("Backend is marked offline, retry later!\n"));
             tevent_req_done(req);
         } else {
-            DEBUG(9, ("User enumeration failed to connect to LDAP server: (%d)[%s]\n",
-                      ret, strerror(ret)));
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  ("Domain enumeration failed to connect to " \
+                   "LDAP server: (%d)[%s]\n", ret, strerror(ret)));
             tevent_req_error(req, ret);
         }
-
         return;
     }
 
     subreq = enum_users_send(state, state->ev,
-                             state->ctx, state->ctx->opts->sdom,
+                             state->ctx, state->sdom,
                              state->op, state->purge);
-    if(!subreq) {
+    if (subreq == NULL) {
         tevent_req_error(req, ENOMEM);
         return;
     }
-
-    tevent_req_set_callback(subreq, ldap_id_enum_users_done, req);
+    tevent_req_set_callback(subreq, sdap_dom_enum_users_done, req);
 }
 
-static void ldap_id_enum_users_done(struct tevent_req *subreq)
+static void sdap_dom_enum_users_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
+    struct sdap_dom_enum_state *state = tevent_req_data(req,
+                                                   struct sdap_dom_enum_state);
     uint64_t err = 0;
     int ret, dp_error = DP_ERR_FATAL;
 
     err = enum_users_recv(subreq);
     talloc_zfree(subreq);
-    if (err != EOK && err != ENOENT) {
+    if (err != EOK) {
         /* We call sdap_id_op_done only on error
          * as the connection is reused by groups enumeration */
         ret = sdap_id_op_done(state->op, (int)err, &dp_error);
         if (dp_error == DP_ERR_OK) {
             /* retry */
-            ret = ldap_id_enumerate_retry(req);
+            ret = sdap_dom_enum_retry(req);
             if (ret == EOK) {
                 return;
             }
@@ -318,29 +196,30 @@ static void ldap_id_enum_users_done(struct tevent_req *subreq)
         if (dp_error == DP_ERR_OFFLINE) {
             tevent_req_done(req);
         } else {
-            DEBUG(9, ("User enumeration failed with: (%d)[%s]\n",
-                      ret, strerror(ret)));
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("User enumeration failed with: (%d)[%s]\n",
+                   ret, strerror(ret)));
             tevent_req_error(req, ret);
         }
         return;
     }
 
     subreq = enum_groups_send(state, state->ev, state->ctx,
-                              state->ctx->opts->sdom,
+                              state->sdom,
                               state->op, state->purge);
-    if (!subreq) {
+    if (subreq == NULL) {
         tevent_req_error(req, ENOMEM);
         return;
     }
-    tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req);
+    tevent_req_set_callback(subreq, sdap_dom_enum_groups_done, req);
 }
 
-static void ldap_id_enum_groups_done(struct tevent_req *subreq)
+static void sdap_dom_enum_groups_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
+    struct sdap_dom_enum_state *state = tevent_req_data(req,
+                                                   struct sdap_dom_enum_state);
     uint64_t err = 0;
     int ret, dp_error = DP_ERR_FATAL;
 
@@ -352,7 +231,7 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq)
         ret = sdap_id_op_done(state->op, (int)err, &dp_error);
         if (dp_error == DP_ERR_OK && ret != EOK) {
             /* retry */
-            ret = ldap_id_enumerate_retry(req);
+            ret = sdap_dom_enum_retry(req);
             if (ret == EOK) {
                 return;
             }
@@ -364,8 +243,9 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq)
             if (dp_error == DP_ERR_OFFLINE) {
                 tevent_req_done(req);
             } else {
-                DEBUG(9, ("Group enumeration failed with: (%d)[%s]\n",
-                          ret, strerror(ret)));
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("Group enumeration failed with: (%d)[%s]\n",
+                       ret, strerror(ret)));
                 tevent_req_error(req, ret);
             }
 
@@ -379,17 +259,17 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq)
         tevent_req_error(req, ENOMEM);
         return;
     }
-    tevent_req_set_callback(subreq, ldap_id_enum_services_done, req);
+    tevent_req_set_callback(subreq, sdap_dom_enum_services_done, req);
 }
 
-static void ldap_id_enum_services_done(struct tevent_req *subreq)
+static void sdap_dom_enum_services_done(struct tevent_req *subreq)
 {
     errno_t ret;
     int dp_error = DP_ERR_FATAL;
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
-    struct global_enum_state *state = tevent_req_data(req,
-                                                 struct global_enum_state);
+    struct sdap_dom_enum_state *state = tevent_req_data(req,
+                                                   struct sdap_dom_enum_state);
 
     ret = enum_services_recv(subreq);
     talloc_zfree(subreq);
@@ -401,7 +281,7 @@ static void ldap_id_enum_services_done(struct tevent_req *subreq)
     ret = sdap_id_op_done(state->op, ret, &dp_error);
     if (dp_error == DP_ERR_OK && ret != EOK) {
         /* retry */
-        ret = ldap_id_enumerate_retry(req);
+        ret = sdap_dom_enum_retry(req);
         if (ret == EOK) {
             return;
         }
@@ -422,22 +302,34 @@ static void ldap_id_enum_services_done(struct tevent_req *subreq)
         return;
     }
 
+    /* Ok, we've completed an enumeration. Save this to the
+     * sysdb so we can postpone starting up the enumeration
+     * process on the next SSSD service restart (to avoid
+     * slowing down system boot-up
+     */
+    ret = sysdb_set_enumerated(state->sdom->dom->sysdb,
+                               state->sdom->dom, true);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Could not mark domain as having enumerated.\n"));
+        /* This error is non-fatal, so continue */
+    }
+
     if (state->purge) {
-
         subreq = ldap_id_cleanup_send(state, state->ev, state->ctx);
         if (!subreq) {
             tevent_req_error(req, ENOMEM);
             return;
         }
 
-        tevent_req_set_callback(subreq, ldap_id_enum_cleanup_done, req);
+        tevent_req_set_callback(subreq, sdap_dom_enum_cleanup_done, req);
         return;
     }
 
     tevent_req_done(req);
 }
 
-static void ldap_id_enum_cleanup_done(struct tevent_req *subreq)
+static void sdap_dom_enum_cleanup_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
@@ -445,8 +337,14 @@ static void ldap_id_enum_cleanup_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
+errno_t sdap_dom_enum_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
 /* ==User-Enumeration===================================================== */
-
 struct enum_users_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
@@ -457,7 +355,7 @@ struct enum_users_state {
     const char **attrs;
 };
 
-static void enum_users_op_done(struct tevent_req *subreq);
+static void enum_users_done(struct tevent_req *subreq);
 
 static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
@@ -480,14 +378,14 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
     state->op = op;
 
     use_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                          ctx->opts->idmap_ctx,
-                                                          sdom->dom->domain_id);
+                                                        ctx->opts->idmap_ctx,
+                                                        sdom->dom->domain_id);
 
     /* We always want to filter on objectclass and an available name */
     state->filter = talloc_asprintf(state,
-                                    "(&(objectclass=%s)(%s=*)",
-                                    ctx->opts->user_map[SDAP_OC_USER].name,
-                                    ctx->opts->user_map[SDAP_AT_USER_NAME].name);
+                                  "(&(objectclass=%s)(%s=*)",
+                                  ctx->opts->user_map[SDAP_OC_USER].name,
+                                  ctx->opts->user_map[SDAP_AT_USER_NAME].name);
     if (!state->filter) {
         DEBUG(SSSDBG_MINOR_FAILURE,
               ("Failed to build base filter\n"));
@@ -567,7 +465,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
         ret = ENOMEM;
         goto fail;
     }
-    tevent_req_set_callback(subreq, enum_users_op_done, req);
+    tevent_req_set_callback(subreq, enum_users_done, req);
 
     return req;
 
@@ -577,7 +475,7 @@ fail:
     return req;
 }
 
-static void enum_users_op_done(struct tevent_req *subreq)
+static void enum_users_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
@@ -597,7 +495,8 @@ static void enum_users_op_done(struct tevent_req *subreq)
 
     if (usn_value) {
         talloc_zfree(state->ctx->srv_opts->max_user_value);
-        state->ctx->srv_opts->max_user_value = talloc_steal(state->ctx, usn_value);
+        state->ctx->srv_opts->max_user_value =
+                                        talloc_steal(state->ctx, usn_value);
 
         usn_number = strtoul(usn_value, &endptr, 10);
         if ((endptr == NULL || (*endptr == '\0' && endptr != usn_value))
@@ -620,7 +519,6 @@ static errno_t enum_users_recv(struct tevent_req *req)
 }
 
 /* =Group-Enumeration===================================================== */
-
 struct enum_groups_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
@@ -631,7 +529,7 @@ struct enum_groups_state {
     const char **attrs;
 };
 
-static void enum_groups_op_done(struct tevent_req *subreq);
+static void enum_groups_done(struct tevent_req *subreq);
 
 static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
@@ -654,14 +552,14 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
     state->op = op;
 
     use_mapping = sdap_idmap_domain_has_algorithmic_mapping(
-                                                          ctx->opts->idmap_ctx,
-                                                          sdom->dom->domain_id);
+                                                        ctx->opts->idmap_ctx,
+                                                        sdom->dom->domain_id);
 
     /* We always want to filter on objectclass and an available name */
     state->filter = talloc_asprintf(state,
-                                    "(&(objectclass=%s)(%s=*)",
-                                    ctx->opts->group_map[SDAP_OC_GROUP].name,
-                                    ctx->opts->group_map[SDAP_AT_GROUP_NAME].name);
+                               "(&(objectclass=%s)(%s=*)",
+                               ctx->opts->group_map[SDAP_OC_GROUP].name,
+                               ctx->opts->group_map[SDAP_AT_GROUP_NAME].name);
     if (!state->filter) {
         DEBUG(SSSDBG_MINOR_FAILURE,
               ("Failed to build base filter\n"));
@@ -736,7 +634,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
         ret = ENOMEM;
         goto fail;
     }
-    tevent_req_set_callback(subreq, enum_groups_op_done, req);
+    tevent_req_set_callback(subreq, enum_groups_done, req);
 
     return req;
 
@@ -746,7 +644,7 @@ fail:
     return req;
 }
 
-static void enum_groups_op_done(struct tevent_req *subreq)
+static void enum_groups_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
diff --git a/src/providers/ldap/sdap_async_enum.h b/src/providers/ldap/sdap_async_enum.h
new file mode 100644
index 0000000000000000000000000000000000000000..04ec8c6dcbec4bcce0de67b9e10acc857c9e9416
--- /dev/null
+++ b/src/providers/ldap/sdap_async_enum.h
@@ -0,0 +1,38 @@
+/*
+    SSSD
+
+    LDAP Enumeration Module
+
+    Authors:
+        Simo Sorce <ssorce at redhat.com>
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 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/>.
+*/
+
+#ifndef _SDAP_ASYNC_ENUM_H_
+#define _SDAP_ASYNC_ENUM_H_
+
+struct tevent_req *
+sdap_dom_enum_send(TALLOC_CTX *memctx,
+                   struct tevent_context *ev,
+                   struct sdap_id_ctx *ctx,
+                   struct sdap_domain *sdom,
+                   struct sdap_id_conn_ctx *conn);
+
+errno_t sdap_dom_enum_recv(struct tevent_req *req);
+
+#endif /* _SDAP_ASYNC_ENUM_H_ */
diff --git a/src/providers/ldap/sdap_reinit.c b/src/providers/ldap/sdap_reinit.c
index b273c1a66cca5e78ab3c281c684feab93f476471..d7b50bbbd61b98abd6a7c50d237544df32a991c3 100644
--- a/src/providers/ldap/sdap_reinit.c
+++ b/src/providers/ldap/sdap_reinit.c
@@ -25,6 +25,7 @@
 
 #include "util/util.h"
 #include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async_enum.h"
 #include "db/sysdb.h"
 #include "db/sysdb_services.h"
 
@@ -79,7 +80,8 @@ struct tevent_req* sdap_reinit_cleanup_send(TALLOC_CTX *mem_ctx,
         goto immediately;
     }
 
-    subreq = ldap_id_enumerate_send(be_ctx->ev, id_ctx);
+    subreq = sdap_dom_enum_send(id_ctx, be_ctx->ev, id_ctx,
+                                id_ctx->opts->sdom, id_ctx->conn);
     if (subreq == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to issue enumeration request\n"));
         ret = ENOMEM;
@@ -199,17 +201,16 @@ static void sdap_reinit_cleanup_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = NULL;
     struct sdap_reinit_cleanup_state *state = NULL;
-    enum tevent_req_state tstate;
-    uint64_t err;
     errno_t ret;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct sdap_reinit_cleanup_state);
 
-    if (tevent_req_is_error(subreq, &tstate, &err)) {
-        ret = err;
+    ret = sdap_dom_enum_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Domain enumeration failed [%d]: %s\n",
-                                    err, strerror(err)));
+                                    ret, strerror(ret)));
         goto fail;
     }
 
-- 
1.8.3.1

-------------- next part --------------
>From 65472567e8139a3b49ab66f1c39f68ede6cb020e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 21 Aug 2013 01:41:16 +0200
Subject: [PATCH 04/11] LDAP: Convert enumeration to the ptask API

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

Identity providers other than LDAP need to customize the enumeration in
different ways while sharing the way the task is scheduled etc. The
easiest way to accomplish it is to leverage the recently introduced
ptask framework.
---
 src/providers/ldap/ldap_common.c  |  30 +----
 src/providers/ldap/ldap_common.h  |   4 +-
 src/providers/ldap/ldap_id_enum.c | 255 ++++++++++++++++++++------------------
 src/providers/ldap/sdap.h         |   1 +
 4 files changed, 143 insertions(+), 147 deletions(-)

diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 9aa98173c865d0b032f02bd6a0ec80b93682af6d..05e487a1e8f57da338c9949b9f1a75688a6595e7 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -941,37 +941,11 @@ int sdap_id_setup_tasks(struct sdap_id_ctx *ctx)
     struct timeval tv;
     int ret = EOK;
     int delay;
-    bool has_enumerated;
 
     /* set up enumeration task */
     if (ctx->be->domain->enumerate) {
-        /* If this is the first startup, we need to kick off
-         * an enumeration immediately, to close a window where
-         * clients requesting get*ent information won't get an
-         * immediate reply with no entries
-         */
-        ret = sysdb_has_enumerated(ctx->be->domain->sysdb, ctx->be->domain,
-                                   &has_enumerated);
-        if (ret != EOK) {
-            return ret;
-        }
-        if (has_enumerated) {
-            /* At least one enumeration has previously run,
-             * so clients will get cached data. We will delay
-             * starting to enumerate by 10s so we don't slow
-             * down the startup process if this is happening
-             * during system boot.
-             */
-            tv = tevent_timeval_current_ofs(10, 0);
-        } else {
-            /* This is our first startup. Schedule the
-             * enumeration to start immediately once we
-             * enter the mainloop.
-             */
-            tv = tevent_timeval_current();
-        }
-
-        ret = ldap_id_enumerate_set_timer(ctx, tv);
+        DEBUG(SSSDBG_TRACE_FUNC, ("Setting up enumeration for %s\n", ctx->be->domain->name));
+        ret = ldap_setup_enumeration(ctx, ctx->conn, ctx->opts->sdom);
     } else {
         /* the enumeration task, runs the cleanup process by itself,
          * but if enumeration is not running we need to schedule it */
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index c9b2f663b72658c9cdcd4d54ec32b1dd74f31326..7ba8e95571854dfbb2e1eb4faf24c992f41b68f4 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -165,7 +165,9 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
                             const char *conf_path,
                             struct sdap_options *opts);
 
-int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
+errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
+                               struct sdap_id_conn_ctx *conn,
+                               struct sdap_domain *sdom);
 int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
 
 void sdap_mark_offline(struct sdap_id_ctx *ctx);
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index 43eb4c9972bb7383435e2e130f01646af46c0c38..961c72f3d888fa92d1002ca483776319d007b679 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -22,143 +22,162 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include <errno.h>
-#include <time.h>
-#include <sys/time.h>
-
 #include "util/util.h"
 #include "db/sysdb.h"
 #include "providers/ldap/ldap_common.h"
-#include "providers/ldap/sdap_async.h"
-#include "providers/ldap/sdap_idmap.h"
 #include "providers/ldap/sdap_async_enum.h"
 
-extern struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
-                                               struct tevent_context *ev,
-                                               struct sdap_id_ctx *ctx);
-
-/* ==Enumeration-Task===================================================== */
-
-static void ldap_id_enumerate_reschedule(struct tevent_req *req);
-
-static void ldap_id_enumerate_timeout(struct tevent_context *ev,
-                                      struct tevent_timer *te,
-                                      struct timeval tv, void *pvt);
-
-static void ldap_id_enumerate_timer(struct tevent_context *ev,
-                                    struct tevent_timer *tt,
-                                    struct timeval tv, void *pvt)
-{
-    struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
-    struct tevent_timer *timeout;
-    struct tevent_req *req;
-    int delay;
-    errno_t ret;
-
-    if (be_is_offline(ctx->be)) {
-        DEBUG(4, ("Backend is marked offline, retry later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ldap_id_enumerate_set_timer(ctx, tv);
-        return;
-    }
-
-    req = sdap_dom_enum_send(ctx, ev, ctx, ctx->opts->sdom, ctx->conn);
-    if (!req) {
-        DEBUG(1, ("Failed to schedule enumeration, retrying later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ret = ldap_id_enumerate_set_timer(ctx, tv);
-        if (ret != EOK) {
-            DEBUG(1, ("Error setting up enumerate timer\n"));
-        }
-        return;
-    }
-    tevent_req_set_callback(req, ldap_id_enumerate_reschedule, ctx);
-
-    /* if enumeration takes so long, either we try to enumerate too
-     * frequently, or something went seriously wrong */
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-    tv = tevent_timeval_current_ofs(delay, 0);
-    timeout = tevent_add_timer(ctx->be->ev, req, tv,
-                               ldap_id_enumerate_timeout, req);
-    if (timeout == NULL) {
-        /* If we can't guarantee a timeout, we
-         * need to cancel the request, to avoid
-         * the possibility of starting another
-         * concurrently
-         */
-        talloc_zfree(req);
-
-        DEBUG(1, ("Failed to schedule enumeration, retrying later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ret = ldap_id_enumerate_set_timer(ctx, tv);
-        if (ret != EOK) {
-            DEBUG(1, ("Error setting up enumerate timer\n"));
-        }
-        return;
-    }
-    return;
-}
-
-static void ldap_id_enumerate_timeout(struct tevent_context *ev,
-                                      struct tevent_timer *te,
-                                      struct timeval tv, void *pvt)
-{
-    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
-    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
-                                                       struct sdap_id_ctx);
-    int delay;
-
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-    DEBUG(1, ("Enumeration timed out! Timeout too small? (%ds)!\n", delay));
-
-    tv = tevent_timeval_current_ofs(delay, 0);
-    ldap_id_enumerate_set_timer(ctx, tv);
-
-    talloc_zfree(req);
-}
-
-static void ldap_id_enumerate_reschedule(struct tevent_req *req)
+static struct tevent_req *
+ldap_enumeration_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct be_ctx *be_ctx,
+                      struct be_ptask *be_ptask,
+                      void *pvt);
+errno_t ldap_enumeration_recv(struct tevent_req *req);
+
+struct ldap_enum_ctx {
+    struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
+    struct sdap_id_conn_ctx *conn;
+};
+
+errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
+                               struct sdap_id_conn_ctx *conn,
+                               struct sdap_domain *sdom)
 {
-    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
-                                                       struct sdap_id_ctx);
-    struct timeval tv;
-    int delay;
     errno_t ret;
+    time_t first_delay;
+    time_t period;
+    bool has_enumerated;
+    struct ldap_enum_ctx *ectx;
 
-    ret = sdap_dom_enum_recv(req);
-    talloc_zfree(req);
+    ret = sysdb_has_enumerated(sdom->dom->sysdb, sdom->dom, &has_enumerated);
     if (ret != EOK) {
-        /* On error schedule starting from now, not the last run */
-        tv = tevent_timeval_current();
+        return ret;
+    }
+
+    if (has_enumerated) {
+        /* At least one enumeration has previously run,
+         * so clients will get cached data. We will delay
+         * starting to enumerate by 10s so we don't slow
+         * down the startup process if this is happening
+         * during system boot.
+         */
+        first_delay = 10;
     } else {
-        tv = ctx->opts->sdom->last_enum;
+        /* This is our first startup. Schedule the
+         * enumeration to start immediately once we
+         * enter the mainloop.
+         */
+        first_delay = 0;
+    }
+
+    period = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
+
+    ectx = talloc(sdom, struct ldap_enum_ctx);
+    if (ectx == NULL) {
+        return ENOMEM;
+    }
+    ectx->ctx = ctx;
+    ectx->sdom = sdom;
+    ectx->conn = conn;
 
+    ret = be_ptask_create(sdom, ctx->be,
+                          period,                   /* period */
+                          first_delay,              /* first_delay */
+                          5,                        /* enabled delay */
+                          period,                   /* timeout */
+                          BE_PTASK_OFFLINE_SKIP,
+                          ldap_enumeration_send, ldap_enumeration_recv,
+                          ectx, "enumeration", &sdom->enum_task);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_FATAL_FAILURE,
+              ("Unable to initialize enumeration periodic task\n"));
+        talloc_free(ectx);
+        return ret;
     }
 
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_ENUM_REFRESH_TIMEOUT);
-    tv = tevent_timeval_add(&tv, delay, 0);
-    ldap_id_enumerate_set_timer(ctx, tv);
+    talloc_steal(sdom->enum_task, ectx);
+    return EOK;
 }
 
-int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
+
+struct ldap_enumeration_state {
+    struct ldap_enum_ctx *ectx;
+    struct sss_domain_info *dom;
+};
+
+static void ldap_enumeration_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ldap_enumeration_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct be_ctx *be_ctx,
+                      struct be_ptask *be_ptask,
+                      void *pvt)
 {
-    struct tevent_timer *enum_task;
+    struct ldap_enumeration_state *state;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct ldap_enum_ctx *ectx;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ldap_enumeration_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    ectx = talloc_get_type(pvt, struct ldap_enum_ctx);
+    if (ectx == NULL) {
+        ret = EFAULT;
+        goto fail;
+    }
+    state->ectx = ectx;
+    state->dom = ectx->sdom->dom;
+
+    subreq = sdap_dom_enum_send(ectx, ev, ectx->ctx, ectx->sdom,
+                                ectx->conn);
+    if (subreq == NULL) {
+        /* The ptask API will reschedule the enumeration on its own on
+         * failure */
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Failed to schedule enumeration, retrying later!\n"));
+        ret = EIO;
+        goto fail;
+    }
 
-    DEBUG(6, ("Scheduling next enumeration at %ld.%ld\n",
-              (long)tv.tv_sec, (long)tv.tv_usec));
+    tevent_req_set_callback(subreq, ldap_enumeration_done, req);
+    return req;
 
-    enum_task = tevent_add_timer(ctx->be->ev, ctx,
-                                 tv, ldap_id_enumerate_timer, ctx);
-    if (!enum_task) {
-        DEBUG(0, ("FATAL: failed to setup enumeration task!\n"));
-        return EFAULT;
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void
+ldap_enumeration_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+
+    ret = sdap_dom_enum_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
     }
 
+    tevent_req_done(req);
+}
+
+errno_t
+ldap_enumeration_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
     return EOK;
 }
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index f5f6d90aa075b5e7cffa4445cda87b1757dd3ca3..5da27fe87ff5900857ec89e6084ab4d766944327 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -384,6 +384,7 @@ struct sdap_domain {
     /* Need to modify the list from a talloc destructor */
     struct sdap_domain **head;
 
+    struct be_ptask *enum_task;
     /* enumeration loop timer */
     struct timeval last_enum;
 };
-- 
1.8.3.1

-------------- next part --------------
>From ebce1490a82710d768fc3018ecb8fe05e6ab3d3d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 21 Aug 2013 03:37:47 +0200
Subject: [PATCH 05/11] LDAP: Make cleanup synchronous

The LDAP cleanup request was asynchronous for no good reason, probably a
leftover from the days of async sysdb. This patch makes it sychronous
again, removing a lot of uneeded code.
---
 src/providers/ldap/ldap_common.h     |   1 +
 src/providers/ldap/ldap_id_cleanup.c | 157 ++++++-----------------------------
 src/providers/ldap/sdap_async_enum.c |  26 ++----
 3 files changed, 34 insertions(+), 150 deletions(-)

diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 7ba8e95571854dfbb2e1eb4faf24c992f41b68f4..1dd69f4e253f9bcf1162b8e0212eeac59ec621fe 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -168,6 +168,7 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
 errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
                                struct sdap_id_conn_ctx *conn,
                                struct sdap_domain *sdom);
+errno_t ldap_id_cleanup(struct sdap_id_ctx *ctx);
 int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
 
 void sdap_mark_offline(struct sdap_id_ctx *ctx);
diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
index 534e2ee015d157a26798c5b14fdd3e8d9a156b63..85c0139dbb9ff2d72b4e132b73b3bd4c8d56aab0 100644
--- a/src/providers/ldap/ldap_id_cleanup.c
+++ b/src/providers/ldap/ldap_id_cleanup.c
@@ -33,23 +33,11 @@
 #include "providers/ldap/sdap_async.h"
 
 /* ==Cleanup-Task========================================================= */
-
-struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
-                                        struct tevent_context *ev,
-                                        struct sdap_id_ctx *ctx);
-static void ldap_id_cleanup_reschedule(struct tevent_req *req);
-
-static void ldap_id_cleanup_timeout(struct tevent_context *ev,
-                                      struct tevent_timer *te,
-                                      struct timeval tv, void *pvt);
-
 static void ldap_id_cleanup_timer(struct tevent_context *ev,
                                   struct tevent_timer *tt,
                                   struct timeval tv, void *pvt)
 {
     struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
-    struct tevent_timer *timeout;
-    struct tevent_req *req;
     int delay;
     errno_t ret;
 
@@ -62,89 +50,20 @@ static void ldap_id_cleanup_timer(struct tevent_context *ev,
         return;
     }
 
-    req = ldap_id_cleanup_send(ctx, ev, ctx);
-    if (!req) {
-        DEBUG(1, ("Failed to schedule cleanup, retrying later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ret = ldap_id_cleanup_set_timer(ctx, tv);
-        if (ret != EOK) {
-            DEBUG(1, ("Error setting up cleanup timer\n"));
-        }
-        return;
-    }
-    tevent_req_set_callback(req, ldap_id_cleanup_reschedule, ctx);
-
-    /* if cleanup takes so long, either we try to cleanup too
-     * frequently, or something went seriously wrong */
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-    tv = tevent_timeval_current_ofs(delay, 0);
-    timeout = tevent_add_timer(ctx->be->ev, req, tv,
-                               ldap_id_cleanup_timeout, req);
-    if (timeout == NULL) {
-        /* If we can't guarantee a timeout, we
-         * need to cancel the request, to avoid
-         * the possibility of starting another
-         * concurrently
-         */
-        talloc_zfree(req);
-
-        DEBUG(1, ("Failed to schedule cleanup, retrying later!\n"));
-        /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-        tv = tevent_timeval_current_ofs(delay, 0);
-        ret = ldap_id_cleanup_set_timer(ctx, tv);
-        if (ret != EOK) {
-            DEBUG(1, ("Error setting up cleanup timer\n"));
-        }
-        return;
-    }
-    return;
-}
-
-static void ldap_id_cleanup_timeout(struct tevent_context *ev,
-                                      struct tevent_timer *te,
-                                      struct timeval tv, void *pvt)
-{
-    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
-    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
-                                                       struct sdap_id_ctx);
-    int delay;
-
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-    DEBUG(1, ("Cleanup timed out! Timeout too small? (%ds)!\n", delay));
-
-    tv = tevent_timeval_current_ofs(delay, 0);
-    ldap_id_cleanup_set_timer(ctx, tv);
-
-    talloc_zfree(req);
-}
-
-static void ldap_id_cleanup_reschedule(struct tevent_req *req)
-{
-    struct sdap_id_ctx *ctx = tevent_req_callback_data(req,
-                                                       struct sdap_id_ctx);
-    enum tevent_req_state tstate;
-    uint64_t err;
-    struct timeval tv;
-    int delay;
-
-    if (tevent_req_is_error(req, &tstate, &err)) {
+    ret = ldap_id_cleanup(ctx);
+    if (ret != EOK) {
         /* On error schedule starting from now, not the last run */
         tv = tevent_timeval_current();
     } else {
         tv = ctx->last_purge;
     }
-    talloc_zfree(req);
 
     delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
     tv = tevent_timeval_add(&tv, delay, 0);
     ldap_id_cleanup_set_timer(ctx, tv);
+    ctx->last_purge = tevent_timeval_current();
 }
 
-
-
 int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
 {
     struct tevent_timer *cleanup_task;
@@ -162,80 +81,58 @@ int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
     return EOK;
 }
 
-
-
-struct global_cleanup_state {
-    struct tevent_context *ev;
-    struct sdap_id_ctx *ctx;
-};
-
 static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx);
 static int cleanup_groups(TALLOC_CTX *memctx,
                           struct sysdb_ctx *sysdb,
                           struct sss_domain_info *domain);
 
-struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
-                                        struct tevent_context *ev,
-                                        struct sdap_id_ctx *ctx)
+errno_t ldap_id_cleanup(struct sdap_id_ctx *ctx)
 {
-    struct global_cleanup_state *state;
-    struct tevent_req *req;
-    int ret;
+    int ret, tret;
     bool in_transaction = false;
+    TALLOC_CTX *tmp_ctx;
 
-    req = tevent_req_create(memctx, &state, struct global_cleanup_state);
-    if (!req) return NULL;
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
 
-    state->ev = ev;
-    state->ctx = ctx;
-
-    ctx->last_purge = tevent_timeval_current();
-
-    ret = sysdb_transaction_start(state->ctx->be->domain->sysdb);
+    ret = sysdb_transaction_start(ctx->be->domain->sysdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
-        goto fail;
+        goto done;
     }
     in_transaction = true;
 
-    ret = cleanup_users(state, state->ctx);
+    ret = cleanup_users(tmp_ctx, ctx);
     if (ret && ret != ENOENT) {
-        goto fail;
+        goto done;
     }
 
-    ret = cleanup_groups(state,
-                         state->ctx->be->domain->sysdb,
-                         state->ctx->be->domain);
+    ret = cleanup_groups(tmp_ctx,
+                         ctx->be->domain->sysdb,
+                         ctx->be->domain);
     if (ret) {
-        goto fail;
+        goto done;
     }
 
-    ret = sysdb_transaction_commit(state->ctx->be->domain->sysdb);
+    ret = sysdb_transaction_commit(ctx->be->domain->sysdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction\n"));
-        goto fail;
+        goto done;
     }
     in_transaction = false;
 
-    tevent_req_done(req);
-    tevent_req_post(req, ev);
-    return req;
-
-fail:
-    DEBUG(1, ("Failed to cleanup caches (%d [%s]), retrying later!\n",
-              (int)ret, strerror(ret)));
+    ret = EOK;
+done:
     if (in_transaction) {
-        ret = sysdb_transaction_cancel(state->ctx->be->domain->sysdb);
-        if (ret != EOK) {
-            DEBUG(1, ("Could not cancel transaction\n"));
-            tevent_req_error(req, ret);
-            tevent_req_post(req, ev);
-            return req;
+        tret = sysdb_transaction_cancel(ctx->be->domain->sysdb);
+        if (tret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n"));
         }
     }
-    tevent_req_done(req);
-    tevent_req_post(req, ev);
-    return req;
+    talloc_free(tmp_ctx);
+    return ret;
 }
 
 
diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
index afd19f10e91c7673e852b4077170510831865ebd..50ff9f4a0b1b921199ce8425f4065306ae06d7a9 100644
--- a/src/providers/ldap/sdap_async_enum.c
+++ b/src/providers/ldap/sdap_async_enum.c
@@ -31,10 +31,6 @@
 #include "providers/ldap/sdap_async.h"
 #include "providers/ldap/sdap_idmap.h"
 
-extern struct tevent_req *ldap_id_cleanup_send(TALLOC_CTX *memctx,
-                                               struct tevent_context *ev,
-                                               struct sdap_id_ctx *ctx);
-
 static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
@@ -67,7 +63,6 @@ static void sdap_dom_enum_conn_done(struct tevent_req *subreq);
 static void sdap_dom_enum_users_done(struct tevent_req *subreq);
 static void sdap_dom_enum_groups_done(struct tevent_req *subreq);
 static void sdap_dom_enum_services_done(struct tevent_req *subreq);
-static void sdap_dom_enum_cleanup_done(struct tevent_req *subreq);
 
 struct tevent_req *
 sdap_dom_enum_send(TALLOC_CTX *memctx,
@@ -316,27 +311,18 @@ static void sdap_dom_enum_services_done(struct tevent_req *subreq)
     }
 
     if (state->purge) {
-        subreq = ldap_id_cleanup_send(state, state->ev, state->ctx);
-        if (!subreq) {
-            tevent_req_error(req, ENOMEM);
-            return;
+        ret = ldap_id_cleanup(state->ctx);
+        if (ret != EOK) {
+            /* Not fatal, worst case we'll have stale entries that would be
+             * removed on a subsequent online lookup
+             */
+            DEBUG(SSSDBG_MINOR_FAILURE, ("Cleanup failed: %d\n", ret));
         }
-
-        tevent_req_set_callback(subreq, sdap_dom_enum_cleanup_done, req);
-        return;
     }
 
     tevent_req_done(req);
 }
 
-static void sdap_dom_enum_cleanup_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq,
-                                                      struct tevent_req);
-    talloc_zfree(subreq);
-    tevent_req_done(req);
-}
-
 errno_t sdap_dom_enum_recv(struct tevent_req *req)
 {
     TEVENT_REQ_RETURN_ON_ERROR(req);
-- 
1.8.3.1

-------------- next part --------------
>From 3e73195c7c0a9eb3c63185258309e899a7870f37 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 22 Aug 2013 11:02:32 +0200
Subject: [PATCH 06/11] LDAP: Make the cleanup task reusable for subdomains

Instead of always performing the cleanup on the main domain, the task
now accepts a sdap_domain structure to perform the cleanup on. This
change will make the cleanup task reusable for subdomains.
---
 src/providers/ldap/ldap_common.c     |  2 +-
 src/providers/ldap/ldap_common.h     | 10 ++--
 src/providers/ldap/ldap_id_cleanup.c | 96 +++++++++++++++++++++++-------------
 src/providers/ldap/sdap.h            |  3 ++
 src/providers/ldap/sdap_async_enum.c |  4 +-
 5 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 05e487a1e8f57da338c9949b9f1a75688a6595e7..ffa8aae59130258b3346e9071ea8d0c867942fca 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -960,7 +960,7 @@ int sdap_id_setup_tasks(struct sdap_id_ctx *ctx)
         /* run the first one in a couple of seconds so that we have time to
          * finish initializations first*/
         tv = tevent_timeval_current_ofs(10, 0);
-        ret = ldap_id_cleanup_set_timer(ctx, tv);
+        ret = ldap_id_cleanup_create_timer(ctx, ctx->opts->sdom, tv);
     }
 
     return ret;
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 1dd69f4e253f9bcf1162b8e0212eeac59ec621fe..fb45845eff805ef1359caf5aa3ea509f6fcd5cd9 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -63,9 +63,6 @@ struct sdap_id_ctx {
     /* connection to a server */
     struct sdap_id_conn_ctx *conn;
 
-    /* cleanup loop timer */
-    struct timeval last_purge;
-
     struct sdap_server_opts *srv_opts;
 };
 
@@ -168,8 +165,11 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
 errno_t ldap_setup_enumeration(struct sdap_id_ctx *ctx,
                                struct sdap_id_conn_ctx *conn,
                                struct sdap_domain *sdom);
-errno_t ldap_id_cleanup(struct sdap_id_ctx *ctx);
-int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
+errno_t ldap_id_cleanup(struct sdap_options *opts,
+                        struct sss_domain_info *dom);
+int ldap_id_cleanup_create_timer(struct sdap_id_ctx *ctx,
+                                 struct sdap_domain *sdom,
+                                 struct timeval tv);
 
 void sdap_mark_offline(struct sdap_id_ctx *ctx);
 
diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
index 85c0139dbb9ff2d72b4e132b73b3bd4c8d56aab0..1fd2ff49fe7e80fc9856eb7fadb9017145fb4b45 100644
--- a/src/providers/ldap/ldap_id_cleanup.c
+++ b/src/providers/ldap/ldap_id_cleanup.c
@@ -33,60 +33,90 @@
 #include "providers/ldap/sdap_async.h"
 
 /* ==Cleanup-Task========================================================= */
+struct ldap_id_cleanup_ctx {
+    struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
+};
+
+static errno_t ldap_id_cleanup_set_timer(struct ldap_id_cleanup_ctx *cctx,
+                                         struct timeval tv);
+
 static void ldap_id_cleanup_timer(struct tevent_context *ev,
                                   struct tevent_timer *tt,
                                   struct timeval tv, void *pvt)
 {
-    struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx);
+    struct ldap_id_cleanup_ctx *cctx = talloc_get_type(pvt,
+                                                struct ldap_id_cleanup_ctx);
     int delay;
     errno_t ret;
 
-    if (be_is_offline(ctx->be)) {
-        DEBUG(4, ("Backend is marked offline, retry later!\n"));
+    if (be_is_offline(cctx->ctx->be)) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Backend is marked offline, retry later!\n"));
         /* schedule starting from now, not the last run */
-        delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+        delay = dp_opt_get_int(cctx->ctx->opts->basic,
+                               SDAP_CACHE_PURGE_TIMEOUT);
         tv = tevent_timeval_current_ofs(delay, 0);
-        ldap_id_cleanup_set_timer(ctx, tv);
+        ldap_id_cleanup_set_timer(cctx, tv);
         return;
     }
 
-    ret = ldap_id_cleanup(ctx);
+    ret = ldap_id_cleanup(cctx->ctx->opts, cctx->sdom->dom);
     if (ret != EOK) {
         /* On error schedule starting from now, not the last run */
         tv = tevent_timeval_current();
     } else {
-        tv = ctx->last_purge;
+        tv = cctx->sdom->last_purge;
     }
 
-    delay = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
+    delay = dp_opt_get_int(cctx->ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
     tv = tevent_timeval_add(&tv, delay, 0);
-    ldap_id_cleanup_set_timer(ctx, tv);
-    ctx->last_purge = tevent_timeval_current();
+    ldap_id_cleanup_set_timer(cctx, tv);
+    cctx->sdom->last_purge = tevent_timeval_current();
 }
 
-int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv)
+static errno_t ldap_id_cleanup_set_timer(struct ldap_id_cleanup_ctx *cctx,
+                                         struct timeval tv)
 {
     struct tevent_timer *cleanup_task;
 
-    DEBUG(6, ("Scheduling next cleanup at %ld.%ld\n",
-              (long)tv.tv_sec, (long)tv.tv_usec));
-
-    cleanup_task = tevent_add_timer(ctx->be->ev, ctx,
-                                    tv, ldap_id_cleanup_timer, ctx);
-    if (!cleanup_task) {
-        DEBUG(0, ("FATAL: failed to setup cleanup task!\n"));
+    cleanup_task = tevent_add_timer(cctx->ctx->be->ev, cctx->ctx,
+                                    tv, ldap_id_cleanup_timer, cctx);
+    if (cleanup_task == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("FATAL: failed to setup cleanup task!\n"));
         return EFAULT;
     }
 
     return EOK;
 }
 
-static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx);
+int ldap_id_cleanup_create_timer(struct sdap_id_ctx *ctx,
+                                 struct sdap_domain *sdom,
+                                 struct timeval tv)
+{
+    struct ldap_id_cleanup_ctx *cctx;
+
+    DEBUG(SSSDBG_FUNC_DATA,
+          ("Scheduling next cleanup at %ld.%ld\n",
+          (long)tv.tv_sec, (long)tv.tv_usec));
+
+    cctx = talloc(ctx, struct ldap_id_cleanup_ctx);
+    if (cctx == NULL) {
+        return ENOMEM;
+    }
+    cctx->ctx = ctx;
+    cctx->sdom = sdom;
+
+    return ldap_id_cleanup_set_timer(cctx, tv);
+}
+
+static int cleanup_users(struct sdap_options *opts,
+                         struct sss_domain_info *dom);
 static int cleanup_groups(TALLOC_CTX *memctx,
                           struct sysdb_ctx *sysdb,
                           struct sss_domain_info *domain);
 
-errno_t ldap_id_cleanup(struct sdap_id_ctx *ctx)
+errno_t ldap_id_cleanup(struct sdap_options *opts,
+                        struct sss_domain_info *dom)
 {
     int ret, tret;
     bool in_transaction = false;
@@ -97,26 +127,24 @@ errno_t ldap_id_cleanup(struct sdap_id_ctx *ctx)
         return ENOMEM;
     }
 
-    ret = sysdb_transaction_start(ctx->be->domain->sysdb);
+    ret = sysdb_transaction_start(dom->sysdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
         goto done;
     }
     in_transaction = true;
 
-    ret = cleanup_users(tmp_ctx, ctx);
+    ret = cleanup_users(opts, dom);
     if (ret && ret != ENOENT) {
         goto done;
     }
 
-    ret = cleanup_groups(tmp_ctx,
-                         ctx->be->domain->sysdb,
-                         ctx->be->domain);
+    ret = cleanup_groups(tmp_ctx, dom->sysdb, dom);
     if (ret) {
         goto done;
     }
 
-    ret = sysdb_transaction_commit(ctx->be->domain->sysdb);
+    ret = sysdb_transaction_commit(dom->sysdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction\n"));
         goto done;
@@ -126,7 +154,7 @@ errno_t ldap_id_cleanup(struct sdap_id_ctx *ctx)
     ret = EOK;
 done:
     if (in_transaction) {
-        tret = sysdb_transaction_cancel(ctx->be->domain->sysdb);
+        tret = sysdb_transaction_cancel(dom->sysdb);
         if (tret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n"));
         }
@@ -141,10 +169,11 @@ done:
 static int cleanup_users_logged_in(hash_table_t *table,
                                    const struct ldb_message *msg);
 
-static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx)
+static int cleanup_users(struct sdap_options *opts,
+                         struct sss_domain_info *dom)
 {
     TALLOC_CTX *tmpctx;
-    struct sysdb_ctx *sysdb = ctx->be->domain->sysdb;
+    struct sysdb_ctx *sysdb = dom->sysdb;
     const char *attrs[] = { SYSDB_NAME, SYSDB_UIDNUM, NULL };
     time_t now = time(NULL);
     char *subfilter = NULL;
@@ -156,13 +185,12 @@ static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx)
     int ret;
     int i;
 
-    tmpctx = talloc_new(memctx);
+    tmpctx = talloc_new(NULL);
     if (!tmpctx) {
         return ENOMEM;
     }
 
-    account_cache_expiration = dp_opt_get_int(ctx->opts->basic,
-                                           SDAP_ACCOUNT_CACHE_EXPIRATION);
+    account_cache_expiration = dp_opt_get_int(opts->basic, SDAP_ACCOUNT_CACHE_EXPIRATION);
     DEBUG(9, ("Cache expiration is set to %d days\n",
               account_cache_expiration));
 
@@ -189,7 +217,7 @@ static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx)
         goto done;
     }
 
-    ret = sysdb_search_users(tmpctx, sysdb, ctx->be->domain,
+    ret = sysdb_search_users(tmpctx, sysdb, dom,
                              subfilter, attrs, &count, &msgs);
     if (ret) {
         if (ret == ENOENT) {
@@ -236,7 +264,7 @@ static int cleanup_users(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx)
 
         /* If not logged in or cannot check the table, delete him */
         DEBUG(9, ("About to delete user %s\n", name));
-        ret = sysdb_delete_user(sysdb, ctx->be->domain, name, 0);
+        ret = sysdb_delete_user(sysdb, dom, name, 0);
         if (ret) {
             goto done;
         }
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 5da27fe87ff5900857ec89e6084ab4d766944327..441ac904bd2d03618376f6770304dc5c4db75923 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -384,9 +384,12 @@ struct sdap_domain {
     /* Need to modify the list from a talloc destructor */
     struct sdap_domain **head;
 
+    /* Enumeration and cleanup periodic task */
     struct be_ptask *enum_task;
     /* enumeration loop timer */
     struct timeval last_enum;
+    /* cleanup loop timer */
+    struct timeval last_purge;
 };
 
 struct sdap_options {
diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
index 50ff9f4a0b1b921199ce8425f4065306ae06d7a9..bddc64b1ddefb18316877e971bb7cb8927332a5c 100644
--- a/src/providers/ldap/sdap_async_enum.c
+++ b/src/providers/ldap/sdap_async_enum.c
@@ -93,7 +93,7 @@ sdap_dom_enum_send(TALLOC_CTX *memctx,
     sdom->last_enum = tevent_timeval_current();
 
     t = dp_opt_get_int(ctx->opts->basic, SDAP_CACHE_PURGE_TIMEOUT);
-    if ((ctx->last_purge.tv_sec + t) < sdom->last_enum.tv_sec) {
+    if ((sdom->last_purge.tv_sec + t) < sdom->last_enum.tv_sec) {
         state->purge = true;
     }
 
@@ -311,7 +311,7 @@ static void sdap_dom_enum_services_done(struct tevent_req *subreq)
     }
 
     if (state->purge) {
-        ret = ldap_id_cleanup(state->ctx);
+        ret = ldap_id_cleanup(state->ctx->opts, state->sdom->dom);
         if (ret != EOK) {
             /* Not fatal, worst case we'll have stale entries that would be
              * removed on a subsequent online lookup
-- 
1.8.3.1

-------------- next part --------------
>From cb08ea57fc30f82c9c8669a67a89e259c5fc33ab Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 22 Aug 2013 11:03:01 +0200
Subject: [PATCH 07/11] LDAP: Make sdap_id_setup_tasks reusable for subdomains

Instead of always performing the setup for the main domain, the setup
can now be performed for subdomains as well.
---
 src/providers/ad/ad_init.c       |  2 +-
 src/providers/ipa/ipa_init.c     |  2 +-
 src/providers/ldap/ldap_common.c | 17 ++++++++++++-----
 src/providers/ldap/ldap_common.h |  7 ++++++-
 src/providers/ldap/ldap_init.c   |  2 +-
 5 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index 816d12f062f0331e6f69256d121ef6776eaa89c5..f181afe6e37ace4cd0d7fba83923129b3161aad3 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -204,7 +204,7 @@ sssm_ad_id_init(struct be_ctx *bectx,
         goto done;
     }
 
-    ret = sdap_id_setup_tasks(ad_ctx->sdap_id_ctx);
+    ret = ldap_id_setup_tasks(ad_ctx->sdap_id_ctx);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 407ab166918c5ff5599382c8281502380aa179fe..36ef702a3af81a739f000cb3a0c9e824eba0d872 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -191,7 +191,7 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
     ret = ipa_idmap_init(sdap_ctx, sdap_ctx, &sdap_ctx->opts->idmap_ctx);
     if (ret != EOK) goto done;
 
-    ret = sdap_id_setup_tasks(sdap_ctx);
+    ret = ldap_id_setup_tasks(sdap_ctx);
     if (ret != EOK) {
         goto done;
     }
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index ffa8aae59130258b3346e9071ea8d0c867942fca..f7ad71182a4f46a20386e70ffda129347c5e3d87 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -936,16 +936,23 @@ void sdap_mark_offline(struct sdap_id_ctx *ctx)
     be_mark_offline(ctx->be);
 }
 
-int sdap_id_setup_tasks(struct sdap_id_ctx *ctx)
+int ldap_id_setup_tasks(struct sdap_id_ctx *ctx)
+{
+    return sdap_id_setup_tasks(ctx, ctx->conn, ctx->opts->sdom);
+}
+
+int sdap_id_setup_tasks(struct sdap_id_ctx *ctx,
+                        struct sdap_id_conn_ctx *conn,
+                        struct sdap_domain *sdom)
 {
     struct timeval tv;
     int ret = EOK;
     int delay;
 
     /* set up enumeration task */
-    if (ctx->be->domain->enumerate) {
-        DEBUG(SSSDBG_TRACE_FUNC, ("Setting up enumeration for %s\n", ctx->be->domain->name));
-        ret = ldap_setup_enumeration(ctx, ctx->conn, ctx->opts->sdom);
+    if (sdom->dom->enumerate) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("Setting up enumeration for %s\n", sdom->dom->name));
+        ret = ldap_setup_enumeration(ctx, conn, sdom);
     } else {
         /* the enumeration task, runs the cleanup process by itself,
          * but if enumeration is not running we need to schedule it */
@@ -960,7 +967,7 @@ int sdap_id_setup_tasks(struct sdap_id_ctx *ctx)
         /* run the first one in a couple of seconds so that we have time to
          * finish initializations first*/
         tv = tevent_timeval_current_ofs(10, 0);
-        ret = ldap_id_cleanup_create_timer(ctx, ctx->opts->sdom, tv);
+        ret = ldap_id_cleanup_create_timer(ctx, sdom, tv);
     }
 
     return ret;
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index fb45845eff805ef1359caf5aa3ea509f6fcd5cd9..e5b7f1151e57fd22bce856c81801c2df7a60f0c3 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -90,7 +90,12 @@ errno_t sdap_reinit_cleanup_recv(struct tevent_req *req);
 void sdap_account_info_handler(struct be_req *breq);
 void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
                               struct sdap_id_conn_ctx *conn);
-int sdap_id_setup_tasks(struct sdap_id_ctx *ctx);
+
+/* Set up enumeration and/or cleanup */
+int ldap_id_setup_tasks(struct sdap_id_ctx *ctx);
+int sdap_id_setup_tasks(struct sdap_id_ctx *ctx,
+                        struct sdap_id_conn_ctx *conn,
+                        struct sdap_domain *sdom);
 
 struct tevent_req *
 sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index 38d4fa7178d65873665da08a572952cc2aeea8c0..341338ca491593d906cff1ee71b040c2d0dea38b 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -160,7 +160,7 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
     ret = sdap_idmap_init(ctx, ctx, &ctx->opts->idmap_ctx);
     if (ret != EOK) goto done;
 
-    ret = sdap_id_setup_tasks(ctx);
+    ret = ldap_id_setup_tasks(ctx);
     if (ret != EOK) {
         goto done;
     }
-- 
1.8.3.1

-------------- next part --------------
>From 288d0e6541da33f6ef399d949b59e580be8c5ff1 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 21 Aug 2013 17:22:59 +0200
Subject: [PATCH 08/11] SYSDB: Store enumerate flag for subdomain

---
 src/db/sysdb.h                     |  3 ++-
 src/db/sysdb_subdomains.c          | 27 +++++++++++++++++++++++++--
 src/providers/ad/ad_subdomains.c   |  4 ++--
 src/providers/ipa/ipa_subdomains.c |  3 ++-
 src/tests/sysdb-tests.c            | 12 +++++++-----
 5 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 96679007af90ee813a580094dafd64cde976fa39..2e7b96f1275ad6abe42aa55c87bb28bf996c14b9 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -126,6 +126,7 @@
 #define SYSDB_SUBDOMAIN_FLAT "flatName"
 #define SYSDB_SUBDOMAIN_ID "domainID"
 #define SYSDB_SUBDOMAIN_MPG "mpg"
+#define SYSDB_SUBDOMAIN_ENUM "enumerate"
 
 #define SYSDB_BASE_ID "baseID"
 #define SYSDB_ID_RANGE_SIZE "idRangeSize"
@@ -370,7 +371,7 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name);
 errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                               const char *name, const char *realm,
                               const char *flat_name, const char *domain_id,
-                              bool mpg);
+                              bool mpg, bool enumerate);
 
 errno_t sysdb_update_subdomains(struct sss_domain_info *domain);
 
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index e6eab46f320f04d50097dd5d1bfc3141e6b29be9..2a74e08e9f0c507117d39a089f5868560738e492 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -374,7 +374,7 @@ done:
 errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                               const char *name, const char *realm,
                               const char *flat_name, const char *domain_id,
-                              bool mpg)
+                              bool mpg, bool enumerate)
 {
     TALLOC_CTX *tmp_ctx;
     struct ldb_message *msg;
@@ -385,6 +385,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                            SYSDB_SUBDOMAIN_FLAT,
                            SYSDB_SUBDOMAIN_ID,
                            SYSDB_SUBDOMAIN_MPG,
+                           SYSDB_SUBDOMAIN_ENUM,
                            NULL};
     const char *tmp_str;
     bool tmp_bool;
@@ -393,6 +394,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
     int flat_flags = 0;
     int id_flags = 0;
     int mpg_flags = 0;
+    int enum_flags = 0;
     int ret;
 
     tmp_ctx = talloc_new(NULL);
@@ -422,6 +424,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
         if (flat_name) flat_flags = LDB_FLAG_MOD_ADD;
         if (domain_id) id_flags = LDB_FLAG_MOD_ADD;
         mpg_flags = LDB_FLAG_MOD_ADD;
+        enum_flags = LDB_FLAG_MOD_ADD;
     } else if (res->count != 1) {
         ret = EINVAL;
         goto done;
@@ -453,10 +456,15 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
         if (tmp_bool != mpg) {
             mpg_flags = LDB_FLAG_MOD_REPLACE;
         }
+        tmp_bool = ldb_msg_find_attr_as_bool(res->msgs[0], SYSDB_SUBDOMAIN_ENUM,
+                                             !enumerate);
+        if (tmp_bool != enumerate) {
+            enum_flags = LDB_FLAG_MOD_REPLACE;
+        }
     }
 
     if (!store && realm_flags == 0 && flat_flags == 0 && id_flags == 0
-            && mpg_flags == 0) {
+            && mpg_flags == 0 && enum_flags == 0) {
         ret = EOK;
         goto done;
     }
@@ -539,6 +547,21 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
         }
     }
 
+    if (enum_flags) {
+        ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_ENUM, enum_flags, NULL);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+
+        ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_ENUM,
+                                 enumerate ? "TRUE" : "FALSE");
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+    }
+
     ret = ldb_modify(sysdb->ldb, msg);
     if (ret != LDB_SUCCESS) {
         DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to add subdomain attributes to "
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index be4781cc5bc01152ffa1fbedf0ad5352f72939ae..f6732f2d719b11c6fa894ce34685aff879087f79 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -156,9 +156,9 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx,
         goto done;
     }
 
-    /* AD subdomains are currently all mpg */
+    /* AD subdomains are currently all mpg and do not enumerate */
     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str,
-                                true);
+                                true, false);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n"));
         goto done;
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 9ded9954bbc819e65e3b222c8968d2440320c4be..9b7000fb7d8ecc165e7a94fd6c18cf71f4e6eeb0 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -445,7 +445,8 @@ static errno_t ipa_subdom_store(struct sss_domain_info *domain,
 
     mpg = sdap_idmap_domain_has_algorithmic_mapping(sdap_idmap_ctx, id);
 
-    ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, id, mpg);
+    ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat,
+                                id, mpg, false);
     if (ret) {
         DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n"));
         goto done;
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 60a20c8b4d1dcb8701286b1589bdcf351d2ccd95..31d2dd3dea7aaef94aad88e398fa779b8c85fce3 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -4524,7 +4524,8 @@ START_TEST(test_sysdb_subdomain_create)
     fail_if(ret != EOK, "Could not set up the test");
 
     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                dom1[0], dom1[1], dom1[2], dom1[3], false);
+                                dom1[0], dom1[1], dom1[2], dom1[3],
+                                false, false);
     fail_if(ret != EOK, "Could not set up the test (dom1)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -4537,7 +4538,8 @@ START_TEST(test_sysdb_subdomain_create)
             dom1[0], test_ctx->domain->subdomains->name);
 
     ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                dom2[0], dom2[1], dom2[2], dom2[3], false);
+                                dom2[0], dom2[1], dom2[2], dom2[3],
+                                false, false);
     fail_if(ret != EOK, "Could not set up the test (dom2)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -4586,7 +4588,7 @@ START_TEST(test_sysdb_subdomain_store_user)
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false);
+                                false, false);
     fail_if(ret != EOK, "Could not set up the test (test subdom)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -4657,7 +4659,7 @@ START_TEST(test_sysdb_subdomain_user_ops)
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false);
+                                false, false);
     fail_if(ret != EOK, "Could not set up the test (test subdom)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -4712,7 +4714,7 @@ START_TEST(test_sysdb_subdomain_group_ops)
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false);
+                                false, false);
     fail_if(ret != EOK, "Could not set up the test (test subdom)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
-- 
1.8.3.1

-------------- next part --------------
>From ed3232ed87e49d9144148885e14c175f1bd89a82 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 21 Aug 2013 17:28:47 +0200
Subject: [PATCH 09/11] Read enumerate state for subdomains from cache

The enumerate flag will be read from the cache for subdomains and
the domain object will be created accordingly.
---
 src/db/sysdb_subdomains.c    | 16 +++++++++++++++-
 src/tests/sysdb-tests.c      |  6 +++---
 src/util/domain_info_utils.c |  5 +++--
 src/util/util.h              |  3 ++-
 4 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index 2a74e08e9f0c507117d39a089f5868560738e492..6250fa432ffaf013066877be51bdfe2b0a56349b 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -34,6 +34,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
                            SYSDB_SUBDOMAIN_FLAT,
                            SYSDB_SUBDOMAIN_ID,
                            SYSDB_SUBDOMAIN_MPG,
+                           SYSDB_SUBDOMAIN_ENUM,
                            NULL};
     struct sss_domain_info *dom;
     struct ldb_dn *basedn;
@@ -42,6 +43,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
     const char *flat;
     const char *id;
     bool mpg;
+    bool enumerate;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -96,6 +98,9 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
         mpg = ldb_msg_find_attr_as_bool(res->msgs[i],
                                         SYSDB_SUBDOMAIN_MPG, false);
 
+        enumerate = ldb_msg_find_attr_as_bool(res->msgs[i],
+                                              SYSDB_SUBDOMAIN_ENUM, false);
+
         /* explicitly use dom->next as we need to check 'disabled' domains */
         for (dom = domain->subdomains; dom; dom = dom->next) {
             if (strcasecmp(dom->name, name) == 0) {
@@ -143,12 +148,21 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
                     dom->mpg = mpg;
                 }
 
+                if (dom->enumerate != enumerate) {
+                    DEBUG(SSSDBG_TRACE_INTERNAL,
+                          ("MPG state change from [%s] to [%s]!\n",
+                           dom->enumerate ? "true" : "false",
+                           enumerate ? "true" : "false"));
+                    dom->enumerate = enumerate;
+                }
+
                 break;
             }
         }
         /* If not found in loop it is a new subdomain */
         if (dom == NULL) {
-            dom = new_subdomain(domain, domain, name, realm, flat, id, mpg);
+            dom = new_subdomain(domain, domain, name, realm,
+                                flat, id, mpg, enumerate);
             if (dom == NULL) {
                 ret = ENOMEM;
                 goto done;
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 31d2dd3dea7aaef94aad88e398fa779b8c85fce3..6f95d248b65fe832447268e4d9eb94ceb2af7c17 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -4584,7 +4584,7 @@ START_TEST(test_sysdb_subdomain_store_user)
 
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false);
+                              false, false);
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
@@ -4655,7 +4655,7 @@ START_TEST(test_sysdb_subdomain_user_ops)
 
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false);
+                              false, false);
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
@@ -4710,7 +4710,7 @@ START_TEST(test_sysdb_subdomain_group_ops)
 
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false);
+                              false, false);
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index 6553927cfd07162dc7f901f022284a839d3ba4e1..be5185698a218797b5966902b2c86eecf1e7bb30 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -76,7 +76,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
                                       const char *realm,
                                       const char *flat_name,
                                       const char *id,
-                                      bool mpg)
+                                      bool mpg,
+                                      bool enumerate)
 {
     struct sss_domain_info *dom;
 
@@ -132,7 +133,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
         }
     }
 
-    dom->enumerate = false;
+    dom->enumerate = enumerate;
     dom->fqnames = true;
     dom->mpg = mpg;
     /* FIXME: get ranges from the server */
diff --git a/src/util/util.h b/src/util/util.h
index d8aeb733e4dbe2db414be5caa43712cecccca7f9..73d1fae60cb9aff88834a5cd565f4e9d67a07db6 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -544,7 +544,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
                                       const char *realm,
                                       const char *flat_name,
                                       const char *id,
-                                      bool mpg);
+                                      bool mpg,
+                                      bool enumerate);
 
 errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
                          struct confdb_ctx *cdb,
-- 
1.8.3.1

-------------- next part --------------
>From 19d8b514e555815fe743a85a7b78886f74137bb0 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 21 Aug 2013 05:15:36 +0200
Subject: [PATCH 10/11] IPA: enable enumeration if parent domain enumerates in
 server mode

---
 src/providers/ipa/ipa_subdomains.c | 38 ++++++++++++++++++++++++++++++++++----
 1 file changed, 34 insertions(+), 4 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 9b7000fb7d8ecc165e7a94fd6c18cf71f4e6eeb0..314e23265f03b8b7d6a19857ea10201a35c6f7af 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -102,6 +102,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     struct ad_options *ad_options;
     struct ad_id_ctx *ad_id_ctx;
     const char *gc_service_name;
+    struct sdap_domain *sdom;
     errno_t ret;
 
     ad_options = ad_create_default_options(id_ctx, id_ctx->server_mode->realm,
@@ -162,6 +163,18 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
         return ret;
     }
 
+    sdom = sdap_domain_get(ad_id_ctx->sdap_id_ctx->opts, subdom);
+    if (sdom == NULL) {
+        return EFAULT;
+    }
+
+    ret = sdap_id_setup_tasks(ad_id_ctx->sdap_id_ctx,
+                              ad_id_ctx->ldap_ctx, sdom);
+    if (ret != EOK) {
+        talloc_free(ad_options);
+        return ret;
+    }
+
     /* Set up the ID mapping object */
     ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
         id_ctx->sdap_id_ctx->opts->idmap_ctx;
@@ -242,6 +255,7 @@ ipa_ad_subdom_remove(struct ipa_subdomains_ctx *ctx,
                      struct sss_domain_info *subdom)
 {
     struct ipa_ad_server_ctx *iter;
+    struct sdap_domain *sdom;
 
     if (dp_opt_get_bool(ctx->id_ctx->ipa_options->basic,
                         IPA_SERVER_MODE) == false) {
@@ -260,6 +274,10 @@ ipa_ad_subdom_remove(struct ipa_subdomains_ctx *ctx,
 
     sdap_domain_remove(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
     DLIST_REMOVE(ctx->id_ctx->server_mode->trusts, iter);
+
+    sdom = sdap_domain_get(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
+    if (sdom == NULL) return;
+    be_ptask_destroy(&sdom->enum_task);
 }
 
 const char *get_flat_name_from_subdomain_name(struct be_ctx *be_ctx,
@@ -404,7 +422,8 @@ done:
 
 static errno_t ipa_subdom_store(struct sss_domain_info *domain,
                                 struct sdap_idmap_ctx *sdap_idmap_ctx,
-                                struct sysdb_attrs *attrs)
+                                struct sysdb_attrs *attrs,
+                                bool server_mode)
 {
     TALLOC_CTX *tmp_ctx;
     const char *name;
@@ -413,6 +432,7 @@ static errno_t ipa_subdom_store(struct sss_domain_info *domain,
     const char *id;
     int ret;
     bool mpg;
+    bool enumerate;
 
     tmp_ctx = talloc_new(domain);
     if (tmp_ctx == NULL) {
@@ -445,8 +465,14 @@ static errno_t ipa_subdom_store(struct sss_domain_info *domain,
 
     mpg = sdap_idmap_domain_has_algorithmic_mapping(sdap_idmap_ctx, id);
 
+    if (server_mode == true) {
+        enumerate = domain->enumerate;
+    } else {
+        enumerate = false;
+    }
+
     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat,
-                                id, mpg, false);
+                                id, mpg, enumerate);
     if (ret) {
         DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n"));
         goto done;
@@ -467,11 +493,15 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
     const char *value;
     int c, h;
     int ret;
+    bool server_mode;
 
     domain = ctx->be_ctx->domain;
     memset(handled, 0, sizeof(bool) * count);
     h = 0;
 
+    server_mode = dp_opt_get_bool(ctx->id_ctx->ipa_options->basic,
+                                  IPA_SERVER_MODE);
+
     /* check existing subdomains */
     for (dom = get_next_domain(domain, true);
          dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
@@ -503,7 +533,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
         } else {
             /* ok let's try to update it */
             ret = ipa_subdom_store(domain, ctx->sdap_id_ctx->opts->idmap_ctx,
-                                   reply[c]);
+                                   reply[c], server_mode);
             if (ret) {
                 /* Nothing we can do about the errorr. Let's at least try
                  * to reuse the existing domain
@@ -533,7 +563,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
          * to reuse the existing domain.
          */
         ret = ipa_subdom_store(domain, ctx->sdap_id_ctx->opts->idmap_ctx,
-                               reply[c]);
+                               reply[c], server_mode);
         if (ret) {
             DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to parse subdom data, "
                   "will try to use cached subdomain\n"));
-- 
1.8.3.1

-------------- next part --------------
>From 680a5f6544c038c106ac0e5994802213ca22603e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 21 Aug 2013 16:27:50 +0200
Subject: [PATCH 11/11] NSS: Descend into subdomains if enumerate=true

Since we now store the enumerate flag in sysdb for subdomains, we can
always descend to all available subdomains and if they do not allow
enumeration, simply skip them.
---
 src/responder/nss/nsssrv_cmd.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 7c35a7b3121dae9ba53135d79d1f7619be1e810b..58651838e4c94c8e1ffbf391f7559621d948c39d 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -1710,7 +1710,7 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
 
     while (dom) {
         while (dom && dom->enumerate == 0) {
-            dom = get_next_domain(dom, false);
+            dom = get_next_domain(dom, true);
         }
 
         if (!dom) break;
@@ -1768,13 +1768,13 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
         if (ret != EOK) {
             DEBUG(1, ("Enum from cache failed, skipping domain [%s]\n",
                       dom->name));
-            dom = get_next_domain(dom, false);
+            dom = get_next_domain(dom, true);
             continue;
         }
 
         if (res->count == 0) {
             DEBUG(4, ("Domain [%s] has no users, skipping.\n", dom->name));
-            dom = get_next_domain(dom, false);
+            dom = get_next_domain(dom, true);
             continue;
         }
 
@@ -1792,7 +1792,7 @@ static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
         nctx->pctx->num++;
 
         /* do not reply until all domain searches are done */
-        dom = get_next_domain(dom, false);
+        dom = get_next_domain(dom, true);
     }
 
     /* We've finished all our lookups
-- 
1.8.3.1



More information about the sssd-devel mailing list