sdap_id_op.c revision 772764e048dcd15c6d9732574126eb83b53a60e2
842ae4bd224140319ae7feec1872b93dfd491143fielding/*
842ae4bd224140319ae7feec1872b93dfd491143fielding SSSD
842ae4bd224140319ae7feec1872b93dfd491143fielding
842ae4bd224140319ae7feec1872b93dfd491143fielding LDAP ID backend operation retry logic and connection cache
842ae4bd224140319ae7feec1872b93dfd491143fielding
842ae4bd224140319ae7feec1872b93dfd491143fielding Authors:
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding Eugene Indenbom <eindenbom@gmail.com>
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding Copyright (C) 2008-2010 Red Hat
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd This program is free software; you can redistribute it and/or modify
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd it under the terms of the GNU General Public License as published by
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd the Free Software Foundation; either version 3 of the License, or
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd (at your option) any later version.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding This program is distributed in the hope that it will be useful,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding but WITHOUT ANY WARRANTY; without even the implied warranty of
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding GNU General Public License for more details.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
b6055b7832a0e4d0818416252fff5925aaebae4brbb You should have received a copy of the GNU General Public License
2d71630471d1c23f0137309e3c3957c633ecbfd6rbb along with this program. If not, see <http://www.gnu.org/licenses/>.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding*/
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding#include "providers/ldap/ldap_common.h"
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding#include "providers/ldap/sdap_async.h"
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding#include "providers/ldap/sdap_id_op.h"
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding/* LDAP async connection cache */
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstruct sdap_id_conn_cache {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_ctx *id_ctx;
3d96ee83babeec32482c9082c9426340cee8c44dwrowe
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* list of all open connections */
2d84861186d70e5396751ba308bb90c8a7db47acnd struct sdap_id_conn_data *connections;
2d84861186d70e5396751ba308bb90c8a7db47acnd /* cached (current) connection */
2d84861186d70e5396751ba308bb90c8a7db47acnd struct sdap_id_conn_data *cached_connection;
2d84861186d70e5396751ba308bb90c8a7db47acnd};
2d84861186d70e5396751ba308bb90c8a7db47acnd
2d84861186d70e5396751ba308bb90c8a7db47acnd/* LDAP async operation tracker:
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * - keeps track of connection usage
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * - keeps track of operation retries */
2d84861186d70e5396751ba308bb90c8a7db47acndstruct sdap_id_op {
e991c6fc032c59eb6cb751d9d382e933a53a2866niq /* ID backend context */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_conn_cache *conn_cache;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* double linked list pointers */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_op *prev, *next;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* current connection */
4f9a74ad7e44b0464f7cf56525a205d788becacbtrawick struct sdap_id_conn_data *conn_data;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* number of reconnects for this operation */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding int reconnect_retry_count;
b6d2c204c150843e48f6787c1090ae75b718896ecovener /* connection request
b6d2c204c150843e48f6787c1090ae75b718896ecovener * It is required as we need to know which requests to notify
7076b40ea4800c8f91c4c0948f9c98c1bacbe96crpluem * when shared connection request to sdap_handle completes.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * This member is cleared when sdap_id_op_connect_state
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron * associated with request is destroyed */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct tevent_req *connect_req;
b6d2c204c150843e48f6787c1090ae75b718896ecovener};
b6d2c204c150843e48f6787c1090ae75b718896ecovener
7076b40ea4800c8f91c4c0948f9c98c1bacbe96crpluem/* LDAP connection cache connection attempt/established connection data */
7076b40ea4800c8f91c4c0948f9c98c1bacbe96crpluemstruct sdap_id_conn_data {
b6d2c204c150843e48f6787c1090ae75b718896ecovener /* LDAP connection cache */
b6d2c204c150843e48f6787c1090ae75b718896ecovener struct sdap_id_conn_cache *conn_cache;
b6d2c204c150843e48f6787c1090ae75b718896ecovener /* double linked list pointers */
7076b40ea4800c8f91c4c0948f9c98c1bacbe96crpluem struct sdap_id_conn_data *prev, *next;
b6d2c204c150843e48f6787c1090ae75b718896ecovener /* sdap handle */
b94e9fbfc855aa89c2a7340c5ca9da5d1cb9f7e3covener struct sdap_handle *sh;
b6d2c204c150843e48f6787c1090ae75b718896ecovener /* connection request */
b6d2c204c150843e48f6787c1090ae75b718896ecovener struct tevent_req *connect_req;
b6d2c204c150843e48f6787c1090ae75b718896ecovener /* timer for connection expiration */
b6d2c204c150843e48f6787c1090ae75b718896ecovener struct tevent_timer *expire_timer;
63de18ba5e922ffaab500317d7d1d0ad6b27b7e2covener /* number of running connection notifies */
b6d2c204c150843e48f6787c1090ae75b718896ecovener int notify_lock;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* list of operations using connect */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_op *ops;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding};
2d84861186d70e5396751ba308bb90c8a7db47acnd
2d84861186d70e5396751ba308bb90c8a7db47acndstatic void sdap_id_conn_cache_be_offline_cb(void *pvt);
2d84861186d70e5396751ba308bb90c8a7db47acnd
2d84861186d70e5396751ba308bb90c8a7db47acndstatic void sdap_id_release_conn_data(struct sdap_id_conn_data *conn_data);
2d84861186d70e5396751ba308bb90c8a7db47acndstatic int sdap_id_conn_data_destroy(struct sdap_id_conn_data *conn_data);
2d84861186d70e5396751ba308bb90c8a7db47acndstatic bool sdap_is_connection_expired(struct sdap_id_conn_data *conn_data, int timeout);
2d84861186d70e5396751ba308bb90c8a7db47acndstatic bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data);
2d84861186d70e5396751ba308bb90c8a7db47acndstatic void sdap_id_conn_data_expire_handler(struct tevent_context *ev,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct tevent_timer *te,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct timeval current_time,
80370e62044bea458bcd0545c59cb864ed117b04niq void *pvt);
e991c6fc032c59eb6cb751d9d382e933a53a2866niqstatic int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_data);
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
b6d2c204c150843e48f6787c1090ae75b718896ecovenerstatic void sdap_id_op_hook_conn_data(struct sdap_id_op *op, struct sdap_id_conn_data *conn_data);
4f9a74ad7e44b0464f7cf56525a205d788becacbtrawickstatic int sdap_id_op_destroy(void *pvt);
2d84861186d70e5396751ba308bb90c8a7db47acndstatic bool sdap_id_op_can_reconnect(struct sdap_id_op *op);
2d84861186d70e5396751ba308bb90c8a7db47acnd
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic void sdap_id_op_connect_req_complete(struct sdap_id_op *op, int dp_error, int ret);
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic int sdap_id_op_connect_state_destroy(void *pvt);
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic int sdap_id_op_connect_step(struct tevent_req *req);
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic void sdap_id_op_connect_done(struct tevent_req *subreq);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron/* Create a connection cache */
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingint sdap_id_conn_cache_create(TALLOC_CTX *memctx,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_ctx *id_ctx,
2d84861186d70e5396751ba308bb90c8a7db47acnd struct sdap_id_conn_cache** conn_cache_out)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding{
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding int ret;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_conn_cache *conn_cache = talloc_zero(memctx, struct sdap_id_conn_cache);
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm if (!conn_cache) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding DEBUG(1, ("talloc_zero(struct sdap_id_conn_cache) failed.\n"));
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron ret = ENOMEM;
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron goto fail;
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron }
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding conn_cache->id_ctx = id_ctx;
2d84861186d70e5396751ba308bb90c8a7db47acnd
2d84861186d70e5396751ba308bb90c8a7db47acnd ret = be_add_offline_cb(conn_cache, id_ctx->be,
e991c6fc032c59eb6cb751d9d382e933a53a2866niq sdap_id_conn_cache_be_offline_cb, conn_cache,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding NULL);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (ret != EOK) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding DEBUG(1, ("be_add_offline_cb failed.\n"));
e991c6fc032c59eb6cb751d9d382e933a53a2866niq goto fail;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq }
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq *conn_cache_out = conn_cache;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq return EOK;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
1eda27e7224742a68c18c09a7f5ae233363465a4niqfail:
e991c6fc032c59eb6cb751d9d382e933a53a2866niq talloc_zfree(conn_cache);
a1efede5943e76f1fcdf10494de41704c9ba70f1niq return ret;
a1efede5943e76f1fcdf10494de41704c9ba70f1niq}
a1efede5943e76f1fcdf10494de41704c9ba70f1niq
80370e62044bea458bcd0545c59cb864ed117b04niq/* Callback on BE going offline */
a1efede5943e76f1fcdf10494de41704c9ba70f1niqstatic void sdap_id_conn_cache_be_offline_cb(void *pvt)
a1efede5943e76f1fcdf10494de41704c9ba70f1niq{
a1efede5943e76f1fcdf10494de41704c9ba70f1niq struct sdap_id_conn_cache *conn_cache = talloc_get_type(pvt, struct sdap_id_conn_cache);
a1efede5943e76f1fcdf10494de41704c9ba70f1niq struct sdap_id_conn_data *cached_connection = conn_cache->cached_connection;
a1efede5943e76f1fcdf10494de41704c9ba70f1niq
a1efede5943e76f1fcdf10494de41704c9ba70f1niq /* Release any cached connection on going offline */
a1efede5943e76f1fcdf10494de41704c9ba70f1niq if (cached_connection != NULL) {
e991c6fc032c59eb6cb751d9d382e933a53a2866niq conn_cache->cached_connection = NULL;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq sdap_id_release_conn_data(cached_connection);
e991c6fc032c59eb6cb751d9d382e933a53a2866niq }
e991c6fc032c59eb6cb751d9d382e933a53a2866niq}
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq/* Release sdap_id_conn_data and destroy it if no longer needed */
e991c6fc032c59eb6cb751d9d382e933a53a2866niqstatic void sdap_id_release_conn_data(struct sdap_id_conn_data *conn_data)
e991c6fc032c59eb6cb751d9d382e933a53a2866niq{
e991c6fc032c59eb6cb751d9d382e933a53a2866niq struct sdap_id_conn_cache *conn_cache;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq if (!conn_data || conn_data->ops || conn_data->notify_lock) {
e991c6fc032c59eb6cb751d9d382e933a53a2866niq /* connection is in use */
e991c6fc032c59eb6cb751d9d382e933a53a2866niq return;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq }
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq conn_cache = conn_data->conn_cache;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq if (conn_data == conn_cache->cached_connection) {
e991c6fc032c59eb6cb751d9d382e933a53a2866niq return;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq }
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq DEBUG(9, ("releasing unused connection\n"));
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq DLIST_REMOVE(conn_cache->connections, conn_data);
e991c6fc032c59eb6cb751d9d382e933a53a2866niq talloc_zfree(conn_data);
e991c6fc032c59eb6cb751d9d382e933a53a2866niq}
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq/* Destructor for struct sdap_id_conn_data */
e991c6fc032c59eb6cb751d9d382e933a53a2866niqstatic int sdap_id_conn_data_destroy(struct sdap_id_conn_data *conn_data)
e991c6fc032c59eb6cb751d9d382e933a53a2866niq{
e991c6fc032c59eb6cb751d9d382e933a53a2866niq struct sdap_id_op *op;
e991c6fc032c59eb6cb751d9d382e933a53a2866niq
e991c6fc032c59eb6cb751d9d382e933a53a2866niq /* we clean out list of ops to make sure that order of destruction does not matter */
e991c6fc032c59eb6cb751d9d382e933a53a2866niq while ((op = conn_data->ops) != NULL) {
e991c6fc032c59eb6cb751d9d382e933a53a2866niq op->conn_data = NULL;
0958c822c32923b9848a73b2f4c94eab07fb416drbb DLIST_REMOVE(conn_data->ops, op);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe return 0;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe}
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe/* Check whether connection will expire after timeout seconds */
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowestatic bool sdap_is_connection_expired(struct sdap_id_conn_data *conn_data, int timeout)
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe{
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe time_t expire_time;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe if (!conn_data || !conn_data->sh || !conn_data->sh->connected) {
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe return true;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe }
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe expire_time = conn_data->sh->expire_time;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe if ((expire_time != 0) && (expire_time < time( NULL ) + timeout) ) {
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe return true;
e95f5a6ebefb83d0e8a82cbc4db7d354957a817dben }
2d84861186d70e5396751ba308bb90c8a7db47acnd
2d84861186d70e5396751ba308bb90c8a7db47acnd return false;
2d84861186d70e5396751ba308bb90c8a7db47acnd}
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
e8f95a682820a599fe41b22977010636be5c2717jim/* Check whether connection can be reused for next LDAP ID operation */
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowestatic bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding{
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe int timeout;
2d84861186d70e5396751ba308bb90c8a7db47acnd
2d84861186d70e5396751ba308bb90c8a7db47acnd if (!conn_data || !conn_data->sh || !conn_data->sh->connected) {
2d84861186d70e5396751ba308bb90c8a7db47acnd return false;
2d84861186d70e5396751ba308bb90c8a7db47acnd }
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
e8f95a682820a599fe41b22977010636be5c2717jim timeout = dp_opt_get_int(conn_data->conn_cache->id_ctx->opts->basic,
e8f95a682820a599fe41b22977010636be5c2717jim SDAP_OPT_TIMEOUT);
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe return !sdap_is_connection_expired(conn_data, timeout);
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron}
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe/* Set expiration timer for connection if needed */
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowestatic int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_data)
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe{
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron int timeout;
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm struct timeval tv;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron memset(&tv, 0, sizeof(tv));
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm tv.tv_sec = conn_data->sh->expire_time;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe if (tv.tv_sec <= 0) {
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron return EOK;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe timeout = dp_opt_get_int(conn_data->conn_cache->id_ctx->opts->basic,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding SDAP_OPT_TIMEOUT);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (timeout > 0) {
c0905aec36b287200b8a255b33fba6c975752bdfgstein tv.tv_sec -= timeout;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
d54c6537f5f5a7bc54ec37ce8dbe04193605043astoddard
d54c6537f5f5a7bc54ec37ce8dbe04193605043astoddard if (tv.tv_sec <= time(NULL)) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding return EOK;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
d54c6537f5f5a7bc54ec37ce8dbe04193605043astoddard
d54c6537f5f5a7bc54ec37ce8dbe04193605043astoddard talloc_zfree(conn_data->expire_timer);
d54c6537f5f5a7bc54ec37ce8dbe04193605043astoddard
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding conn_data->expire_timer =
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding tevent_add_timer(conn_data->conn_cache->id_ctx->be->ev,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding conn_data, tv,
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe sdap_id_conn_data_expire_handler,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding conn_data);
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe if (!conn_data->expire_timer) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding return ENOMEM;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe }
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe return EOK;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe}
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe/* Handler for connection expiration timer */
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaronstatic void sdap_id_conn_data_expire_handler(struct tevent_context *ev,
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe struct tevent_timer *te,
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron struct timeval current_time,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding void *pvt)
433dcb1fbaae82d36634f5120bff71a04296904ddirkx{
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_conn_data *conn_data = talloc_get_type(pvt,
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm struct sdap_id_conn_data);
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
e8f95a682820a599fe41b22977010636be5c2717jim
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm DEBUG(3, ("connection is about to expire, releasing it\n"));
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm if (conn_cache->cached_connection == conn_data) {
e8f95a682820a599fe41b22977010636be5c2717jim conn_cache->cached_connection = NULL;
e8f95a682820a599fe41b22977010636be5c2717jim
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm sdap_id_release_conn_data(conn_data);
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm }
a4be7c7d73005dedac190ce6cb68ab4dd38383bbcolm}
eea521297270de3f9ae70d8822f8665c513de574nd
eea521297270de3f9ae70d8822f8665c513de574nd/* Create an operation object */
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowestruct sdap_id_op *sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_conn_cache *conn_cache)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding{
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_op *op = talloc_zero(memctx, struct sdap_id_op);
e8f95a682820a599fe41b22977010636be5c2717jim if (!op) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding return NULL;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron op->conn_cache = conn_cache;
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding talloc_set_destructor((void*)op, sdap_id_op_destroy);
10a4cdd68ef1ca0e54af296fe1d08ac00150c90bwrowe return op;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding}
066877f1a045103acfdd376d48cdd473c33f409bdougm
066877f1a045103acfdd376d48cdd473c33f409bdougm/* Attach/detach connection to sdap_id_op */
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaronstatic void sdap_id_op_hook_conn_data(struct sdap_id_op *op, struct sdap_id_conn_data *conn_data)
066877f1a045103acfdd376d48cdd473c33f409bdougm{
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron if (!op) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding DEBUG(0, ("NULL op passed!!!\n"));
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding return;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding struct sdap_id_conn_data *current = op->conn_data;
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe if (conn_data == current) {
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe return;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (current) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding DLIST_REMOVE(current->ops, op);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron op->conn_data = conn_data;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron if (conn_data) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding DLIST_ADD_END(conn_data->ops, op, struct sdap_id_op*);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (current) {
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe sdap_id_release_conn_data(current);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding }
3f65070bf58882ab06bfa08cc4e04c7c90ac04f8wrowe}
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding/* Destructor for sdap_id_op */
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic int sdap_id_op_destroy(void *pvt)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding{
1eda27e7224742a68c18c09a7f5ae233363465a4niq struct sdap_id_op *op = talloc_get_type(pvt, struct sdap_id_op);
1eda27e7224742a68c18c09a7f5ae233363465a4niq
1eda27e7224742a68c18c09a7f5ae233363465a4niq if (op->conn_data) {
1eda27e7224742a68c18c09a7f5ae233363465a4niq DEBUG(9, ("releasing operation connection\n"));
1eda27e7224742a68c18c09a7f5ae233363465a4niq sdap_id_op_hook_conn_data(op, NULL);
1eda27e7224742a68c18c09a7f5ae233363465a4niq }
1eda27e7224742a68c18c09a7f5ae233363465a4niq
1eda27e7224742a68c18c09a7f5ae233363465a4niq return 0;
1eda27e7224742a68c18c09a7f5ae233363465a4niq}
1eda27e7224742a68c18c09a7f5ae233363465a4niq
1eda27e7224742a68c18c09a7f5ae233363465a4niq/* Check whether retry with reconnect can be performed for the operation */
1eda27e7224742a68c18c09a7f5ae233363465a4niqstatic bool sdap_id_op_can_reconnect(struct sdap_id_op *op)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding{
73e8b26287de5c06fa470d36162e103dbac9c7e5wrowe /* we allow 2 retries for failover server configured:
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * - one for connection broken during request execution
1eda27e7224742a68c18c09a7f5ae233363465a4niq * - one for the following (probably failed) reconnect attempt */
e95f5a6ebefb83d0e8a82cbc4db7d354957a817dben int max_retries;
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding int count;
36ef8f77bffe75d1aa327882be1b5bdbe2ff567asf
fd0edaa8e3d4dd67d0604ccef2e96b071db96643fielding count = be_fo_get_server_count(op->conn_cache->id_ctx->be,
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron op->conn_cache->id_ctx->service->name);
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron max_retries = 2 * count -1;
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron if (max_retries < 1) {
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron max_retries = 1;
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron }
06a537b50a7a5d4f8543231d2b6067b8f6805dd3aaron
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding return op->reconnect_retry_count < max_retries;
}
/* state of connect request */
struct sdap_id_op_connect_state {
struct sdap_id_ctx *id_ctx;
struct tevent_context *ev;
struct sdap_id_op *op;
int dp_error;
int result;
};
/* Destructor for operation connection request */
static int sdap_id_op_connect_state_destroy(void *pvt)
{
struct sdap_id_op_connect_state *state = talloc_get_type(pvt,
struct sdap_id_op_connect_state);
if (state->op != NULL) {
/* clear destroyed connection request */
state->op->connect_req = NULL;
}
return 0;
}
/* Begin to connect to LDAP server */
struct tevent_req *sdap_id_op_connect_send(struct sdap_id_op *op,
TALLOC_CTX *memctx,
int *ret_out)
{
struct tevent_req *req = NULL;
struct sdap_id_op_connect_state *state;
int ret = EOK;
if (!memctx) {
DEBUG(1, ("Bug: no memory context passed.\n"));
ret = EINVAL;
goto done;
}
if (op->connect_req) {
/* Connection already in progress, invalid operation */
DEBUG(1, ("Bug: connection request is already running or completed and leaked.\n"));
ret = EINVAL;
goto done;
}
req = tevent_req_create(memctx, &state, struct sdap_id_op_connect_state);
if (!req) {
ret = ENOMEM;
goto done;
}
talloc_set_destructor((void*)state, sdap_id_op_connect_state_destroy);
state->id_ctx = op->conn_cache->id_ctx;
state->ev = state->id_ctx->be->ev;
state->op = op;
op->connect_req = req;
if (op->conn_data) {
/* If the operation is already connected,
* reuse existing connection regardless of its status */
DEBUG(9, ("reusing operation connection\n"));
ret = EOK;
goto done;
}
ret = sdap_id_op_connect_step(req);
if (ret != EOK) {
goto done;
}
done:
if (ret != EOK) {
talloc_zfree(req);
} else if (op->conn_data && !op->conn_data->connect_req) {
/* Connection is already established */
tevent_req_done(req);
tevent_req_post(req, state->ev);
}
if (ret_out) {
*ret_out = ret;
}
return req;
}
/* Begin a connection retry to LDAP server */
static int sdap_id_op_connect_step(struct tevent_req *req)
{
struct sdap_id_op_connect_state *state =
tevent_req_data(req, struct sdap_id_op_connect_state);
struct sdap_id_op *op = state->op;
struct sdap_id_conn_cache *conn_cache = op->conn_cache;
int ret = EOK;
struct sdap_id_conn_data *conn_data;
struct tevent_req *subreq = NULL;
/* Try to reuse context cached connection */
conn_data = conn_cache->cached_connection;
if (conn_data) {
if (conn_data->connect_req) {
DEBUG(9, ("waiting for connection to complete\n"));
sdap_id_op_hook_conn_data(op, conn_data);
goto done;
}
if (sdap_can_reuse_connection(conn_data)) {
DEBUG(9, ("reusing cached connection\n"));
sdap_id_op_hook_conn_data(op, conn_data);
goto done;
}
DEBUG(9, ("releasing expired cached connection\n"));
conn_cache->cached_connection = NULL;
sdap_id_release_conn_data(conn_data);
}
DEBUG(9, ("beginning to connect\n"));
conn_data = talloc_zero(conn_cache, struct sdap_id_conn_data);
if (!conn_data) {
ret = ENOMEM;
goto done;
}
talloc_set_destructor(conn_data, sdap_id_conn_data_destroy);
conn_data->conn_cache = conn_cache;
subreq = sdap_cli_connect_send(conn_data, state->ev,
state->id_ctx->opts,
state->id_ctx->be,
state->id_ctx->service, false);
if (!subreq) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, sdap_id_op_connect_done, conn_data);
conn_data->connect_req = subreq;
DLIST_ADD(conn_cache->connections, conn_data);
conn_cache->cached_connection = conn_data;
sdap_id_op_hook_conn_data(op, conn_data);
done:
if (ret != EOK && conn_data) {
sdap_id_release_conn_data(conn_data);
}
if (ret != EOK) {
talloc_zfree(subreq);
}
return ret;
}
/* Subrequest callback for connection completion */
static void sdap_id_op_connect_done(struct tevent_req *subreq)
{
struct sdap_id_conn_data *conn_data =
tevent_req_callback_data(subreq, struct sdap_id_conn_data);
struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
struct sdap_server_opts *srv_opts = NULL;
struct sdap_server_opts *current_srv_opts = NULL;
bool can_retry = false;
bool is_offline = false;
int ret;
ret = sdap_cli_connect_recv(subreq, conn_data, &can_retry,
&conn_data->sh, &srv_opts);
conn_data->connect_req = NULL;
talloc_zfree(subreq);
conn_data->notify_lock++;
if (ret == ENOTSUP) {
DEBUG(0, ("Authentication mechanism not Supported by server\n"));
}
if (ret == EOK && (!conn_data->sh || !conn_data->sh->connected)) {
DEBUG(0, ("sdap_cli_connect_recv returned bogus connection\n"));
ret = EFAULT;
}
if (ret != EOK && !can_retry) {
/* be is going offline as there is no more servers to try */
DEBUG(1, ("Failed to connect, going offline (%d [%s])\n",
ret, strerror(ret)));
be_mark_offline(conn_cache->id_ctx->be);
is_offline = true;
}
if (ret == EOK) {
current_srv_opts = conn_cache->id_ctx->srv_opts;
if (current_srv_opts) {
DEBUG(8, ("Old USN: %lu, New USN: %lu\n", current_srv_opts->last_usn, srv_opts->last_usn));
if (strcmp(srv_opts->server_id, current_srv_opts->server_id) == 0 &&
srv_opts->supports_usn &&
current_srv_opts->last_usn > srv_opts->last_usn) {
DEBUG(5, ("Server was probably re-initialized\n"));
current_srv_opts->max_user_value = 0;
current_srv_opts->max_group_value = 0;
current_srv_opts->last_usn = srv_opts->last_usn;
}
}
ret = sdap_id_conn_data_set_expire_timer(conn_data);
sdap_steal_server_opts(conn_cache->id_ctx, &srv_opts);
}
if (can_retry) {
switch (ret) {
case EOK:
case ENOTSUP:
case EACCES:
case EIO:
case EFAULT:
case ETIMEDOUT:
break;
default:
/* do not attempt to retry on errors like ENOMEM */
can_retry = false;
is_offline = true;
be_mark_offline(conn_cache->id_ctx->be);
break;
}
}
int notify_count = 0;
/* Notify about connection */
for(;;) {
struct sdap_id_op *op;
if (ret == EOK && !conn_data->sh->connected) {
DEBUG(9, ("connection was broken after %d notifies\n", notify_count));
}
DLIST_FOR_EACH(op, conn_data->ops) {
if (op->connect_req) {
break;
}
}
if (!op) {
break;
}
/* another operation to notify */
notify_count++;
if (ret != EOK || !conn_data->sh->connected) {
/* failed to connect or connection got broken during notify */
bool retry = false;
/* drop connection from cache now */
if (conn_cache->cached_connection == conn_data) {
conn_cache->cached_connection = NULL;
}
if (can_retry) {
/* determining whether retry is possible */
if (be_is_offline(conn_cache->id_ctx->be)) {
/* be is offline, no retry possible */
if (ret == EOK) {
DEBUG(9, ("skipping automatic retry on op #%d as be is offline\n", notify_count));
ret = EIO;
}
can_retry = false;
is_offline = true;
} else {
if (ret == EOK) {
DEBUG(9, ("attempting automatic retry on op #%d\n", notify_count));
retry = true;
} else if (sdap_id_op_can_reconnect(op)) {
DEBUG(9, ("attempting failover retry on op #%d\n", notify_count));
op->reconnect_retry_count++;
retry = true;
}
}
}
if (retry && op->connect_req) {
int retry_ret = sdap_id_op_connect_step(op->connect_req);
if (retry_ret != EOK) {
can_retry = false;
sdap_id_op_connect_req_complete(op, DP_ERR_FATAL, retry_ret);
}
continue;
}
}
if (ret == EOK) {
DEBUG(9, ("notify connected to op #%d\n", notify_count));
sdap_id_op_connect_req_complete(op, DP_ERR_OK, ret);
} else if (is_offline) {
DEBUG(9, ("notify offline to op #%d\n", notify_count));
sdap_id_op_connect_req_complete(op, DP_ERR_OFFLINE, EAGAIN);
} else {
DEBUG(9, ("notify error to op #%d: %d [%s]\n", notify_count, ret, strerror(ret)));
sdap_id_op_connect_req_complete(op, DP_ERR_FATAL, ret);
}
}
/* all operations notified */
if (conn_data->notify_lock > 0) {
conn_data->notify_lock--;
}
if ((ret == EOK) &&
conn_data->sh->connected &&
!be_is_offline(conn_cache->id_ctx->be)) {
DEBUG(9, ("caching successful connection after %d notifies\n", notify_count));
conn_cache->cached_connection = conn_data;
/* Run any post-connection routines */
be_run_online_cb(conn_cache->id_ctx->be);
} else {
if (conn_cache->cached_connection == conn_data) {
conn_cache->cached_connection = NULL;
}
sdap_id_release_conn_data(conn_data);
}
}
/* Mark operation connection request as complete */
static void sdap_id_op_connect_req_complete(struct sdap_id_op *op, int dp_error, int ret)
{
struct tevent_req *req = op->connect_req;
struct sdap_id_op_connect_state *state;
if (!req) {
return;
}
op->connect_req = NULL;
state = tevent_req_data(req, struct sdap_id_op_connect_state);
state->dp_error = dp_error;
state->result = ret;
if (ret == EOK) {
tevent_req_done(req);
} else {
sdap_id_op_hook_conn_data(op, NULL);
tevent_req_error(req, ret);
}
}
/* Get the result of an asynchronous connect operation on sdap_id_op
*
* In dp_error data provider error code is returned:
* DP_ERR_OK - connection established
* DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN
* DP_ERR_FATAL - operation failed
*/
int sdap_id_op_connect_recv(struct tevent_req *req, int *dp_error)
{
struct sdap_id_op_connect_state *state = tevent_req_data(req,
struct sdap_id_op_connect_state);
*dp_error = state->dp_error;
return state->result;
}
/* Report completion of LDAP operation and release associated connection.
* Returns operation result (possible updated) passed in ret parameter.
*
* In dp_error data provider error code is returned:
* DP_ERR_OK (operation result = EOK) - operation completed
* DP_ERR_OK (operation result != EOK) - operation can be retried
* DP_ERR_OFFLINE - backend is offline, operation result is set EAGAIN
* DP_ERR_FATAL - operation failed */
int sdap_id_op_done(struct sdap_id_op *op, int retval, int *dp_err_out)
{
bool communication_error;
switch (retval) {
case EIO:
case ETIMEDOUT:
/* this currently the only possible communication error after connection is established */
communication_error = true;
break;
default:
communication_error = false;
break;
}
if (communication_error && op->conn_data != 0
&& op->conn_data == op->conn_cache->cached_connection) {
/* do not reuse failed connection */
op->conn_cache->cached_connection = NULL;
DEBUG(5, ("communication error on cached connection, moving to next server\n"));
be_fo_try_next_server(op->conn_cache->id_ctx->be,
op->conn_cache->id_ctx->service->name);
}
int dp_err;
if (retval == EOK) {
dp_err = DP_ERR_OK;
} else if (be_is_offline(op->conn_cache->id_ctx->be)) {
/* if backend is already offline, just report offline, do not duplicate errors */
dp_err = DP_ERR_OFFLINE;
retval = EAGAIN;
DEBUG(9, ("falling back to offline data...\n"));
} else if (communication_error) {
/* communication error, can try to reconnect */
if (!sdap_id_op_can_reconnect(op)) {
dp_err = DP_ERR_FATAL;
DEBUG(9, ("too many communication failures, giving up...\n"));
} else {
dp_err = DP_ERR_OK;
retval = EAGAIN;
}
} else {
dp_err = DP_ERR_FATAL;
}
if (dp_err == DP_ERR_OK && retval != EOK) {
/* reconnect retry */
op->reconnect_retry_count++;
DEBUG(9, ("advising for connection retry #%i\n", op->reconnect_retry_count));
} else {
/* end of request */
op->reconnect_retry_count = 0;
}
if (op->conn_data) {
DEBUG(9, ("releasing operation connection\n"));
sdap_id_op_hook_conn_data(op, NULL);
}
*dp_err_out = dp_err;
return retval;
}
/* Get SDAP handle associated with operation by sdap_id_op_connect */
struct sdap_handle *sdap_id_op_handle(struct sdap_id_op *op)
{
return op && op->conn_data ? op->conn_data->sh : NULL;
}