openldap: Remove custom string/bool/int config functions.
[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         char *dn;
45 };
46 typedef struct ldap_s ldap_t; /* }}} */
47
48 static void ldap_free (ldap_t *st) /* {{{ */
49 {
50         if (st == NULL)
51                 return;
52
53         sfree (st->cacert);
54         sfree (st->host);
55         sfree (st->name);
56         sfree (st->url);
57         if (st->ld)
58                 ldap_memfree (st->ld);
59         sfree (st);
60 } /* }}} void ldap_free */
61
62 /* initialize ldap for each host */
63 static int ldap_init_host (ldap_t *st) /* {{{ */
64 {
65         LDAP *ld;
66         int rc;
67         rc = ldap_initialize (&ld, st->url);
68         if (rc != LDAP_SUCCESS)
69         {
70                 ERROR ("openldap plugin: ldap_initialize failed: %s",
71                         ldap_err2string (rc));
72                 st->state = 0;
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                 return (-1);
215         }
216
217         for (e = ldap_first_entry (st->ld, result); e != NULL;
218                 e = ldap_next_entry (st->ld, e))
219         {
220                 if ((dn = ldap_get_dn (st->ld, e)) != NULL)
221                 {
222                         unsigned long long counter = 0;
223                         unsigned long long opc = 0;
224                         unsigned long long opi = 0;
225                         unsigned long long info = 0;
226
227                         struct berval counter_data;
228                         struct berval opc_data;
229                         struct berval opi_data;
230                         struct berval info_data;
231                         struct berval olmbdb_data;
232                         struct berval nc_data;
233
234                         struct berval **counter_list;
235                         struct berval **opc_list;
236                         struct berval **opi_list;
237                         struct berval **info_list;
238                         struct berval **olmbdb_list;
239                         struct berval **nc_list;
240
241                         if ((counter_list = ldap_get_values_len (st->ld, e,
242                                 "monitorCounter")) != NULL)
243                         {
244                                 counter_data = *counter_list[0];
245                                 counter = atoll (counter_data.bv_val);
246                         }
247
248                         if ((opc_list = ldap_get_values_len (st->ld, e,
249                                 "monitorOpCompleted")) != NULL)
250                         {
251                                 opc_data = *opc_list[0];
252                                 opc = atoll (opc_data.bv_val);
253                         }
254
255                         if ((opi_list = ldap_get_values_len (st->ld, e,
256                                 "monitorOpInitiated")) != NULL)
257                         {
258                                 opi_data = *opi_list[0];
259                                 opi = atoll (opi_data.bv_val);
260                         }
261
262                         if ((info_list = ldap_get_values_len (st->ld, e,
263                                 "monitoredInfo")) != NULL)
264                         {
265                                 info_data = *info_list[0];
266                                 info = atoll (info_data.bv_val);
267                         }
268
269                         if (strcmp (dn, "cn=Total,cn=Connections,cn=Monitor")
270                                         == 0)
271                         {
272                                 ldap_submit_derive ("total_connections", NULL,
273                                         counter, st);
274                         }
275                         else if (strcmp (dn,
276                                         "cn=Current,cn=Connections,cn=Monitor")
277                                         == 0)
278                         {
279                                 ldap_submit_gauge ("current_connections", NULL,
280                                         counter, st);
281                         }
282                         else if (strcmp (dn,
283                                         "cn=Operations,cn=Monitor") == 0)
284                         {
285                                 ldap_submit_derive ("operations",
286                                         "completed", opc, st);
287                                 ldap_submit_derive ("operations",
288                                         "initiated", opi, st);
289                         }
290                         else if (strcmp (dn,
291                                         "cn=Bind,cn=Operations,cn=Monitor")
292                                         == 0)
293                         {
294                                 ldap_submit_derive ("operations",
295                                         "bind-completed", opc, st);
296                                 ldap_submit_derive ("operations",
297                                         "bind-initiated", opi, st);
298                         }
299                         else if (strcmp (dn,
300                                         "cn=UnBind,cn=Operations,cn=Monitor")
301                                         == 0)
302                         {
303                                 ldap_submit_derive ("operations",
304                                         "unbind-completed", opc, st);
305                                 ldap_submit_derive ("operations",
306                                         "unbind-initiated", opi, st);
307                         }
308                         else if (strcmp (dn,
309                                         "cn=Search,cn=Operations,cn=Monitor")
310                                         == 0)
311                         {
312                                 ldap_submit_derive ("operations",
313                                         "search-completed", opc, st);
314                                 ldap_submit_derive ("operations",
315                                         "search-initiated", opi, st);
316                         }
317                         else if (strcmp (dn,
318                                         "cn=Compare,cn=Operations,cn=Monitor")
319                                         == 0)
320                         {
321                                 ldap_submit_derive ("operations",
322                                         "compare-completed", opc, st);
323                                 ldap_submit_derive ("operations",
324                                         "compare-initiated", opi, st);
325                         }
326                         else if (strcmp (dn,
327                                         "cn=Modify,cn=Operations,cn=Monitor")
328                                         == 0)
329                         {
330                                 ldap_submit_derive ("operations",
331                                         "modify-completed", opc, st);
332                                 ldap_submit_derive ("operations",
333                                         "modify-initiated", opi, st);
334                         }
335                         else if (strcmp (dn,
336                                         "cn=Modrdn,cn=Operations,cn=Monitor")
337                                         == 0)
338                         {
339                                 ldap_submit_derive ("operations",
340                                         "modrdn-completed", opc, st);
341                                 ldap_submit_derive ("operations",
342                                         "modrdn-initiated", opi, st);
343                         }
344                         else if (strcmp (dn,
345                                         "cn=Add,cn=Operations,cn=Monitor")
346                                         == 0)
347                         {
348                                 ldap_submit_derive ("operations",
349                                         "add-completed", opc, st);
350                                 ldap_submit_derive ("operations",
351                                         "add-initiated", opi, st);
352                         }
353                         else if (strcmp (dn,
354                                         "cn=Delete,cn=Operations,cn=Monitor")
355                                         == 0)
356                         {
357                                 ldap_submit_derive ("operations",
358                                         "delete-completed", opc, st);
359                                 ldap_submit_derive ("operations",
360                                         "delete-initiated", opi, st);
361                         }
362                         else if (strcmp (dn,
363                                         "cn=Abandon,cn=Operations,cn=Monitor")
364                                         == 0)
365                         {
366                                 ldap_submit_derive ("operations",
367                                         "abandon-completed", opc, st);
368                                 ldap_submit_derive ("operations",
369                                         "abandon-initiated", opi, st);
370                         }
371                         else if (strcmp (dn,
372                                         "cn=Extended,cn=Operations,cn=Monitor")
373                                         == 0)
374                         {
375                                 ldap_submit_derive ("operations",
376                                         "extended-completed", opc, st);
377                                 ldap_submit_derive ("operations",
378                                         "extended-initiated", opi, st);
379                         }
380                         else if ((strncmp (dn, "cn=Database", 11) == 0)
381                                 && ((nc_list = ldap_get_values_len
382                                                 (st->ld, e, "namingContexts")) != NULL))
383                         {
384                                 nc_data = *nc_list[0];
385                                 char typeinst[DATA_MAX_NAME_LEN];
386
387                                 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
388                                         "olmBDBEntryCache")) != NULL)
389                                 {
390                                         olmbdb_data = *olmbdb_list[0];
391                                         ssnprintf (typeinst, sizeof (typeinst),
392                                                 "bdbentrycache-%s", nc_data.bv_val);
393                                         ldap_submit_gauge ("cache_size", typeinst,
394                                                 atoll (olmbdb_data.bv_val), st);
395                                         ldap_value_free_len (olmbdb_list);
396                                 }
397
398                                 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
399                                         "olmBDBDNCache")) != NULL)
400                                 {
401                                         olmbdb_data = *olmbdb_list[0];
402                                         ssnprintf (typeinst, sizeof (typeinst),
403                                                 "bdbdncache-%s", nc_data.bv_val);
404                                         ldap_submit_gauge ("cache_size", typeinst,
405                                                 atoll (olmbdb_data.bv_val), st);
406                                         ldap_value_free_len (olmbdb_list);
407                                 }
408
409                                 if ((olmbdb_list = ldap_get_values_len (st->ld, e,
410                                         "olmBDBIDLCache")) != NULL)
411                                 {
412                                         olmbdb_data = *olmbdb_list[0];
413                                         ssnprintf (typeinst, sizeof (typeinst),
414                                                 "bdbidlcache-%s", nc_data.bv_val);
415                                         ldap_submit_gauge ("cache_size", typeinst,
416                                                 atoll (olmbdb_data.bv_val), st);
417                                         ldap_value_free_len (olmbdb_list);
418                                 }
419
420                                 ldap_value_free_len (nc_list);
421                         }
422                         else if (strcmp (dn,
423                                         "cn=Bytes,cn=Statistics,cn=Monitor")
424                                         == 0)
425                         {
426                                 ldap_submit_derive ("derive", "statistics-bytes",
427                                         counter, st);
428                         }
429                         else if (strcmp (dn,
430                                         "cn=PDU,cn=Statistics,cn=Monitor")
431                                         == 0)
432                         {
433                                 ldap_submit_derive ("derive", "statistics-pdu",
434                                         counter, st);
435                         }
436                         else if (strcmp (dn,
437                                         "cn=Entries,cn=Statistics,cn=Monitor")
438                                         == 0)
439                         {
440                                 ldap_submit_derive ("derive", "statistics-entries",
441                                         counter, st);
442                         }
443                         else if (strcmp (dn,
444                                         "cn=Referrals,cn=Statistics,cn=Monitor")
445                                         == 0)
446                         {
447                                 ldap_submit_derive ("derive", "statistics-referrals",
448                                         counter, st);
449                         }
450                         else if (strcmp (dn,
451                                         "cn=Open,cn=Threads,cn=Monitor")
452                                         == 0)
453                         {
454                                 ldap_submit_gauge ("threads", "threads-open",
455                                         info, st);
456                         }
457                         else if (strcmp (dn,
458                                         "cn=Starting,cn=Threads,cn=Monitor")
459                                         == 0)
460                         {
461                                 ldap_submit_gauge ("threads", "threads-starting",
462                                         info, st);
463                         }
464                         else if (strcmp (dn,
465                                         "cn=Active,cn=Threads,cn=Monitor")
466                                         == 0)
467                         {
468                                 ldap_submit_gauge ("threads", "threads-active",
469                                         info, st);
470                         }
471                         else if (strcmp (dn,
472                                         "cn=Pending,cn=Threads,cn=Monitor")
473                                         == 0)
474                         {
475                                 ldap_submit_gauge ("threads", "threads-pending",
476                                         info, st);
477                         }
478                         else if (strcmp (dn,
479                                         "cn=Backload,cn=Threads,cn=Monitor")
480                                         == 0)
481                         {
482                                 ldap_submit_gauge ("threads", "threads-backload",
483                                         info, st);
484                         }
485                         else if (strcmp (dn,
486                                         "cn=Read,cn=Waiters,cn=Monitor")
487                                         == 0)
488                         {
489                                 ldap_submit_derive ("derive", "waiters-read",
490                                         counter, st);
491                         }
492                         else if (strcmp (dn,
493                                         "cn=Write,cn=Waiters,cn=Monitor")
494                                         == 0)
495                         {
496                                 ldap_submit_derive ("derive", "waiters-write",
497                                         counter, st);
498                         }
499
500                         ldap_value_free_len (counter_list);
501                         ldap_value_free_len (opc_list);
502                         ldap_value_free_len (opi_list);
503                         ldap_value_free_len (info_list);
504                 }
505
506                 ldap_memfree (dn);
507         }
508
509         ldap_msgfree (result);
510         ldap_unbind_ext_s (st->ld, NULL, NULL);
511         return (0);
512 } /* }}} int ldap_read_host */
513
514 /* Configuration handling functions {{{
515  *
516  * <Plugin ldap>
517  *   <Instance "plugin_instance1">
518  *     URL "ldap://localhost"
519  *     ...
520  *   </Instance>
521  * </Plugin>
522  */
523
524 static int ldap_config_add (oconfig_item_t *ci) /* {{{ */
525 {
526         ldap_t *st;
527         int i;
528         int status;
529
530         st = malloc (sizeof (*st));
531         if (st == NULL)
532         {
533                 ERROR ("openldap plugin: malloc failed.");
534                 return (-1);
535         }
536         memset (st, 0, sizeof (*st));
537
538         status = cf_util_get_string (ci, &st->name);
539         if (status != 0)
540         {
541                 sfree (st);
542                 return (status);
543         }
544
545         st->starttls = 0;
546         st->timeout = -1;
547         st->verifyhost = 1;
548         st->version = LDAP_VERSION3;
549
550         for (i = 0; i < ci->children_num; i++)
551         {
552                 oconfig_item_t *child = ci->children + i;
553
554                 if (strcasecmp ("CACert", child->key) == 0)
555                         status = cf_util_get_string (child, &st->cacert);
556                 else if (strcasecmp ("StartTLS", child->key) == 0)
557                         status = cf_util_get_boolean (child, &st->starttls);
558                 else if (strcasecmp ("Timeout", child->key) == 0)
559                         status = cf_util_get_int (child, &st->timeout);
560                 else if (strcasecmp ("URL", child->key) == 0)
561                         status = cf_util_get_string (child, &st->url);
562                 else if (strcasecmp ("VerifyHost", child->key) == 0)
563                         status = cf_util_get_boolean (child, &st->verifyhost);
564                 else if (strcasecmp ("Version", child->key) == 0)
565                         status = cf_util_get_int (child, &st->version);
566                 else
567                 {
568                         WARNING ("openldap plugin: Option `%s' not allowed here.",
569                                         child->key);
570                         status = -1;
571                 }
572
573                 if (status != 0)
574                         break;
575         }
576
577         /* Check if struct is complete.. */
578         if ((status == 0) && (st->url == NULL))
579         {
580                 ERROR ("openldap plugin: Instance `%s': "
581                                 "No URL has been configured.",
582                                 st->name);
583                 status = -1;
584         }
585
586         /* Check if URL is valid */
587         if ((status == 0) && (st->url != NULL))
588         {
589                 LDAPURLDesc *ludpp;
590                 int rc;
591
592                 if ((rc = ldap_url_parse (st->url, &ludpp)) != 0)
593                 {
594                         ERROR ("openldap plugin: Instance `%s': "
595                                 "Invalid URL: `%s'",
596                                 st->name, st->url);
597                         status = -1;
598                 }
599                 else
600                 {
601                         st->host = strdup (ludpp->lud_host);
602                 }
603
604                 ldap_free_urldesc (ludpp);
605         }
606
607         if (status == 0)
608         {
609                 user_data_t ud;
610                 char callback_name[3*DATA_MAX_NAME_LEN];
611
612                 memset (&ud, 0, sizeof (ud));
613                 ud.data = st;
614
615                 memset (callback_name, 0, sizeof (callback_name));
616                 ssnprintf (callback_name, sizeof (callback_name),
617                                 "openldap/%s/%s",
618                                 (st->host != NULL) ? st->host : hostname_g,
619                                 (st->name != NULL) ? st->name : "default"),
620
621                 status = plugin_register_complex_read (/* group = */ NULL,
622                                 /* name      = */ callback_name,
623                                 /* callback  = */ ldap_read_host,
624                                 /* interval  = */ NULL,
625                                 /* user_data = */ &ud);
626         }
627
628         if (status != 0)
629         {
630                 ldap_free (st);
631                 return (-1);
632         }
633
634         return (0);
635 } /* }}} int ldap_config_add */
636
637 static int ldap_config (oconfig_item_t *ci) /* {{{ */
638 {
639         int i;
640         int status = 0;
641
642         for (i = 0; i < ci->children_num; i++)
643         {
644                 oconfig_item_t *child = ci->children + i;
645
646                 if (strcasecmp ("Instance", child->key) == 0)
647                         ldap_config_add (child);
648                 else
649                         WARNING ("openldap plugin: The configuration option "
650                                         "\"%s\" is not allowed here. Did you "
651                                         "forget to add an <Instance /> block "
652                                         "around the configuration?",
653                                         child->key);
654         } /* for (ci->children) */
655
656         return (status);
657 } /* }}} int ldap_config */
658
659 /* }}} End of configuration handling functions */
660
661 static int ldap_init (void) /* {{{ */
662 {
663         /* Initialize LDAP library while still single-threaded as recommended in
664          * ldap_initialize(3) */
665         int debug_level;
666         ldap_get_option (NULL, LDAP_OPT_DEBUG_LEVEL, &debug_level);
667         return (0);
668 } /* }}} int ldap_init */
669
670 void module_register (void) /* {{{ */
671 {
672         plugin_register_complex_config ("openldap", ldap_config);
673         plugin_register_init ("openldap", ldap_init);
674 } /* }}} void module_register */