sdap_id_op.c revision 0a320004a9937295ba66a348d1e60682cfdceb26
/*
SSSD
LDAP ID backend operation retry logic and connection cache
Authors:
Eugene Indenbom <eindenbom@gmail.com>
Copyright (C) 2008-2010 Red Hat
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 "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/sdap_id_op.h"
/* LDAP async connection cache */
struct sdap_id_conn_cache {
struct sdap_id_conn_ctx *id_conn;
/* list of all open connections */
struct sdap_id_conn_data *connections;
/* cached (current) connection */
struct sdap_id_conn_data *cached_connection;
};
/* LDAP async operation tracker:
* - keeps track of connection usage
* - keeps track of operation retries */
struct sdap_id_op {
/* ID backend context */
struct sdap_id_conn_cache *conn_cache;
/* double linked list pointers */
/* current connection */
struct sdap_id_conn_data *conn_data;
/* number of reconnects for this operation */
/* connection request
* It is required as we need to know which requests to notify
* when shared connection request to sdap_handle completes.
* This member is cleared when sdap_id_op_connect_state
* associated with request is destroyed */
struct tevent_req *connect_req;
};
/* LDAP connection cache connection attempt/established connection data */
struct sdap_id_conn_data {
/* LDAP connection cache */
struct sdap_id_conn_cache *conn_cache;
/* double linked list pointers */
/* sdap handle */
struct sdap_handle *sh;
/* connection request */
struct tevent_req *connect_req;
/* timer for connection expiration */
struct tevent_timer *expire_timer;
/* number of running connection notifies */
int notify_lock;
/* list of operations using connect */
struct sdap_id_op *ops;
/* A flag which is signalizing that this
* connection will be disconnected and should
* not be used any more */
bool disconnecting;
};
static void sdap_id_conn_cache_be_offline_cb(void *pvt);
static void sdap_id_conn_cache_fo_reconnect_cb(void *pvt);
struct tevent_timer *te,
struct timeval current_time,
void *pvt);
static int sdap_id_op_destroy(void *pvt);
static int sdap_id_op_connect_state_destroy(void *pvt);
/* Create a connection cache */
struct sdap_id_ctx *id_ctx,
struct sdap_id_conn_ctx *id_conn,
struct sdap_id_conn_cache** conn_cache_out)
{
int ret;
if (!conn_cache) {
goto fail;
}
NULL);
goto fail;
}
NULL);
goto fail;
}
return EOK;
fail:
return ret;
}
/* Callback on BE going offline */
static void sdap_id_conn_cache_be_offline_cb(void *pvt)
{
/* Release any cached connection on going offline */
if (cached_connection != NULL) {
}
}
/* Callback for attempt to reconnect to primary server */
static void sdap_id_conn_cache_fo_reconnect_cb(void *pvt)
{
/* Release any cached connection on going offline */
if (cached_connection != NULL) {
cached_connection->disconnecting = true;
}
}
/* Release sdap_id_conn_data and destroy it if no longer needed */
{
struct sdap_id_conn_cache *conn_cache;
/* connection is in use */
return;
}
return;
}
}
/* Destructor for struct sdap_id_conn_data */
{
struct sdap_id_op *op;
/* we clean out list of ops to make sure that order of destruction does not matter */
}
return 0;
}
/* Check whether connection will expire after timeout seconds */
{
return true;
}
return true;
}
return false;
}
/* Check whether connection can be reused for next LDAP ID operation */
{
int timeout;
return false;
}
}
/* Set expiration timer for connection if needed */
{
int timeout;
return EOK;
}
if (timeout > 0) {
}
return EOK;
}
if (!conn_data->expire_timer) {
return ENOMEM;
}
return EOK;
}
/* Handler for connection expiration timer */
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
struct sdap_id_conn_data);
}
}
/* Create an operation object */
{
if (!op) {
return NULL;
}
return op;
}
{
if (!op) {
DEBUG(0, ("NULL op passed!!!\n"));
return;
}
return;
}
if (current) {
}
if (conn_data) {
}
if (current) {
}
}
/* Destructor for sdap_id_op */
static int sdap_id_op_destroy(void *pvt)
{
}
return 0;
}
/* Check whether retry with reconnect can be performed for the operation */
{
/* we allow 2 retries for failover server configured:
* - one for connection broken during request execution
* - one for the following (probably failed) reconnect attempt */
int max_retries;
int count;
if (max_retries < 1) {
max_retries = 1;
}
}
/* state of connect request */
struct sdap_id_op_connect_state {
struct sdap_id_conn_ctx *id_conn;
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);
/* clear destroyed connection request */
}
return 0;
}
/* Begin to connect to LDAP server */
int *ret_out)
{
struct sdap_id_op_connect_state *state;
if (!memctx) {
goto done;
}
if (op->connect_req) {
/* Connection already in progress, invalid operation */
goto done;
}
if (!req) {
goto done;
}
/* If the operation is already connected,
* reuse existing connection regardless of its status */
goto done;
}
goto done;
}
done:
/* Connection is already established */
}
if (ret_out) {
}
return req;
}
/* Begin a connection retry to LDAP server */
{
struct sdap_id_op_connect_state *state =
struct sdap_id_conn_data *conn_data;
/* Try to reuse context cached connection */
if (conn_data) {
if (conn_data->connect_req) {
goto done;
}
if (sdap_can_reuse_connection(conn_data)) {
goto done;
}
}
if (!conn_data) {
goto done;
}
CON_TLS_DFL, false);
if (!subreq) {
goto done;
}
done:
}
}
return ret;
}
/* Subrequest callback for connection completion */
{
struct sdap_id_conn_data *conn_data =
bool can_retry = false;
bool is_offline = false;
bool reinit = false;
int ret;
conn_data->notify_lock++;
DEBUG(0, ("Authentication mechanism not Supported by server\n"));
}
DEBUG(0, ("sdap_cli_connect_recv returned bogus connection\n"));
}
/* be is going offline as there is no more servers to try */
is_offline = true;
}
if (current_srv_opts) {
srv_opts->supports_usn &&
reinit = true;
}
}
}
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;
break;
}
}
int notify_count = 0;
/* Notify about connection */
for(;;) {
struct sdap_id_op *op;
}
if (op->connect_req) {
break;
}
}
if (!op) {
break;
}
/* another operation to notify */
notify_count++;
/* failed to connect or connection got broken during notify */
bool retry = false;
/* drop connection from cache now */
}
if (can_retry) {
/* determining whether retry is possible */
/* be is offline, no retry possible */
}
can_retry = false;
is_offline = true;
} else {
retry = true;
} else if (sdap_id_op_can_reconnect(op)) {
retry = true;
}
}
}
can_retry = false;
}
continue;
}
}
} else if (is_offline) {
} else {
}
}
/* all operations notified */
if (conn_data->notify_lock > 0) {
conn_data->notify_lock--;
}
/* Run any post-connection routines */
} else {
}
}
if (reinit) {
"Cleaning cache.\n"));
if (reinit_req == NULL) {
"clean up.\n"));
return;
}
NULL);
}
}
{
/* not fatal */
return;
}
}
/* Mark operation connection request as complete */
{
struct sdap_id_op_connect_state *state;
if (!req) {
return;
}
} else {
}
}
/* 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
*/
{
struct sdap_id_op_connect_state);
}
/* 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 */
{
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 && current_conn != 0
/* do not reuse failed connection */
}
int dp_err;
/* if backend is already offline, just report offline, do not duplicate errors */
} else if (communication_error) {
/* communication error, can try to reconnect */
if (!sdap_id_op_can_reconnect(op)) {
} else {
}
} else {
}
/* reconnect retry */
} else {
/* end of request */
op->reconnect_retry_count = 0;
}
if (current_conn) {
}
*dp_err_out = dp_err;
return retval;
}
/* Get SDAP handle associated with operation by sdap_id_op_connect */
{
}