From: Kimo Rosenbaum Date: Tue, 18 Jan 2011 21:35:23 +0000 (-0800) Subject: new plugin: openldap X-Git-Tag: collectd-5.5.0~152^2~24 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=6ecb1f031e09d4ac781ce18ec6430bea24acb02f;p=collectd.git new plugin: openldap --- diff --git a/configure.ac b/configure.ac index 0d0d0435..38df1c0e 100644 --- a/configure.ac +++ b/configure.ac @@ -2232,6 +2232,64 @@ AC_SUBST(JAVA_LIBS) AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes") # }}} +# --with-libldap {{{ +AC_ARG_WITH(libldap, [AS_HELP_STRING([--with-libldap@<:@=PREFIX@:>@], [Path to libldap.])], +[ + if test "x$withval" = "xyes" + then + with_libldap="yes" + else if test "x$withval" = "xno" + then + with_libldap="no" + else + with_libldap="yes" + LIBLDAP_CPPFLAGS="$LIBLDAP_CPPFLAGS -I$withval/include" + LIBLDAP_LDFLAGS="$LIBLDAP_LDFLAGS -L$withval/lib" + fi; fi +], +[with_libldap="yes"]) + +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" + +CPPFLAGS="$CPPFLAGS $LIBLDAP_CPPFLAGS" +LDFLAGS="$LDFLAGS $LIBLDAP_LDFLAGS" + +if test "x$with_libldap" = "xyes" +then + if test "x$LIBLDAP_CPPFLAGS" != "x" + then + AC_MSG_NOTICE([libldap CPPFLAGS: $LIBLDAP_CPPFLAGS]) + fi + AC_CHECK_HEADERS(ldap.h, + [with_libldap="yes"], + [with_libldap="no ('ldap.h' not found)"]) +fi +if test "x$with_libldap" = "xyes" +then + if test "x$LIBLDAP_LDFLAGS" != "x" + then + AC_MSG_NOTICE([libldap LDFLAGS: $LIBLDAP_LDFLAGS]) + fi + AC_CHECK_LIB(ldap, ldap_initialize, + [with_libldap="yes"], + [with_libldap="no (symbol 'ldap_initialize' not found)"]) + +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" + +if test "x$with_libldap" = "xyes" +then + BUILD_WITH_LIBLDAP_CPPFLAGS="$LIBLDAP_CPPFLAGS" + BUILD_WITH_LIBLDAP_LDFLAGS="$LIBLDAP_LDFLAGS" + AC_SUBST(BUILD_WITH_LIBLDAP_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBLDAP_LDFLAGS) +fi +AM_CONDITIONAL(BUILD_WITH_LIBLDAP, test "x$with_libldap" = "xyes") +# }}} + # --with-liblvm2app {{{ with_liblvm2app_cppflags="" with_liblvm2app_ldflags="" @@ -5365,6 +5423,7 @@ AC_PLUGIN([numa], [$plugin_numa], [NUMA virtual memory statistics]) AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics]) AC_PLUGIN([olsrd], [yes], [olsrd statistics]) AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics]) +AC_PLUGIN([openldap], [$with_libldap], [OpenLDAP statistics]) AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics]) AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin]) AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter]) @@ -5615,6 +5674,7 @@ Configuration: libjvm . . . . . . . $with_java libkstat . . . . . . $with_kstat libkvm . . . . . . . $with_libkvm + libldap . . . . . . . $with_libldap liblvm2app . . . . . $with_liblvm2app libmemcached . . . . $with_libmemcached libmnl . . . . . . . $with_libmnl @@ -5730,6 +5790,7 @@ Configuration: nut . . . . . . . . . $enable_nut olsrd . . . . . . . . $enable_olsrd onewire . . . . . . . $enable_onewire + openldap . . . . . . $enable_openldap openvpn . . . . . . . $enable_openvpn oracle . . . . . . . $enable_oracle perl . . . . . . . . $enable_perl diff --git a/src/Makefile.am b/src/Makefile.am index b8623653..61b7d47c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -917,6 +917,16 @@ collectd_LDADD += "-dlopen" onewire.la collectd_DEPENDENCIES += onewire.la endif +if BUILD_PLUGIN_OPENLDAP +pkglib_LTLIBRARIES += openldap.la +openldap_la_SOURCES = openldap.c +openldap_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBLDAP_LDFLAGS) +openldap_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBLDAP_CPPFLAGS) +openldap_la_LIBADD = -lldap +collectd_LDADD += "-dlopen" openldap.la +collectd_DEPENDENCIES += openldap.la +endif + if BUILD_PLUGIN_OPENVPN pkglib_LTLIBRARIES += openvpn.la openvpn_la_SOURCES = openvpn.c diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index bba90557..014bfdc1 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -4229,6 +4229,53 @@ short: If it works for you: Great! But keep in mind that the config I change, though this is unlikely. Oh, and if you want to help improving this plugin, just send a short notice to the mailing list. ThanksE:) +=head2 Plugin C + +To configure the C-plugin you first need to configure the OpenLDAP +server correctly. The backend database C needs to be loaded and +working. + +The configuration of the I plugin consists of one or more +CInstanceE/E> blocks. Each block requires one string argument +as the instance name. For example: + + + + URL "ldap://localhost/" + + + URL "ldaps://localhost/" + + + +The instance name will be used as the I. To emulate the old +(versionE4) behavior, you can use an empty string (""). In order for the +plugin to work correctly, each instance name must be unique. This is not +enforced by the plugin and it is your responsibility to ensure it. + +The following options are accepted within each I block: + +=over 4 + +=item B I + +Sets the URL of the C server. This option is I. + +=item B B + +Enable or disable peer host name verification. If enabled, the plugin checks +if the C or a C field of the SSL +certificate matches the host name provided by the B option. If this +identity check fails, the connection is aborted. Enabled by default. + +=item B I + +File that holds one or more SSL certificates. If you want to use TLS/SSL you may +possibly need this option. What CA certificates come bundled with C +and are checked by default depends on the distribution you use. + +=back + =head2 Plugin C The OpenVPN plugin reads a status file maintained by OpenVPN and gathers diff --git a/src/openldap.c b/src/openldap.c new file mode 100644 index 00000000..6a9bd2d6 --- /dev/null +++ b/src/openldap.c @@ -0,0 +1,648 @@ +/** + * collectd - src/openldap.c + * Copyright (C) 2011 Kimo Rosenbaum + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Kimo Rosenbaum + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#include +#include + +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 {{{ + * + * + * + * URL "ldap://localhost" + * ... + * + * + */ + +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 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 */