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