35eae32e2ee891bd81cc0ce9a4737804777a64ec
1 /**
2 * collectd - src/openldap.c
3 * Copyright (C) 2011 Kimo Rosenbaum
4 * Copyright (C) 2014-2015 Marc Fournier
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 * Kimo Rosenbaum <kimor79 at yahoo.com>
26 * Marc Fournier <marc.fournier at camptocamp.com>
27 **/
29 #include "collectd.h"
31 #include "common.h"
32 #include "plugin.h"
33 #include "configfile.h"
35 #if defined(__APPLE__)
36 #pragma clang diagnostic push
37 #pragma clang diagnostic warning "-Wdeprecated-declarations"
38 #endif
40 #include <lber.h>
41 #include <ldap.h>
43 struct cldap_s /* {{{ */
44 {
45 char *name;
47 char *binddn;
48 char *password;
49 char *cacert;
50 char *host;
51 int state;
52 _Bool starttls;
53 int timeout;
54 char *url;
55 _Bool verifyhost;
56 int version;
58 LDAP *ld;
59 };
60 typedef struct cldap_s cldap_t; /* }}} */
62 static cldap_t **databases = NULL;
63 static size_t databases_num = 0;
65 static void cldap_free (cldap_t *st) /* {{{ */
66 {
67 if (st == NULL)
68 return;
70 sfree (st->binddn);
71 sfree (st->password);
72 sfree (st->cacert);
73 sfree (st->host);
74 sfree (st->name);
75 sfree (st->url);
76 if (st->ld)
77 ldap_memfree (st->ld);
78 sfree (st);
79 } /* }}} void cldap_free */
81 /* initialize ldap for each host */
82 static int cldap_init_host (cldap_t *st) /* {{{ */
83 {
84 LDAP *ld;
85 int rc;
87 if (st->state && st->ld)
88 {
89 DEBUG ("openldap plugin: Already connected to %s", st->url);
90 return (0);
91 }
93 rc = ldap_initialize (&ld, st->url);
94 if (rc != LDAP_SUCCESS)
95 {
96 ERROR ("openldap plugin: ldap_initialize failed: %s",
97 ldap_err2string (rc));
98 st->state = 0;
99 ldap_unbind_ext_s (ld, NULL, NULL);
100 return (-1);
101 }
103 st->ld = ld;
105 ldap_set_option (st->ld, LDAP_OPT_PROTOCOL_VERSION, &st->version);
107 ldap_set_option (st->ld, LDAP_OPT_TIMEOUT,
108 &(const struct timeval){st->timeout, 0});
110 ldap_set_option (st->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
112 if (st->cacert != NULL)
113 ldap_set_option (st->ld, LDAP_OPT_X_TLS_CACERTFILE, st->cacert);
115 if (st->verifyhost == 0)
116 {
117 int never = LDAP_OPT_X_TLS_NEVER;
118 ldap_set_option (st->ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &never);
119 }
121 if (st->starttls != 0)
122 {
123 rc = ldap_start_tls_s (ld, NULL, NULL);
124 if (rc != LDAP_SUCCESS)
125 {
126 ERROR ("openldap plugin: Failed to start tls on %s: %s",
127 st->url, ldap_err2string (rc));
128 st->state = 0;
129 ldap_unbind_ext_s (st->ld, NULL, NULL);
130 return (-1);
131 }
132 }
134 struct berval cred;
135 if (st->password != NULL)
136 {
137 cred.bv_val = st->password;
138 cred.bv_len = strlen (st->password);
139 }
140 else
141 {
142 cred.bv_val = "";
143 cred.bv_len = 0;
144 }
146 rc = ldap_sasl_bind_s (st->ld, st->binddn, LDAP_SASL_SIMPLE, &cred,
147 NULL, NULL, NULL);
148 if (rc != LDAP_SUCCESS)
149 {
150 ERROR ("openldap plugin: Failed to bind to %s: %s",
151 st->url, ldap_err2string (rc));
152 st->state = 0;
153 ldap_unbind_ext_s (st->ld, NULL, NULL);
154 return (-1);
155 }
156 else
157 {
158 DEBUG ("openldap plugin: Successfully connected to %s",
159 st->url);
160 st->state = 1;
161 return (0);
162 }
163 } /* }}} static cldap_init_host */
165 static void cldap_submit_value (const char *type, const char *type_instance, /* {{{ */
166 value_t value, cldap_t *st)
167 {
168 value_list_t vl = VALUE_LIST_INIT;
170 vl.values = &value;
171 vl.values_len = 1;
173 if ((st->host == NULL)
174 || (strcmp ("", st->host) == 0)
175 || (strcmp ("localhost", st->host) == 0))
176 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
177 else
178 sstrncpy (vl.host, st->host, sizeof (vl.host));
180 sstrncpy (vl.plugin, "openldap", sizeof (vl.plugin));
181 if (st->name != NULL)
182 sstrncpy (vl.plugin_instance, st->name,
183 sizeof (vl.plugin_instance));
185 sstrncpy (vl.type, type, sizeof (vl.type));
186 if (type_instance != NULL)
187 sstrncpy (vl.type_instance, type_instance,
188 sizeof (vl.type_instance));
190 plugin_dispatch_values (&vl);
191 } /* }}} void cldap_submit_value */
193 static void cldap_submit_derive (const char *type, const char *type_instance, /* {{{ */
194 derive_t d, cldap_t *st)
195 {
196 value_t v;
197 v.derive = d;
198 cldap_submit_value (type, type_instance, v, st);
199 } /* }}} void cldap_submit_derive */
201 static void cldap_submit_gauge (const char *type, const char *type_instance, /* {{{ */
202 gauge_t g, cldap_t *st)
203 {
204 value_t v;
205 v.gauge = g;
206 cldap_submit_value (type, type_instance, v, st);
207 } /* }}} void cldap_submit_gauge */
209 static int cldap_read_host (user_data_t *ud) /* {{{ */
210 {
211 cldap_t *st;
212 LDAPMessage *e, *result;
213 char *dn;
214 int rc;
215 int status;
217 char *attrs[9] = { "monitorCounter",
218 "monitorOpCompleted",
219 "monitorOpInitiated",
220 "monitoredInfo",
221 "olmBDBEntryCache",
222 "olmBDBDNCache",
223 "olmBDBIDLCache",
224 "namingContexts",
225 NULL };
227 if ((ud == NULL) || (ud->data == NULL))
228 {
229 ERROR ("openldap plugin: cldap_read_host: Invalid user data.");
230 return (-1);
231 }
233 st = (cldap_t *) ud->data;
235 status = cldap_init_host (st);
236 if (status != 0)
237 return (-1);
239 rc = ldap_search_ext_s (st->ld, "cn=Monitor", LDAP_SCOPE_SUBTREE,
240 "(|(!(cn=* *))(cn=Database*))", attrs, 0,
241 NULL, NULL, NULL, 0, &result);
243 if (rc != LDAP_SUCCESS)
244 {
245 ERROR ("openldap plugin: Failed to execute search: %s",
246 ldap_err2string (rc));
247 ldap_msgfree (result);
248 st->state = 0;
249 ldap_unbind_ext_s (st->ld, NULL, NULL);
250 return (-1);
251 }
253 for (e = ldap_first_entry (st->ld, result); e != NULL;
254 e = ldap_next_entry (st->ld, e))
255 {
256 if ((dn = ldap_get_dn (st->ld, e)) != NULL)
257 {
258 unsigned long long counter = 0;
259 unsigned long long opc = 0;
260 unsigned long long opi = 0;
261 unsigned long long info = 0;
263 struct berval counter_data;
264 struct berval opc_data;
265 struct berval opi_data;
266 struct berval info_data;
267 struct berval olmbdb_data;
268 struct berval nc_data;
270 struct berval **counter_list;
271 struct berval **opc_list;
272 struct berval **opi_list;
273 struct berval **info_list;
274 struct berval **olmbdb_list;
275 struct berval **nc_list;
277 if ((counter_list = ldap_get_values_len (st->ld, e,
278 "monitorCounter")) != NULL)
279 {
280 counter_data = *counter_list[0];
281 counter = atoll (counter_data.bv_val);
282 }
284 if ((opc_list = ldap_get_values_len (st->ld, e,
285 "monitorOpCompleted")) != NULL)
286 {
287 opc_data = *opc_list[0];
288 opc = atoll (opc_data.bv_val);
289 }
291 if ((opi_list = ldap_get_values_len (st->ld, e,
292 "monitorOpInitiated")) != NULL)
293 {
294 opi_data = *opi_list[0];
295 opi = atoll (opi_data.bv_val);
296 }
298 if ((info_list = ldap_get_values_len (st->ld, e,
299 "monitoredInfo")) != NULL)
300 {
301 info_data = *info_list[0];
302 info = atoll (info_data.bv_val);
303 }
305 if (strcmp (dn, "cn=Total,cn=Connections,cn=Monitor")
306 == 0)
307 {
308 cldap_submit_derive ("total_connections", NULL,
309 counter, st);
310 }
311 else if (strcmp (dn,
312 "cn=Current,cn=Connections,cn=Monitor")
313 == 0)
314 {
315 cldap_submit_gauge ("current_connections", NULL,
316 counter, st);
317 }
318 else if (strcmp (dn,
319 "cn=Operations,cn=Monitor") == 0)
320 {
321 cldap_submit_derive ("operations",
322 "completed", opc, st);
323 cldap_submit_derive ("operations",
324 "initiated", opi, st);
325 }
326 else if (strcmp (dn,
327 "cn=Bind,cn=Operations,cn=Monitor")
328 == 0)
329 {
330 cldap_submit_derive ("operations",
331 "bind-completed", opc, st);
332 cldap_submit_derive ("operations",
333 "bind-initiated", opi, st);
334 }
335 else if (strcmp (dn,
336 "cn=UnBind,cn=Operations,cn=Monitor")
337 == 0)
338 {
339 cldap_submit_derive ("operations",
340 "unbind-completed", opc, st);
341 cldap_submit_derive ("operations",
342 "unbind-initiated", opi, st);
343 }
344 else if (strcmp (dn,
345 "cn=Search,cn=Operations,cn=Monitor")
346 == 0)
347 {
348 cldap_submit_derive ("operations",
349 "search-completed", opc, st);
350 cldap_submit_derive ("operations",
351 "search-initiated", opi, st);
352 }
353 else if (strcmp (dn,
354 "cn=Compare,cn=Operations,cn=Monitor")
355 == 0)
356 {
357 cldap_submit_derive ("operations",
358 "compare-completed", opc, st);
359 cldap_submit_derive ("operations",
360 "compare-initiated", opi, st);
361 }
362 else if (strcmp (dn,
363 "cn=Modify,cn=Operations,cn=Monitor")
364 == 0)
365 {
366 cldap_submit_derive ("operations",
367 "modify-completed", opc, st);
368 cldap_submit_derive ("operations",
369 "modify-initiated", opi, st);
370 }
371 else if (strcmp (dn,
372 "cn=Modrdn,cn=Operations,cn=Monitor")
373 == 0)
374 {
375 cldap_submit_derive ("operations",
376 "modrdn-completed", opc, st);
377 cldap_submit_derive ("operations",
378 "modrdn-initiated", opi, st);
379 }
380 else if (strcmp (dn,
381 "cn=Add,cn=Operations,cn=Monitor")
382 == 0)
383 {
384 cldap_submit_derive ("operations",
385 "add-completed", opc, st);
386 cldap_submit_derive ("operations",
387 "add-initiated", opi, st);
388 }
389 else if (strcmp (dn,
390 "cn=Delete,cn=Operations,cn=Monitor")
391 == 0)
392 {
393 cldap_submit_derive ("operations",
394 "delete-completed", opc, st);
395 cldap_submit_derive ("operations",
396 "delete-initiated", opi, st);
397 }
398 else if (strcmp (dn,
399 "cn=Abandon,cn=Operations,cn=Monitor")
400 == 0)
401 {
402 cldap_submit_derive ("operations",
403 "abandon-completed", opc, st);
404 cldap_submit_derive ("operations",
405 "abandon-initiated", opi, st);
406 }
407 else if (strcmp (dn,
408 "cn=Extended,cn=Operations,cn=Monitor")
409 == 0)
410 {
411 cldap_submit_derive ("operations",
412 "extended-completed", opc, st);
413 cldap_submit_derive ("operations",
414 "extended-initiated", opi, st);
415 }
416 else if ((strncmp (dn, "cn=Database", 11) == 0)
417 && ((nc_list = ldap_get_values_len
418 (st->ld, e, "namingContexts")) != NULL))
419 {
420 nc_data = *nc_list[0];
421 char typeinst[DATA_MAX_NAME_LEN];
423 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
424 "olmBDBEntryCache")) != NULL)
425 {
426 olmbdb_data = *olmbdb_list[0];
427 ssnprintf (typeinst, sizeof (typeinst),
428 "bdbentrycache-%s", nc_data.bv_val);
429 cldap_submit_gauge ("cache_size", typeinst,
430 atoll (olmbdb_data.bv_val), st);
431 ldap_value_free_len (olmbdb_list);
432 }
434 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
435 "olmBDBDNCache")) != NULL)
436 {
437 olmbdb_data = *olmbdb_list[0];
438 ssnprintf (typeinst, sizeof (typeinst),
439 "bdbdncache-%s", nc_data.bv_val);
440 cldap_submit_gauge ("cache_size", typeinst,
441 atoll (olmbdb_data.bv_val), st);
442 ldap_value_free_len (olmbdb_list);
443 }
445 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
446 "olmBDBIDLCache")) != NULL)
447 {
448 olmbdb_data = *olmbdb_list[0];
449 ssnprintf (typeinst, sizeof (typeinst),
450 "bdbidlcache-%s", nc_data.bv_val);
451 cldap_submit_gauge ("cache_size", typeinst,
452 atoll (olmbdb_data.bv_val), st);
453 ldap_value_free_len (olmbdb_list);
454 }
456 ldap_value_free_len (nc_list);
457 }
458 else if (strcmp (dn,
459 "cn=Bytes,cn=Statistics,cn=Monitor")
460 == 0)
461 {
462 cldap_submit_derive ("derive", "statistics-bytes",
463 counter, st);
464 }
465 else if (strcmp (dn,
466 "cn=PDU,cn=Statistics,cn=Monitor")
467 == 0)
468 {
469 cldap_submit_derive ("derive", "statistics-pdu",
470 counter, st);
471 }
472 else if (strcmp (dn,
473 "cn=Entries,cn=Statistics,cn=Monitor")
474 == 0)
475 {
476 cldap_submit_derive ("derive", "statistics-entries",
477 counter, st);
478 }
479 else if (strcmp (dn,
480 "cn=Referrals,cn=Statistics,cn=Monitor")
481 == 0)
482 {
483 cldap_submit_derive ("derive", "statistics-referrals",
484 counter, st);
485 }
486 else if (strcmp (dn,
487 "cn=Open,cn=Threads,cn=Monitor")
488 == 0)
489 {
490 cldap_submit_gauge ("threads", "threads-open",
491 info, st);
492 }
493 else if (strcmp (dn,
494 "cn=Starting,cn=Threads,cn=Monitor")
495 == 0)
496 {
497 cldap_submit_gauge ("threads", "threads-starting",
498 info, st);
499 }
500 else if (strcmp (dn,
501 "cn=Active,cn=Threads,cn=Monitor")
502 == 0)
503 {
504 cldap_submit_gauge ("threads", "threads-active",
505 info, st);
506 }
507 else if (strcmp (dn,
508 "cn=Pending,cn=Threads,cn=Monitor")
509 == 0)
510 {
511 cldap_submit_gauge ("threads", "threads-pending",
512 info, st);
513 }
514 else if (strcmp (dn,
515 "cn=Backload,cn=Threads,cn=Monitor")
516 == 0)
517 {
518 cldap_submit_gauge ("threads", "threads-backload",
519 info, st);
520 }
521 else if (strcmp (dn,
522 "cn=Read,cn=Waiters,cn=Monitor")
523 == 0)
524 {
525 cldap_submit_derive ("derive", "waiters-read",
526 counter, st);
527 }
528 else if (strcmp (dn,
529 "cn=Write,cn=Waiters,cn=Monitor")
530 == 0)
531 {
532 cldap_submit_derive ("derive", "waiters-write",
533 counter, st);
534 }
536 ldap_value_free_len (counter_list);
537 ldap_value_free_len (opc_list);
538 ldap_value_free_len (opi_list);
539 ldap_value_free_len (info_list);
540 }
542 ldap_memfree (dn);
543 }
545 ldap_msgfree (result);
546 return (0);
547 } /* }}} int cldap_read_host */
549 /* Configuration handling functions {{{
550 *
551 * <Plugin ldap>
552 * <Instance "plugin_instance1">
553 * URL "ldap://localhost"
554 * ...
555 * </Instance>
556 * </Plugin>
557 */
559 static int cldap_config_add (oconfig_item_t *ci) /* {{{ */
560 {
561 cldap_t *st;
562 int i;
563 int status;
565 st = calloc (1, sizeof (*st));
566 if (st == NULL)
567 {
568 ERROR ("openldap plugin: calloc failed.");
569 return (-1);
570 }
572 status = cf_util_get_string (ci, &st->name);
573 if (status != 0)
574 {
575 sfree (st);
576 return (status);
577 }
579 st->starttls = 0;
580 st->timeout = (long) (CDTIME_T_TO_MS(plugin_get_interval()) / 1000);
581 st->verifyhost = 1;
582 st->version = LDAP_VERSION3;
584 for (i = 0; i < ci->children_num; i++)
585 {
586 oconfig_item_t *child = ci->children + i;
588 if (strcasecmp ("BindDN", child->key) == 0)
589 status = cf_util_get_string (child, &st->binddn);
590 else if (strcasecmp ("Password", child->key) == 0)
591 status = cf_util_get_string (child, &st->password);
592 else if (strcasecmp ("CACert", child->key) == 0)
593 status = cf_util_get_string (child, &st->cacert);
594 else if (strcasecmp ("StartTLS", child->key) == 0)
595 status = cf_util_get_boolean (child, &st->starttls);
596 else if (strcasecmp ("Timeout", child->key) == 0)
597 status = cf_util_get_int (child, &st->timeout);
598 else if (strcasecmp ("URL", child->key) == 0)
599 status = cf_util_get_string (child, &st->url);
600 else if (strcasecmp ("VerifyHost", child->key) == 0)
601 status = cf_util_get_boolean (child, &st->verifyhost);
602 else if (strcasecmp ("Version", child->key) == 0)
603 status = cf_util_get_int (child, &st->version);
604 else
605 {
606 WARNING ("openldap plugin: Option `%s' not allowed here.",
607 child->key);
608 status = -1;
609 }
611 if (status != 0)
612 break;
613 }
615 /* Check if struct is complete.. */
616 if ((status == 0) && (st->url == NULL))
617 {
618 ERROR ("openldap plugin: Instance `%s': "
619 "No URL has been configured.",
620 st->name);
621 status = -1;
622 }
624 /* Check if URL is valid */
625 if ((status == 0) && (st->url != NULL))
626 {
627 LDAPURLDesc *ludpp;
629 if (ldap_url_parse (st->url, &ludpp) != 0)
630 {
631 ERROR ("openldap plugin: Instance `%s': "
632 "Invalid URL: `%s'",
633 st->name, st->url);
634 status = -1;
635 }
637 if ((status == 0) && (ludpp->lud_host != NULL))
638 st->host = strdup (ludpp->lud_host);
640 ldap_free_urldesc (ludpp);
641 }
643 if (status == 0)
644 {
645 cldap_t **temp;
647 temp = (cldap_t **) realloc (databases,
648 sizeof (*databases) * (databases_num + 1));
650 if (temp == NULL)
651 {
652 ERROR ("openldap plugin: realloc failed");
653 status = -1;
654 }
655 else
656 {
657 user_data_t ud = { 0 };
658 char callback_name[3*DATA_MAX_NAME_LEN] = { 0 };
660 databases = temp;
661 databases[databases_num] = st;
662 databases_num++;
664 ud.data = st;
666 ssnprintf (callback_name, sizeof (callback_name),
667 "openldap/%s/%s",
668 (st->host != NULL) ? st->host : hostname_g,
669 (st->name != NULL) ? st->name : "default"),
671 status = plugin_register_complex_read (/* group = */ NULL,
672 /* name = */ callback_name,
673 /* callback = */ cldap_read_host,
674 /* interval = */ 0,
675 /* user_data = */ &ud);
676 }
677 }
679 if (status != 0)
680 {
681 cldap_free (st);
682 return (-1);
683 }
685 return (0);
686 } /* }}} int cldap_config_add */
688 static int cldap_config (oconfig_item_t *ci) /* {{{ */
689 {
690 int i;
691 int status = 0;
693 for (i = 0; i < ci->children_num; i++)
694 {
695 oconfig_item_t *child = ci->children + i;
697 if (strcasecmp ("Instance", child->key) == 0)
698 cldap_config_add (child);
699 else
700 WARNING ("openldap plugin: The configuration option "
701 "\"%s\" is not allowed here. Did you "
702 "forget to add an <Instance /> block "
703 "around the configuration?",
704 child->key);
705 } /* for (ci->children) */
707 return (status);
708 } /* }}} int cldap_config */
710 /* }}} End of configuration handling functions */
712 static int cldap_init (void) /* {{{ */
713 {
714 /* Initialize LDAP library while still single-threaded as recommended in
715 * ldap_initialize(3) */
716 int debug_level;
717 ldap_get_option (NULL, LDAP_OPT_DEBUG_LEVEL, &debug_level);
718 return (0);
719 } /* }}} int cldap_init */
721 static int cldap_shutdown (void) /* {{{ */
722 {
723 size_t i;
725 for (i = 0; i < databases_num; i++)
726 if (databases[i]->ld != NULL)
727 ldap_unbind_ext_s (databases[i]->ld, NULL, NULL);
728 sfree (databases);
729 databases_num = 0;
731 return (0);
732 } /* }}} int cldap_shutdown */
734 void module_register (void) /* {{{ */
735 {
736 plugin_register_complex_config ("openldap", cldap_config);
737 plugin_register_init ("openldap", cldap_init);
738 plugin_register_shutdown ("openldap", cldap_shutdown);
739 } /* }}} void module_register */
741 #if defined(__APPLE__)
742 #pragma clang diagnostic pop
743 #endif