mirror of
https://git.openldap.org/openldap/openldap.git
synced 2026-01-21 14:25:24 -05:00
515 lines
14 KiB
C
515 lines
14 KiB
C
/* resultstats.c - gather result code statistics per operation */
|
|
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 2005-2025 The OpenLDAP Foundation.
|
|
* Copyright 2025 Symas Corp. All Rights Reserved.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in the file LICENSE in the
|
|
* top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
/* ACKNOWLEDGEMENTS:
|
|
* This work was developed by Nadezhda Ivanova for Symas Corp. for inclusion
|
|
* in OpenLDAP Software.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#ifdef SLAPD_OVER_RESULTSTATS
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ac/unistd.h>
|
|
#include <ac/string.h>
|
|
#include <ac/ctype.h>
|
|
#include <ac/socket.h>
|
|
|
|
#include "slap.h"
|
|
#include "slap-config.h"
|
|
#include "lutil.h"
|
|
#include "back-monitor/back-monitor.h"
|
|
|
|
static slap_overinst resultstats;
|
|
|
|
#define STATS_SIZE LDAP_OTHER+2
|
|
|
|
static struct resultstats_ops_t {
|
|
struct berval rdn;
|
|
struct berval nrdn;
|
|
} resultstats_op[] = {
|
|
{ BER_BVC( "cn=Bind" ), BER_BVC( "cn=bind" ), },
|
|
{ BER_BVC( "cn=Unbind" ), BER_BVC( "cn=unbind" ), },
|
|
{ BER_BVC( "cn=Search" ), BER_BVC( "cn=search" ), },
|
|
{ BER_BVC( "cn=Compare" ), BER_BVC( "cn=compare" ), },
|
|
{ BER_BVC( "cn=Modify" ), BER_BVC( "cn=modify" ), },
|
|
{ BER_BVC( "cn=Modrdn" ), BER_BVC( "cn=modrdn" ), },
|
|
{ BER_BVC( "cn=Add" ), BER_BVC( "cn=add" ), },
|
|
{ BER_BVC( "cn=Delete" ), BER_BVC( "cn=delete" ), },
|
|
{ BER_BVC( "cn=Abandon" ), BER_BVC( "cn=abandon" ), },
|
|
{ BER_BVC( "cn=Extended" ), BER_BVC( "cn=extended" ), },
|
|
{ BER_BVNULL, BER_BVNULL }
|
|
};
|
|
|
|
typedef struct resultstats_t {
|
|
uintptr_t stats[ SLAP_OP_LAST ][ STATS_SIZE ];
|
|
struct berval monitor_ndn;
|
|
struct berval rslt_rdn;
|
|
struct berval mss_ndn;
|
|
monitor_subsys_t *mss;
|
|
} resultstats_t;
|
|
|
|
|
|
static int resultstats_monitor_db_init( void );
|
|
static int resultstats_monitor_db_open( BackendDB *be );
|
|
static int resultstats_monitor_db_close( BackendDB *be );
|
|
//static int resultstats_monitor_db_destroy( BackendDB *be );
|
|
|
|
static AttributeDescription *ad_olmResultCodeStat;
|
|
static ObjectClass *oc_olmResultStatOperation;
|
|
static ObjectClass *oc_monitorContainer;
|
|
|
|
|
|
static struct {
|
|
char *desc;
|
|
ObjectClass **ocp;
|
|
} s_oc[] = {
|
|
{ "( OLcfgCtOc:12.1 "
|
|
"NAME ( 'olmResultStatOperation' ) "
|
|
"SUP monitoredObject "
|
|
"MAY ( "
|
|
"olmResultCodeStat"
|
|
" ) )",
|
|
&oc_olmResultStatOperation },
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
static struct {
|
|
char *desc;
|
|
AttributeDescription **adp;
|
|
} s_ad[] = {
|
|
{ "( OLcfgCtAt:12.1 "
|
|
"NAME 'olmResultCodeStat' "
|
|
"DESC 'Number of times an LDAP code result has been sent for this operation type' "
|
|
"EQUALITY integerMatch "
|
|
"ORDERING integerOrderingMatch "
|
|
"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
|
|
&ad_olmResultCodeStat },
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
static AttributeDescription *ads[ STATS_SIZE ];
|
|
|
|
static int
|
|
resultstats_monitor_ops_update(
|
|
Operation *op,
|
|
SlapReply *rs,
|
|
Entry *e,
|
|
void *priv )
|
|
{
|
|
uintptr_t *stats = (uintptr_t *)priv;
|
|
int i, rc;
|
|
for ( i = 0; i < STATS_SIZE; i++ ) {
|
|
Attribute *a;
|
|
AttributeDescription *ad = NULL;
|
|
char name_buf[ BUFSIZ ];
|
|
char val_buf[ BUFSIZ ];
|
|
const char *text;
|
|
struct berval bv;
|
|
unsigned long int value = __atomic_load_n( &stats[i], __ATOMIC_RELAXED );
|
|
if ( value == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
/* TODO This should be optimised by maintaining the attributes in the entry sorted by code,
|
|
and avoid searching through the full list every time */
|
|
ad = ads[i];
|
|
if ( ad == NULL ) {
|
|
if ( i <= LDAP_OTHER )
|
|
snprintf( name_buf, sizeof( name_buf ), "olmResultCodeStat;x-resultcode-%d", i );
|
|
else
|
|
snprintf( name_buf, sizeof( name_buf ), "olmResultCodeStat;x-resultcode-more");
|
|
|
|
rc = slap_str2ad( name_buf, &ad, &text );
|
|
if ( rc != 0 ) {
|
|
Debug( LDAP_DEBUG_ANY, "resultstats_monitor_ops_update: "
|
|
"unable to find attribute description %s \n ", name_buf );
|
|
return 0;
|
|
}
|
|
}
|
|
ads[i] = ad;
|
|
bv.bv_val = val_buf;
|
|
bv.bv_len = snprintf( val_buf, sizeof( val_buf ), "%lu", value );
|
|
a = attr_find( e->e_attrs, ad );
|
|
if ( a != NULL ) {
|
|
ber_bvreplace( &a->a_vals[ 0 ], &bv );
|
|
} else {
|
|
attr_merge_one( e, ad, &bv, NULL );
|
|
}
|
|
}
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_initialize( void )
|
|
{
|
|
static int resultstats_monitor_initialized = 0;
|
|
int code, i;
|
|
|
|
if ( backend_info( "monitor" ) == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_initialize: resultstats overlay requires cn=monitor");
|
|
return -1;
|
|
}
|
|
|
|
if ( resultstats_monitor_initialized++ ) {
|
|
return 0;
|
|
}
|
|
|
|
ad_define_option( "x-resultcode-", __FILE__, __LINE__ );
|
|
for ( i = 0; s_ad[i].desc != NULL; i++ ) {
|
|
code = register_at( s_ad[i].desc, s_ad[i].adp, 0 );
|
|
if ( code ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_initialize: register_at #%d failed\n", i );
|
|
return code;
|
|
}
|
|
}
|
|
|
|
for ( i = 0; s_oc[i].desc != NULL; i++ ) {
|
|
code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 );
|
|
if ( code ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstat_monitor_initialize: register_oc #%d failed\n", i );
|
|
return code;
|
|
}
|
|
}
|
|
|
|
oc_monitorContainer = oc_find( "monitorContainer" );
|
|
if ( !oc_monitorContainer ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_initialize: failed to find objectClass (monitorContainer)\n" );
|
|
return 5;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_register_entries( monitor_extra_t *mbe,
|
|
resultstats_t *rslt,
|
|
monitor_subsys_t *ms,
|
|
Entry *parent )
|
|
{
|
|
int i, rc;
|
|
Entry *e;
|
|
for ( i = 0; i < SLAP_OP_LAST; i++ ) {
|
|
monitor_callback_t *cb;
|
|
|
|
e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn,
|
|
&resultstats_op[i].rdn,
|
|
oc_olmResultStatOperation, NULL, NULL );
|
|
if ( e == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_register_entries: "
|
|
"unable to create entry \"%s,%s\"\n",
|
|
resultstats_op[i].rdn.bv_val,
|
|
rslt->monitor_ndn.bv_val );
|
|
return( -1 );
|
|
}
|
|
cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
|
|
cb->mc_update = resultstats_monitor_ops_update;
|
|
cb->mc_private = (void *)rslt->stats[i];
|
|
|
|
rc = mbe->register_entry( e, cb, ms, 0 );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_register_entries: "
|
|
"unable to register entry \"%s\" for monitoring\n",
|
|
e->e_name.bv_val );
|
|
ch_free( cb );
|
|
entry_free( e );
|
|
return rc;
|
|
}
|
|
entry_free( e );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_db_init( void )
|
|
{
|
|
return resultstats_monitor_initialize();
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_mss_init(
|
|
BackendDB *be,
|
|
monitor_subsys_t *ms )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)ms->mss_private;
|
|
resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private;
|
|
monitor_extra_t *mbe;
|
|
|
|
Entry *parent;
|
|
int rc;
|
|
|
|
assert( be != NULL );
|
|
mbe = (monitor_extra_t *) be->bd_info->bi_extra;
|
|
|
|
parent = mbe->entry_stub( &rslt->monitor_ndn, &rslt->monitor_ndn,
|
|
&rslt->rslt_rdn, oc_monitorContainer, NULL, NULL );
|
|
if ( parent == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_mss_init: "
|
|
"unable to create entry \"%s,%s\"\n",
|
|
rslt->rslt_rdn.bv_val,
|
|
rslt->monitor_ndn.bv_val );
|
|
return( -1 );
|
|
}
|
|
|
|
ber_dupbv( &ms->mss_dn, &parent->e_name );
|
|
ber_dupbv( &ms->mss_ndn, &parent->e_nname );
|
|
ber_dupbv( &ms->mss_rdn, &rslt->rslt_rdn );
|
|
|
|
rc = mbe->register_entry( parent, NULL, ms, MONITOR_F_PERSISTENT_CH );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_mss_init: "
|
|
"unable to register entry \"%s,%s\"\n",
|
|
ms->mss_rdn.bv_val,
|
|
ms->mss_ndn.bv_val );
|
|
entry_free( parent );
|
|
return ( -1 );
|
|
}
|
|
|
|
rc = resultstats_monitor_register_entries( mbe, rslt, ms, parent );
|
|
|
|
entry_free( parent );
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_mss_destroy (
|
|
BackendDB *be,
|
|
monitor_subsys_t *ms )
|
|
{
|
|
if ( ms->mss_ndn.bv_len > 0 ) {
|
|
ch_free( ms->mss_ndn.bv_val );
|
|
}
|
|
if ( ms->mss_dn.bv_len > 0 ) {
|
|
ch_free( ms->mss_dn.bv_val );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_db_open( BackendDB *be )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private;
|
|
BackendInfo *bi;
|
|
int rc = 0;
|
|
monitor_extra_t *mbe;
|
|
Entry * parent;
|
|
|
|
/* check if monitor is configured and usable */
|
|
bi = backend_info( "monitor" );
|
|
if ( !bi || !bi->bi_extra ) {
|
|
return -1;
|
|
}
|
|
mbe = bi->bi_extra;
|
|
|
|
/* don't bother if monitor is not configured */
|
|
if ( !mbe->is_configured() ) {
|
|
Debug( LDAP_DEBUG_CONFIG, "resultstats_monitor_db_open: "
|
|
"monitoring disabled; "
|
|
"configure monitor database to enable\n" );
|
|
return -1;
|
|
}
|
|
|
|
BER_BVZERO( &rslt->monitor_ndn );
|
|
if ( ( rc = mbe->register_overlay( be, on, &rslt->monitor_ndn ) ) != 0 ) {
|
|
return rc;
|
|
}
|
|
parent = mbe->entry_stub( &rslt->monitor_ndn, &rslt->monitor_ndn,
|
|
&rslt->rslt_rdn, oc_monitorContainer, NULL, NULL );
|
|
if ( parent == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_mss_init: "
|
|
"unable to create entry \"%s,%s\"\n",
|
|
rslt->rslt_rdn.bv_val,
|
|
rslt->monitor_ndn.bv_val );
|
|
return( -1 );
|
|
}
|
|
|
|
ber_dupbv( &rslt->mss_ndn, &parent->e_nname );
|
|
/* Check if the subsystem already exsists. This can happen if the overlay
|
|
has previously added and removed. For now it is safe to assume
|
|
that the dn will be unique, as databases cannot be removed.
|
|
This should be re-done when we enable database removal and fix monitor
|
|
so that subsystems can be unregistered */
|
|
rslt->mss = monitor_back_get_subsys_by_dn( &rslt->mss_ndn, 0 );
|
|
if ( rslt->mss == NULL ) {
|
|
/* this will leak at monitor_db_destroy, but it can't be helped */
|
|
rslt->mss = (monitor_subsys_t *)ch_calloc( 1, sizeof( monitor_subsys_t ) );
|
|
rslt->mss->mss_name = "Result code statistics";
|
|
rslt->mss->mss_flags = MONITOR_F_PERSISTENT_CH;
|
|
rslt->mss->mss_open = resultstats_monitor_mss_init;
|
|
rslt->mss->mss_destroy = resultstats_monitor_mss_destroy;
|
|
rslt->mss->mss_private = on;
|
|
|
|
if ( mbe->register_subsys_late( rslt->mss ) ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultsats_monitor_db_open: "
|
|
"failed to register result statistics subsystem" );
|
|
return -1;
|
|
}
|
|
} else {
|
|
rslt->mss->mss_private = on;
|
|
rc = mbe->register_entry( parent, NULL, rslt->mss, MONITOR_F_PERSISTENT_CH );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"resultstats_monitor_db_open: "
|
|
"unable to register entry \"%s,%s\"\n",
|
|
rslt->mss->mss_rdn.bv_val,
|
|
rslt->mss->mss_ndn.bv_val );
|
|
entry_free( parent );
|
|
return ( -1 );
|
|
}
|
|
rc = resultstats_monitor_register_entries( mbe, rslt, rslt->mss, parent );
|
|
}
|
|
entry_free( parent );
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
resultstats_monitor_db_close( BackendDB *be )
|
|
{
|
|
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private;
|
|
monitor_extra_t *mbe;
|
|
BackendInfo *mi = backend_info( "monitor" );
|
|
|
|
if ( mi && mi->bi_extra ) {
|
|
int i;
|
|
mbe = mi->bi_extra;
|
|
for ( i = 0; i < SLAP_OP_LAST; i++ ) {
|
|
struct berval bv;
|
|
char buf[ BACKMONITOR_BUFSIZE ];
|
|
bv.bv_len = snprintf( buf, sizeof( buf ), "%s,%s",
|
|
resultstats_op[i].nrdn.bv_val,
|
|
rslt->mss_ndn.bv_val );
|
|
bv.bv_val = buf;
|
|
mbe->unregister_entry( &bv );
|
|
|
|
}
|
|
mbe->unregister_entry( &rslt->mss_ndn );
|
|
}
|
|
if ( !BER_BVISNULL(&rslt->mss_ndn) ) {
|
|
ch_free( rslt->mss_ndn.bv_val );
|
|
BER_BVZERO( &rslt->mss_ndn );
|
|
}
|
|
/* Make sure this does not point to a non-existent overlay instance */
|
|
rslt->mss->mss_private = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
resultstats_response( Operation *op, SlapReply *rs )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
|
|
resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private;
|
|
int code, opidx;
|
|
|
|
/* skip internal ops */
|
|
if ( rs->sr_type != REP_RESULT || op->o_do_not_cache ) {
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
code = slap_map_api2result( rs );
|
|
if ( code > LDAP_OTHER )
|
|
code = LDAP_OTHER+1;
|
|
|
|
opidx = slap_req2op( op->o_tag );
|
|
__atomic_fetch_add( &(rslt->stats[opidx][code]), 1, __ATOMIC_RELAXED );
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
static int
|
|
resultstats_db_init( BackendDB *be, ConfigReply *cr )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
resultstats_t *rslt;
|
|
|
|
rslt = (resultstats_t *)ch_calloc( 1, sizeof( resultstats_t ) );
|
|
|
|
on->on_bi.bi_private = (void *)rslt;
|
|
ber_str2bv( "cn=Result Stats", 0, 1,
|
|
&rslt->rslt_rdn );
|
|
return resultstats_monitor_db_init();
|
|
}
|
|
|
|
static int
|
|
resultstats_db_open( BackendDB *be, ConfigReply *cr )
|
|
{
|
|
return resultstats_monitor_db_open( be );
|
|
}
|
|
|
|
static int
|
|
resultstats_db_destroy( BackendDB *be, ConfigReply *cr )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private;
|
|
if ( rslt->rslt_rdn.bv_len > 0 ) {
|
|
ch_free( rslt->rslt_rdn.bv_val );
|
|
}
|
|
ch_free( rslt );
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
resultstats_db_close( BackendDB *be, ConfigReply *cr )
|
|
{
|
|
return resultstats_monitor_db_close( be );
|
|
}
|
|
|
|
int
|
|
resultstats_initialize( void )
|
|
{
|
|
int code;
|
|
|
|
resultstats.on_bi.bi_type = "resultstats";
|
|
resultstats.on_bi.bi_db_init = resultstats_db_init;
|
|
resultstats.on_bi.bi_db_open = resultstats_db_open;
|
|
resultstats.on_bi.bi_db_destroy = resultstats_db_destroy;
|
|
resultstats.on_bi.bi_db_close = resultstats_db_close;
|
|
resultstats.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
|
|
resultstats.on_response = resultstats_response;
|
|
|
|
code = resultstats_monitor_initialize();
|
|
if ( code != 0)
|
|
return code;
|
|
|
|
return overlay_register( &resultstats );
|
|
}
|
|
|
|
#if SLAPD_OVER_RESULTSTATS == SLAPD_MOD_DYNAMIC
|
|
int
|
|
init_module( int argc, char *argv[] )
|
|
{
|
|
return resultstats_initialize();
|
|
}
|
|
#endif /* SLAPD_OVER_RESULTSTATS == SLAPD_MOD_DYNAMIC */
|
|
|
|
#endif /* SLAPD_OVER_RESULTSTATS */
|