LCOV - code coverage report
Current view: top level - providers - dp_ptask.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 158 199 79.4 %
Date: 2015-10-19 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /*
       2             :     Authors:
       3             :         Pavel Březina <pbrezina@redhat.com>
       4             : 
       5             :     Copyright (C) 2013 Red Hat
       6             : 
       7             :     This program is free software; you can redistribute it and/or modify
       8             :     it under the terms of the GNU General Public License as published by
       9             :     the Free Software Foundation; either version 3 of the License, or
      10             :     (at your option) any later version.
      11             : 
      12             :     This program is distributed in the hope that it will be useful,
      13             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :     GNU General Public License for more details.
      16             : 
      17             :     You should have received a copy of the GNU General Public License
      18             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include <tevent.h>
      22             : #include <talloc.h>
      23             : #include <time.h>
      24             : #include <string.h>
      25             : 
      26             : #include "util/util.h"
      27             : #include "providers/dp_backend.h"
      28             : #include "providers/dp_ptask_private.h"
      29             : #include "providers/dp_ptask.h"
      30             : 
      31             : #define backoff_allowed(ptask) (ptask->max_backoff != 0)
      32             : 
      33             : enum be_ptask_schedule {
      34             :     BE_PTASK_SCHEDULE_FROM_NOW,
      35             :     BE_PTASK_SCHEDULE_FROM_LAST
      36             : };
      37             : 
      38             : enum be_ptask_delay {
      39             :     BE_PTASK_FIRST_DELAY,
      40             :     BE_PTASK_ENABLED_DELAY,
      41             :     BE_PTASK_PERIOD
      42             : };
      43             : 
      44             : static void be_ptask_schedule(struct be_ptask *task,
      45             :                               enum be_ptask_delay delay_type,
      46             :                               enum be_ptask_schedule from);
      47             : 
      48          18 : static int be_ptask_destructor(void *pvt)
      49             : {
      50             :     struct be_ptask *task;
      51             : 
      52          18 :     task = talloc_get_type(pvt, struct be_ptask);
      53          18 :     if (task == NULL) {
      54           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "BUG: task is NULL\n");
      55           0 :         return 0;
      56             :     }
      57             : 
      58          18 :     DEBUG(SSSDBG_TRACE_FUNC, "Terminating periodic task [%s]\n", task->name);
      59             : 
      60          18 :     return 0;
      61             : }
      62             : 
      63           0 : static void be_ptask_online_cb(void *pvt)
      64             : {
      65           0 :     struct be_ptask *task = NULL;
      66             : 
      67           0 :     task = talloc_get_type(pvt, struct be_ptask);
      68           0 :     if (task == NULL) {
      69           0 :         DEBUG(SSSDBG_FATAL_FAILURE, "BUG: task is NULL\n");
      70           0 :         return;
      71             :     }
      72             : 
      73           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Back end is online\n");
      74           0 :     be_ptask_enable(task);
      75             : }
      76             : 
      77           0 : static void be_ptask_offline_cb(void *pvt)
      78             : {
      79           0 :     struct be_ptask *task = NULL;
      80           0 :     task = talloc_get_type(pvt, struct be_ptask);
      81             : 
      82           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Back end is offline\n");
      83           0 :     be_ptask_disable(task);
      84           0 : }
      85             : 
      86           1 : static void be_ptask_timeout(struct tevent_context *ev,
      87             :                              struct tevent_timer *tt,
      88             :                              struct timeval tv,
      89             :                              void *pvt)
      90             : {
      91           1 :     struct be_ptask *task = NULL;
      92           1 :     task = talloc_get_type(pvt, struct be_ptask);
      93             : 
      94           1 :     DEBUG(SSSDBG_OP_FAILURE, "Task [%s]: timed out\n", task->name);
      95             : 
      96           1 :     talloc_zfree(task->req);
      97           1 :     be_ptask_schedule(task, BE_PTASK_PERIOD, BE_PTASK_SCHEDULE_FROM_NOW);
      98           1 : }
      99             : 
     100             : static void be_ptask_done(struct tevent_req *req);
     101             : 
     102          19 : static void be_ptask_execute(struct tevent_context *ev,
     103             :                              struct tevent_timer *tt,
     104             :                              struct timeval tv,
     105             :                              void *pvt)
     106             : {
     107          19 :     struct be_ptask *task = NULL;
     108          19 :     struct tevent_timer *timeout = NULL;
     109             : 
     110          19 :     task = talloc_get_type(pvt, struct be_ptask);
     111          19 :     task->timer = NULL; /* timer is freed by tevent */
     112             : 
     113          19 :     if (be_is_offline(task->be_ctx)) {
     114           3 :         DEBUG(SSSDBG_TRACE_FUNC, "Back end is offline\n");
     115           3 :         switch (task->offline) {
     116             :         case BE_PTASK_OFFLINE_SKIP:
     117           1 :             be_ptask_schedule(task, BE_PTASK_PERIOD,
     118             :                               BE_PTASK_SCHEDULE_FROM_NOW);
     119           1 :             return;
     120             :         case BE_PTASK_OFFLINE_DISABLE:
     121             :             /* This case is normally handled by offline callback but we
     122             :              * should handle it here as well since we can get here in some
     123             :              * special cases for example unit tests or tevent events order. */
     124           1 :             be_ptask_disable(task);
     125           1 :             return;
     126             :         case BE_PTASK_OFFLINE_EXECUTE:
     127             :             /* continue */
     128           1 :             break;
     129             :         }
     130             :     }
     131             : 
     132          17 :     DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: executing task, timeout %lu "
     133             :                               "seconds\n", task->name, task->timeout);
     134             : 
     135          17 :     task->last_execution = tv.tv_sec;
     136             : 
     137          17 :     task->req = task->send_fn(task, task->ev, task->be_ctx, task, task->pvt);
     138          17 :     if (task->req == NULL) {
     139             :         /* skip this iteration and try again later */
     140           1 :         DEBUG(SSSDBG_OP_FAILURE, "Task [%s]: failed to execute task, "
     141             :               "will try again later\n", task->name);
     142             : 
     143           1 :         be_ptask_schedule(task, BE_PTASK_PERIOD, BE_PTASK_SCHEDULE_FROM_NOW);
     144           1 :         return;
     145             :     }
     146             : 
     147          16 :     tevent_req_set_callback(task->req, be_ptask_done, task);
     148             : 
     149             :     /* schedule timeout */
     150          16 :     if (task->timeout > 0) {
     151           1 :         tv = tevent_timeval_current_ofs(task->timeout, 0);
     152           1 :         timeout = tevent_add_timer(task->ev, task->req, tv,
     153             :                                    be_ptask_timeout, task);
     154           1 :         if (timeout == NULL) {
     155             :             /* If we can't guarantee a timeout,
     156             :              * we need to cancel the request. */
     157           0 :             talloc_zfree(task->req);
     158             : 
     159           0 :             DEBUG(SSSDBG_OP_FAILURE, "Task [%s]: failed to set timeout, "
     160             :                   "the task will be rescheduled\n", task->name);
     161             : 
     162           0 :             be_ptask_schedule(task, BE_PTASK_PERIOD,
     163             :                               BE_PTASK_SCHEDULE_FROM_NOW);
     164             :         }
     165             :     }
     166             : 
     167          16 :     return;
     168             : }
     169             : 
     170          14 : static void be_ptask_done(struct tevent_req *req)
     171             : {
     172          14 :     struct be_ptask *task = NULL;
     173             :     errno_t ret;
     174             : 
     175          14 :     task = tevent_req_callback_data(req, struct be_ptask);
     176             : 
     177          14 :     ret = task->recv_fn(req);
     178          14 :     talloc_zfree(req);
     179          14 :     task->req = NULL;
     180          14 :     switch (ret) {
     181             :     case EOK:
     182          10 :         DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: finished successfully\n",
     183             :                                   task->name);
     184             : 
     185          10 :         be_ptask_schedule(task, BE_PTASK_PERIOD, BE_PTASK_SCHEDULE_FROM_LAST);
     186          10 :         break;
     187             :     default:
     188           4 :         DEBUG(SSSDBG_OP_FAILURE, "Task [%s]: failed with [%d]: %s\n",
     189             :                                   task->name, ret, sss_strerror(ret));
     190             : 
     191           4 :         be_ptask_schedule(task, BE_PTASK_PERIOD, BE_PTASK_SCHEDULE_FROM_NOW);
     192           4 :         break;
     193             :     }
     194          14 : }
     195             : 
     196          37 : static void be_ptask_schedule(struct be_ptask *task,
     197             :                               enum be_ptask_delay delay_type,
     198             :                               enum be_ptask_schedule from)
     199             : {
     200             :     struct timeval tv;
     201          37 :     time_t delay = 0;
     202             : 
     203          37 :     if (!task->enabled) {
     204           0 :         DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: disabled\n", task->name);
     205           0 :         return;
     206             :     }
     207             : 
     208          37 :     switch (delay_type) {
     209             :     case BE_PTASK_FIRST_DELAY:
     210          18 :         delay = task->first_delay;
     211          18 :         break;
     212             :     case BE_PTASK_ENABLED_DELAY:
     213           2 :         delay = task->enabled_delay;
     214           2 :         break;
     215             :     case BE_PTASK_PERIOD:
     216          17 :         delay = task->period;
     217             : 
     218          17 :         if (backoff_allowed(task) && task->period * 2 <= task->max_backoff) {
     219             :             /* double the period for the next execution */
     220           2 :             task->period *= 2;
     221             :         }
     222          17 :         break;
     223             :     }
     224             : 
     225             :     /* add random offset */
     226          37 :     if (task->random_offset != 0) {
     227           0 :         delay = delay + (rand_r(&task->ro_seed) % task->random_offset);
     228             :     }
     229             : 
     230          37 :     switch (from) {
     231             :     case BE_PTASK_SCHEDULE_FROM_NOW:
     232          27 :         tv = tevent_timeval_current_ofs(delay, 0);
     233             : 
     234          27 :         DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: scheduling task %lu seconds "
     235             :               "from now [%lu]\n", task->name, delay, tv.tv_sec);
     236          27 :         break;
     237             :     case BE_PTASK_SCHEDULE_FROM_LAST:
     238          10 :         tv = tevent_timeval_set(task->last_execution + delay, 0);
     239             : 
     240          10 :         DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: scheduling task %lu seconds "
     241             :                                   "from last execution time [%lu]\n",
     242             :                                   task->name, delay, tv.tv_sec);
     243          10 :         break;
     244             :     }
     245             : 
     246          37 :     if (task->timer != NULL) {
     247           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Task [%s]: another timer is already "
     248             :                                      "active?\n", task->name);
     249           0 :         talloc_zfree(task->timer);
     250             :     }
     251             : 
     252          37 :     task->timer = tevent_add_timer(task->ev, task, tv, be_ptask_execute, task);
     253          37 :     if (task->timer == NULL) {
     254             :         /* nothing we can do about it */
     255           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Unable to schedule task [%s]\n",
     256             :                                     task->name);
     257           0 :         be_ptask_disable(task);
     258             :     }
     259             : 
     260          37 :     task->next_execution = tv.tv_sec;
     261             : }
     262             : 
     263          24 : errno_t be_ptask_create(TALLOC_CTX *mem_ctx,
     264             :                         struct be_ctx *be_ctx,
     265             :                         time_t period,
     266             :                         time_t first_delay,
     267             :                         time_t enabled_delay,
     268             :                         time_t random_offset,
     269             :                         time_t timeout,
     270             :                         enum be_ptask_offline offline,
     271             :                         time_t max_backoff,
     272             :                         be_ptask_send_t send_fn,
     273             :                         be_ptask_recv_t recv_fn,
     274             :                         void *pvt,
     275             :                         const char *name,
     276             :                         struct be_ptask **_task)
     277             : {
     278          24 :     struct be_ptask *task = NULL;
     279             :     errno_t ret;
     280             : 
     281          24 :     if (be_ctx == NULL || period == 0 || send_fn == NULL || recv_fn == NULL
     282          19 :         || name == NULL) {
     283           6 :         return EINVAL;
     284             :     }
     285             : 
     286          18 :     task = talloc_zero(mem_ctx, struct be_ptask);
     287          18 :     if (task == NULL) {
     288           0 :         ret = ENOMEM;
     289           0 :         goto done;
     290             :     }
     291             : 
     292          18 :     task->ev = be_ctx->ev;
     293          18 :     task->be_ctx = be_ctx;
     294          18 :     task->period = period;
     295          18 :     task->orig_period = period;
     296          18 :     task->first_delay = first_delay;
     297          18 :     task->enabled_delay = enabled_delay;
     298          18 :     task->random_offset = random_offset;
     299          18 :     task->ro_seed = time(NULL) * getpid();
     300          18 :     task->max_backoff = max_backoff;
     301          18 :     task->timeout = timeout;
     302          18 :     task->offline = offline;
     303          18 :     task->send_fn = send_fn;
     304          18 :     task->recv_fn = recv_fn;
     305          18 :     task->pvt = pvt;
     306          18 :     task->name = talloc_strdup(task, name);
     307          18 :     if (task->name == NULL) {
     308           0 :         ret = ENOMEM;
     309           0 :         goto done;
     310             :     }
     311             : 
     312          18 :     task->enabled = true;
     313             : 
     314          18 :     talloc_set_destructor((TALLOC_CTX*)task, be_ptask_destructor);
     315             : 
     316          18 :     if (offline == BE_PTASK_OFFLINE_DISABLE) {
     317             :         /* install offline and online callbacks */
     318           1 :         ret = be_add_online_cb(task, be_ctx, be_ptask_online_cb, task, NULL);
     319           1 :         if (ret != EOK) {
     320           0 :             DEBUG(SSSDBG_OP_FAILURE,
     321             :                   "Unable to install online callback [%d]: %s\n",
     322             :                   ret, sss_strerror(ret));
     323           0 :             goto done;
     324             :         }
     325             : 
     326           1 :         ret = be_add_offline_cb(task, be_ctx, be_ptask_offline_cb, task, NULL);
     327           1 :         if (ret != EOK) {
     328           0 :             DEBUG(SSSDBG_OP_FAILURE,
     329             :                   "Unable to install offline callback [%d]: %s\n",
     330             :                   ret, sss_strerror(ret));
     331           0 :             goto done;
     332             :         }
     333             :     }
     334             : 
     335          18 :     DEBUG(SSSDBG_TRACE_FUNC, "Periodic task [%s] was created\n", task->name);
     336             : 
     337          18 :     be_ptask_schedule(task, BE_PTASK_FIRST_DELAY, BE_PTASK_SCHEDULE_FROM_NOW);
     338             : 
     339          18 :     if (_task != NULL) {
     340          18 :         *_task = task;
     341             :     }
     342             : 
     343          18 :     ret = EOK;
     344             : 
     345             : done:
     346          18 :     if (ret != EOK) {
     347           0 :         talloc_free(task);
     348             :     }
     349             : 
     350          18 :     return ret;
     351             : }
     352             : 
     353           2 : void be_ptask_enable(struct be_ptask *task)
     354             : {
     355           2 :     if (task->enabled) {
     356           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "Task [%s]: already enabled\n",
     357             :                                      task->name);
     358           0 :         return;
     359             :     }
     360             : 
     361           2 :     DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: enabling task\n", task->name);
     362             : 
     363           2 :     task->enabled = true;
     364           2 :     be_ptask_schedule(task, BE_PTASK_ENABLED_DELAY, BE_PTASK_SCHEDULE_FROM_NOW);
     365             : }
     366             : 
     367             : /* Disable the task, but if a request already in progress, let it finish. */
     368           4 : void be_ptask_disable(struct be_ptask *task)
     369             : {
     370           4 :     DEBUG(SSSDBG_TRACE_FUNC, "Task [%s]: disabling task\n", task->name);
     371             : 
     372           4 :     talloc_zfree(task->timer);
     373           4 :     task->enabled = false;
     374           4 :     task->period = task->orig_period;
     375           4 : }
     376             : 
     377          20 : void be_ptask_destroy(struct be_ptask **task)
     378             : {
     379          20 :     talloc_zfree(*task);
     380          20 : }
     381             : 
     382           1 : time_t be_ptask_get_period(struct be_ptask *task)
     383             : {
     384           1 :     return task->period;
     385             : }
     386             : 
     387             : struct be_ptask_sync_ctx {
     388             :     be_ptask_sync_t fn;
     389             :     void *pvt;
     390             : };
     391             : 
     392             : struct be_ptask_sync_state {
     393             :     int dummy;
     394             : };
     395             : 
     396             : /* This is not an asynchronous request so there is not any _done function. */
     397             : static struct tevent_req *
     398           5 : be_ptask_sync_send(TALLOC_CTX *mem_ctx,
     399             :                    struct tevent_context *ev,
     400             :                    struct be_ctx *be_ctx,
     401             :                    struct be_ptask *be_ptask,
     402             :                    void *pvt)
     403             : {
     404           5 :     struct be_ptask_sync_ctx *ctx = NULL;
     405           5 :     struct be_ptask_sync_state *state = NULL;
     406           5 :     struct tevent_req *req = NULL;
     407             :     errno_t ret;
     408             : 
     409           5 :     req = tevent_req_create(mem_ctx, &state, struct be_ptask_sync_state);
     410           5 :     if (req == NULL) {
     411           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
     412           0 :         return NULL;
     413             :     }
     414             : 
     415           5 :     ctx = talloc_get_type(pvt, struct be_ptask_sync_ctx);
     416           5 :     ret = ctx->fn(mem_ctx, ev, be_ctx, be_ptask, ctx->pvt);
     417             : 
     418           5 :     if (ret == EOK) {
     419           2 :         tevent_req_done(req);
     420             :     } else {
     421           3 :         tevent_req_error(req, ret);
     422             :     }
     423           5 :     tevent_req_post(req, ev);
     424             : 
     425           5 :     return req;
     426             : }
     427             : 
     428           4 : static errno_t be_ptask_sync_recv(struct tevent_req *req)
     429             : {
     430           7 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     431             : 
     432           1 :     return EOK;
     433             : }
     434             : 
     435           5 : errno_t be_ptask_create_sync(TALLOC_CTX *mem_ctx,
     436             :                              struct be_ctx *be_ctx,
     437             :                              time_t period,
     438             :                              time_t first_delay,
     439             :                              time_t enabled_delay,
     440             :                              time_t random_offset,
     441             :                              time_t timeout,
     442             :                              enum be_ptask_offline offline,
     443             :                              time_t max_backoff,
     444             :                              be_ptask_sync_t fn,
     445             :                              void *pvt,
     446             :                              const char *name,
     447             :                              struct be_ptask **_task)
     448             : {
     449             :     errno_t ret;
     450           5 :     struct be_ptask_sync_ctx *ctx = NULL;
     451             : 
     452           5 :     ctx = talloc_zero(mem_ctx, struct be_ptask_sync_ctx);
     453           5 :     if (ctx == NULL) {
     454           0 :         ret = ENOMEM;
     455           0 :         goto done;
     456             :     }
     457             : 
     458           5 :     ctx->fn = fn;
     459           5 :     ctx->pvt = pvt;
     460             : 
     461           5 :     ret = be_ptask_create(mem_ctx, be_ctx, period, first_delay,
     462             :                           enabled_delay, random_offset, timeout, offline,
     463             :                           max_backoff, be_ptask_sync_send, be_ptask_sync_recv,
     464             :                           ctx, name, _task);
     465           5 :     if (ret != EOK) {
     466           1 :         goto done;
     467             :     }
     468             : 
     469           4 :     ret = EOK;
     470             : 
     471             : done:
     472           5 :     if (ret != EOK) {
     473           1 :         talloc_free(ctx);
     474             :     }
     475             : 
     476           5 :     return ret;
     477             : }

Generated by: LCOV version 1.10