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