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