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