diff options
Diffstat (limited to 'src/providers')
-rw-r--r-- | src/providers/dp_ptask.c | 354 | ||||
-rw-r--r-- | src/providers/dp_ptask.h | 93 |
2 files changed, 447 insertions, 0 deletions
diff --git a/src/providers/dp_ptask.c b/src/providers/dp_ptask.c new file mode 100644 index 00000000..d3580981 --- /dev/null +++ b/src/providers/dp_ptask.c @@ -0,0 +1,354 @@ +/* + 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 <string.h> + +#include "util/util.h" +#include "providers/dp_backend.h" +#include "providers/dp_ptask.h" + +enum be_ptask_schedule { + BE_PTASK_SCHEDULE_FROM_NOW, + BE_PTASK_SCHEDULE_FROM_LAST +}; + +struct be_ptask { + struct tevent_context *ev; + struct be_ctx *be_ctx; + time_t period; + time_t enabled_delay; + time_t timeout; + enum be_ptask_offline offline; + be_ptask_send_t send; + be_ptask_recv_t recv; + void *pvt; + const char *name; + + time_t last_execution; /* last time when send was called */ + struct tevent_req *req; /* active tevent request */ + struct tevent_timer *timer; /* active tevent timer */ + bool enabled; +}; + +static void be_ptask_schedule(struct be_ptask *task, + time_t delay, + enum be_ptask_schedule from); + +static int be_ptask_destructor(void *pvt) +{ + struct be_ptask *task; + + task = talloc_get_type(pvt, struct be_ptask); + if (task == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("BUG: task is NULL\n")); + return 0; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Terminating periodic task [%s]\n", task->name)); + + return 0; +} + +static void be_ptask_online_cb(void *pvt) +{ + struct be_ptask *task = NULL; + + task = talloc_get_type(pvt, struct be_ptask); + if (task == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("BUG: task is NULL\n")); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Back end is online\n")); + be_ptask_enable(task); +} + +static void be_ptask_offline_cb(void *pvt) +{ + struct be_ptask *task = NULL; + task = talloc_get_type(pvt, struct be_ptask); + + DEBUG(SSSDBG_TRACE_FUNC, ("Back end is offline\n")); + be_ptask_disable(task); +} + +static void be_ptask_timeout(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt) +{ + struct be_ptask *task = NULL; + task = talloc_get_type(pvt, struct be_ptask); + + DEBUG(SSSDBG_OP_FAILURE, ("Task [%s]: timed out\n", task->name)); + + talloc_zfree(task->req); + be_ptask_schedule(task, task->period, BE_PTASK_SCHEDULE_FROM_NOW); +} + +static void be_ptask_done(struct tevent_req *req); + +static void be_ptask_execute(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt) +{ + struct be_ptask *task = NULL; + struct tevent_timer *timeout = NULL; + + task = talloc_get_type(pvt, struct be_ptask); + task->timer = NULL; /* timer is freed by tevent */ + + if (be_is_offline(task->be_ctx)) { + DEBUG(SSSDBG_TRACE_FUNC, ("Back end is offline\n")); + switch (task->offline) { + case BE_PTASK_OFFLINE_SKIP: + be_ptask_schedule(task, task->period, BE_PTASK_SCHEDULE_FROM_NOW); + return; + case BE_PTASK_OFFLINE_DISABLE: + /* This case is handled by offline callback. */ + return; + case BE_PTASK_OFFLINE_EXECUTE: + /* continue */ + break; + } + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: executing task, timeout %lu " + "seconds\n", task->name, task->timeout)); + + task->last_execution = time(NULL); + + task->req = task->send(task, task->ev, task->be_ctx, task, task->pvt); + if (task->req == NULL) { + /* skip this iteration and try again later */ + DEBUG(SSSDBG_OP_FAILURE, ("Task [%s]: failed to execute task, " + "will try again later\n", task->name)); + + be_ptask_schedule(task, task->period, BE_PTASK_SCHEDULE_FROM_NOW); + return; + } + + tevent_req_set_callback(task->req, be_ptask_done, task); + + /* schedule timeout */ + if (task->timeout > 0) { + tv = tevent_timeval_current_ofs(task->timeout, 0); + timeout = tevent_add_timer(task->ev, task->req, tv, + be_ptask_timeout, task); + if (timeout == NULL) { + /* If we can't guarantee a timeout, + * we need to cancel the request. */ + talloc_zfree(task->req); + + DEBUG(SSSDBG_OP_FAILURE, ("Task [%s]: failed to set timeout, " + "the task will be rescheduled\n", task->name)); + + be_ptask_schedule(task, task->period, BE_PTASK_SCHEDULE_FROM_NOW); + } + } + + return; +} + +static void be_ptask_done(struct tevent_req *req) +{ + struct be_ptask *task = NULL; + errno_t ret; + + task = tevent_req_callback_data(req, struct be_ptask); + + ret = task->recv(req); + talloc_zfree(req); + task->req = NULL; + switch (ret) { + case EOK: + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: finished successfully\n", + task->name)); + + be_ptask_schedule(task, task->period, BE_PTASK_SCHEDULE_FROM_LAST); + break; + default: + DEBUG(SSSDBG_OP_FAILURE, ("Task [%s]: failed with [%d]: %s\n", + task->name, ret, sss_strerror(ret))); + + be_ptask_schedule(task, task->period, BE_PTASK_SCHEDULE_FROM_NOW); + break; + } +} + +static void be_ptask_schedule(struct be_ptask *task, + time_t delay, + enum be_ptask_schedule from) +{ + struct timeval tv; + + if (!task->enabled) { + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: disabled\n", task->name)); + return; + } + + switch (from) { + case BE_PTASK_SCHEDULE_FROM_NOW: + tv = tevent_timeval_current_ofs(delay, 0); + + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: scheduling task %lu seconds " + "from now [%lu]\n", task->name, delay, tv.tv_sec)); + break; + case BE_PTASK_SCHEDULE_FROM_LAST: + tv = tevent_timeval_set(task->last_execution + delay, 0); + + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: scheduling task %lu seconds " + "from last execution time [%lu]\n", + task->name, delay, tv.tv_sec)); + break; + } + + if (task->timer != NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Task [%s]: another timer is already " + "active?\n", task->name)); + talloc_zfree(task->timer); + } + + task->timer = tevent_add_timer(task->ev, task, tv, be_ptask_execute, task); + if (task->timer == NULL) { + /* nothing we can do about it */ + DEBUG(SSSDBG_CRIT_FAILURE, ("FATAL: Unable to schedule task [%s]\n", + task->name)); + be_ptask_disable(task); + } +} + +errno_t be_ptask_create(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + time_t period, + time_t first_delay, + time_t enabled_delay, + time_t timeout, + enum be_ptask_offline offline, + be_ptask_send_t send, + be_ptask_recv_t recv, + void *pvt, + const char *name, + struct be_ptask **_task) +{ + struct be_ptask *task = NULL; + errno_t ret; + + if (be_ctx == NULL || period == 0 || send == NULL || recv == NULL + || name == NULL) { + return EINVAL; + } + + task = talloc_zero(mem_ctx, struct be_ptask); + if (task == NULL) { + ret = ENOMEM; + goto done; + } + + task->ev = be_ctx->ev; + task->be_ctx = be_ctx; + task->period = period; + task->enabled_delay = enabled_delay; + task->timeout = timeout; + task->offline = offline; + task->send = send; + task->recv = recv; + task->pvt = pvt; + task->name = talloc_strdup(task, name); + if (task->name == NULL) { + ret = ENOMEM; + goto done; + } + + task->enabled = true; + + talloc_set_destructor((TALLOC_CTX*)task, be_ptask_destructor); + + if (offline == BE_PTASK_OFFLINE_DISABLE) { + /* install offline and online callbacks */ + ret = be_add_online_cb(task, be_ctx, be_ptask_online_cb, task, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Unable to install online callback " + "[%d]: %s", ret, sss_strerror(ret))); + goto done; + } + + ret = be_add_offline_cb(task, be_ctx, be_ptask_offline_cb, task, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Unable to install offline callback " + "[%d]: %s", ret, sss_strerror(ret))); + goto done; + } + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Periodic task [%s] was created\n", task->name)); + + be_ptask_schedule(task, first_delay, BE_PTASK_SCHEDULE_FROM_NOW); + + if (_task != NULL) { + *_task = task; + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(task); + } + + return ret; +} + +void be_ptask_enable(struct be_ptask *task) +{ + if (task->enabled) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Task [%s]: already enabled\n", + task->name)); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: enabling task\n", task->name)); + + task->enabled = true; + be_ptask_schedule(task, task->enabled_delay, BE_PTASK_SCHEDULE_FROM_NOW); +} + +/* Disable the task, but if a request already in progress, let it finish. */ +void be_ptask_disable(struct be_ptask *task) +{ + DEBUG(SSSDBG_TRACE_FUNC, ("Task [%s]: disabling task\n", task->name)); + + talloc_zfree(task->timer); + task->enabled = false; +} + +void be_ptask_destroy(struct be_ptask **task) +{ + talloc_zfree(*task); +} + +time_t be_ptask_get_period(struct be_ptask *task) +{ + return task->period; +} diff --git a/src/providers/dp_ptask.h b/src/providers/dp_ptask.h new file mode 100644 index 00000000..5a1d62c8 --- /dev/null +++ b/src/providers/dp_ptask.h @@ -0,0 +1,93 @@ +/* + 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_PTASK_H_ +#define _DP_PTASK_H_ + +#include <tevent.h> +#include <talloc.h> +#include <time.h> + +#include "providers/dp_backend.h" + +struct be_ptask; + +/** + * Defines how should task behave when back end is offline. + */ +enum be_ptask_offline { + /* current request will be skipped and rescheduled to 'now + period' */ + BE_PTASK_OFFLINE_SKIP, + + /* An offline and online callback is registered. The task is disabled + * immediately when back end goes offline and then enabled again + * when back end goes back online */ + BE_PTASK_OFFLINE_DISABLE, + + /* current request will be executed as planned */ + BE_PTASK_OFFLINE_EXECUTE +}; + +typedef struct tevent_req * +(*be_ptask_send_t)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct be_ptask *be_ptask, + void *pvt); + +/** + * If EOK, task will be scheduled again to 'last_execution_time + period'. + * If other error code, task will be rescheduled to 'now + period'. + */ +typedef errno_t +(*be_ptask_recv_t)(struct tevent_req *req); + +/** + * The first execution is scheduled first_delay seconds after the task is + * created. + * + * If request does not complete in timeout seconds, it will be + * cancelled and rescheduled to 'now + period'. + * + * If the task is reenabled, it will be scheduled again to + * 'now + enabled_delay'. + * + * If an internal error occurred, the task is automatically disabled. + */ +errno_t be_ptask_create(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + time_t period, + time_t first_delay, + time_t enabled_delay, + time_t timeout, + enum be_ptask_offline offline, + be_ptask_send_t send, + be_ptask_recv_t recv, + void *pvt, + const char *name, + struct be_ptask **_task); + +void be_ptask_enable(struct be_ptask *task); +void be_ptask_disable(struct be_ptask *task); +void be_ptask_destroy(struct be_ptask **task); + +time_t be_ptask_get_period(struct be_ptask *task); + +#endif /* _DP_PTASK_H_ */ |