Created
June 4, 2014 15:52
-
-
Save kimor79/684e2e7777f4f48037e9 to your computer and use it in GitHub Desktop.
openldap plugin collectd original email
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment