From 65f4ba71b045a0e25f262f693cf9cd7ed8a0fca3 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Tue, 27 Apr 2010 14:57:37 -0400
Subject: [PATCH] Add callback when the ID provider switches from offline to online

Allow backends to set a callback in the be_ctx that should be
invoked when the ID provider goes online.

This can be used to perform regular maintenance tasks that are
valid only when going online.
---
 src/providers/data_provider_be.c           |  138 ++++++++++++++++++++++++++++
 src/providers/dp_backend.h                 |   17 ++++
 src/providers/ldap/sdap_async_connection.c |    9 ++
 src/providers/proxy.c                      |   10 ++
 4 files changed, 174 insertions(+), 0 deletions(-)

diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index 69aba6cebf03b9922e5d843763eace3117e7d1fb..1b8e6bc8f39702db0129f1a56d2fba41d1fc5fe6 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -166,8 +166,146 @@ void be_mark_offline(struct be_ctx *ctx)
 
     ctx->offstat.went_offline = time(NULL);
     ctx->offstat.offline = true;
+    ctx->run_online_cb = true;
 }
 
+
+struct be_conn_online_cb {
+    struct be_conn_online_cb *prev;
+    struct be_conn_online_cb *next;
+
+    be_conn_online_callback_t cb;
+    void *pvt;
+
+    struct be_ctx *be;
+};
+
+static int online_cb_destructor(TALLOC_CTX *ptr);
+int be_add_online_cb(TALLOC_CTX *mem_ctx,
+                     struct be_ctx *ctx,
+                     be_conn_online_callback_t cb,
+                     void *pvt,
+                     struct be_conn_online_cb **online_cb)
+{
+    struct be_conn_online_cb *on_cb;
+
+    if (!ctx || !cb) {
+        return EINVAL;
+    }
+
+    on_cb = talloc(mem_ctx, struct be_conn_online_cb);
+    if (!online_cb) {
+        return ENOMEM;
+    }
+
+    on_cb->cb = cb;
+    on_cb->pvt = pvt;
+    on_cb->be = ctx;
+
+    DLIST_ADD(ctx->online_cb_list, on_cb);
+
+    talloc_set_destructor((TALLOC_CTX *)on_cb, online_cb_destructor);
+
+    /* Make sure we run the callback for the first
+     * connection after startup.
+     */
+    ctx->run_online_cb = true;
+
+    if (online_cb) {
+        *online_cb = on_cb;
+    }
+
+    return EOK;
+}
+
+static int online_cb_destructor(TALLOC_CTX *ptr)
+{
+    struct be_conn_online_cb *cb =
+            talloc_get_type(ptr, struct be_conn_online_cb);
+    DLIST_REMOVE(cb->be->online_cb_list, cb);
+    return 0;
+}
+
+
+struct be_online_cb_ctx {
+    struct be_ctx *be;
+    struct be_conn_online_cb *callback;
+};
+
+static void be_run_online_cb_step(struct tevent_context *ev,
+                                  struct tevent_timer *te,
+                                  struct timeval current_time,
+                                  void *pvt);
+void be_run_online_cb(struct be_ctx *be) {
+    struct timeval soon;
+    struct tevent_timer *te;
+    struct be_online_cb_ctx *cb_ctx;
+
+    if (be->run_online_cb && be->online_cb_list) {
+        /* Reset the flag. We only want to run these
+         * callbacks when transitioning to online
+         */
+        be->run_online_cb = false;
+
+        DEBUG(3, ("Going online. Running callbacks.\n"));
+
+        cb_ctx = talloc(be, struct be_online_cb_ctx);
+        if (!cb_ctx) {
+            DEBUG(0, ("Out of memory. Could not invoke callbacks\n"));
+            return;
+        }
+        cb_ctx->be = be;
+        cb_ctx->callback = be->online_cb_list;
+
+        /* Delay 30ms so we don't block any other events */
+        soon = tevent_timeval_current_ofs(0, 30000);
+        te = tevent_add_timer(be->ev, cb_ctx, soon,
+                              be_run_online_cb_step,
+                              cb_ctx);
+        if (!te) {
+            DEBUG(0, ("Out of memory. Could not invoke callbacks\n"));
+            talloc_free(cb_ctx);
+        }
+    }
+}
+
+static void be_run_online_cb_step(struct tevent_context *ev,
+                                  struct tevent_timer *te,
+                                  struct timeval current_time,
+                                  void *pvt)
+{
+    struct be_online_cb_ctx *cb_ctx =
+            talloc_get_type(pvt, struct be_online_cb_ctx);
+    struct tevent_timer *tev;
+    struct timeval soon;
+
+    /* Call the callback */
+    cb_ctx->callback->cb(cb_ctx->callback->pvt);
+
+    if (cb_ctx->callback->next) {
+        cb_ctx->callback = cb_ctx->callback->next;
+
+        /* Delay 30ms so we don't block any other events */
+        soon = tevent_timeval_current_ofs(0, 30000);
+        tev = tevent_add_timer(cb_ctx->be->ev, cb_ctx, soon,
+                               be_run_online_cb_step,
+                               cb_ctx);
+        if (!te) {
+            DEBUG(0, ("Out of memory. Could not invoke callbacks\n"));
+            goto final;
+        }
+        return;
+    }
+
+final:
+    /* Steal the timer event onto the be_ctx so it doesn't
+     * get freed with the cb_ctx
+     */
+    talloc_steal(cb_ctx->be, te);
+    talloc_free(cb_ctx);
+}
+
+
 static int be_check_online(DBusMessage *message, struct sbus_connection *conn)
 {
     struct be_client *becli;
diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h
index f1069d0db313ca278e6125908668e2aad2c25b76..330f0f03b7b107cfbe272c92afed83e687f32d70 100644
--- a/src/providers/dp_backend.h
+++ b/src/providers/dp_backend.h
@@ -35,6 +35,8 @@ typedef void (*be_shutdown_fn)(void *);
 typedef void (*be_req_fn_t)(struct be_req *);
 typedef void (*be_async_callback_t)(struct be_req *, int, int, const char *);
 
+typedef void (*be_conn_online_callback_t)(void *);
+
 enum bet_type {
     BET_NULL = 0,
     BET_ID,
@@ -76,6 +78,8 @@ struct be_client {
 
 struct be_failover_ctx;
 
+struct be_conn_online_cb;
+
 struct be_ctx {
     struct tevent_context *ev;
     struct confdb_ctx *cdb;
@@ -85,6 +89,12 @@ struct be_ctx {
     const char *conf_path;
     struct be_failover_ctx *be_fo;
 
+    /* Functions to be invoked when the
+     * backend goes online
+     */
+    struct be_conn_online_cb *online_cb_list;
+    bool run_online_cb;
+
     struct be_offline_status offstat;
 
     struct sbus_connection *mon_conn;
@@ -122,6 +132,13 @@ struct be_acct_req {
 bool be_is_offline(struct be_ctx *ctx);
 void be_mark_offline(struct be_ctx *ctx);
 
+int be_add_online_cb(TALLOC_CTX *mem_ctx,
+                     struct be_ctx *ctx,
+                     be_conn_online_callback_t cb,
+                     void *pvt,
+                     struct be_conn_online_cb **online_cb);
+void be_run_online_cb(struct be_ctx *be);
+
 /* from data_provider_fo.c */
 typedef void (be_svc_callback_fn_t)(void *, struct fo_server *);
 
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index 9871dc2109d2c0eac00f95401ba5dae2d16486ce..214c58efb63d71580f64dce95c8a9a1bec302581 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -813,6 +813,7 @@ struct sdap_cli_connect_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
     struct sdap_service *service;
+    struct be_ctx *be;
 
     bool use_rootdse;
     struct sysdb_attrs *rootdse;
@@ -848,6 +849,7 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
     state->opts = opts;
     state->service = service;
     state->srv = NULL;
+    state->be = be;
 
     if (rootdse) {
         state->use_rootdse = true;
@@ -1077,6 +1079,8 @@ static void sdap_cli_auth_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
+    struct sdap_cli_connect_state *state = tevent_req_data(req,
+                                             struct sdap_cli_connect_state);
     enum sdap_result result;
     int ret;
 
@@ -1091,6 +1095,11 @@ static void sdap_cli_auth_done(struct tevent_req *subreq)
         return;
     }
 
+    /* Reconnection succeeded
+     * Run any post-connection routines
+     */
+    be_run_online_cb(state->be);
+
     tevent_req_done(req);
 }
 
diff --git a/src/providers/proxy.c b/src/providers/proxy.c
index 4da89cccd8c68fc474a172b3b70e8c43b85a0a52..3a1f75a86de5c6bfde457779e9bb8fdcc1f35321 100644
--- a/src/providers/proxy.c
+++ b/src/providers/proxy.c
@@ -277,6 +277,16 @@ static void proxy_pam_handler(struct be_req *req) {
 static void proxy_reply(struct be_req *req, int dp_err,
                         int error, const char *errstr)
 {
+    if (!req->be_ctx->offstat.offline) {
+        /* This action took place online.
+         * Fire any online callbacks if necessary.
+         * Note: we're checking the offline value directly,
+         * because if the activity took a long time to
+         * complete, calling be_is_offline() might report false
+         * incorrectly.
+         */
+        be_run_online_cb(req->be_ctx);
+    }
     return req->fn(req, dp_err, error, errstr);
 }
 
-- 
1.7.0.1

