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