diff options
Diffstat (limited to 'src/providers/data_provider_callbacks.c')
-rw-r--r-- | src/providers/data_provider_callbacks.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/providers/data_provider_callbacks.c b/src/providers/data_provider_callbacks.c new file mode 100644 index 0000000..24e125e --- /dev/null +++ b/src/providers/data_provider_callbacks.c @@ -0,0 +1,306 @@ +/* + SSSD + + Data Provider Process - Callback + + Authors: + + Stephen Gallagher <sgallagh@redhat.com> + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2010 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 "util/util.h" +#include "providers/backend.h" + +struct be_cb { + struct be_cb *prev; + struct be_cb *next; + + be_callback_t cb; + void *pvt; + + struct be_cb **list; + struct be_ctx *be; +}; + +struct be_cb_ctx { + struct be_ctx *be; + struct be_cb *callback; +}; + +static int cb_destructor(TALLOC_CTX *ptr) +{ + struct be_cb *cb = talloc_get_type(ptr, struct be_cb); + DLIST_REMOVE(*(cb->list), cb); + return 0; +} + +static int be_add_cb(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, + be_callback_t cb, void *pvt, struct be_cb **cb_list, + struct be_cb **return_cb) +{ + struct be_cb *new_cb; + + if (!ctx || !cb) { + return EINVAL; + } + + new_cb = talloc(mem_ctx, struct be_cb); + if (!new_cb) { + return ENOMEM; + } + + new_cb->cb = cb; + new_cb->pvt = pvt; + new_cb->list = cb_list; + new_cb->be = ctx; + + DLIST_ADD(*cb_list, new_cb); + + talloc_set_destructor((TALLOC_CTX *) new_cb, cb_destructor); + + if (return_cb) { + *return_cb = new_cb; + } + + return EOK; +} + +static void be_run_cb_step(struct tevent_context *ev, struct tevent_timer *te, + struct timeval current_time, void *pvt) +{ + struct be_cb_ctx *cb_ctx = talloc_get_type(pvt, struct be_cb_ctx); + struct be_cb *next_cb; + struct tevent_timer *tev; + struct timeval soon; + + /* Store next callback in case this callback frees itself */ + next_cb = cb_ctx->callback->next; + + /* Call the callback */ + cb_ctx->callback->cb(cb_ctx->callback->pvt); + + if (next_cb) { + cb_ctx->callback = next_cb; + + /* 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_cb_step, + cb_ctx); + if (!tev) { + DEBUG(SSSDBG_FATAL_FAILURE, + "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 errno_t be_run_cb(struct be_ctx *be, struct be_cb *cb_list) +{ + struct timeval soon; + struct tevent_timer *te; + struct be_cb_ctx *cb_ctx; + + if (cb_list == NULL) { + return EOK; + } + + cb_ctx = talloc(be, struct be_cb_ctx); + if (!cb_ctx) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Out of memory. Could not invoke callbacks\n"); + return ENOMEM; + } + cb_ctx->be = be; + cb_ctx->callback = 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_cb_step, + cb_ctx); + if (!te) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Out of memory. Could not invoke callbacks\n"); + talloc_free(cb_ctx); + return ENOMEM; + } + + return EOK; +} + +int be_add_reconnect_cb(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, be_callback_t cb, + void *pvt, struct be_cb **reconnect_cb) +{ + int ret; + + ret = be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->reconnect_cb_list, reconnect_cb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_add_cb failed.\n"); + return ret; + } + + return EOK; +} + +void be_run_reconnect_cb(struct be_ctx *be) +{ + struct be_cb *callback = be->reconnect_cb_list; + struct be_cb *next_cb; + + if (callback) { + DEBUG(SSSDBG_TRACE_FUNC, "Reconnecting. Running callbacks.\n"); + + /** + * Call the callback: we have to call this right away + * so the provider doesn't go into offline even for + * a little while + */ + do { + /* Store next callback in case this callback frees itself */ + next_cb = callback->next; + + callback->cb(callback->pvt); + callback = next_cb; + } while(callback != NULL); + } else { + DEBUG(SSSDBG_TRACE_INTERNAL, "Reconnect call back list is empty, nothing to do.\n"); + } +} + +int be_add_online_cb(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, be_callback_t cb, + void *pvt, struct be_cb **online_cb) +{ + int ret; + + ret = be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->online_cb_list, online_cb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_add_cb failed.\n"); + return ret; + } + + /* Make sure we run the callback for the first + * connection after startup. + */ + ctx->run_online_cb = true; + + return EOK; +} + +void be_run_online_cb(struct be_ctx *be) { + int ret; + + if (be->run_online_cb) { + /* Reset the flag. We only want to run these + * callbacks when transitioning to online + */ + be->run_online_cb = false; + + if (be->online_cb_list) { + DEBUG(SSSDBG_MINOR_FAILURE, "Going online. Running callbacks.\n"); + + ret = be_run_cb(be, be->online_cb_list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_run_cb failed.\n"); + } + + } else { + DEBUG(SSSDBG_TRACE_ALL, + "Online call back list is empty, nothing to do.\n"); + } + } +} + +int be_add_unconditional_online_cb(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, + be_callback_t cb, void *pvt, + struct be_cb **unconditional_online_cb) +{ + return be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->unconditional_online_cb_list, + unconditional_online_cb); +} + +void be_run_unconditional_online_cb(struct be_ctx *be) +{ + int ret; + + if (be->unconditional_online_cb_list) { + DEBUG(SSSDBG_TRACE_FUNC, "Running unconditional online callbacks.\n"); + + ret = be_run_cb(be, be->unconditional_online_cb_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "be_run_cb failed.\n"); + } + + } else { + DEBUG(SSSDBG_TRACE_ALL, + "List of unconditional online callbacks is empty, " \ + "nothing to do.\n"); + } +} + +int be_add_offline_cb(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, be_callback_t cb, + void *pvt, struct be_cb **offline_cb) +{ + int ret; + + ret = be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->offline_cb_list, offline_cb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_add_cb failed.\n"); + return ret; + } + + /* Make sure we run the callback when SSSD goes offline */ + ctx->run_offline_cb = true; + + return EOK; +} + +void be_run_offline_cb(struct be_ctx *be) { + int ret; + + if (be->run_offline_cb) { + /* Reset the flag, we only want to run these callbacks once when going + * offline */ + be->run_offline_cb = false; + + if (be->offline_cb_list) { + DEBUG(SSSDBG_MINOR_FAILURE, "Going offline. Running callbacks.\n"); + + ret = be_run_cb(be, be->offline_cb_list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_run_cb failed.\n"); + } + + } else { + DEBUG(SSSDBG_TRACE_ALL, + "Offline call back list is empty, nothing to do.\n"); + } + } else { + DEBUG(SSSDBG_TRACE_ALL, + "Flag indicates that offline callback were already called.\n"); + } +} |