From 334ded9472083664e72eadcda40c284467f0e0a9 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 | 306 +++++++++++++++++++++++++++++++++++++++++++++
 src/providers/dp_refresh.h |  66 ++++++++++
 3 files changed, 374 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 17325cd9f5ad7ca5df23e2c26483683424945462..41e859290c9efc932e1b45b5b23d5da808b2a606 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -439,6 +439,7 @@ dist_noinst_HEADERS = \
     src/providers/dp_backend.h \
     src/providers/dp_dyndns.h \
     src/providers/dp_ptask.h \
+    src/providers/dp_refresh.h \
     src/providers/fail_over.h \
     src/providers/fail_over_srv.h \
     src/util/child_common.h \
@@ -710,6 +711,7 @@ sssd_be_SOURCES = \
     src/providers/data_provider_callbacks.c \
     src/providers/dp_dyndns.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..b4e4cb68e061dbd2866b24cdf98e90b84dcfa1a6
--- /dev/null
+++ b/src/providers/dp_refresh.c
@@ -0,0 +1,306 @@
+/*
+    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 "util/util_errors.h"
+#include "db/sysdb.h"
+
+static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx,
+                                     struct sss_domain_info *domain,
+                                     const char *objectclass,
+                                     struct ldb_dn *base_dn,
+                                     const char *attr,
+                                     char ***_values)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    const char *attrs[] = {attr, NULL};
+    const char *filter = NULL;
+    char **values = NULL;
+    struct ldb_message **msgs = NULL;
+    struct sysdb_attrs **records = NULL;
+    size_t count;
+    time_t now = time(NULL);
+    errno_t ret;
+
+    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;
+    }
+
+    ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &records);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Could not convert ldb message to sysdb_attrs\n"));
+        goto done;
+    }
+
+    ret = sysdb_attrs_to_list(tmp_ctx, records, count, attr, &values);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    *_values = talloc_steal(mem_ctx, values);
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}
+
+static errno_t be_refresh_get_netgroups(TALLOC_CTX *mem_ctx,
+                                        struct sss_domain_info *domain,
+                                        char ***_values)
+{
+    struct ldb_dn *base_dn = NULL;
+    errno_t ret;
+
+    base_dn = sysdb_netgroup_base_dn(domain->sysdb, mem_ctx, domain);
+    if (base_dn == NULL) {
+        return ENOMEM;
+    }
+
+    ret = be_refresh_get_values(mem_ctx, domain, SYSDB_NETGROUP_CLASS,
+                                base_dn, SYSDB_NAME, _values);
+
+    talloc_free(base_dn);
+    return ret;
+}
+
+typedef errno_t
+(*be_refresh_get_values_t)(TALLOC_CTX *mem_ctx,
+                           struct sss_domain_info *domain,
+                           char ***_values);
+
+
+struct be_refresh_cb {
+    bool enabled;
+    be_refresh_get_values_t get_values;
+    be_refresh_send_t send;
+    be_refresh_recv_t recv;
+    void *pvt;
+};
+
+struct be_refresh_ctx {
+    struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL];
+};
+
+struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx)
+{
+    struct be_refresh_ctx *ctx = NULL;
+
+    ctx = talloc_zero(mem_ctx, struct be_refresh_ctx);
+    if (ctx == NULL) {
+        return NULL;
+    }
+
+    ctx->callbacks[BE_REFRESH_TYPE_NETGROUPS].get_values \
+        = be_refresh_get_netgroups;
+
+    return ctx;
+}
+
+errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx,
+                          enum be_refresh_type 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_type 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, sss_strerror(ret)));
+        goto immediately;
+    }
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t be_refresh_step(struct tevent_req *req)
+{
+    struct be_refresh_state *state = NULL;
+    struct tevent_req *subreq = NULL;
+    char **values = NULL;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct be_refresh_state);
+
+    state->cb = &state->ctx->callbacks[state->index];
+    while (state->index != BE_REFRESH_TYPE_SENTINEL && !state->cb->enabled) {
+        state->index++;
+        state->cb = &state->ctx->callbacks[state->index];
+    }
+
+    if (state->index == BE_REFRESH_TYPE_SENTINEL) {
+        ret = EOK;
+        goto done;
+    }
+
+    if (state->cb->get_values == NULL || state->cb->send == NULL
+        || state->cb->recv == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = state->cb->get_values(state, state->be_ctx->domain, &values);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to obtain DN list [%d]: %s\n",
+                                    ret, sss_strerror(ret)));
+        goto done;
+    }
+
+    subreq = state->cb->send(state, state->ev, state->be_ctx,
+                             values, 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, values);
+
+    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..cae2f7876bbd6a98b8c4710c41687fce617396f2
--- /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,
+                     char **values,
+                     void *pvt);
+
+typedef errno_t
+(*be_refresh_recv_t)(struct tevent_req *req);
+
+enum be_refresh_type {
+    BE_REFRESH_TYPE_NETGROUPS,
+    BE_REFRESH_TYPE_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 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

