mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-24 00:29:35 -05:00
Introduce infra to handle config changes
This commit is contained in:
parent
a7f8f58a63
commit
cfe9065824
2 changed files with 335 additions and 0 deletions
|
|
@ -1394,6 +1394,306 @@ daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg )
|
|||
}
|
||||
|
||||
#ifdef BALANCER_MODULE
|
||||
int
|
||||
backend_conn_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
|
||||
{
|
||||
LloadConnection *c = startarg;
|
||||
LloadBackend *b = arg;
|
||||
|
||||
if ( b == NULL || c->c_private == b ) {
|
||||
if ( start == upstream_bind ) {
|
||||
/* FIXME: is upstream_bind safe without a reference of its own? */
|
||||
CONNECTION_LOCK(c);
|
||||
} else {
|
||||
CONNECTION_LOCK_DECREF(c);
|
||||
}
|
||||
CONNECTION_DESTROY(c);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
client_tls_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
|
||||
{
|
||||
LloadConnection *c = startarg;
|
||||
|
||||
if ( c->c_destroy == client_destroy &&
|
||||
c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
|
||||
CONNECTION_LOCK_DESTROY(c);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lload_handle_backend_invalidation( LloadChange *change )
|
||||
{
|
||||
LloadBackend *b = change->target;
|
||||
|
||||
assert( change->object == LLOAD_BACKEND );
|
||||
|
||||
if ( change->type == LDAP_REQ_ADD ) {
|
||||
backend_retry( b );
|
||||
return;
|
||||
} else if ( change->type == LDAP_REQ_DELETE ) {
|
||||
lload_backend_destroy( b );
|
||||
return;
|
||||
}
|
||||
assert( change->type == LDAP_REQ_MODIFY );
|
||||
assert( change->flags.generic != 0 );
|
||||
|
||||
/*
|
||||
* A change that can't be handled gracefully, terminate all connections and
|
||||
* start over.
|
||||
*/
|
||||
if ( change->flags.backend & LLOAD_BACKEND_MOD_OTHER ) {
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, handle_pdus, backend_conn_cb, b );
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, upstream_bind, backend_conn_cb, b );
|
||||
backend_reset( b );
|
||||
backend_retry( b );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle changes to number of connections:
|
||||
* - a change might get the connection limit above the pool size:
|
||||
* - consider closing (in order of priority?):
|
||||
* - connections awaiting connect() completion
|
||||
* - connections currently preparing
|
||||
* - bind connections over limit (which is 0 if 'feature vc' is on
|
||||
* - regular connections over limit
|
||||
* - below pool size
|
||||
* - call backend_retry if there are no opening connections
|
||||
* - one pool size above and one below the configured size
|
||||
* - still close the ones above limit, it should sort itself out
|
||||
* the only issue is if a closing connection isn't guaranteed to do
|
||||
* that at some point
|
||||
*/
|
||||
if ( change->flags.backend & LLOAD_BACKEND_MOD_CONNS ) {
|
||||
int bind_requested = 0, need_close = 0, need_open = 0;
|
||||
LloadConnection *c;
|
||||
|
||||
bind_requested =
|
||||
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
||||
(lload_features & LLOAD_FEATURE_VC) ? 0 :
|
||||
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
|
||||
b->b_numbindconns;
|
||||
|
||||
if ( b->b_bindavail > bind_requested ) {
|
||||
need_close += b->b_bindavail - bind_requested;
|
||||
} else if ( b->b_bindavail < bind_requested ) {
|
||||
need_open = 1;
|
||||
}
|
||||
|
||||
if ( b->b_active > b->b_numconns ) {
|
||||
need_close += b->b_active - b->b_numconns;
|
||||
} else if ( b->b_active < b->b_numconns ) {
|
||||
need_open = 1;
|
||||
}
|
||||
|
||||
if ( !need_open ) {
|
||||
need_close += b->b_opening;
|
||||
|
||||
while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
|
||||
LloadPendingConnection *p = LDAP_LIST_FIRST( &b->b_connecting );
|
||||
|
||||
LDAP_LIST_REMOVE( p, next );
|
||||
event_free( p->event );
|
||||
evutil_closesocket( p->fd );
|
||||
ch_free( p );
|
||||
b->b_opening--;
|
||||
need_close--;
|
||||
}
|
||||
}
|
||||
|
||||
if ( need_close || !need_open ) {
|
||||
/* It might be too late to repurpose a preparing connection, just
|
||||
* close them all */
|
||||
while ( !LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) ) {
|
||||
c = LDAP_CIRCLEQ_FIRST( &b->b_preparing );
|
||||
|
||||
event_del( c->c_read_event );
|
||||
CONNECTION_LOCK_DESTROY(c);
|
||||
assert( c == NULL );
|
||||
b->b_opening--;
|
||||
need_close--;
|
||||
}
|
||||
event_del( b->b_retry_event );
|
||||
assert( b->b_opening == 0 );
|
||||
}
|
||||
|
||||
if ( b->b_bindavail > bind_requested ) {
|
||||
int diff = b->b_bindavail - bind_requested;
|
||||
|
||||
assert( need_close >= diff );
|
||||
|
||||
LDAP_CIRCLEQ_FOREACH ( c, &b->b_bindconns, c_next ) {
|
||||
lload_connection_close( c );
|
||||
need_close--;
|
||||
diff--;
|
||||
if ( !diff ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( diff == 0 );
|
||||
}
|
||||
|
||||
if ( b->b_active > b->b_numconns ) {
|
||||
int diff = b->b_active - b->b_numconns;
|
||||
|
||||
assert( need_close >= diff );
|
||||
|
||||
LDAP_CIRCLEQ_FOREACH ( c, &b->b_conns, c_next ) {
|
||||
lload_connection_close( c );
|
||||
need_close--;
|
||||
diff--;
|
||||
if ( !diff ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( diff == 0 );
|
||||
}
|
||||
assert( need_close == 0 );
|
||||
|
||||
if ( need_open ) {
|
||||
backend_retry( b );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lload_handle_bindconf_invalidation( LloadChange *change )
|
||||
{
|
||||
LloadBackend *b;
|
||||
LloadConnection *c;
|
||||
|
||||
assert( change->type == LDAP_REQ_MODIFY );
|
||||
assert( change->object == LLOAD_BINDCONF );
|
||||
|
||||
if ( change->flags.bindconf == LLOAD_BINDCONF_MOD_TIMEOUTS ) {
|
||||
/* Nothing needs doing, things will generally fall into place */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only timeout changes can be handled gracefully, terminate all
|
||||
* connections and start over.
|
||||
*/
|
||||
/*
|
||||
* - terminate all backends' connections
|
||||
* - reconsider the PRIVILEGED flag on all clients
|
||||
*/
|
||||
/*
|
||||
* walk the task queue to remove any tasks belonging to our connections.
|
||||
*/
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, handle_pdus, backend_conn_cb, NULL );
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, upstream_bind, backend_conn_cb, NULL );
|
||||
|
||||
LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
|
||||
backend_reset( b );
|
||||
backend_retry( b );
|
||||
}
|
||||
|
||||
LDAP_CIRCLEQ_FOREACH ( c, &clients, c_next ) {
|
||||
int privileged = ber_bvstrcasecmp( &c->c_auth, &lloadd_identity );
|
||||
|
||||
/* We have just terminated all pending operations, there should be no
|
||||
* connections still binding/closing */
|
||||
assert( c->c_state == LLOAD_C_READY );
|
||||
|
||||
c->c_type = privileged ? LLOAD_C_PRIVILEGED : LLOAD_C_OPEN;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void
|
||||
lload_handle_global_invalidation( LloadChange *change )
|
||||
{
|
||||
assert( change->type == LDAP_REQ_MODIFY );
|
||||
assert( change->object == LLOAD_DAEMON );
|
||||
|
||||
if ( change->flags.daemon & LLOAD_DAEMON_MOD_THREADS ) {
|
||||
/* walk the task queue to remove any tasks belonging to us. */
|
||||
/* TODO: initiate a full module restart, everything will fall into
|
||||
* place at that point */
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, handle_pdus, backend_conn_cb, NULL );
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, upstream_bind, backend_conn_cb, NULL );
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( change->flags.daemon & LLOAD_DAEMON_MOD_FEATURES ) {
|
||||
/* TODO: how do we detect what the old feature value was, maybe store
|
||||
* it in change->target? */
|
||||
/* TODO: feature change handling:
|
||||
* - VC:
|
||||
* - on: terminate all bind connections
|
||||
* - off: cancel all bind operations in progress, reopen bind connections
|
||||
* - ProxyAuthz: nothing needed?
|
||||
*/
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if ( change->flags.daemon & LLOAD_DAEMON_MOD_TLS ) {
|
||||
/* terminate all clients with TLS set up */
|
||||
ldap_pvt_thread_pool_walk(
|
||||
&connection_pool, handle_pdus, client_tls_cb, NULL );
|
||||
if ( !LDAP_CIRCLEQ_EMPTY( &clients ) ) {
|
||||
LloadConnection *c = LDAP_CIRCLEQ_FIRST( &clients );
|
||||
unsigned long first_connid = c->c_connid;
|
||||
|
||||
while ( c ) {
|
||||
LloadConnection *next =
|
||||
LDAP_CIRCLEQ_LOOP_NEXT( &clients, c, c_next );
|
||||
if ( c->c_is_tls ) {
|
||||
CONNECTION_LOCK(c);
|
||||
CONNECTION_DESTROY(c);
|
||||
assert( c == NULL );
|
||||
}
|
||||
c = next;
|
||||
if ( c->c_connid <= first_connid ) {
|
||||
c = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
lload_handle_invalidation( LloadChange *change )
|
||||
{
|
||||
if ( change->type == LDAP_REQ_MODIFY && change->flags.generic == 0 ) {
|
||||
Debug( LDAP_DEBUG_ANY, "lload_handle_invalidation: "
|
||||
"a modify where apparently nothing changed\n" );
|
||||
}
|
||||
|
||||
switch ( change->object ) {
|
||||
case LLOAD_BACKEND:
|
||||
lload_handle_backend_invalidation( change );
|
||||
break;
|
||||
case LLOAD_DAEMON:
|
||||
lload_handle_global_invalidation( change );
|
||||
break;
|
||||
case LLOAD_BINDCONF:
|
||||
lload_handle_bindconf_invalidation( change );
|
||||
break;
|
||||
default:
|
||||
Debug( LDAP_DEBUG_ANY, "lload_handle_invalidation: "
|
||||
"unrecognised change\n" );
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return LDAP_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal the event base to terminate processing as soon as it can and wait for
|
||||
* lload_base_dispatch to notify us this has happened.
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ typedef struct LloadBackend LloadBackend;
|
|||
typedef struct LloadPendingConnection LloadPendingConnection;
|
||||
typedef struct LloadConnection LloadConnection;
|
||||
typedef struct LloadOperation LloadOperation;
|
||||
typedef struct LloadChange LloadChange;
|
||||
/* end of forward declarations */
|
||||
|
||||
typedef LDAP_CIRCLEQ_HEAD(BeSt, LloadBackend) lload_b_head;
|
||||
|
|
@ -105,6 +106,40 @@ typedef int lload_cf_aux_table_parse_x( struct berval *val,
|
|||
|
||||
typedef struct LloadListener LloadListener;
|
||||
|
||||
enum lc_object {
|
||||
LLOAD_UNDEFINED = 0,
|
||||
LLOAD_DAEMON,
|
||||
LLOAD_BINDCONF,
|
||||
LLOAD_BACKEND,
|
||||
};
|
||||
|
||||
enum lcf_daemon {
|
||||
LLOAD_DAEMON_MOD_THREADS = 1 << 0,
|
||||
LLOAD_DAEMON_MOD_FEATURES = 1 << 1,
|
||||
LLOAD_DAEMON_MOD_TLS = 1 << 2,
|
||||
};
|
||||
|
||||
enum lcf_bindconf {
|
||||
LLOAD_BINDCONF_MOD_TIMEOUTS = 1 << 0,
|
||||
};
|
||||
|
||||
enum lcf_backend {
|
||||
LLOAD_BACKEND_MOD_OTHER = 1 << 0,
|
||||
LLOAD_BACKEND_MOD_CONNS = 1 << 1,
|
||||
};
|
||||
|
||||
struct LloadChange {
|
||||
ber_tag_t type;
|
||||
enum lc_object object;
|
||||
union {
|
||||
int generic;
|
||||
enum lcf_daemon daemon;
|
||||
enum lcf_bindconf bindconf;
|
||||
enum lcf_backend backend;
|
||||
} flags;
|
||||
void *target;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
||||
LLOAD_FEATURE_VC = 1 << 0,
|
||||
|
|
|
|||
Loading…
Reference in a new issue