Hi,

I've managed to cobble together an openldap plugin to monitor the various 
counters from an openldap server. This is based mainly off of the apache and 
mysql plugins. I am not a c programmer so apologies if the code is horrible.

Two things I need help on are:

1) Initializing the global ldap options. I don't know where this would go.

>From ldap_open(3):
 
       Note:  the first call into the LDAP library also initializes the global
       options for the library. As such  the  first  call  should  be  single-
       threaded or otherwise protected to insure that only one call is active.
       It is recommended that ldap_get_option() or ldap_set_option()  be  used
       in the program's main thread before any additional threads are created.
       See ldap_get_option(3).

2) Specifying a timeout. LDAP_OPT_TIMEOUT needs to be a struct timeval * but I 
couldn't work out the proper way to define and use it.

The full diff is available in my githhub clone of collectd5 in the 
kimor79/openldap branch:

https://github.com/kimor79/collectd/commit/e08947a62a7b48b8e6d82e742379709be1633a25

https://github.com/kimor79/collectd/commit/c871b9095d60c641dd2cb81323bac412fc1de5de
 

Thanks
Kimo

--

#include "collectd.h"
#include "common.h"
#include "plugin.h"
#include "configfile.h"

#include <lber.h>
#include <ldap.h>

struct ldap_s /* {{{ */
{
    char *name;

    char *cacert;
    char *host;
    int   state;
    int   starttls;
    int   timeout;
    char *url;
    int   verifyhost;
    int   version;

    LDAP *ld;
    char *dn;
};
typedef struct ldap_s ldap_t; /* }}} */

static int ldap_read_host (user_data_t *ud);

static void ldap_free (ldap_t *st) /* {{{ */
{
    if(st == NULL)
        return;

    sfree (st->cacert);
    sfree (st->host);
    sfree (st->name);
    sfree (st->url);
    if(st->ld)
        ldap_memfree(st->ld);
    sfree (st);
} /* }}} void ldap_free */

/* Configuration handling functions {{{
 *
 * <Plugin ldap>
 *   <Instance "plugin_instance1">
 *     URL "ldap://localhost";
 *     ...
 *   </Instance>
 * </Plugin>
 */

static int config_set_string (char **ret_string, /* {{{ */
                    oconfig_item_t *ci)
{
    char *string;

    if ((ci->values_num != 1)
        || (ci->values[0].type != OCONFIG_TYPE_STRING))
    {
        WARNING ("openldap plugin: The `%s' config option "
             "needs exactly one string argument.", ci->key);
        return (-1);
    }

    string = strdup (ci->values[0].value.string);
    if (string == NULL)
    {
        ERROR ("openldap plugin: strdup failed.");
        return (-1);
    }

    if (*ret_string != NULL)
        free (*ret_string);
    *ret_string = string;

    return (0);
} /* }}} int config_set_string */

static int config_set_int (int *ret_int, /* {{{ */
                 oconfig_item_t *ci)
{
    if ((ci->values_num != 1)
        || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
    {
        WARNING ("openldap plugin: The `%s' config option "
             "needs exactly one string argument.", ci->key);
        return (-1);
    }

    *ret_int = ci->values[0].value.number;

    return (0);
} /* }}} int config_set_int */

static int config_set_bool (int *ret_boolean, /* {{{ */
                oconfig_item_t *ci)
{
    int status = 0;

    if (ci->values_num != 1)
        status = -1;

    if (status == 0)
    {
        if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
            *ret_boolean = ci->values[0].value.boolean;
        else if (ci->values[0].type == OCONFIG_TYPE_STRING)
        {
            if (IS_TRUE (ci->values[0].value.string))
                *ret_boolean = 1;
            else if (IS_FALSE (ci->values[0].value.string))
                *ret_boolean = 0;
            else
                status = -1;
        }
        else
            status = -1;
    }

    if (status != 0)
    {
        WARNING ("openldap plugin: The `%s' config option "
            "needs exactly one boolean argument.", ci->key);
        return (-1);
    }
    return (0);
} /* }}} config_set_bool */

static int config_add (oconfig_item_t *ci) /* {{{ */
{
    ldap_t *st;
    int i;
    int status;

    if ((ci->values_num != 1)
        || (ci->values[0].type != OCONFIG_TYPE_STRING))
    {
        WARNING ("openldap plugin: The `%s' config option "
             "needs exactly one string argument.", ci->key);
        return (-1);
    }

    st = (ldap_t *) malloc (sizeof (*st));
    if (st == NULL)
    {
        ERROR ("openldap plugin: malloc failed.");
        return (-1);
    }
    memset (st, 0, sizeof (*st));

    status = config_set_string (&st->name, ci);
    if (status != 0)
    {
        sfree (st);
        return (status);
    }

    st->verifyhost = 1;
    st->version = LDAP_VERSION3;

    for (i = 0; i < ci->children_num; i++)
    {
        oconfig_item_t *child = ci->children + i;

        if (strcasecmp ("CACert", child->key) == 0)
            status = config_set_string (&st->cacert, child);
        else if (strcasecmp ("StartTLS", child->key) == 0)
            status = config_set_bool (&st->starttls, child);
        else if (strcasecmp ("Timeout", child->key) == 0)
            status = config_set_int (&st->timeout, child);
        else if (strcasecmp ("URL", child->key) == 0)
            status = config_set_string (&st->url, child);
        else if (strcasecmp ("VerifyHost", child->key) == 0)
            status = config_set_bool (&st->verifyhost, child);
        else if (strcasecmp ("Version", child->key) == 0)
            status = config_set_int (&st->version, child);
        else
        {
            WARNING ("openldap plugin: Option `%s' not allowed here.",
                    child->key);
            status = -1;
        }

        if (status != 0)
            break;
    }

    /* Check if struct is complete.. */
    if ((status == 0) && (st->url == NULL))
    {
        ERROR ("openldap plugin: Instance `%s': "
                "No URL has been configured.",
                st->name);
        status = -1;
    }

    /* Check if URL is valid */
    if ((status == 0) && (st->url != NULL))
    {
        LDAPURLDesc *ludpp;
        int rc;

        if ((rc = ldap_url_parse( st->url, &ludpp)) != 0)
        {
            ERROR ("openldap plugin: Instance `%s': "
                "Invalid URL: `%s'",
                st->name, st->url);
            status = -1;
        }
        else
        {
            st->host = strdup (ludpp->lud_host);
        }

        ldap_free_urldesc(ludpp);
    }

    if (status == 0)
    {
        user_data_t ud;
        char callback_name[3*DATA_MAX_NAME_LEN];

        memset (&ud, 0, sizeof (ud));
        ud.data = st;
        ud.free_func = (void *) ldap_free;

        memset (callback_name, 0, sizeof (callback_name));
        ssnprintf (callback_name, sizeof (callback_name),
                "openldap/%s/%s",
                (st->host != NULL) ? st->host : hostname_g,
                (st->name != NULL) ? st->name : "default"),

        status = plugin_register_complex_read (/* group = */ NULL,
                /* name      = */ callback_name,
                /* callback  = */ ldap_read_host,
                /* interval  = */ NULL,
                /* user_data = */ &ud);
    }

    if (status != 0)
    {
        ldap_free (st);
        return (-1);
    }

    return (0);
} /* }}} int config_add */

static int config (oconfig_item_t *ci)
{
    int i;
    int status = 0;

    for (i = 0; i < ci->children_num; i++)
    {
        oconfig_item_t *child = ci->children + i;

        if (strcasecmp ("Instance", child->key) == 0)
            config_add (child);
        else
            WARNING ("openldap plugin: The configuration option "
                    "\"%s\" is not allowed here. Did you "
                    "forget to add an <Instance /> block "
                    "around the configuration?",
                    child->key);
    } /* for (ci->children) */

    return (status);
} /* int config */

/* }}} End of configuration handling functions */

/* initialize ldap for each host */
static int init_host (ldap_t *st)
{
    LDAP *ld;
    int rc;
    rc = ldap_initialize (&ld, st->url);
    if (rc != LDAP_SUCCESS)
    {
        char errbuf[1024];
        sstrerror (errno, errbuf, sizeof (errbuf));
        ERROR ("ldap_initialize failed: %s", errbuf);
        st->state = 0;
        return (-1);
    }

    st->ld = ld;

    ldap_set_option (st->ld, LDAP_OPT_PROTOCOL_VERSION, &st->version);

    if(st->cacert != NULL)
        ldap_set_option (st->ld, LDAP_OPT_X_TLS_CACERTFILE, st->cacert);

    if(st->verifyhost == 0)
    {
        int never = LDAP_OPT_X_TLS_NEVER;
        ldap_set_option (st->ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &never);
    }

    if(st->starttls != 0)
    {
        rc = ldap_start_tls_s(ld, NULL, NULL);
        if (rc != LDAP_SUCCESS)
        {
            ERROR ("openldap plugin: Failed to start tls on %s: %s",
                    st->url, ldap_err2string (rc));
            st->state = 0;
            return (-1);
        }
    }

    struct berval cred;
    cred.bv_val = "";
    cred.bv_len = 0;

    rc = ldap_sasl_bind_s(st->ld, NULL, NULL, &cred, NULL, NULL, NULL);
    if (rc != LDAP_SUCCESS)
    {
        ERROR ("openldap plugin: Failed to bind to %s: %s",
                st->url, ldap_err2string (rc));
        st->state = 0;
        return (-1);
    }
    else
    {
        DEBUG ("openldap plugin: Successfully connected to %s",
                st->url);
        st->state = 1;
        return (0);
    }
} /* static init_host (ldap_t *st) */

static void submit_value (const char *type, const char *type_instance,
        value_t value, ldap_t *st)
{
    value_list_t vl = VALUE_LIST_INIT;

    vl.values     = &value;
    vl.values_len = 1;

    if ((st->host == NULL)
            || (strcmp ("", st->host) == 0)
            || (strcmp ("localhost", st->host) == 0))
    {
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
    }
    else
    {
        sstrncpy (vl.host, st->host, sizeof (vl.host));
    }

    sstrncpy (vl.plugin, "openldap", sizeof (vl.plugin));
    if (st->name != NULL)
        sstrncpy (vl.plugin_instance, st->name,
                sizeof (vl.plugin_instance));

    sstrncpy (vl.type, type, sizeof (vl.type));
    if (type_instance != NULL)
        sstrncpy (vl.type_instance, type_instance,
                sizeof (vl.type_instance));

    plugin_dispatch_values (&vl);
} /* submit */

static void submit_derive (const char *type, const char *type_instance,
        derive_t d, ldap_t *st)
{
    value_t v;
    v.derive = d;
    submit_value (type, type_instance, v, st);
} /* void submit_derive */

static void submit_gauge (const char *type, const char *type_instance,
        gauge_t g, ldap_t *st)
{
    value_t v;
    v.gauge = g;
    submit_value (type, type_instance, v, st);
} /* void submit_gauge */

static int ldap_read_host (user_data_t *ud)
{
    ldap_t *st;
    LDAPMessage *e, *result;
    char *dn;
    int rc;
    int status;

    char *attrs[3] = { "monitorCounter",
                "monitorOpCompleted",
                "monitorOpInitiated" };

    if ((ud == NULL) || (ud->data == NULL))
    {
        ERROR ("openldap plugin: ldap_read_host: Invalid user data.");
        return (-1);
    }

    st = (ldap_t *) ud->data;

    status = init_host (st);
    if (status != 0)
        return (-1);

    rc = ldap_search_ext_s (st->ld, "cn=Monitor", LDAP_SCOPE_SUBTREE,
        "(!(cn=* *))", attrs, 0,
        NULL, NULL, NULL, 0, &result);

    if (rc != LDAP_SUCCESS)
    {
        ERROR ("openldap plugin: Failed to execute search: %s",
                ldap_err2string (rc));
        ldap_msgfree (result);
        return (-1);
    }

    for (e = ldap_first_entry (st->ld, result); e != NULL;
        e = ldap_next_entry (st->ld, e))
    {
        if ((dn = ldap_get_dn (st->ld, e)) != NULL)
        {
            unsigned long long counter = 0;
            unsigned long long opc = 0;
            unsigned long long opi = 0;

            struct berval counter_data;
            struct berval opc_data;
            struct berval opi_data;

            struct berval **counter_list;
            struct berval **opc_list;
            struct berval **opi_list;

            if ((counter_list = ldap_get_values_len (st->ld, e,
                "monitorCounter")) != NULL)
            {
                counter_data = *counter_list[0];
                counter = atoll (counter_data.bv_val);
            }

            if ((opc_list = ldap_get_values_len (st->ld, e,
                "monitorOpCompleted")) != NULL)
            {
                opc_data = *opc_list[0];
                opc = atoll (opc_data.bv_val);
            }

            if ((opi_list = ldap_get_values_len (st->ld, e,
                "monitorOpInitiated")) != NULL)
            {
                opi_data = *opi_list[0];
                opi = atoll (opi_data.bv_val);
            }

            if (strcmp (dn, "cn=Total,cn=Connections,cn=Monitor")
                    == 0)
            {
                submit_derive ("total_connections", NULL,
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=Current,cn=Connections,cn=Monitor")
                    == 0)
            {
                submit_gauge ("current_connections", NULL,
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=Operations,cn=Monitor") == 0)
            {
                submit_derive ("operations",
                    "completed", opc, st);
                submit_derive ("operations",
                    "initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Bind,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "bind-completed", opc, st);
                submit_derive ("operations",
                    "bind-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=UnBind,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "unbind-completed", opc, st);
                submit_derive ("operations",
                    "unbind-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Search,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "search-completed", opc, st);
                submit_derive ("operations",
                    "search-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Compare,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "compare-completed", opc, st);
                submit_derive ("operations",
                    "compare-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Modify,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "modify-completed", opc, st);
                submit_derive ("operations",
                    "modify-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Modrdn,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "modrdn-completed", opc, st);
                submit_derive ("operations",
                    "modrdn-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Add,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "add-completed", opc, st);
                submit_derive ("operations",
                    "add-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Delete,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "delete-completed", opc, st);
                submit_derive ("operations",
                    "delete-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Abandon,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "abandon-completed", opc, st);
                submit_derive ("operations",
                    "abandon-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Extended,cn=Operations,cn=Monitor")
                    == 0)
            {
                submit_derive ("operations",
                    "extended-completed", opc, st);
                submit_derive ("operations",
                    "extended-initiated", opi, st);
            }
            else if (strcmp (dn,
                    "cn=Bytes,cn=Statistics,cn=Monitor")
                    == 0)
            {
                submit_derive ("derive", "statistics-bytes",
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=PUD,cn=Statistics,cn=Monitor")
                    == 0)
            {
                submit_derive ("derive", "statistics-pdu",
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=Entries,cn=Statistics,cn=Monitor")
                    == 0)
            {
                submit_derive ("derive", "statistics-entries",
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=Referrals,cn=Statistics,cn=Monitor")
                    == 0)
            {
                submit_derive ("derive", "statistics-referrals",
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=Read,cn=Waiters,cn=Monitor")
                    == 0)
            {
                submit_derive ("derive", "waiters-read",
                    counter, st);
            }
            else if (strcmp (dn,
                    "cn=Write,cn=Waiters,cn=Monitor")
                    == 0)
            {
                submit_derive ("derive", "waiters-write",
                    counter, st);
            }

            ldap_value_free_len (counter_list);
            ldap_value_free_len (opc_list);
            ldap_value_free_len (opi_list);
        }

        ldap_memfree (dn);
    }

    ldap_msgfree (result);
    ldap_unbind_ext_s (st->ld, NULL, NULL);
    return (0);
} /* int ldap_read_host */

void module_register (void)
{
    plugin_register_complex_config ("openldap", config);
} /* void module_register */

_______________________________________________
collectd mailing list
[email protected]
http://mailman.verplant.org/listinfo/collectd

Reply via email to