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