From e80f28bf77430283ef2b910966ae2ae2018d402c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Mon, 13 May 2013 10:05:00 +0200
Subject: [PATCH 2/4] back end: periodical refresh of expired records API

https://fedorahosted.org/sssd/ticket/1713
---
 Makefile.am                |   2 +
 src/providers/dp_refresh.c | 310 +++++++++++++++++++++++++++++++++++++++++++++
 src/providers/dp_refresh.h |  66 ++++++++++
 3 files changed, 378 insertions(+)
 create mode 100644 src/providers/dp_refresh.c
 create mode 100644 src/providers/dp_refresh.h

diff --git a/Makefile.am b/Makefile.am
index 049f47023d6586adcaf045adf919e38fb4c0c324..4cbd4a0b9a669411440b24f90d7daacec74d6386 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -407,6 +407,7 @@ dist_noinst_HEADERS = \
     src/providers/data_provider.h \
     src/providers/dp_backend.h \
     src/providers/dp_ptask.h \
+    src/providers/dp_refresh.h \
     src/providers/fail_over.h \
     src/providers/providers.h \
     src/util/child_common.h \
@@ -649,6 +650,7 @@ sssd_be_SOURCES = \
     src/providers/data_provider_opts.c \
     src/providers/data_provider_callbacks.c \
     src/providers/dp_ptask.c \
+    src/providers/dp_refresh.c \
     $(SSSD_FAILOVER_OBJ)
 sssd_be_LDADD = \
     -ldl \
diff --git a/src/providers/dp_refresh.c b/src/providers/dp_refresh.c
new file mode 100644
index 0000000000000000000000000000000000000000..b5e316b0df19a001c7ce7ab9ac7af01c62b3f545
--- /dev/null
+++ b/src/providers/dp_refresh.c
@@ -0,0 +1,310 @@
+/*
+    Authors:
+        Pavel Březina <pbrezina@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/>.
+*/
+
+#include <tevent.h>
+#include <talloc.h>
+#include <time.h>
+#include <ldb.h>
+
+#include "providers/dp_backend.h"
+#include "providers/dp_ptask.h"
+#include "providers/dp_refresh.h"
+#include "db/sysdb.h"
+
+static errno_t be_refresh_get_names(TALLOC_CTX *mem_ctx,
+                                    struct sss_domain_info *domain,
+                                    const char *objectclass,
+                                    struct ldb_dn *base_dn,
+                                    const char ***_name_list)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    static const char *attrs[] = {SYSDB_NAME, NULL};
+    const char *filter = NULL;
+    const char **name_list = NULL;
+    const char *val = NULL;
+    struct ldb_message **msgs = NULL;
+    size_t count;
+    time_t now = time(NULL);
+    errno_t ret;
+    size_t i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    filter = talloc_asprintf(tmp_ctx, "(&(%s<=%lld))",
+                             SYSDB_CACHE_EXPIRE, (long long)now);
+    if (filter == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_search_entry(tmp_ctx, domain->sysdb, base_dn,
+                             LDB_SCOPE_SUBTREE, filter, attrs,
+                             &count, &msgs);
+    if (ret == ENOENT) {
+        count = 0;
+    } else if (ret != EOK) {
+        goto done;
+    }
+
+    name_list = talloc_array(tmp_ctx, const char *, count + 1);
+    if (name_list == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < count; i++) {
+        val = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+        if (val == NULL) {
+            ret = EINVAL;
+            goto done;
+        }
+
+        name_list[i] = talloc_steal(name_list, val);
+    }
+
+    name_list[i] = NULL;
+
+    *_name_list = talloc_steal(mem_ctx, name_list);
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static errno_t be_refresh_get_netgroups_names(TALLOC_CTX *mem_ctx,
+                                              struct sss_domain_info *domain,
+                                              const char ***_name_list)
+{
+    struct ldb_dn *base_dn = NULL;
+    errno_t ret;
+
+    base_dn = sysdb_netgroup_base_dn(domain->sysdb, mem_ctx);
+    if (base_dn == NULL) {
+        return ENOMEM;
+    }
+
+    ret = be_refresh_get_names(mem_ctx, domain, SYSDB_NETGROUP_CLASS,
+                               base_dn, _name_list);
+
+    talloc_free(base_dn);
+    return ret;
+}
+
+typedef errno_t
+(*be_refresh_get_names_t)(TALLOC_CTX *mem_ctx,
+                          struct sss_domain_info *domain,
+                          const char ***_name_list);
+
+
+struct be_refresh_cb {
+    bool enabled;
+    be_refresh_get_names_t get_names;
+    be_refresh_send_t send;
+    be_refresh_recv_t recv;
+    void *pvt;
+};
+
+struct be_refresh_ctx {
+    struct be_refresh_cb callbacks[BE_REFRESH_SENTINEL];
+};
+
+struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx)
+{
+    struct be_refresh_ctx *ctx = NULL;
+
+    ctx = talloc_zero(mem_ctx, struct be_refresh_ctx);
+    if (ctx == NULL) {
+        return NULL;
+    }
+
+    ctx->callbacks[BE_REFRESH_NETGROUPS].get_names = be_refresh_get_netgroups_names;
+
+    return ctx;
+}
+
+errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
+                          enum be_refresh type,
+                          be_refresh_send_t send,
+                          be_refresh_recv_t recv,
+                          void *pvt)
+{
+    if (ctx == NULL || send == NULL || recv == NULL) {
+        return EINVAL;
+    }
+
+    if (ctx->callbacks[type].enabled) {
+        return EEXIST;
+    }
+
+    ctx->callbacks[type].enabled = true;
+    ctx->callbacks[type].send = send;
+    ctx->callbacks[type].recv = recv;
+    ctx->callbacks[type].pvt = pvt;
+
+    return EOK;
+}
+
+struct be_refresh_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct be_refresh_ctx *ctx;
+    struct be_refresh_cb *cb;
+    enum be_refresh index;
+};
+
+static errno_t be_refresh_step(struct tevent_req *req);
+static void be_refresh_done(struct tevent_req *subreq);
+
+struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct be_ctx *be_ctx,
+                                   struct be_ptask *be_ptask,
+                                   void *pvt)
+{
+    struct be_refresh_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct be_refresh_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->ctx = talloc_get_type(pvt, struct be_refresh_ctx);
+    if (state->ctx == NULL) {
+        ret = EINVAL;
+        goto immediately;
+    }
+
+    ret = be_refresh_step(req);
+    if (ret == EOK) {
+        goto immediately;
+    } else if (ret != EAGAIN) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("be_refresh_step() failed [%d]: %s\n",
+                                    ret, strerror(ret)));
+        goto immediately;
+    }
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t be_refresh_step(struct tevent_req *req)
+{
+    struct be_refresh_state *state = NULL;
+    struct tevent_req *subreq = NULL;
+    const char **dn = NULL;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct be_refresh_state);
+
+    state->cb = &state->ctx->callbacks[state->index];
+    while (state->index != BE_REFRESH_SENTINEL && !state->cb->enabled) {
+        state->index++;
+        state->cb = &state->ctx->callbacks[state->index];
+    }
+
+    if (state->index == BE_REFRESH_SENTINEL) {
+        ret = EOK;
+        goto done;
+    }
+
+    if (state->cb->get_names == NULL || state->cb->send == NULL
+        || state->cb->recv == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = state->cb->get_names(state, state->be_ctx->domain, &dn);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to obtain DN list [%d]: %s\n",
+                                    ret, strerror(ret)));
+        goto done;
+    }
+
+    subreq = state->cb->send(state, state->ev, state->be_ctx,
+                             dn, state->cb->pvt);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, be_refresh_done, req);
+
+    /* make the list disappear with subreq */
+    talloc_steal(subreq, dn);
+
+    state->index++;
+    ret = EAGAIN;
+
+done:
+    return ret;
+}
+
+static void be_refresh_done(struct tevent_req *subreq)
+{
+    struct be_refresh_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct be_refresh_state);
+
+    ret = state->cb->recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = be_refresh_step(req);
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+errno_t be_refresh_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
diff --git a/src/providers/dp_refresh.h b/src/providers/dp_refresh.h
new file mode 100644
index 0000000000000000000000000000000000000000..54c6aac99866415d4f33f61ac05d487f71b8c49c
--- /dev/null
+++ b/src/providers/dp_refresh.h
@@ -0,0 +1,66 @@
+/*
+    Authors:
+        Pavel Březina <pbrezina@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 _DP_REFRESH_H_
+#define _DP_REFRESH_H_
+
+#include <tevent.h>
+#include <talloc.h>
+
+#include "providers/dp_backend.h"
+#include "providers/dp_ptask.h"
+
+/**
+ * name_list contains SYSDB_NAME of all expired records.
+ */
+typedef struct tevent_req *
+(*be_refresh_send_t)(TALLOC_CTX *mem_ctx,
+                     struct tevent_context *ev,
+                     struct be_ctx *be_ctx,
+                     const char **name_list,
+                     void *pvt);
+
+typedef errno_t
+(*be_refresh_recv_t)(struct tevent_req *req);
+
+enum be_refresh {
+    BE_REFRESH_NETGROUPS,
+    BE_REFRESH_SENTINEL
+};
+
+struct be_refresh_ctx;
+
+struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx);
+
+errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
+                          enum be_refresh type,
+                          be_refresh_send_t send,
+                          be_refresh_recv_t recv,
+                          void *pvt);
+
+struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct be_ctx *be_ctx,
+                                   struct be_ptask *be_ptask,
+                                   void *pvt);
+
+errno_t be_refresh_recv(struct tevent_req *req);
+
+#endif /* _DP_REFRESH_H_ */
-- 
1.7.11.7

