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