From 2479352011f96a8f64c403d1278b791c73df703a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 6 Nov 2015 13:00:47 +0100
Subject: [PATCH 01/32] SUDO: convert periodical refreshes to be_ptask

This removes old sudo timer and simplyfies code a lot. It also
allows to manage offline/online state.

- Full and smart refresh are disabled when offline.
- Full refresh is run immediately when sssd is back online.
- Smart refresh is scheduled normally when sssd is back online.

Resolves:
https://fedorahosted.org/sssd/ticket/1943
---
 Makefile.am                                |   2 +-
 src/providers/ldap/sdap_async_sudo_timer.c | 178 -----------
 src/providers/ldap/sdap_sudo.c             | 470 +----------------------------
 src/providers/ldap/sdap_sudo.h             |  37 +--
 src/providers/ldap/sdap_sudo_refresh.c     | 157 ++++++++++
 5 files changed, 186 insertions(+), 658 deletions(-)
 delete mode 100644 src/providers/ldap/sdap_async_sudo_timer.c
 create mode 100644 src/providers/ldap/sdap_sudo_refresh.c

diff --git a/Makefile.am b/Makefile.am
index d7a6f29520980d933cf0afddf8d041b2d6f3b2ef..23d4ae38e78baf795f8bbecb3e50e5473ba0d709 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2841,8 +2841,8 @@ if BUILD_SUDO
 libsss_ldap_common_la_SOURCES += \
     src/providers/ldap/sdap_sudo_cache.c \
     src/providers/ldap/sdap_async_sudo.c \
-    src/providers/ldap/sdap_async_sudo_timer.c \
     src/providers/ldap/sdap_async_sudo_hostinfo.c \
+    src/providers/ldap/sdap_sudo_refresh.c \
     src/providers/ldap/sdap_sudo.c
 endif
 
diff --git a/src/providers/ldap/sdap_async_sudo_timer.c b/src/providers/ldap/sdap_async_sudo_timer.c
deleted file mode 100644
index 035341909599a81d6de3a2d05296f84390c15cfd..0000000000000000000000000000000000000000
--- a/src/providers/ldap/sdap_async_sudo_timer.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-    Authors:
-        Pavel Březina <pbrezina@redhat.com>
-
-    Copyright (C) 2011 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 <errno.h>
-#include <tevent.h>
-#include <talloc.h>
-
-#include "util/util.h"
-#include "providers/ldap/sdap.h"
-#include "providers/ldap/sdap_id_op.h"
-#include "providers/ldap/sdap_sudo.h"
-
-struct sdap_sudo_timer_state {
-    struct tevent_context *ev;
-    struct sdap_sudo_ctx *sudo_ctx;
-    time_t timeout;          /* relative time how many seconds wait before
-                                canceling fn request */
-    sdap_sudo_timer_fn_t fn; /* request executed on 'when' */
-
-    struct tevent_req *subreq;
-    struct tevent_timer *timer_timeout;
-};
-
-static void sdap_sudo_timer(struct tevent_context *ev,
-                            struct tevent_timer *tt,
-                            struct timeval tv, void *pvt);
-
-static void sdap_sudo_timer_done(struct tevent_req *subreq);
-
-static void sdap_sudo_timer_timeout(struct tevent_context *ev,
-                                    struct tevent_timer *tt,
-                                    struct timeval tv, void *pvt);
-
-struct tevent_req * sdap_sudo_timer_send(TALLOC_CTX *mem_ctx,
-                                         struct tevent_context *ev,
-                                         struct sdap_sudo_ctx *sudo_ctx,
-                                         struct timeval when,
-                                         time_t timeout,
-                                         sdap_sudo_timer_fn_t fn)
-{
-    struct tevent_req *req = NULL;
-    struct tevent_timer *timer = NULL;
-    struct sdap_sudo_timer_state *state = NULL;
-    int ret;
-
-    /* create request */
-    req = tevent_req_create(mem_ctx, &state, struct sdap_sudo_timer_state);
-    if (req == NULL) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "tevent_req_create() failed\n");
-        return NULL;
-    }
-
-    state->ev = ev;
-    state->sudo_ctx = sudo_ctx;
-    state->timeout = timeout;
-    state->fn = fn;
-
-    /* set timer */
-    timer = tevent_add_timer(ev, req, when, sdap_sudo_timer, req);
-    if (timer == NULL) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "tevent_add_timer() failed\n");
-        ret = ENOMEM;
-        goto immediately;
-    }
-
-    return req;
-
-immediately:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
-    tevent_req_post(req, ev);
-
-    return req;
-}
-
-int sdap_sudo_timer_recv(TALLOC_CTX *mem_ctx,
-                         struct tevent_req *req,
-                         struct tevent_req **_subreq)
-{
-    struct sdap_sudo_timer_state *state = NULL;
-
-    state = tevent_req_data(req, struct sdap_sudo_timer_state);
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    *_subreq = talloc_steal(mem_ctx, state->subreq);
-
-    return EOK;
-}
-
-static void sdap_sudo_timer(struct tevent_context *ev,
-                            struct tevent_timer *tt,
-                            struct timeval tv, void *pvt)
-{
-    struct tevent_req *req = NULL;
-    struct sdap_sudo_timer_state *state = NULL;
-
-    req = talloc_get_type(pvt, struct tevent_req);
-    state = tevent_req_data(req, struct sdap_sudo_timer_state);
-
-    /* issue request */
-    state->subreq = state->fn(state, state->sudo_ctx);
-    if (state->subreq == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to issue timed request!\n");
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
-
-    tevent_req_set_callback(state->subreq, sdap_sudo_timer_done, req);
-
-    /* schedule timeout */
-    tv = tevent_timeval_current_ofs(state->timeout, 0);
-    state->timer_timeout = tevent_add_timer(state->ev, state->subreq, tv,
-                                            sdap_sudo_timer_timeout, req);
-    if (state->timer_timeout == NULL) {
-        /* If we can't guarantee a timeout, we
-         * need to cancel the request, to avoid
-         * the possibility of starting another
-         * concurrently
-         */
-        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set timeout, "
-                                    "canceling request!\n");
-        talloc_zfree(state->subreq);
-        tevent_req_error(req, ENOMEM);
-    }
-}
-
-static void sdap_sudo_timer_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = NULL;
-    struct sdap_sudo_timer_state *state = NULL;
-
-    req = tevent_req_callback_data(subreq, struct tevent_req);
-    /* do not free subreq, it is returned in recv */
-
-    /* cancel timeout */
-    state = tevent_req_data(req, struct sdap_sudo_timer_state);
-    talloc_zfree(state->timer_timeout);
-
-    tevent_req_done(req);
-}
-
-static void sdap_sudo_timer_timeout(struct tevent_context *ev,
-                                    struct tevent_timer *tt,
-                                    struct timeval tv, void *pvt)
-{
-    struct tevent_req *req = NULL;
-    struct sdap_sudo_timer_state *state = NULL;
-
-    req = talloc_get_type(pvt, struct tevent_req);
-    state = tevent_req_data(req, struct sdap_sudo_timer_state);
-
-    DEBUG(SSSDBG_CRIT_FAILURE, "Request timed out. Is timeout too small?"
-                                " (%lds)!\n", state->timeout);
-
-    talloc_zfree(state->subreq);
-
-    tevent_req_error(req, EAGAIN);
-}
diff --git a/src/providers/ldap/sdap_sudo.c b/src/providers/ldap/sdap_sudo.c
index ccc775fb0c3b8a1e4dd9209265ba7cad37570494..4885564ecb727e6b41441c9ee91f931bc0300cd3 100644
--- a/src/providers/ldap/sdap_sudo.c
+++ b/src/providers/ldap/sdap_sudo.c
@@ -30,8 +30,6 @@
 #include "providers/ldap/sdap_sudo_cache.h"
 #include "db/sysdb_sudo.h"
 
-#define SUDO_MAX_FIRST_REFRESH_DELAY 16
-
 struct sdap_sudo_full_refresh_state {
     struct sdap_sudo_ctx *sudo_ctx;
     struct sdap_id_ctx *id_ctx;
@@ -41,15 +39,8 @@ struct sdap_sudo_full_refresh_state {
     int error;
 };
 
-static struct tevent_req *sdap_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
-                                                      struct sdap_sudo_ctx *sudo_ctx);
-
 static void sdap_sudo_full_refresh_done(struct tevent_req *subreq);
 
-static int sdap_sudo_full_refresh_recv(struct tevent_req *req,
-                                       int *dp_error,
-                                       int *error);
-
 struct sdap_sudo_rules_refresh_state {
     struct sdap_id_ctx *id_ctx;
     size_t num_rules;
@@ -76,35 +67,8 @@ struct sdap_sudo_smart_refresh_state {
     struct sysdb_ctx *sysdb;
 };
 
-static struct tevent_req *sdap_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
-                                                      struct sdap_sudo_ctx *sudo_ctx);
-
 static void sdap_sudo_smart_refresh_done(struct tevent_req *subreq);
 
-static int sdap_sudo_smart_refresh_recv(struct tevent_req *req,
-                                        int *dp_error,
-                                        int *error);
-
-static void sdap_sudo_periodical_first_refresh_done(struct tevent_req *req);
-
-static void sdap_sudo_periodical_full_refresh_done(struct tevent_req *req);
-
-static void sdap_sudo_periodical_smart_refresh_done(struct tevent_req *req);
-
-static int sdap_sudo_schedule_refresh(TALLOC_CTX *mem_ctx,
-                                      struct sdap_sudo_ctx *sudo_ctx,
-                                      enum sdap_sudo_refresh_type refresh,
-                                      tevent_req_fn callback,
-                                      time_t delay,
-                                      time_t timeout,
-                                      struct tevent_req **_req);
-
-static int sdap_sudo_schedule_full_refresh(struct sdap_sudo_ctx *sudo_ctx,
-                                           time_t delay);
-
-static int sdap_sudo_schedule_smart_refresh(struct sdap_sudo_ctx *sudo_ctx,
-                                            time_t delay);
-
 static void
 sdap_sudo_shutdown(struct be_req *req)
 {
@@ -117,7 +81,6 @@ struct bet_ops sdap_sudo_ops = {
 };
 
 static void sdap_sudo_get_hostinfo_done(struct tevent_req *req);
-static int sdap_sudo_setup_periodical_refresh(struct sdap_sudo_ctx *sudo_ctx);
 
 int sdap_sudo_init(struct be_ctx *be_ctx,
                    struct sdap_id_ctx *id_ctx,
@@ -162,7 +125,7 @@ int sdap_sudo_init(struct be_ctx *be_ctx,
 
         sudo_ctx->use_host_filter = false;
 
-        ret = sdap_sudo_setup_periodical_refresh(sudo_ctx);
+        ret = sdap_sudo_ptask_setup(sudo_ctx->id_ctx->be, sudo_ctx);
         if (ret != EOK) {
              DEBUG(SSSDBG_OP_FAILURE,
                    "Unable to setup periodical refresh"
@@ -200,94 +163,13 @@ static void sdap_sudo_get_hostinfo_done(struct tevent_req *req)
     sudo_ctx->hostnames = talloc_move(sudo_ctx, &hostnames);
     sudo_ctx->ip_addr = talloc_move(sudo_ctx, &ip_addr);
 
-    ret = sdap_sudo_setup_periodical_refresh(sudo_ctx);
+    ret = sdap_sudo_ptask_setup(sudo_ctx->id_ctx->be, sudo_ctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Unable to setup periodical refresh"
                                   "of sudo rules [%d]: %s\n", ret, strerror(ret));
     }
 }
 
-static int sdap_sudo_setup_periodical_refresh(struct sdap_sudo_ctx *sudo_ctx)
-{
-    struct sdap_id_ctx *id_ctx = sudo_ctx->id_ctx;
-    time_t smart_default;
-    time_t smart_interval;
-    time_t full_interval;
-    time_t last_full;
-    time_t delay;
-    int ret;
-
-    smart_interval = dp_opt_get_int(id_ctx->opts->basic,
-                                    SDAP_SUDO_SMART_REFRESH_INTERVAL);
-
-    full_interval = dp_opt_get_int(id_ctx->opts->basic,
-                                   SDAP_SUDO_FULL_REFRESH_INTERVAL);
-
-    if (smart_interval == 0 && full_interval == 0) {
-        smart_default = id_ctx->opts->basic[SDAP_SUDO_SMART_REFRESH_INTERVAL].def_val.number;
-
-        DEBUG(SSSDBG_MINOR_FAILURE, "At least one periodical update has to be "
-              "enabled. Setting smart refresh interval to default value (%ld).\n",
-              smart_default);
-
-        ret = dp_opt_set_int(id_ctx->opts->basic,
-                             SDAP_SUDO_SMART_REFRESH_INTERVAL,
-                             smart_default);
-        if (ret != EOK) {
-            return ret;
-        }
-    }
-
-    if (full_interval <= smart_interval) {
-        DEBUG(SSSDBG_MINOR_FAILURE, "Full refresh interval has to be greater"
-              "than smart refresh interval. Periodical full refresh will be "
-              "disabled.\n");
-        ret = dp_opt_set_int(id_ctx->opts->basic,
-                             SDAP_SUDO_FULL_REFRESH_INTERVAL,
-                             0);
-        if (ret != EOK) {
-            return ret;
-        }
-    }
-
-    ret = sysdb_sudo_get_last_full_refresh(id_ctx->be->domain,
-                                           &last_full);
-    if (ret != EOK) {
-        return ret;
-    }
-
-    if (last_full == 0) {
-        /* If this is the first startup, we need to kick off
-         * an refresh immediately, to close a window where
-         * clients requesting sudo information won't get an
-         * immediate reply with no entries
-         */
-        delay = 0;
-    } else {
-        /* At least one update has previously run,
-         * so clients will get cached data.
-         * We will delay the refresh so we don't slow
-         * down the startup process if this is happening
-         * during system boot.
-         */
-
-        /* delay at least by 10s */
-        delay = 10;
-    }
-
-    ret = sdap_sudo_schedule_refresh(sudo_ctx, sudo_ctx,
-                                     SDAP_SUDO_REFRESH_FULL,
-                                     sdap_sudo_periodical_first_refresh_done,
-                                     delay, full_interval, NULL);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule full refresh of sudo "
-              "rules! Periodical updates will not work!\n");
-        return ret;
-    }
-
-    return EOK;
-}
-
 static void sdap_sudo_set_usn(struct sdap_server_opts *srv_opts, char *usn)
 {
     unsigned int usn_number;
@@ -532,8 +414,8 @@ fail:
 }
 
 /* issue full refresh of sudo rules */
-static struct tevent_req *sdap_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
-                                                      struct sdap_sudo_ctx *sudo_ctx)
+struct tevent_req *sdap_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
+                                               struct sdap_sudo_ctx *sudo_ctx)
 {
     struct tevent_req *req = NULL;
     struct tevent_req *subreq = NULL;
@@ -657,9 +539,9 @@ done:
     tevent_req_done(req);
 }
 
-static int sdap_sudo_full_refresh_recv(struct tevent_req *req,
-                                       int *dp_error,
-                                       int *error)
+int sdap_sudo_full_refresh_recv(struct tevent_req *req,
+                                int *dp_error,
+                                int *error)
 {
     struct sdap_sudo_full_refresh_state *state = NULL;
     state = tevent_req_data(req, struct sdap_sudo_full_refresh_state);
@@ -835,8 +717,8 @@ static int sdap_sudo_rules_refresh_recv(struct tevent_req *req,
 }
 
 /* issue smart refresh of sudo rules */
-static struct tevent_req *sdap_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
-                                                       struct sdap_sudo_ctx *sudo_ctx)
+struct tevent_req *sdap_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
+                                                struct sdap_sudo_ctx *sudo_ctx)
 {
     struct tevent_req *req = NULL;
     struct tevent_req *subreq = NULL;
@@ -959,9 +841,9 @@ done:
     tevent_req_done(req);
 }
 
-static int sdap_sudo_smart_refresh_recv(struct tevent_req *req,
-                                        int *dp_error,
-                                        int *error)
+int sdap_sudo_smart_refresh_recv(struct tevent_req *req,
+                                 int *dp_error,
+                                 int *error)
 {
     struct sdap_sudo_smart_refresh_state *state = NULL;
     state = tevent_req_data(req, struct sdap_sudo_smart_refresh_state);
@@ -971,331 +853,3 @@ static int sdap_sudo_smart_refresh_recv(struct tevent_req *req,
     return sdap_sudo_refresh_recv(state, state->subreq, dp_error, error,
                                   NULL, NULL);
 }
-
-static void sdap_sudo_full_refresh_online_cb(void *pvt)
-{
-    struct sdap_sudo_ctx *sudo_ctx = NULL;
-    time_t timeout;
-    int ret;
-
-    sudo_ctx = talloc_get_type(pvt, struct sdap_sudo_ctx);
-
-    /* remove online callback */
-    talloc_zfree(sudo_ctx->first_refresh_online_cb);
-
-    /* schedule new first refresh only if this callback wasn't triggered
-     * by ongoing full refresh */
-    if (sudo_ctx->full_refresh_in_progress) {
-        return;
-    }
-
-    /* otherwise cancel the concurrent timer for full refresh */
-    talloc_zfree(sudo_ctx->first_refresh_timer);
-
-    /* and fire full refresh immediately */
-    timeout = dp_opt_get_int(sudo_ctx->id_ctx->opts->basic,
-                             SDAP_SUDO_FULL_REFRESH_INTERVAL);
-    if (timeout == 0) {
-        /* runtime configuration change? */
-        DEBUG(SSSDBG_TRACE_FUNC, "Periodical full refresh of sudo rules "
-                                  "is disabled\n");
-        return;
-    }
-
-    ret = sdap_sudo_schedule_refresh(sudo_ctx, sudo_ctx,
-                                     SDAP_SUDO_REFRESH_FULL,
-                                     sdap_sudo_periodical_first_refresh_done,
-                                     0, timeout, NULL);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule full refresh of sudo "
-              "rules! Periodical updates will not work!\n");
-    }
-}
-
-static void sdap_sudo_periodical_first_refresh_done(struct tevent_req *req)
-{
-    struct tevent_req *subreq = NULL; /* req from sdap_sudo_full_refresh_send() */
-    struct sdap_sudo_ctx *sudo_ctx = NULL;
-    time_t delay;
-    time_t timeout;
-    int dp_error = DP_ERR_OK;
-    int error = EOK;
-    int ret;
-
-    ret = sdap_sudo_timer_recv(req, req, &subreq);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Sudo timer failed [%d]: %s\n", ret, strerror(ret));
-        goto schedule;
-    }
-
-    ret = sdap_sudo_full_refresh_recv(subreq, &dp_error, &error);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Periodical full refresh of sudo rules "
-                                  "failed [%d]: %s)\n", ret, strerror(ret));
-        goto schedule;
-    }
-
-    if (dp_error != DP_ERR_OK || error != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Periodical full refresh of sudo rules "
-                                  "failed [dp_error: %d] ([%d]: %s)\n",
-                                  dp_error, error, strerror(error));
-        goto schedule;
-    }
-
-schedule:
-    sudo_ctx = tevent_req_callback_data(req, struct sdap_sudo_ctx);
-    if (sudo_ctx->first_refresh_timer == req) {
-        sudo_ctx->first_refresh_timer = NULL;
-    }
-    talloc_zfree(req);
-
-    /* full refresh */
-    delay = dp_opt_get_int(sudo_ctx->id_ctx->opts->basic,
-                           SDAP_SUDO_FULL_REFRESH_INTERVAL);
-    if (delay == 0) {
-        /* runtime configuration change? */
-        DEBUG(SSSDBG_TRACE_FUNC, "Periodical full refresh of sudo rules "
-                                  "is disabled\n");
-        return;
-    }
-
-    /* if we are offline, we will try to perform another full refresh */
-    if (dp_error == DP_ERR_OFFLINE) {
-        sudo_ctx->full_refresh_attempts++;
-        timeout = delay;
-        delay = sudo_ctx->full_refresh_attempts << 1;
-        if (delay > SUDO_MAX_FIRST_REFRESH_DELAY) {
-            delay = SUDO_MAX_FIRST_REFRESH_DELAY;
-        }
-
-        DEBUG(SSSDBG_TRACE_FUNC, "Data provider is offline. "
-              "Scheduling another full refresh in %ld minutes.\n", delay);
-
-        ret = sdap_sudo_schedule_refresh(sudo_ctx, sudo_ctx,
-                                         SDAP_SUDO_REFRESH_FULL,
-                                         sdap_sudo_periodical_first_refresh_done,
-                                         delay * 60, timeout,
-                                         &sudo_ctx->first_refresh_timer);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule full refresh of sudo "
-                  "rules! Periodical updates will not work!\n");
-        }
-
-        /* also setup online callback to make sure the refresh is fired as soon
-         * as possible */
-        ret = be_add_online_cb(sudo_ctx->id_ctx->be, sudo_ctx->id_ctx->be,
-                               sdap_sudo_full_refresh_online_cb,
-                               sudo_ctx, &sudo_ctx->first_refresh_online_cb);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "Could not set up online callback\n");
-        }
-
-        return;
-    }
-
-    ret = sdap_sudo_schedule_full_refresh(sudo_ctx, delay);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Full periodical refresh will not work.\n");
-    }
-
-    /* smart refresh */
-    delay = dp_opt_get_int(sudo_ctx->id_ctx->opts->basic,
-                           SDAP_SUDO_SMART_REFRESH_INTERVAL);
-    if (delay == 0) {
-        /* runtime configuration change? */
-        DEBUG(SSSDBG_TRACE_FUNC, "Periodical smart refresh of sudo rules "
-                                  "is disabled\n");
-        return;
-    }
-
-    ret = sdap_sudo_schedule_smart_refresh(sudo_ctx, delay);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Smart periodical refresh will not work.\n");
-    }
-}
-
-static void sdap_sudo_periodical_full_refresh_done(struct tevent_req *req)
-{
-    struct tevent_req *subreq = NULL; /* req from sdap_sudo_full_refresh_send() */
-    struct sdap_sudo_ctx *sudo_ctx = NULL;
-    time_t delay;
-    int dp_error = DP_ERR_FATAL;
-    int error = EFAULT;
-    int ret;
-
-    ret = sdap_sudo_timer_recv(req, req, &subreq);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Sudo timer failed [%d]: %s\n", ret, strerror(ret));
-        goto schedule;
-    }
-
-    ret = sdap_sudo_full_refresh_recv(subreq, &dp_error, &error);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Periodical full refresh of sudo rules "
-                                  "failed [%d]: %s)\n", ret, strerror(ret));
-        goto schedule;
-    }
-
-    if (dp_error != DP_ERR_OK || error != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Periodical full refresh of sudo rules "
-                                  "failed [dp_error: %d] ([%d]: %s)\n",
-                                  dp_error, error, strerror(error));
-        goto schedule;
-    }
-
-schedule:
-    sudo_ctx = tevent_req_callback_data(req, struct sdap_sudo_ctx);
-    talloc_zfree(req);
-
-    delay = dp_opt_get_int(sudo_ctx->id_ctx->opts->basic,
-                           SDAP_SUDO_FULL_REFRESH_INTERVAL);
-    if (delay == 0) {
-        /* runtime configuration change? */
-        DEBUG(SSSDBG_TRACE_FUNC, "Periodical full refresh of sudo rules "
-                                  "is disabled\n");
-        return;
-    }
-
-    ret = sdap_sudo_schedule_full_refresh(sudo_ctx, delay);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Full periodical refresh will not work.\n");
-    }
-}
-
-static void sdap_sudo_periodical_smart_refresh_done(struct tevent_req *req)
-{
-    struct tevent_req *subreq = NULL; /* req from sdap_sudo_smart_refresh_send() */
-    struct sdap_sudo_ctx *sudo_ctx = NULL;
-    time_t delay;
-    int dp_error;
-    int error;
-    int ret;
-
-    ret = sdap_sudo_timer_recv(req, req, &subreq);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Sudo timer failed [%d]: %s\n", ret, strerror(ret));
-        goto schedule;
-    }
-
-    ret = sdap_sudo_smart_refresh_recv(subreq, &dp_error, &error);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Periodical smart refresh of sudo rules "
-                                  "failed [%d]: %s\n", ret, strerror(ret));
-    }
-
-    if (dp_error != DP_ERR_OK || error != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Periodical smart refresh of sudo rules "
-                                  "failed [dp_error: %d] ([%d]: %s)\n",
-                                  dp_error, error, strerror(error));
-        goto schedule;
-    }
-
-schedule:
-    sudo_ctx = tevent_req_callback_data(req, struct sdap_sudo_ctx);
-    talloc_zfree(req);
-
-    delay = dp_opt_get_int(sudo_ctx->id_ctx->opts->basic,
-                           SDAP_SUDO_SMART_REFRESH_INTERVAL);
-    if (delay == 0) {
-        /* runtime configuration change? */
-        DEBUG(SSSDBG_TRACE_FUNC, "Periodical smart refresh of sudo rules "
-                                  "is disabled\n");
-        return;
-    }
-
-    ret = sdap_sudo_schedule_smart_refresh(sudo_ctx, delay);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Smart periodical refresh will not work.\n");
-    }
-}
-
-static int sdap_sudo_schedule_refresh(TALLOC_CTX *mem_ctx,
-                                      struct sdap_sudo_ctx *sudo_ctx,
-                                      enum sdap_sudo_refresh_type refresh,
-                                      tevent_req_fn callback,
-                                      time_t delay,
-                                      time_t timeout,
-                                      struct tevent_req **_req)
-{
-    struct tevent_req *req = NULL;
-    sdap_sudo_timer_fn_t send_fn = NULL;
-    const char *name = NULL;
-    struct timeval when;
-
-    when = tevent_timeval_current_ofs(delay, 0);
-
-    switch (refresh) {
-    case SDAP_SUDO_REFRESH_FULL:
-        send_fn = sdap_sudo_full_refresh_send;
-        name = "Full refresh";
-        break;
-    case SDAP_SUDO_REFRESH_SMART:
-        send_fn = sdap_sudo_smart_refresh_send;
-        name = "Smart refresh";
-        break;
-    case SDAP_SUDO_REFRESH_RULES:
-        DEBUG(SSSDBG_OP_FAILURE, "Rules refresh can't be scheduled!\n");
-        return EINVAL;
-    default:
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown refresh type [%d].\n", refresh);
-        return EINVAL;
-    }
-
-
-
-    req = sdap_sudo_timer_send(mem_ctx, sudo_ctx->id_ctx->be->ev, sudo_ctx,
-                               when, timeout, send_fn);
-    if (req == NULL) {
-        return ENOMEM;
-    }
-
-    tevent_req_set_callback(req, callback, sudo_ctx);
-
-    DEBUG(SSSDBG_TRACE_FUNC, "%s scheduled at: %lld\n",
-                              name, (long long)when.tv_sec);
-
-    if (_req != NULL) {
-        *_req = req;
-    }
-
-    return EOK;
-}
-
-static int sdap_sudo_schedule_full_refresh(struct sdap_sudo_ctx *sudo_ctx,
-                                           time_t delay)
-{
-    int ret;
-
-    ret = sdap_sudo_schedule_refresh(sudo_ctx, sudo_ctx,
-                                     SDAP_SUDO_REFRESH_FULL,
-                                     sdap_sudo_periodical_full_refresh_done,
-                                     delay, delay, NULL);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule full refresh of sudo "
-              "rules!\n");
-        return ret;
-    }
-
-    return EOK;
-}
-
-static int sdap_sudo_schedule_smart_refresh(struct sdap_sudo_ctx *sudo_ctx,
-                                            time_t delay)
-{
-    int ret;
-
-    ret = sdap_sudo_schedule_refresh(sudo_ctx, sudo_ctx,
-                                     SDAP_SUDO_REFRESH_SMART,
-                                     sdap_sudo_periodical_smart_refresh_done,
-                                     delay, delay, NULL);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule smart refresh of sudo "
-              "rules!\n");
-        return ret;
-    }
-
-    return EOK;
-}
diff --git a/src/providers/ldap/sdap_sudo.h b/src/providers/ldap/sdap_sudo.h
index e2764b90c91470ed64e4c07a792206fa034646a6..51e99f561b0f195e1b78e6fd8926f5b9b341ee72 100644
--- a/src/providers/ldap/sdap_sudo.h
+++ b/src/providers/ldap/sdap_sudo.h
@@ -21,6 +21,9 @@
 #ifndef _SDAP_SUDO_H_
 #define _SDAP_SUDO_H_
 
+#include "providers/dp_backend.h"
+#include "providers/ldap/ldap_common.h"
+
 struct sdap_sudo_ctx {
     struct sdap_id_ctx *id_ctx;
 
@@ -32,15 +35,6 @@ struct sdap_sudo_ctx {
 
     bool full_refresh_done;
     bool full_refresh_in_progress;
-    int full_refresh_attempts;
-    struct be_cb *first_refresh_online_cb;
-    struct tevent_req *first_refresh_timer;
-};
-
-enum sdap_sudo_refresh_type {
-    SDAP_SUDO_REFRESH_FULL,
-    SDAP_SUDO_REFRESH_SMART,
-    SDAP_SUDO_REFRESH_RULES
 };
 
 /* Common functions from ldap_sudo.c */
@@ -65,21 +59,22 @@ int sdap_sudo_refresh_recv(TALLOC_CTX *mem_ctx,
                            char **usn,
                            size_t *num_rules);
 
-/* timer */
+struct tevent_req *sdap_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
+                                               struct sdap_sudo_ctx *sudo_ctx);
 
-typedef struct tevent_req * (*sdap_sudo_timer_fn_t)(TALLOC_CTX *mem_ctx,
-                                                    struct sdap_sudo_ctx *sudo_ctx);
+int sdap_sudo_full_refresh_recv(struct tevent_req *req,
+                                int *dp_error,
+                                int *error);
 
-struct tevent_req * sdap_sudo_timer_send(TALLOC_CTX *mem_ctx,
-                                         struct tevent_context *ev,
-                                         struct sdap_sudo_ctx *sudo_ctx,
-                                         struct timeval when,
-                                         time_t timeout,
-                                         sdap_sudo_timer_fn_t fn);
+struct tevent_req *sdap_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
+                                                struct sdap_sudo_ctx *sudo_ctx);
 
-int sdap_sudo_timer_recv(TALLOC_CTX *mem_ctx,
-                         struct tevent_req *req,
-                         struct tevent_req **_subreq);
+int sdap_sudo_smart_refresh_recv(struct tevent_req *req,
+                                 int *dp_error,
+                                 int *error);
+
+errno_t
+sdap_sudo_ptask_setup(struct be_ctx *be_ctx, struct sdap_sudo_ctx *sudo_ctx);
 
 /* host info */
 struct tevent_req * sdap_sudo_get_hostinfo_send(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ldap/sdap_sudo_refresh.c b/src/providers/ldap/sdap_sudo_refresh.c
new file mode 100644
index 0000000000000000000000000000000000000000..39821840a8a550dcc1a6c954dfb53fc73245e9f6
--- /dev/null
+++ b/src/providers/ldap/sdap_sudo_refresh.c
@@ -0,0 +1,157 @@
+/*
+    Authors:
+        Pavel Březina <pbrezina@redhat.com>
+
+    Copyright (C) 2015 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 <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "providers/dp_ptask.h"
+#include "providers/ldap/sdap_sudo.h"
+#include "db/sysdb_sudo.h"
+
+static struct tevent_req *
+sdap_sudo_ptask_full_refresh_send(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct be_ctx *be_ctx,
+                                  struct be_ptask *be_ptask,
+                                  void *pvt)
+{
+    struct sdap_sudo_ctx *sudo_ctx;
+    sudo_ctx = talloc_get_type(pvt, struct sdap_sudo_ctx);
+
+    return sdap_sudo_full_refresh_send(mem_ctx, sudo_ctx);
+}
+
+static errno_t
+sdap_sudo_ptask_full_refresh_recv(struct tevent_req *req)
+{
+    int dp_error;
+    int error;
+
+    return sdap_sudo_full_refresh_recv(req, &dp_error, &error);
+}
+
+static struct tevent_req *
+sdap_sudo_ptask_smart_refresh_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct be_ctx *be_ctx,
+                                   struct be_ptask *be_ptask,
+                                   void *pvt)
+{
+    struct sdap_sudo_ctx *sudo_ctx;
+    sudo_ctx = talloc_get_type(pvt, struct sdap_sudo_ctx);
+
+    return sdap_sudo_smart_refresh_send(mem_ctx, sudo_ctx);
+}
+
+static errno_t
+sdap_sudo_ptask_smart_refresh_recv(struct tevent_req *req)
+{
+    int dp_error;
+    int error;
+
+    return sdap_sudo_smart_refresh_recv(req, &dp_error, &error);
+}
+
+errno_t
+sdap_sudo_ptask_setup(struct be_ctx *be_ctx, struct sdap_sudo_ctx *sudo_ctx)
+{
+    struct dp_option *opts = sudo_ctx->id_ctx->opts->basic;
+    time_t smart;
+    time_t full;
+    time_t delay;
+    time_t last_refresh;
+    errno_t ret;
+
+    smart = dp_opt_get_int(opts, SDAP_SUDO_SMART_REFRESH_INTERVAL);
+    full = dp_opt_get_int(opts, SDAP_SUDO_FULL_REFRESH_INTERVAL);
+
+    if (smart == 0 && full == 0) {
+        /* We don't allow both types to be disabled. At least smart refresh
+         * needs to be enabled. In this case smart refresh will catch up new
+         * and modified rules and deleted rules are caught when expired. */
+        smart = opts[SDAP_SUDO_SMART_REFRESH_INTERVAL].def_val.number;
+
+        DEBUG(SSSDBG_CONF_SETTINGS, "At least smart refresh needs to be "
+              "enabled. Setting smart refresh interval to default value "
+              "(%ld) seconds.\n", smart);
+    } else if (full <= smart) {
+        /* In this case it does not make any sense to run smart refresh. */
+        smart = 0;
+
+        DEBUG(SSSDBG_CONF_SETTINGS, "Smart refresh interval has to be lower "
+              "than full refresh interval. Periodical smart refresh will be "
+              "disabled.\n");
+    }
+
+    ret = sysdb_sudo_get_last_full_refresh(be_ctx->domain, &last_refresh);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to obtain time of last full "
+              "refresh. Assuming none was performed so far.\n");
+        last_refresh = 0;
+    }
+
+    if (last_refresh == 0) {
+        /* If this is the first startup, we need to kick off an refresh
+         * immediately, to close a window where clients requesting sudo
+         * information won't get an immediate reply with no entries */
+        delay = 0;
+    } else {
+        /* At least one update has previously run, so clients will get cached
+         * data. We will delay the refresh so we don't slow down the startup
+         * process if this is happening during system boot. */
+        delay = 10;
+    }
+
+    /* Full refresh.
+     *
+     * Disable when offline and run immediately when SSSD goes back online.
+     * Since we have periodical online check we don't have to run this task
+     * when offline. */
+    ret = be_ptask_create(be_ctx, be_ctx, full, delay, 0, 0, full,
+                          BE_PTASK_OFFLINE_DISABLE, 0,
+                          sdap_sudo_ptask_full_refresh_send,
+                          sdap_sudo_ptask_full_refresh_recv,
+                          sudo_ctx, "SUDO Full Refresh", NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup full refresh ptask "
+              "[%d]: %s\n", ret, sss_strerror(ret));
+        return ret;
+    }
+
+    /* Smart refresh.
+     *
+     * Disable when offline and reschedule normally when SSSD goes back online.
+     * Since we have periodical online check we don't have to run this task
+     * when offline. */
+    ret = be_ptask_create(be_ctx, be_ctx, smart, delay + smart, smart, 0, smart,
+                          BE_PTASK_OFFLINE_DISABLE, 0,
+                          sdap_sudo_ptask_smart_refresh_send,
+                          sdap_sudo_ptask_smart_refresh_recv,
+                          sudo_ctx, "SUDO Smart Refresh", NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup smart refresh ptask "
+              "[%d]: %s\n", ret, sss_strerror(ret));
+        return ret;
+    }
+
+    return EOK;
+}
-- 
2.1.0

