Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / openldap.c
1 /**
2  * collectd - src/openldap.c
3  * Copyright (C) 2011       Kimo Rosenbaum
4  * Copyright (C) 2014       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  **/
28
29 #include "collectd.h"
30 #include "common.h"
31 #include "plugin.h"
32 #include "configfile.h"
33
34 #if defined(__APPLE__)
35 #pragma clang diagnostic push
36 #pragma clang diagnostic warning "-Wdeprecated-declarations"
37 #endif
38
39 #include <lber.h>
40 #include <ldap.h>
41
42 struct cldap_s /* {{{ */
43 {
44         char *name;
45
46         char *cacert;
47         char *host;
48         int   state;
49         _Bool starttls;
50         int   timeout;
51         char *url;
52         _Bool verifyhost;
53         int   version;
54
55         LDAP *ld;
56 };
57 typedef struct cldap_s cldap_t; /* }}} */
58
59 static void cldap_free (cldap_t *st) /* {{{ */
60 {
61         if (st == NULL)
62                 return;
63
64         sfree (st->cacert);
65         sfree (st->host);
66         sfree (st->name);
67         sfree (st->url);
68         if (st->ld)
69                 ldap_memfree (st->ld);
70         sfree (st);
71 } /* }}} void cldap_free */
72
73 /* initialize ldap for each host */
74 static int cldap_init_host (cldap_t *st) /* {{{ */
75 {
76         LDAP *ld;
77         int rc;
78         rc = ldap_initialize (&ld, st->url);
79         if (rc != LDAP_SUCCESS)
80         {
81                 ERROR ("openldap plugin: ldap_initialize failed: %s",
82                         ldap_err2string (rc));
83                 st->state = 0;
84                 ldap_unbind_ext_s (ld, NULL, NULL);
85                 return (-1);
86         }
87
88         st->ld = ld;
89
90         ldap_set_option (st->ld, LDAP_OPT_PROTOCOL_VERSION, &st->version);
91
92         ldap_set_option (st->ld, LDAP_OPT_TIMEOUT,
93                 &(const struct timeval){st->timeout, 0});
94
95         if (st->cacert != NULL)
96                 ldap_set_option (st->ld, LDAP_OPT_X_TLS_CACERTFILE, st->cacert);
97
98         if (st->verifyhost == 0)
99         {
100                 int never = LDAP_OPT_X_TLS_NEVER;
101                 ldap_set_option (st->ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &never);
102         }
103
104         if (st->starttls != 0)
105         {
106                 rc = ldap_start_tls_s (ld, NULL, NULL);
107                 if (rc != LDAP_SUCCESS)
108                 {
109                         ERROR ("openldap plugin: Failed to start tls on %s: %s",
110                                         st->url, ldap_err2string (rc));
111                         st->state = 0;
112                         ldap_unbind_ext_s (st->ld, NULL, NULL);
113                         return (-1);
114                 }
115         }
116
117         struct berval cred;
118         cred.bv_val = "";
119         cred.bv_len = 0;
120
121         rc = ldap_sasl_bind_s (st->ld, NULL, NULL, &cred, NULL, NULL, NULL);
122         if (rc != LDAP_SUCCESS)
123         {
124                 ERROR ("openldap plugin: Failed to bind to %s: %s",
125                                 st->url, ldap_err2string (rc));
126                 st->state = 0;
127                 ldap_unbind_ext_s (st->ld, NULL, NULL);
128                 return (-1);
129         }
130         else
131         {
132                 DEBUG ("openldap plugin: Successfully connected to %s",
133                                 st->url);
134                 st->state = 1;
135                 return (0);
136         }
137 } /* }}} static cldap_init_host */
138
139 static void cldap_submit_value (const char *type, const char *type_instance, /* {{{ */
140                 value_t value, cldap_t *st)
141 {
142         value_list_t vl = VALUE_LIST_INIT;
143
144         vl.values     = &value;
145         vl.values_len = 1;
146
147         if ((st->host == NULL)
148                         || (strcmp ("", st->host) == 0)
149                         || (strcmp ("localhost", st->host) == 0))
150         {
151                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
152         }
153         else
154         {
155                 sstrncpy (vl.host, st->host, sizeof (vl.host));
156         }
157
158         sstrncpy (vl.plugin, "openldap", sizeof (vl.plugin));
159         if (st->name != NULL)
160                 sstrncpy (vl.plugin_instance, st->name,
161                                 sizeof (vl.plugin_instance));
162
163         sstrncpy (vl.type, type, sizeof (vl.type));
164         if (type_instance != NULL)
165                 sstrncpy (vl.type_instance, type_instance,
166                                 sizeof (vl.type_instance));
167
168         plugin_dispatch_values (&vl);
169 } /* }}} void cldap_submit_value */
170
171 static void cldap_submit_derive (const char *type, const char *type_instance, /* {{{ */
172                 derive_t d, cldap_t *st)
173 {
174         value_t v;
175         v.derive = d;
176         cldap_submit_value (type, type_instance, v, st);
177 } /* }}} void cldap_submit_derive */
178
179 static void cldap_submit_gauge (const char *type, const char *type_instance, /* {{{ */
180                 gauge_t g, cldap_t *st)
181 {
182         value_t v;
183         v.gauge = g;
184         cldap_submit_value (type, type_instance, v, st);
185 } /* }}} void cldap_submit_gauge */
186
187 static int cldap_read_host (user_data_t *ud) /* {{{ */
188 {
189         cldap_t *st;
190         LDAPMessage *e, *result;
191         char *dn;
192         int rc;
193         int status;
194
195         char *attrs[9] = { "monitorCounter",
196                                 "monitorOpCompleted",
197                                 "monitorOpInitiated",
198                                 "monitoredInfo",
199                                 "olmBDBEntryCache",
200                                 "olmBDBDNCache",
201                                 "olmBDBIDLCache",
202                                 "namingContexts",
203                                 NULL };
204
205         if ((ud == NULL) || (ud->data == NULL))
206         {
207                 ERROR ("openldap plugin: cldap_read_host: Invalid user data.");
208                 return (-1);
209         }
210
211         st = (cldap_t *) ud->data;
212
213         status = cldap_init_host (st);
214         if (status != 0)
215                 return (-1);
216
217         rc = ldap_search_ext_s (st->ld, "cn=Monitor", LDAP_SCOPE_SUBTREE,
218                 "(|(!(cn=* *))(cn=Database*))", attrs, 0,
219                 NULL, NULL, NULL, 0, &result);
220
221         if (rc != LDAP_SUCCESS)
222         {
223                 ERROR ("openldap plugin: Failed to execute search: %s",
224                                 ldap_err2string (rc));
225                 ldap_msgfree (result);
226                 ldap_unbind_ext_s (st->ld, NULL, NULL);
227                 return (-1);
228         }
229
230         for (e = ldap_first_entry (st->ld, result); e != NULL;
231                 e = ldap_next_entry (st->ld, e))
232         {
233                 if ((dn = ldap_get_dn (st->ld, e)) != NULL)
234                 {
235                         unsigned long long counter = 0;
236                         unsigned long long opc = 0;
237                         unsigned long long opi = 0;
238                         unsigned long long info = 0;
239
240                         struct berval counter_data;
241                         struct berval opc_data;
242                         struct berval opi_data;
243                         struct berval info_data;
244                         struct berval olmbdb_data;
245                         struct berval nc_data;
246
247                         struct berval **counter_list;
248                         struct berval **opc_list;
249                         struct berval **opi_list;
250                         struct berval **info_list;
251                         struct berval **olmbdb_list;
252                         struct berval **nc_list;
253
254                         if ((counter_list = ldap_get_values_len (st->ld, e,
255                                 "monitorCounter")) != NULL)
256                         {
257                                 counter_data = *counter_list[0];
258                                 counter = atoll (counter_data.bv_val);
259                         }
260
261                         if ((opc_list = ldap_get_values_len (st->ld, e,
262                                 "monitorOpCompleted")) != NULL)
263                         {
264                                 opc_data = *opc_list[0];
265                                 opc = atoll (opc_data.bv_val);
266                         }
267
268                         if ((opi_list = ldap_get_values_len (st->ld, e,
269                                 "monitorOpInitiated")) != NULL)
270                         {
271                                 opi_data = *opi_list[0];
272                                 opi = atoll (opi_data.bv_val);
273                         }
274
275                         if ((info_list = ldap_get_values_len (st->ld, e,
276                                 "monitoredInfo")) != NULL)
277                         {
278                                 info_data = *info_list[0];
279                                 info = atoll (info_data.bv_val);
280                         }
281
282                         if (strcmp (dn, "cn=Total,cn=Connections,cn=Monitor")
283                                         == 0)
284                         {
285                                 cldap_submit_derive ("total_connections", NULL,
286                                         counter, st);
287                         }
288                         else if (strcmp (dn,
289                                         "cn=Current,cn=Connections,cn=Monitor")
290                                         == 0)
291                         {
292                                 cldap_submit_gauge ("current_connections", NULL,
293                                         counter, st);
294                         }
295                         else if (strcmp (dn,
296                                         "cn=Operations,cn=Monitor") == 0)
297                         {
298                                 cldap_submit_derive ("operations",
299                                         "completed", opc, st);
300                                 cldap_submit_derive ("operations",
301                                         "initiated", opi, st);
302                         }
303                         else if (strcmp (dn,
304                                         "cn=Bind,cn=Operations,cn=Monitor")
305                                         == 0)
306                         {
307                                 cldap_submit_derive ("operations",
308                                         "bind-completed", opc, st);
309                                 cldap_submit_derive ("operations",
310                                         "bind-initiated", opi, st);
311                         }
312                         else if (strcmp (dn,
313                                         "cn=UnBind,cn=Operations,cn=Monitor")
314                                         == 0)
315                         {
316                                 cldap_submit_derive ("operations",
317                                         "unbind-completed", opc, st);
318                                 cldap_submit_derive ("operations",
319                                         "unbind-initiated", opi, st);
320                         }
321                         else if (strcmp (dn,
322                                         "cn=Search,cn=Operations,cn=Monitor")
323                                         == 0)
324                         {
325                                 cldap_submit_derive ("operations",
326                                         "search-completed", opc, st);
327                                 cldap_submit_derive ("operations",
328                                         "search-initiated", opi, st);
329                         }
330                         else if (strcmp (dn,
331                                         "cn=Compare,cn=Operations,cn=Monitor")
332                                         == 0)
333                         {
334                                 cldap_submit_derive ("operations",
335                                         "compare-completed", opc, st);
336                                 cldap_submit_derive ("operations",
337                                         "compare-initiated", opi, st);
338                         }
339                         else if (strcmp (dn,
340                                         "cn=Modify,cn=Operations,cn=Monitor")
341                                         == 0)
342                         {
343                                 cldap_submit_derive ("operations",
344                                         "modify-completed", opc, st);
345                                 cldap_submit_derive ("operations",
346                                         "modify-initiated", opi, st);
347                         }
348                         else if (strcmp (dn,
349                                         "cn=Modrdn,cn=Operations,cn=Monitor")
350                                         == 0)
351                         {
352                                 cldap_submit_derive ("operations",
353                                         "modrdn-completed", opc, st);
354                                 cldap_submit_derive ("operations",
355                                         "modrdn-initiated", opi, st);
356                         }
357                         else if (strcmp (dn,
358                                         "cn=Add,cn=Operations,cn=Monitor")
359                                         == 0)
360                         {
361                                 cldap_submit_derive ("operations",
362                                         "add-completed", opc, st);
363                                 cldap_submit_derive ("operations",
364                                         "add-initiated", opi, st);
365                         }
366                         else if (strcmp (dn,
367                                         "cn=Delete,cn=Operations,cn=Monitor")
368                                         == 0)
369                         {
370                                 cldap_submit_derive ("operations",
371                                         "delete-completed", opc, st);
372                                 cldap_submit_derive ("operations",
373                                         "delete-initiated", opi, st);
374                         }
375                         else if (strcmp (dn,
376                                         "cn=Abandon,cn=Operations,cn=Monitor")
377                                         == 0)
378                         {
379                                 cldap_submit_derive ("operations",
380                                         "abandon-completed", opc, st);
381                                 cldap_submit_derive ("operations",
382                                         "abandon-initiated", opi, st);
383                         }
384                         else if (strcmp (dn,
385                                         "cn=Extended,cn=Operations,cn=Monitor")
386                                         == 0)
387                         {
388                                 cldap_submit_derive ("operations",
389                                         "extended-completed", opc, st);
390                                 cldap_submit_derive ("operations",
391                                         "extended-initiated", opi, st);
392                         }
393                         else if ((strncmp (dn, "cn=Database", 11) == 0)
394                                 && ((nc_list = ldap_get_values_len
395                                                 (st->ld, e, "namingContexts")) != NULL))
396                         {
397                                 nc_data = *nc_list[0];
398                                 char typeinst[DATA_MAX_NAME_LEN];
399
400                                 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
401                                         "olmBDBEntryCache")) != NULL)
402                                 {
403                                         olmbdb_data = *olmbdb_list[0];
404                                         ssnprintf (typeinst, sizeof (typeinst),
405                                                 "bdbentrycache-%s", nc_data.bv_val);
406                                         cldap_submit_gauge ("cache_size", typeinst,
407                                                 atoll (olmbdb_data.bv_val), st);
408                                         ldap_value_free_len (olmbdb_list);
409                                 }
410
411                                 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
412                                         "olmBDBDNCache")) != NULL)
413                                 {
414                                         olmbdb_data = *olmbdb_list[0];
415                                         ssnprintf (typeinst, sizeof (typeinst),
416                                                 "bdbdncache-%s", nc_data.bv_val);
417                                         cldap_submit_gauge ("cache_size", typeinst,
418                                                 atoll (olmbdb_data.bv_val), st);
419                                         ldap_value_free_len (olmbdb_list);
420                                 }
421
422                                 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
423                                         "olmBDBIDLCache")) != NULL)
424                                 {
425                                         olmbdb_data = *olmbdb_list[0];
426                                         ssnprintf (typeinst, sizeof (typeinst),
427                                                 "bdbidlcache-%s", nc_data.bv_val);
428                                         cldap_submit_gauge ("cache_size", typeinst,
429                                                 atoll (olmbdb_data.bv_val), st);
430                                         ldap_value_free_len (olmbdb_list);
431                                 }
432
433                                 ldap_value_free_len (nc_list);
434                         }
435                         else if (strcmp (dn,
436                                         "cn=Bytes,cn=Statistics,cn=Monitor")
437                                         == 0)
438                         {
439                                 cldap_submit_derive ("derive", "statistics-bytes",
440                                         counter, st);
441                         }
442                         else if (strcmp (dn,
443                                         "cn=PDU,cn=Statistics,cn=Monitor")
444                                         == 0)
445                         {
446                                 cldap_submit_derive ("derive", "statistics-pdu",
447                                         counter, st);
448                         }
449                         else if (strcmp (dn,
450                                         "cn=Entries,cn=Statistics,cn=Monitor")
451                                         == 0)
452                         {
453                                 cldap_submit_derive ("derive", "statistics-entries",
454                                         counter, st);
455                         }
456                         else if (strcmp (dn,
457                                         "cn=Referrals,cn=Statistics,cn=Monitor")
458                                         == 0)
459                         {
460                                 cldap_submit_derive ("derive", "statistics-referrals",
461                                         counter, st);
462                         }
463                         else if (strcmp (dn,
464                                         "cn=Open,cn=Threads,cn=Monitor")
465                                         == 0)
466                         {
467                                 cldap_submit_gauge ("threads", "threads-open",
468                                         info, st);
469                         }
470                         else if (strcmp (dn,
471                                         "cn=Starting,cn=Threads,cn=Monitor")
472                                         == 0)
473                         {
474                                 cldap_submit_gauge ("threads", "threads-starting",
475                                         info, st);
476                         }
477                         else if (strcmp (dn,
478                                         "cn=Active,cn=Threads,cn=Monitor")
479                                         == 0)
480                         {
481                                 cldap_submit_gauge ("threads", "threads-active",
482                                         info, st);
483                         }
484                         else if (strcmp (dn,
485                                         "cn=Pending,cn=Threads,cn=Monitor")
486                                         == 0)
487                         {
488                                 cldap_submit_gauge ("threads", "threads-pending",
489                                         info, st);
490                         }
491                         else if (strcmp (dn,
492                                         "cn=Backload,cn=Threads,cn=Monitor")
493                                         == 0)
494                         {
495                                 cldap_submit_gauge ("threads", "threads-backload",
496                                         info, st);
497                         }
498                         else if (strcmp (dn,
499                                         "cn=Read,cn=Waiters,cn=Monitor")
500                                         == 0)
501                         {
502                                 cldap_submit_derive ("derive", "waiters-read",
503                                         counter, st);
504                         }
505                         else if (strcmp (dn,
506                                         "cn=Write,cn=Waiters,cn=Monitor")
507                                         == 0)
508                         {
509                                 cldap_submit_derive ("derive", "waiters-write",
510                                         counter, st);
511                         }
512
513                         ldap_value_free_len (counter_list);
514                         ldap_value_free_len (opc_list);
515                         ldap_value_free_len (opi_list);
516                         ldap_value_free_len (info_list);
517                 }
518
519                 ldap_memfree (dn);
520         }
521
522         ldap_msgfree (result);
523         ldap_unbind_ext_s (st->ld, NULL, NULL);
524         return (0);
525 } /* }}} int cldap_read_host */
526
527 /* Configuration handling functions {{{
528  *
529  * <Plugin ldap>
530  *   <Instance "plugin_instance1">
531  *     URL "ldap://localhost"
532  *     ...
533  *   </Instance>
534  * </Plugin>
535  */
536
537 static int cldap_config_add (oconfig_item_t *ci) /* {{{ */
538 {
539         cldap_t *st;
540         int i;
541         int status;
542
543         st = malloc (sizeof (*st));
544         if (st == NULL)
545         {
546                 ERROR ("openldap plugin: malloc failed.");
547                 return (-1);
548         }
549         memset (st, 0, sizeof (*st));
550
551         status = cf_util_get_string (ci, &st->name);
552         if (status != 0)
553         {
554                 sfree (st);
555                 return (status);
556         }
557
558         st->starttls = 0;
559         st->timeout = -1;
560         st->verifyhost = 1;
561         st->version = LDAP_VERSION3;
562
563         for (i = 0; i < ci->children_num; i++)
564         {
565                 oconfig_item_t *child = ci->children + i;
566
567                 if (strcasecmp ("CACert", child->key) == 0)
568                         status = cf_util_get_string (child, &st->cacert);
569                 else if (strcasecmp ("StartTLS", child->key) == 0)
570                         status = cf_util_get_boolean (child, &st->starttls);
571                 else if (strcasecmp ("Timeout", child->key) == 0)
572                         status = cf_util_get_int (child, &st->timeout);
573                 else if (strcasecmp ("URL", child->key) == 0)
574                         status = cf_util_get_string (child, &st->url);
575                 else if (strcasecmp ("VerifyHost", child->key) == 0)
576                         status = cf_util_get_boolean (child, &st->verifyhost);
577                 else if (strcasecmp ("Version", child->key) == 0)
578                         status = cf_util_get_int (child, &st->version);
579                 else
580                 {
581                         WARNING ("openldap plugin: Option `%s' not allowed here.",
582                                         child->key);
583                         status = -1;
584                 }
585
586                 if (status != 0)
587                         break;
588         }
589
590         /* Check if struct is complete.. */
591         if ((status == 0) && (st->url == NULL))
592         {
593                 ERROR ("openldap plugin: Instance `%s': "
594                                 "No URL has been configured.",
595                                 st->name);
596                 status = -1;
597         }
598
599         /* Check if URL is valid */
600         if ((status == 0) && (st->url != NULL))
601         {
602                 LDAPURLDesc *ludpp;
603                 int rc;
604
605                 if ((rc = ldap_url_parse (st->url, &ludpp)) != 0)
606                 {
607                         ERROR ("openldap plugin: Instance `%s': "
608                                 "Invalid URL: `%s'",
609                                 st->name, st->url);
610                         status = -1;
611                 }
612
613                 if ((status == 0) && (ludpp->lud_host != NULL))
614                 {
615                         st->host = strdup (ludpp->lud_host);
616                 }
617
618                 ldap_free_urldesc (ludpp);
619         }
620
621         if (status == 0)
622         {
623                 user_data_t ud;
624                 char callback_name[3*DATA_MAX_NAME_LEN];
625
626                 memset (&ud, 0, sizeof (ud));
627                 ud.data = st;
628
629                 memset (callback_name, 0, sizeof (callback_name));
630                 ssnprintf (callback_name, sizeof (callback_name),
631                                 "openldap/%s/%s",
632                                 (st->host != NULL) ? st->host : hostname_g,
633                                 (st->name != NULL) ? st->name : "default"),
634
635                 status = plugin_register_complex_read (/* group = */ NULL,
636                                 /* name      = */ callback_name,
637                                 /* callback  = */ cldap_read_host,
638                                 /* interval  = */ NULL,
639                                 /* user_data = */ &ud);
640         }
641
642         if (status != 0)
643         {
644                 cldap_free (st);
645                 return (-1);
646         }
647
648         return (0);
649 } /* }}} int cldap_config_add */
650
651 static int cldap_config (oconfig_item_t *ci) /* {{{ */
652 {
653         int i;
654         int status = 0;
655
656         for (i = 0; i < ci->children_num; i++)
657         {
658                 oconfig_item_t *child = ci->children + i;
659
660                 if (strcasecmp ("Instance", child->key) == 0)
661                         cldap_config_add (child);
662                 else
663                         WARNING ("openldap plugin: The configuration option "
664                                         "\"%s\" is not allowed here. Did you "
665                                         "forget to add an <Instance /> block "
666                                         "around the configuration?",
667                                         child->key);
668         } /* for (ci->children) */
669
670         return (status);
671 } /* }}} int cldap_config */
672
673 /* }}} End of configuration handling functions */
674
675 static int cldap_init (void) /* {{{ */
676 {
677         /* Initialize LDAP library while still single-threaded as recommended in
678          * ldap_initialize(3) */
679         int debug_level;
680         ldap_get_option (NULL, LDAP_OPT_DEBUG_LEVEL, &debug_level);
681         return (0);
682 } /* }}} int cldap_init */
683
684 void module_register (void) /* {{{ */
685 {
686         plugin_register_complex_config ("openldap", cldap_config);
687         plugin_register_init ("openldap", cldap_init);
688 } /* }}} void module_register */
689
690 #if defined(__APPLE__)
691 #pragma clang diagnostic pop
692 #endif