URL: https://github.com/SSSD/sssd/pull/812 Author: jhrozek Title: #812: Implement background refresh for IPA and AD domains and subdomains Action: synchronized
To pull the PR as Git branch: git remote add ghsssd https://github.com/SSSD/sssd git fetch ghsssd pull/812/head:pr812 git checkout pr812
From 3165cd3a1b5629b645e679187b667542aaca516a Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Wed, 24 Apr 2019 21:09:53 +0200 Subject: [PATCH 01/10] BE: Enable refresh for multiple domains Descend into subdomains on back end refresh and make sure to start from users again. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index e8cf5da758..6e590ec03a 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -256,7 +256,9 @@ static errno_t be_refresh_step(struct tevent_req *req) /* if not found than continue with next domain */ if (state->index == BE_REFRESH_TYPE_SENTINEL) { - state->domain = get_next_domain(state->domain, 0); + state->domain = get_next_domain(state->domain, + SSS_GND_DESCEND); + state->index = 0; continue; } From b24a8f078b1793a9953e8f23361ed4a925950b55 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Mon, 20 May 2019 22:32:13 +0200 Subject: [PATCH 02/10] BE: Make be_refresh_ctx_init set up the periodical task, too This is mostly a preparatory patch that rolls in setting up the ptask into be_refresh_ctx_init. Since in later patches we will call be_refresh_ctx_init from several different places, this will prevent code duplication. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 21 +++++++++++++++++++-- src/providers/be_refresh.h | 2 +- src/providers/data_provider_be.c | 14 -------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index 6e590ec03a..d5ffb3d166 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -135,11 +135,13 @@ struct be_refresh_ctx { struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL]; }; -struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx) +struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx) { struct be_refresh_ctx *ctx = NULL; + uint32_t refresh_interval; + errno_t ret; - ctx = talloc_zero(mem_ctx, struct be_refresh_ctx); + ctx = talloc_zero(be_ctx, struct be_refresh_ctx); if (ctx == NULL) { return NULL; } @@ -148,6 +150,21 @@ struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx) ctx->callbacks[BE_REFRESH_TYPE_GROUPS].name = "groups"; ctx->callbacks[BE_REFRESH_TYPE_NETGROUPS].name = "netgroups"; + refresh_interval = be_ctx->domain->refresh_expired_interval; + if (refresh_interval > 0) { + ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0, + refresh_interval, BE_PTASK_OFFLINE_SKIP, 0, + be_refresh_send, be_refresh_recv, + be_ctx->refresh_ctx, "Refresh Records", NULL); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Unable to initialize refresh periodic task [%d]: %s\n", + ret, sss_strerror(ret)); + talloc_free(ctx); + return NULL; + } + } + return ctx; } diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h index 927fa4a33b..664f018163 100644 --- a/src/providers/be_refresh.h +++ b/src/providers/be_refresh.h @@ -52,7 +52,7 @@ enum be_refresh_type { struct be_refresh_ctx; -struct be_refresh_ctx *be_refresh_ctx_init(TALLOC_CTX *mem_ctx); +struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx); errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx, enum be_refresh_type type, diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index 147627b798..5e6235786b 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -454,7 +454,6 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb) { - uint32_t refresh_interval; struct tevent_req *req; struct be_ctx *be_ctx; char *str = NULL; @@ -545,19 +544,6 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx, goto done; } - refresh_interval = be_ctx->domain->refresh_expired_interval; - if (refresh_interval > 0) { - ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0, - refresh_interval, BE_PTASK_OFFLINE_SKIP, 0, - be_refresh_send, be_refresh_recv, - be_ctx->refresh_ctx, "Refresh Records", NULL); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - "Unable to initialize refresh periodic task\n"); - goto done; - } - } - req = dp_init_send(be_ctx, be_ctx->ev, be_ctx, be_ctx->uid, be_ctx->gid); if (req == NULL) { ret = ENOMEM; From 426a9ef977fea3f9cf1e429433cd95e479d357a3 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Mon, 20 May 2019 22:42:47 +0200 Subject: [PATCH 03/10] BE/LDAP: Call be_refresh_ctx_init() in the provider libraries, not in back end Since later patches will pass different parameters to be_refresh_ctx_init(), let's call the init function in the provider libraries not directly in the back end. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/ad/ad_init.c | 2 +- src/providers/data_provider_be.c | 8 -------- src/providers/ipa/ipa_init.c | 2 +- src/providers/ldap/ldap_common.h | 2 +- src/providers/ldap/ldap_init.c | 2 +- src/providers/ldap/sdap_refresh.c | 17 +++++++++++++---- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 4f1bd6dd16..7de2da6699 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -408,7 +408,7 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx, return ret; } - ret = sdap_refresh_init(be_ctx->refresh_ctx, sdap_id_ctx); + ret = sdap_refresh_init(be_ctx, sdap_id_ctx); if (ret != EOK && ret != EEXIST) { DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " "will not work [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index 5e6235786b..e573f69a5f 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -536,14 +536,6 @@ errno_t be_process_init(TALLOC_CTX *mem_ctx, goto done; } - /* Initialize be_refresh periodic task. */ - be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx); - if (be_ctx->refresh_ctx == NULL) { - DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); - ret = ENOMEM; - goto done; - } - req = dp_init_send(be_ctx, be_ctx->ev, be_ctx, be_ctx->uid, be_ctx->gid); if (req == NULL) { ret = ENOMEM; diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c index 6818e21714..b3060e228c 100644 --- a/src/providers/ipa/ipa_init.c +++ b/src/providers/ipa/ipa_init.c @@ -594,7 +594,7 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx, } } - ret = sdap_refresh_init(be_ctx->refresh_ctx, sdap_id_ctx); + ret = sdap_refresh_init(be_ctx, sdap_id_ctx); if (ret != EOK && ret != EEXIST) { DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " "will not work [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index 4afcd1902a..c551c9d575 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -365,7 +365,7 @@ struct sdap_id_ctx * sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, struct sdap_service *sdap_service); -errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx, +errno_t sdap_refresh_init(struct be_ctx *be_ctx, struct sdap_id_ctx *id_ctx); errno_t sdap_init_certmap(TALLOC_CTX *mem_ctx, struct sdap_id_ctx *id_ctx); diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c index 95e6561236..5fe2e280f7 100644 --- a/src/providers/ldap/ldap_init.c +++ b/src/providers/ldap/ldap_init.c @@ -432,7 +432,7 @@ static errno_t ldap_init_misc(struct be_ctx *be_ctx, } /* Setup periodical refresh of expired records */ - ret = sdap_refresh_init(be_ctx->refresh_ctx, id_ctx); + ret = sdap_refresh_init(be_ctx, id_ctx); if (ret != EOK && ret != EEXIST) { DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh will not work " "[%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c index 6d6c43e20c..457df8be23 100644 --- a/src/providers/ldap/sdap_refresh.c +++ b/src/providers/ldap/sdap_refresh.c @@ -255,12 +255,19 @@ static errno_t sdap_refresh_netgroups_recv(struct tevent_req *req) return sdap_refresh_recv(req); } -errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx, +errno_t sdap_refresh_init(struct be_ctx *be_ctx, struct sdap_id_ctx *id_ctx) { errno_t ret; - ret = be_refresh_add_cb(refresh_ctx, BE_REFRESH_TYPE_USERS, + be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx); + if (be_ctx->refresh_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); + return ENOMEM; + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, sdap_refresh_users_send, sdap_refresh_users_recv, id_ctx); @@ -269,7 +276,8 @@ errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx, "will not work [%d]: %s\n", ret, strerror(ret)); } - ret = be_refresh_add_cb(refresh_ctx, BE_REFRESH_TYPE_GROUPS, + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, sdap_refresh_groups_send, sdap_refresh_groups_recv, id_ctx); @@ -278,7 +286,8 @@ errno_t sdap_refresh_init(struct be_refresh_ctx *refresh_ctx, "will not work [%d]: %s\n", ret, strerror(ret)); } - ret = be_refresh_add_cb(refresh_ctx, BE_REFRESH_TYPE_NETGROUPS, + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, sdap_refresh_netgroups_send, sdap_refresh_netgroups_recv, id_ctx); From 3ca4b0cf65e0a0a45b235b193918a93b848df808 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Tue, 21 May 2019 12:09:24 +0200 Subject: [PATCH 04/10] BE: Pass in attribute to look up with instead of hardcoding SYSDB_NAME In later patches, we will implement refreshes for AD or IPA which might refresh objects that do not have a name yet, but always do have a different attribute, like a SID or a uniqueID. In this case, it's better to use that different attribute instead of name. This patch allows the caller to tell the refresh module which attribute to use. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 12 ++++++++---- src/providers/be_refresh.h | 3 ++- src/providers/ldap/sdap_refresh.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index d5ffb3d166..f7ef8452e2 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -90,6 +90,7 @@ static errno_t be_refresh_get_values_ex(TALLOC_CTX *mem_ctx, static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx, enum be_refresh_type type, + const char *attr_name, struct sss_domain_info *domain, time_t period, char ***_values) @@ -117,7 +118,7 @@ static errno_t be_refresh_get_values(TALLOC_CTX *mem_ctx, } ret = be_refresh_get_values_ex(mem_ctx, domain, period, - base_dn, SYSDB_NAME, _values); + base_dn, attr_name, _values); talloc_free(base_dn); return ret; @@ -132,10 +133,12 @@ struct be_refresh_cb { }; struct be_refresh_ctx { + const char *attr_name; struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL]; }; -struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx) +struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx, + const char *attr_name) { struct be_refresh_ctx *ctx = NULL; uint32_t refresh_interval; @@ -146,6 +149,7 @@ struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx) return NULL; } + ctx->attr_name = attr_name; ctx->callbacks[BE_REFRESH_TYPE_USERS].name = "users"; ctx->callbacks[BE_REFRESH_TYPE_GROUPS].name = "groups"; ctx->callbacks[BE_REFRESH_TYPE_NETGROUPS].name = "netgroups"; @@ -285,8 +289,8 @@ static errno_t be_refresh_step(struct tevent_req *req) goto done; } - ret = be_refresh_get_values(state, state->index, state->domain, - state->period, &values); + ret = be_refresh_get_values(state, state->index, state->ctx->attr_name, + state->domain, state->period, &values); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain DN list [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h index 664f018163..8c7b1d0ba8 100644 --- a/src/providers/be_refresh.h +++ b/src/providers/be_refresh.h @@ -52,7 +52,8 @@ enum be_refresh_type { struct be_refresh_ctx; -struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx); +struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx, + const char *attr_name); errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx, enum be_refresh_type type, diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c index 457df8be23..ed04da36a0 100644 --- a/src/providers/ldap/sdap_refresh.c +++ b/src/providers/ldap/sdap_refresh.c @@ -260,7 +260,7 @@ errno_t sdap_refresh_init(struct be_ctx *be_ctx, { errno_t ret; - be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx); + be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx, SYSDB_NAME); if (be_ctx->refresh_ctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); return ENOMEM; From 84df48ce2774cd1fb9ac9c4b532e9c4e216adcf3 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Tue, 21 May 2019 12:07:34 +0200 Subject: [PATCH 05/10] BE: Change be_refresh_ctx_init to return errno and set be_ctx->refresh_ctx It is a bit odd that a caller to a be_ function would set a property of be_ctx. IMO it is cleaner if the function has a side-effect and sets the property internally and rather returns errno. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 13 +++++++------ src/providers/be_refresh.h | 4 ++-- src/providers/ldap/sdap_refresh.c | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index f7ef8452e2..af3615a0d2 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -137,8 +137,8 @@ struct be_refresh_ctx { struct be_refresh_cb callbacks[BE_REFRESH_TYPE_SENTINEL]; }; -struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx, - const char *attr_name) +errno_t be_refresh_ctx_init(struct be_ctx *be_ctx, + const char *attr_name) { struct be_refresh_ctx *ctx = NULL; uint32_t refresh_interval; @@ -146,7 +146,7 @@ struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx, ctx = talloc_zero(be_ctx, struct be_refresh_ctx); if (ctx == NULL) { - return NULL; + return ENOMEM; } ctx->attr_name = attr_name; @@ -159,17 +159,18 @@ struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx, ret = be_ptask_create(be_ctx, be_ctx, refresh_interval, 30, 5, 0, refresh_interval, BE_PTASK_OFFLINE_SKIP, 0, be_refresh_send, be_refresh_recv, - be_ctx->refresh_ctx, "Refresh Records", NULL); + ctx, "Refresh Records", NULL); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh periodic task [%d]: %s\n", ret, sss_strerror(ret)); talloc_free(ctx); - return NULL; + return ret; } } - return ctx; + be_ctx->refresh_ctx = ctx; + return EOK; } errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx, diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h index 8c7b1d0ba8..980ac7d060 100644 --- a/src/providers/be_refresh.h +++ b/src/providers/be_refresh.h @@ -52,8 +52,8 @@ enum be_refresh_type { struct be_refresh_ctx; -struct be_refresh_ctx *be_refresh_ctx_init(struct be_ctx *be_ctx, - const char *attr_name); +errno_t be_refresh_ctx_init(struct be_ctx *be_ctx, + const char *attr_name); errno_t be_refresh_add_cb(struct be_refresh_ctx *ctx, enum be_refresh_type type, diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c index ed04da36a0..baa7fa59fb 100644 --- a/src/providers/ldap/sdap_refresh.c +++ b/src/providers/ldap/sdap_refresh.c @@ -260,8 +260,8 @@ errno_t sdap_refresh_init(struct be_ctx *be_ctx, { errno_t ret; - be_ctx->refresh_ctx = be_refresh_ctx_init(be_ctx, SYSDB_NAME); - if (be_ctx->refresh_ctx == NULL) { + ret = be_refresh_ctx_init(be_ctx, SYSDB_NAME); + if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); return ENOMEM; } From 14693fc22f022efc941df7d5ba76065ed5092d8b Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Wed, 8 May 2019 14:38:44 +0200 Subject: [PATCH 06/10] BE/LDAP: Splir out a helper function from sdap_refresh for later reuse Every refresh request will send a similar account_req. Let's split out the function that creates the account_req into a reusable one. Also removes the type string as it was only used in DEBUG messages and there is already a function in the back end API that provides the same functionality. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 18 ++++++++++++++++++ src/providers/be_refresh.h | 4 ++++ src/providers/ldap/sdap_refresh.c | 29 +++++------------------------ 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index af3615a0d2..5701547a5b 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -363,3 +363,21 @@ errno_t be_refresh_recv(struct tevent_req *req) return EOK; } + +struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx, + uint32_t entry_type, + struct sss_domain_info *domain) +{ + struct dp_id_data *account_req; + + account_req = talloc_zero(mem_ctx, struct dp_id_data); + if (account_req == NULL) { + return NULL; + } + + account_req->entry_type = entry_type; + account_req->filter_type = BE_FILTER_NAME; + account_req->extra_value = NULL; + account_req->domain = domain->name; + return account_req; +} diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h index 980ac7d060..b7ba5d4c23 100644 --- a/src/providers/be_refresh.h +++ b/src/providers/be_refresh.h @@ -69,4 +69,8 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx, errno_t be_refresh_recv(struct tevent_req *req); +struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx, + uint32_t entry_type, + struct sss_domain_info *domain); + #endif /* _DP_REFRESH_H_ */ diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c index baa7fa59fb..af39d86861 100644 --- a/src/providers/ldap/sdap_refresh.c +++ b/src/providers/ldap/sdap_refresh.c @@ -30,7 +30,6 @@ struct sdap_refresh_state { struct dp_id_data *account_req; struct sdap_id_ctx *id_ctx; struct sdap_domain *sdom; - const char *type; char **names; size_t index; }; @@ -74,32 +73,12 @@ static struct tevent_req *sdap_refresh_send(TALLOC_CTX *mem_ctx, goto immediately; } - switch (entry_type) { - case BE_REQ_USER: - state->type = "user"; - break; - case BE_REQ_GROUP: - state->type = "group"; - break; - case BE_REQ_NETGROUP: - state->type = "netgroup"; - break; - default: - DEBUG(SSSDBG_CRIT_FAILURE, "Invalid entry type [%d]!\n", entry_type); - } - - state->account_req = talloc_zero(state, struct dp_id_data); + state->account_req = be_refresh_acct_req(state, entry_type, domain); if (state->account_req == NULL) { ret = ENOMEM; goto immediately; } - state->account_req->entry_type = entry_type; - state->account_req->filter_type = BE_FILTER_NAME; - state->account_req->extra_value = NULL; - state->account_req->domain = domain->name; - /* filter will be filled later */ - ret = sdap_refresh_step(req); if (ret == EOK) { DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n"); @@ -143,7 +122,8 @@ static errno_t sdap_refresh_step(struct tevent_req *req) } DEBUG(SSSDBG_TRACE_FUNC, "Issuing refresh of %s %s\n", - state->type, state->account_req->filter_value); + be_req2str(state->account_req->entry_type), + state->account_req->filter_value); subreq = sdap_handle_acct_req_send(state, state->be_ctx, state->account_req, state->id_ctx, @@ -178,7 +158,8 @@ static void sdap_refresh_done(struct tevent_req *subreq) talloc_zfree(subreq); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, " - "sdap_ret: %d, errno: %d]: %s\n", state->type, + "sdap_ret: %d, errno: %d]: %s\n", + be_req2str(state->account_req->entry_type), dp_error, sdap_ret, ret, err_msg); goto done; } From 569a3b3b2ae5dede7ed4f2bfcf78ac398bd26f0c Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Tue, 21 May 2019 12:07:59 +0200 Subject: [PATCH 07/10] BE: Pass in filter_type when creating the refresh account request For refreshing AD users and groups, we'll want to create a request by SID, for all other requests we'll want to create a request by name. This patch allows parametrizing the request creation by the caller. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 3 ++- src/providers/be_refresh.h | 1 + src/providers/ldap/sdap_refresh.c | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index 5701547a5b..8ecbaf14ae 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -366,6 +366,7 @@ errno_t be_refresh_recv(struct tevent_req *req) struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx, uint32_t entry_type, + uint32_t filter_type, struct sss_domain_info *domain) { struct dp_id_data *account_req; @@ -376,7 +377,7 @@ struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx, } account_req->entry_type = entry_type; - account_req->filter_type = BE_FILTER_NAME; + account_req->filter_type = filter_type; account_req->extra_value = NULL; account_req->domain = domain->name; return account_req; diff --git a/src/providers/be_refresh.h b/src/providers/be_refresh.h index b7ba5d4c23..c7b4872df1 100644 --- a/src/providers/be_refresh.h +++ b/src/providers/be_refresh.h @@ -71,6 +71,7 @@ errno_t be_refresh_recv(struct tevent_req *req); struct dp_id_data *be_refresh_acct_req(TALLOC_CTX *mem_ctx, uint32_t entry_type, + uint32_t filter_type, struct sss_domain_info *domain); #endif /* _DP_REFRESH_H_ */ diff --git a/src/providers/ldap/sdap_refresh.c b/src/providers/ldap/sdap_refresh.c index af39d86861..2206d6670a 100644 --- a/src/providers/ldap/sdap_refresh.c +++ b/src/providers/ldap/sdap_refresh.c @@ -73,7 +73,8 @@ static struct tevent_req *sdap_refresh_send(TALLOC_CTX *mem_ctx, goto immediately; } - state->account_req = be_refresh_acct_req(state, entry_type, domain); + state->account_req = be_refresh_acct_req(state, entry_type, + BE_FILTER_NAME, domain); if (state->account_req == NULL) { ret = ENOMEM; goto immediately; From 2c5010376154e71fe5be6c657fe70437ded8c0a0 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Wed, 8 May 2019 23:16:07 +0200 Subject: [PATCH 08/10] BE: Send refresh requests in batches As we extend the background refresh into larger domains, the amount of data that SSSD refreshes on the background might be larger. And refreshing all expired entries in a single request might block sssd_be for a long time, either triggering the watchdog or starving other legitimate requests. Therefore the background refresh will be done in batches of 200 entries. The first batch of every type (up to 200 users, up to 200 groups, ...) will be scheduled imediatelly and subsequent batches with a 0.5 second delay. Related: https://pagure.io/SSSD/sssd/issue/4012 --- src/providers/be_refresh.c | 130 ++++++++++++++++++++++---- src/tests/cmocka/test_expire_common.c | 6 +- src/tests/sss_idmap-tests.c | 8 +- src/util/util.h | 8 ++ 4 files changed, 127 insertions(+), 25 deletions(-) diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c index 8ecbaf14ae..c84b69975c 100644 --- a/src/providers/be_refresh.c +++ b/src/providers/be_refresh.c @@ -205,8 +205,21 @@ struct be_refresh_state { struct sss_domain_info *domain; enum be_refresh_type index; time_t period; + + char **refresh_values; + size_t refresh_val_size; + size_t refresh_index; + + size_t batch_size; + char **refresh_batch; }; +static errno_t be_refresh_batch_step(struct tevent_req *req, + uint32_t msec_delay); +static void be_refresh_batch_step_wakeup(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt); static errno_t be_refresh_step(struct tevent_req *req); static void be_refresh_done(struct tevent_req *subreq); @@ -237,6 +250,13 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx, goto immediately; } + state->batch_size = 200; + state->refresh_batch = talloc_zero_array(state, char *, state->batch_size+1); + if (state->refresh_batch == NULL) { + ret = ENOMEM; + goto immediately; + } + ret = be_refresh_step(req); if (ret == EOK) { goto immediately; @@ -262,8 +282,6 @@ struct tevent_req *be_refresh_send(TALLOC_CTX *mem_ctx, static errno_t be_refresh_step(struct tevent_req *req) { struct be_refresh_state *state = NULL; - struct tevent_req *subreq = NULL; - char **values = NULL; errno_t ret; state = tevent_req_data(req, struct be_refresh_state); @@ -290,42 +308,102 @@ static errno_t be_refresh_step(struct tevent_req *req) goto done; } + talloc_zfree(state->refresh_values); ret = be_refresh_get_values(state, state->index, state->ctx->attr_name, - state->domain, state->period, &values); + state->domain, state->period, + &state->refresh_values); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to obtain DN list [%d]: %s\n", ret, sss_strerror(ret)); goto done; } - DEBUG(SSSDBG_TRACE_FUNC, "Refreshing %s in domain %s\n", - state->cb->name, state->domain->name); + for (state->refresh_val_size = 0; + state->refresh_values[state->refresh_val_size] != NULL; + state->refresh_val_size++); + + DEBUG(SSSDBG_TRACE_FUNC, "Refreshing %zu %s in domain %s\n", + state->refresh_val_size, state->cb->name, state->domain->name); - subreq = state->cb->send_fn(state, state->ev, state->be_ctx, - state->domain, values, state->cb->pvt); - if (subreq == NULL) { - ret = ENOMEM; + ret = be_refresh_batch_step(req, 0); + if (ret == EOK) { + state->index++; + continue; + } else if (ret != EAGAIN) { goto done; } - - /* make the list disappear with subreq */ - talloc_steal(subreq, values); - - tevent_req_set_callback(subreq, be_refresh_done, req); + /* EAGAIN only, refreshing something.. */ state->index++; - ret = EAGAIN; goto done; } ret = EOK; done: - if (ret != EOK && ret != EAGAIN) { - talloc_free(values); + return ret; +} + +static errno_t be_refresh_batch_step(struct tevent_req *req, + uint32_t msec_delay) +{ + struct be_refresh_state *state = tevent_req_data(req, struct be_refresh_state); + struct timeval tv; + struct tevent_timer *timeout = NULL; + + size_t remaining; + size_t batch_size; + + memset(state->refresh_batch, 0, sizeof(char *) * state->batch_size); + + if (state->refresh_index >= state->refresh_val_size) { + DEBUG(SSSDBG_FUNC_DATA, "The batch is done\n"); + return EOK; } - return ret; + remaining = state->refresh_val_size - state->refresh_index; + batch_size = MIN(remaining, state->batch_size); + DEBUG(SSSDBG_FUNC_DATA, + "This batch will refresh %zu entries (so far %zu/%zu)\n", + batch_size, state->refresh_index, state->refresh_val_size); + + for (size_t i = 0; i < batch_size; i++) { + state->refresh_batch[i] = state->refresh_values[state->refresh_index]; + state->refresh_index++; + } + + tv = tevent_timeval_current_ofs(0, msec_delay * 1000); + timeout = tevent_add_timer(state->be_ctx->ev, req, tv, + be_refresh_batch_step_wakeup, req); + if (timeout == NULL) { + return ENOMEM; + } + + return EAGAIN; +} + +static void be_refresh_batch_step_wakeup(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, + void *pvt) +{ + struct tevent_req *req; + struct tevent_req *subreq = NULL; + struct be_refresh_state *state = NULL; + + req = talloc_get_type(pvt, struct tevent_req); + state = tevent_req_data(req, struct be_refresh_state); + + DEBUG(SSSDBG_TRACE_INTERNAL, "Issuing refresh\n"); + subreq = state->cb->send_fn(state, state->ev, state->be_ctx, + state->domain, + state->refresh_batch, + state->cb->pvt); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, be_refresh_done, req); } static void be_refresh_done(struct tevent_req *subreq) @@ -343,8 +421,24 @@ static void be_refresh_done(struct tevent_req *subreq) goto done; } + ret = be_refresh_batch_step(req, 500); + if (ret == EAGAIN) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Another batch in this step in progress\n"); + return; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "be_refresh_batch_step failed [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "All batches in this step refreshed\n"); + + /* Proceed to the next step */ ret = be_refresh_step(req); if (ret == EAGAIN) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Another step in progress\n"); return; } diff --git a/src/tests/cmocka/test_expire_common.c b/src/tests/cmocka/test_expire_common.c index 5d3ea02f3e..4f61681904 100644 --- a/src/tests/cmocka/test_expire_common.c +++ b/src/tests/cmocka/test_expire_common.c @@ -32,7 +32,7 @@ #include "tests/common_check.h" #include "tests/cmocka/test_expire_common.h" -#define MAX 100 +#define MAX_VAL 100 static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s) { @@ -41,10 +41,10 @@ static char *now_str(TALLOC_CTX *mem_ctx, const char* format, int s) size_t len; char *timestr; - timestr = talloc_array(mem_ctx, char, MAX); + timestr = talloc_array(mem_ctx, char, MAX_VAL); tm = gmtime(&t); - len = strftime(timestr, MAX, format, tm); + len = strftime(timestr, MAX_VAL, format, tm); if (len == 0) { return NULL; } diff --git a/src/tests/sss_idmap-tests.c b/src/tests/sss_idmap-tests.c index 96f0861ac6..e5f3f70416 100644 --- a/src/tests/sss_idmap-tests.c +++ b/src/tests/sss_idmap-tests.c @@ -140,8 +140,8 @@ void idmap_add_domain_with_sec_slices_setup_cb_fail(void) } -#define MAX 1000 -char data[MAX]; +#define DATA_MAX 1000 +char data[DATA_MAX]; enum idmap_error_code cb2(const char *dom_name, const char *dom_sid, @@ -154,10 +154,10 @@ enum idmap_error_code cb2(const char *dom_name, char *p = (char*)pvt; size_t len; - len = snprintf(p, MAX, "%s, %s %s, %"PRIu32", %"PRIu32", %" PRIu32, + len = snprintf(p, DATA_MAX, "%s, %s %s, %"PRIu32", %"PRIu32", %" PRIu32, dom_name, dom_sid, range_id, min_id, max_id, first_rid); - if (len >= MAX) { + if (len >= DATA_MAX) { return IDMAP_OUT_OF_MEMORY; } return IDMAP_SUCCESS; diff --git a/src/util/util.h b/src/util/util.h index c5680d89ae..13e434b627 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -67,6 +67,14 @@ #define NULL 0 #endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + #define SSSD_MAIN_OPTS SSSD_DEBUG_OPTS #define SSSD_SERVER_OPTS(uid, gid) \ From 11a3c65d3bfa9980e7dddd2c7a27b2be9f982dc6 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Wed, 24 Apr 2019 20:52:11 +0200 Subject: [PATCH 09/10] AD: Implement background refresh for AD domains Split out the actual useful functionality from the AD account handler into a tevent request. This tevent request is then subsequently used by a new ad_refresh module. Related: https://pagure.io/SSSD/sssd/issue/4012 --- Makefile.am | 5 +- src/providers/ad/ad_common.h | 3 + src/providers/ad/ad_id.c | 139 +++++++++++++---- src/providers/ad/ad_id.h | 10 ++ src/providers/ad/ad_init.c | 2 +- src/providers/ad/ad_refresh.c | 283 ++++++++++++++++++++++++++++++++++ 6 files changed, 410 insertions(+), 32 deletions(-) create mode 100644 src/providers/ad/ad_refresh.c diff --git a/Makefile.am b/Makefile.am index 9614334643..f09991e97d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4500,7 +4500,10 @@ libsss_ad_la_SOURCES = \ src/providers/ad/ad_gpo_ndr.c \ src/providers/ad/ad_srv.c \ src/providers/ad/ad_subdomains.c \ - src/providers/ad/ad_domain_info.c + src/providers/ad/ad_domain_info.c \ + src/providers/ad/ad_refresh.c \ + $(NULL) + if BUILD_SUDO libsss_ad_la_SOURCES += \ diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h index 39be3028fb..057303ca96 100644 --- a/src/providers/ad/ad_common.h +++ b/src/providers/ad/ad_common.h @@ -219,4 +219,7 @@ errno_t netlogon_get_domain_info(TALLOC_CTX *mem_ctx, char **_site, char **_forest); +errno_t ad_refresh_init(struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx); + #endif /* AD_COMMON_H_ */ diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index c3bda16626..9946f66f33 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -360,44 +360,36 @@ get_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx, return clist; } -struct ad_account_info_handler_state { - struct sss_domain_info *domain; - struct dp_reply_std reply; +struct ad_account_info_state { + const char *err_msg; + int dp_error; }; -static void ad_account_info_handler_done(struct tevent_req *subreq); +static void ad_account_info_done(struct tevent_req *subreq); struct tevent_req * -ad_account_info_handler_send(TALLOC_CTX *mem_ctx, - struct ad_id_ctx *id_ctx, - struct dp_id_data *data, - struct dp_req_params *params) +ad_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_id_data *data) { - struct ad_account_info_handler_state *state; - struct sdap_id_conn_ctx **clist; - struct sdap_id_ctx *sdap_id_ctx; - struct sss_domain_info *domain; + struct sss_domain_info *domain = NULL; + struct ad_account_info_state *state = NULL; + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct sdap_id_conn_ctx **clist = NULL; + struct sdap_id_ctx *sdap_id_ctx = NULL; struct sdap_domain *sdom; - struct tevent_req *subreq; - struct tevent_req *req; - struct be_ctx *be_ctx; errno_t ret; - sdap_id_ctx = id_ctx->sdap_id_ctx; - be_ctx = params->be_ctx; - req = tevent_req_create(mem_ctx, &state, - struct ad_account_info_handler_state); + struct ad_account_info_state); if (req == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); return NULL; } - if (sdap_is_enum_request(data)) { - DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); - ret = EOK; - goto immediately; - } + sdap_id_ctx = id_ctx->sdap_id_ctx; domain = be_ctx->domain; if (strcasecmp(data->domain, be_ctx->domain->name) != 0) { @@ -406,6 +398,7 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, } if (domain == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain\n"); ret = EINVAL; goto immediately; } @@ -413,6 +406,7 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, /* Determine whether to connect to GC, LDAP or try both. */ clist = get_conn_list(state, id_ctx, domain, data); if (clist == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create conn list\n"); ret = EIO; goto immediately; } @@ -423,14 +417,99 @@ ad_account_info_handler_send(TALLOC_CTX *mem_ctx, goto immediately; } - state->domain = sdom->dom; - subreq = ad_handle_acct_info_send(state, data, sdap_id_ctx, id_ctx->ad_options, sdom, clist); if (subreq == NULL) { ret = ENOMEM; goto immediately; } + tevent_req_set_callback(subreq, ad_account_info_done, req); + return req; + +immediately: + tevent_req_error(req, ret); + tevent_req_post(req, be_ctx->ev); + return req; +} + +static void ad_account_info_done(struct tevent_req *subreq) +{ + struct ad_account_info_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_account_info_state); + + ret = ad_handle_acct_info_recv(subreq, &state->dp_error, &state->err_msg); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "ad_handle_acct_info_recv failed [%d]: %s\n", + ret, sss_strerror(ret)); + /* The caller wouldn't fail either, just report the error up */ + } + talloc_zfree(subreq); + tevent_req_done(req); +} + +errno_t ad_account_info_recv(struct tevent_req *req, + int *_dp_error, + const char **_err_msg) +{ + struct ad_account_info_state *state = NULL; + + state = tevent_req_data(req, struct ad_account_info_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_err_msg != NULL) { + *_err_msg = state->err_msg; + } + + if (_dp_error) { + *_dp_error = state->dp_error; + } + + return EOK; +} + +struct ad_account_info_handler_state { + struct sss_domain_info *domain; + struct dp_reply_std reply; +}; + +static void ad_account_info_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ad_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct ad_id_ctx *id_ctx, + struct dp_id_data *data, + struct dp_req_params *params) +{ + struct ad_account_info_handler_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + + req = tevent_req_create(mem_ctx, &state, + struct ad_account_info_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + if (sdap_is_enum_request(data)) { + DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); + ret = EOK; + goto immediately; + } + + subreq = ad_account_info_send(state, params->be_ctx, id_ctx, data); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } tevent_req_set_callback(subreq, ad_account_info_handler_done, req); @@ -451,13 +530,13 @@ static void ad_account_info_handler_done(struct tevent_req *subreq) struct ad_account_info_handler_state *state; struct tevent_req *req; const char *err_msg; - int dp_error; + int dp_error = DP_ERR_FATAL; errno_t ret; req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ad_account_info_handler_state); - ret = ad_handle_acct_info_recv(subreq, &dp_error, &err_msg); + ret = ad_account_info_recv(subreq, &dp_error, &err_msg); talloc_zfree(subreq); /* TODO For backward compatibility we always return EOK to DP now. */ @@ -466,8 +545,8 @@ static void ad_account_info_handler_done(struct tevent_req *subreq) } errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx, - struct tevent_req *req, - struct dp_reply_std *data) + struct tevent_req *req, + struct dp_reply_std *data) { struct ad_account_info_handler_state *state = NULL; diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h index 5154393c5f..19cc54eec9 100644 --- a/src/providers/ad/ad_id.h +++ b/src/providers/ad/ad_id.h @@ -33,6 +33,16 @@ errno_t ad_account_info_handler_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, struct dp_reply_std *data); +struct tevent_req * +ad_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx, + struct dp_id_data *data); + +errno_t ad_account_info_recv(struct tevent_req *req, + int *_dp_error, + const char **_err_msg); + struct tevent_req * ad_handle_acct_info_send(TALLOC_CTX *mem_ctx, struct dp_id_data *ar, diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 7de2da6699..38e6a38e41 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -408,7 +408,7 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx, return ret; } - ret = sdap_refresh_init(be_ctx, sdap_id_ctx); + ret = ad_refresh_init(be_ctx, ad_id_ctx); if (ret != EOK && ret != EEXIST) { DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " "will not work [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/ad/ad_refresh.c b/src/providers/ad/ad_refresh.c new file mode 100644 index 0000000000..ee541056fe --- /dev/null +++ b/src/providers/ad/ad_refresh.c @@ -0,0 +1,283 @@ +/* + Copyright (C) 2019 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 <talloc.h> +#include <tevent.h> + +#include "providers/ad/ad_common.h" +#include "providers/ad/ad_id.h" + +struct ad_refresh_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct dp_id_data *account_req; + struct ad_id_ctx *id_ctx; + char **names; + size_t index; +}; + +static errno_t ad_refresh_step(struct tevent_req *req); +static void ad_refresh_done(struct tevent_req *subreq); + +static struct tevent_req *ad_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + int entry_type, + char **names, + void *pvt) +{ + struct ad_refresh_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + uint32_t filter_type; + + req = tevent_req_create(mem_ctx, &state, + struct ad_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + if (names == NULL) { + ret = EOK; + goto immediately; + } + + state->ev = ev; + state->be_ctx = be_ctx; + state->id_ctx = talloc_get_type(pvt, struct ad_id_ctx); + state->names = names; + state->index = 0; + + switch (entry_type) { + case BE_REQ_NETGROUP: + filter_type = BE_FILTER_NAME; + break; + case BE_REQ_USER: + case BE_REQ_GROUP: + filter_type = BE_FILTER_SECID; + break; + default: + ret = EINVAL; + goto immediately; + } + + state->account_req = be_refresh_acct_req(state, entry_type, + filter_type, domain); + if (state->account_req == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = ad_refresh_step(req); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n"); + goto immediately; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_CRIT_FAILURE, "ad_refresh_step() failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + 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; +} + +static errno_t ad_refresh_step(struct tevent_req *req) +{ + struct ad_refresh_state *state = NULL; + struct tevent_req *subreq = NULL; + errno_t ret; + + state = tevent_req_data(req, struct ad_refresh_state); + + if (state->names == NULL) { + ret = EOK; + goto done; + } + + state->account_req->filter_value = state->names[state->index]; + if (state->account_req->filter_value == NULL) { + ret = EOK; + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Issuing refresh of %s %s\n", + be_req2str(state->account_req->entry_type), + state->account_req->filter_value); + + subreq = ad_account_info_send(state, state->be_ctx, state->id_ctx, + state->account_req); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ad_refresh_done, req); + + state->index++; + ret = EAGAIN; + +done: + return ret; +} + +static void ad_refresh_done(struct tevent_req *subreq) +{ + struct ad_refresh_state *state = NULL; + struct tevent_req *req = NULL; + const char *err_msg = NULL; + errno_t dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_refresh_state); + + ret = ad_account_info_recv(subreq, &dp_error, &err_msg); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, " + "errno: %d]: %s\n", be_req2str(state->account_req->entry_type), + dp_error, ret, err_msg); + goto done; + } + + ret = ad_refresh_step(req); + if (ret == EAGAIN) { + return; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t ad_refresh_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static struct tevent_req * +ad_refresh_users_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ad_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_USER, names, pvt); +} + +static errno_t ad_refresh_users_recv(struct tevent_req *req) +{ + return ad_refresh_recv(req); +} + +static struct tevent_req * +ad_refresh_groups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ad_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_GROUP, names, pvt); +} + +static errno_t ad_refresh_groups_recv(struct tevent_req *req) +{ + return ad_refresh_recv(req); +} + +static struct tevent_req * +ad_refresh_netgroups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ad_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_NETGROUP, names, pvt); +} + +static errno_t ad_refresh_netgroups_recv(struct tevent_req *req) +{ + return ad_refresh_recv(req); +} + +errno_t ad_refresh_init(struct be_ctx *be_ctx, + struct ad_id_ctx *id_ctx) +{ + errno_t ret; + + ret = be_refresh_ctx_init(be_ctx, SYSDB_SID_STR); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); + return ret; + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, + ad_refresh_users_send, + ad_refresh_users_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of users " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_GROUPS, + ad_refresh_groups_send, + ad_refresh_groups_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of groups " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_NETGROUPS, + ad_refresh_netgroups_send, + ad_refresh_netgroups_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of netgroups " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + return ret; +} From 790b04f16d16f168a1d208acc31ada08cb7d531a Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhro...@redhat.com> Date: Wed, 8 May 2019 14:39:23 +0200 Subject: [PATCH 10/10] IPA: Implement background refresh for IPA domains Split out the actual useful functionality from the IPA account lookup handler into a tevent request. This tevent request is then used in a new ipa_refresh module. Related: https://pagure.io/SSSD/sssd/issue/4012 --- Makefile.am | 1 + src/providers/ipa/ipa_common.h | 3 + src/providers/ipa/ipa_id.c | 140 +++++++++++++---- src/providers/ipa/ipa_id.h | 8 + src/providers/ipa/ipa_init.c | 2 +- src/providers/ipa/ipa_refresh.c | 264 ++++++++++++++++++++++++++++++++ 6 files changed, 386 insertions(+), 32 deletions(-) create mode 100644 src/providers/ipa/ipa_refresh.c diff --git a/Makefile.am b/Makefile.am index f09991e97d..c44ae2bf96 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4428,6 +4428,7 @@ libsss_ipa_la_SOURCES = \ src/providers/ipa/ipa_srv.c \ src/providers/ipa/ipa_idmap.c \ src/providers/ipa/ipa_dn.c \ + src/providers/ipa/ipa_refresh.c \ src/providers/ad/ad_opts.c \ src/providers/ad/ad_common.c \ src/providers/ad/ad_dyndns.c \ diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 31e671eb50..6bb1739efb 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -301,4 +301,7 @@ errno_t ipa_get_host_attrs(struct dp_option *ipa_options, struct sysdb_attrs **hosts, struct sysdb_attrs **_ipa_host); +errno_t ipa_refresh_init(struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx); + #endif /* _IPA_COMMON_H_ */ diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c index e644af5ffc..9abee34cb4 100644 --- a/src/providers/ipa/ipa_id.c +++ b/src/providers/ipa/ipa_id.c @@ -1344,43 +1344,39 @@ ipa_decide_account_info_type(struct dp_id_data *data, struct be_ctx *be_ctx) return IPA_ACCOUNT_INFO_OTHER; } -struct ipa_account_info_handler_state { +struct ipa_account_info_state { enum ipa_account_info_type type; - struct dp_reply_std reply; + + const char *err_msg; + int dp_error; }; -static void ipa_account_info_handler_done(struct tevent_req *subreq); +static void ipa_account_info_done(struct tevent_req *subreq); struct tevent_req * -ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, - struct ipa_id_ctx *id_ctx, - struct dp_id_data *data, - struct dp_req_params *params) +ipa_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_id_data *data) { - struct ipa_account_info_handler_state *state; + struct ipa_account_info_state *state = NULL; + struct tevent_req *req = NULL; struct tevent_req *subreq = NULL; - struct tevent_req *req; errno_t ret; req = tevent_req_create(mem_ctx, &state, - struct ipa_account_info_handler_state); + struct ipa_account_info_state); if (req == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); return NULL; } - state->type = ipa_decide_account_info_type(data, params->be_ctx); - - if (sdap_is_enum_request(data)) { - DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); - ret = EOK; - goto immediately; - } + state->type = ipa_decide_account_info_type(data, be_ctx); switch (state->type) { case IPA_ACCOUNT_INFO_SUBDOMAIN: /* Subdomain lookups are handled differently on server and client. */ - subreq = ipa_subdomain_account_send(state, params->ev, id_ctx, data); + subreq = ipa_subdomain_account_send(state, be_ctx->ev, id_ctx, data); break; case IPA_ACCOUNT_INFO_NETGROUP: if (data->filter_type != BE_FILTER_NAME) { @@ -1388,11 +1384,11 @@ ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, goto immediately; } - subreq = ipa_id_get_netgroup_send(state, params->ev, id_ctx, + subreq = ipa_id_get_netgroup_send(state, be_ctx->ev, id_ctx, data->filter_value); break; case IPA_ACCOUNT_INFO_OTHER: - subreq = ipa_id_get_account_info_send(state, params->ev, id_ctx, data); + subreq = ipa_id_get_account_info_send(state, be_ctx->ev, id_ctx, data); break; } @@ -1400,7 +1396,99 @@ ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, ret = ENOMEM; goto immediately; } + tevent_req_set_callback(subreq, ipa_account_info_done, req); + return req; + +immediately: + tevent_req_error(req, ret); + tevent_req_post(req, be_ctx->ev); + return req; +} + +static void ipa_account_info_done(struct tevent_req *subreq) +{ + struct ipa_account_info_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_account_info_state); + + switch (state->type) { + case IPA_ACCOUNT_INFO_SUBDOMAIN: + ret = ipa_subdomain_account_recv(subreq, &state->dp_error); + break; + case IPA_ACCOUNT_INFO_NETGROUP: + ret = ipa_id_get_netgroup_recv(subreq, &state->dp_error); + break; + case IPA_ACCOUNT_INFO_OTHER: + ret = ipa_id_get_account_info_recv(subreq, &state->dp_error); + break; + default: + ret = EINVAL; + break; + } + talloc_zfree(subreq); + + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} +errno_t ipa_account_info_recv(struct tevent_req *req, + int *_dp_error) +{ + struct ipa_account_info_state *state = NULL; + + state = tevent_req_data(req, struct ipa_account_info_state); + + /* Fail the request after collecting the dp_error */ + if (_dp_error) { + *_dp_error = state->dp_error; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct ipa_account_info_handler_state { + struct dp_reply_std reply; +}; + +static void ipa_account_info_handler_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_id_data *data, + struct dp_req_params *params) +{ + struct ipa_account_info_handler_state *state; + struct tevent_req *subreq = NULL; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_account_info_handler_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + if (sdap_is_enum_request(data)) { + DEBUG(SSSDBG_TRACE_LIBS, "Skipping enumeration on demand\n"); + ret = EOK; + goto immediately; + } + + subreq = ipa_account_info_send(state, params->be_ctx, id_ctx, data); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } tevent_req_set_callback(subreq, ipa_account_info_handler_done, req); return req; @@ -1425,17 +1513,7 @@ static void ipa_account_info_handler_done(struct tevent_req *subreq) req = tevent_req_callback_data(subreq, struct tevent_req); state = tevent_req_data(req, struct ipa_account_info_handler_state); - switch (state->type) { - case IPA_ACCOUNT_INFO_SUBDOMAIN: - ret = ipa_subdomain_account_recv(subreq, &dp_error); - break; - case IPA_ACCOUNT_INFO_NETGROUP: - ret = ipa_id_get_netgroup_recv(subreq, &dp_error); - break; - case IPA_ACCOUNT_INFO_OTHER: - ret = ipa_id_get_account_info_recv(subreq, &dp_error); - break; - } + ret = ipa_account_info_recv(subreq, &dp_error); talloc_zfree(subreq); /* TODO For backward compatibility we always return EOK to DP now. */ diff --git a/src/providers/ipa/ipa_id.h b/src/providers/ipa/ipa_id.h index 4b25498821..fe9acfeef0 100644 --- a/src/providers/ipa/ipa_id.h +++ b/src/providers/ipa/ipa_id.h @@ -33,6 +33,14 @@ #define IPA_DEFAULT_VIEW_NAME "Default Trust View" +struct tevent_req * +ipa_account_info_send(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx, + struct dp_id_data *data); +errno_t ipa_account_info_recv(struct tevent_req *req, + int *_dp_error); + struct tevent_req * ipa_account_info_handler_send(TALLOC_CTX *mem_ctx, struct ipa_id_ctx *id_ctx, diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c index b3060e228c..cdfd11d7ae 100644 --- a/src/providers/ipa/ipa_init.c +++ b/src/providers/ipa/ipa_init.c @@ -594,7 +594,7 @@ static errno_t ipa_init_misc(struct be_ctx *be_ctx, } } - ret = sdap_refresh_init(be_ctx, sdap_id_ctx); + ret = ipa_refresh_init(be_ctx, ipa_id_ctx); if (ret != EOK && ret != EEXIST) { DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh " "will not work [%d]: %s\n", ret, sss_strerror(ret)); diff --git a/src/providers/ipa/ipa_refresh.c b/src/providers/ipa/ipa_refresh.c new file mode 100644 index 0000000000..72051cfdd9 --- /dev/null +++ b/src/providers/ipa/ipa_refresh.c @@ -0,0 +1,264 @@ +/* + Copyright (C) 2019 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 <talloc.h> +#include <tevent.h> + +#include "providers/ipa/ipa_common.h" +#include "providers/ipa/ipa_id.h" + +struct ipa_refresh_state { + struct tevent_context *ev; + struct be_ctx *be_ctx; + struct dp_id_data *account_req; + struct ipa_id_ctx *id_ctx; + char **names; + size_t index; +}; + +static errno_t ipa_refresh_step(struct tevent_req *req); +static void ipa_refresh_done(struct tevent_req *subreq); + +static struct tevent_req *ipa_refresh_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + int entry_type, + char **names, + void *pvt) +{ + struct ipa_refresh_state *state = NULL; + struct tevent_req *req = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_refresh_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; + } + + if (names == NULL) { + ret = EOK; + goto immediately; + } + + state->ev = ev; + state->be_ctx = be_ctx; + state->id_ctx = talloc_get_type(pvt, struct ipa_id_ctx); + state->names = names; + state->index = 0; + + state->account_req = be_refresh_acct_req(state, entry_type, + BE_FILTER_NAME, domain); + if (state->account_req == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = ipa_refresh_step(req); + if (ret == EOK) { + DEBUG(SSSDBG_TRACE_FUNC, "Nothing to refresh\n"); + goto immediately; + } else if (ret != EAGAIN) { + DEBUG(SSSDBG_CRIT_FAILURE, "ipa_refresh_step() failed " + "[%d]: %s\n", ret, sss_strerror(ret)); + 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; +} + +static errno_t ipa_refresh_step(struct tevent_req *req) +{ + struct ipa_refresh_state *state = NULL; + struct tevent_req *subreq = NULL; + errno_t ret; + + state = tevent_req_data(req, struct ipa_refresh_state); + + if (state->names == NULL) { + ret = EOK; + goto done; + } + + state->account_req->filter_value = state->names[state->index]; + if (state->account_req->filter_value == NULL) { + ret = EOK; + goto done; + } + + subreq = ipa_account_info_send(state, state->be_ctx, state->id_ctx, + state->account_req); + if (subreq == NULL) { + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ipa_refresh_done, req); + + state->index++; + ret = EAGAIN; + +done: + return ret; +} + +static void ipa_refresh_done(struct tevent_req *subreq) +{ + struct ipa_refresh_state *state = NULL; + struct tevent_req *req = NULL; + errno_t dp_error; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ipa_refresh_state); + + ret = ipa_account_info_recv(subreq, &dp_error); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh %s [dp_error: %d, " + "errno: %d]\n", be_req2str(state->account_req->entry_type), + dp_error, ret); + goto done; + } + + ret = ipa_refresh_step(req); + if (ret == EAGAIN) { + return; + } + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t ipa_refresh_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static struct tevent_req * +ipa_refresh_users_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ipa_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_USER, names, pvt); +} + +static errno_t ipa_refresh_users_recv(struct tevent_req *req) +{ + return ipa_refresh_recv(req); +} + +static struct tevent_req * +ipa_refresh_groups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ipa_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_GROUP, names, pvt); +} + +static errno_t ipa_refresh_groups_recv(struct tevent_req *req) +{ + return ipa_refresh_recv(req); +} + +static struct tevent_req * +ipa_refresh_netgroups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_ctx *be_ctx, + struct sss_domain_info *domain, + char **names, + void *pvt) +{ + return ipa_refresh_send(mem_ctx, ev, be_ctx, domain, + BE_REQ_NETGROUP, names, pvt); +} + +static errno_t ipa_refresh_netgroups_recv(struct tevent_req *req) +{ + return ipa_refresh_recv(req); +} + +errno_t ipa_refresh_init(struct be_ctx *be_ctx, + struct ipa_id_ctx *id_ctx) +{ + errno_t ret; + + ret = be_refresh_ctx_init(be_ctx, SYSDB_NAME); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize refresh_ctx\n"); + return ENOMEM; + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_USERS, + ipa_refresh_users_send, + ipa_refresh_users_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of users " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_GROUPS, + ipa_refresh_groups_send, + ipa_refresh_groups_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of groups " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + ret = be_refresh_add_cb(be_ctx->refresh_ctx, + BE_REFRESH_TYPE_NETGROUPS, + ipa_refresh_netgroups_send, + ipa_refresh_netgroups_recv, + id_ctx); + if (ret != EOK && ret != EEXIST) { + DEBUG(SSSDBG_MINOR_FAILURE, "Periodical refresh of netgroups " + "will not work [%d]: %s\n", ret, strerror(ret)); + } + + return ret; +}
_______________________________________________ sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org