data_provider_callbacks.c revision 0a320004a9937295ba66a348d1e60682cfdceb26
/*
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/dp_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(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 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(0, ("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(0, ("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(1, ("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(3, ("Going online. Running callbacks.\n"));
ret = be_run_cb(be, be->online_cb_list);
if (ret != EOK) {
DEBUG(1, ("be_run_cb failed.\n"));
}
} else {
DEBUG(9, ("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)
{
return be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->offline_cb_list, offline_cb);
}
void be_run_offline_cb(struct be_ctx *be) {
int ret;
if (be->offline_cb_list) {
DEBUG(3, ("Going offline. Running callbacks.\n"));
ret = be_run_cb(be, be->offline_cb_list);
if (ret != EOK) {
DEBUG(1, ("be_run_cb failed.\n"));
}
} else {
DEBUG(9, ("Offline call back list is empty, nothing to do.\n"));
}
}