xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-ldap/monitor.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: monitor.c,v 1.1.1.3 2010/12/12 15:23:06 adam Exp $	*/
2 
3 /* monitor.c - monitor ldap backend */
4 /* OpenLDAP: pkg/ldap/servers/slapd/back-ldap/monitor.c,v 1.2.2.7 2010/04/13 20:23:29 kurt Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2003-2010 The OpenLDAP Foundation.
8  * Portions Copyright 1999-2003 Howard Chu.
9  * Portions Copyright 2000-2003 Pierangelo Masarati.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted only as authorized by the OpenLDAP
14  * Public License.
15  *
16  * A copy of this license is available in the file LICENSE in the
17  * top-level directory of the distribution or, alternatively, at
18  * <http://www.OpenLDAP.org/license.html>.
19  */
20 /* ACKNOWLEDGEMENTS:
21  * This work was initially developed by the Howard Chu for inclusion
22  * in OpenLDAP Software and subsequently enhanced by Pierangelo
23  * Masarati.
24  */
25 
26 #include "portable.h"
27 
28 #include <stdio.h>
29 #include <ac/string.h>
30 #include <ac/unistd.h>
31 #include <ac/stdlib.h>
32 #include <ac/errno.h>
33 #include <sys/stat.h>
34 #include "lutil.h"
35 #include "back-ldap.h"
36 
37 #include "config.h"
38 
39 static ObjectClass		*oc_olmLDAPDatabase;
40 
41 static AttributeDescription	*ad_olmDbURIList;
42 
43 /*
44  * NOTE: there's some confusion in monitor OID arc;
45  * by now, let's consider:
46  *
47  * Subsystems monitor attributes	1.3.6.1.4.1.4203.666.1.55.0
48  * Databases monitor attributes		1.3.6.1.4.1.4203.666.1.55.0.1
49  * LDAP database monitor attributes	1.3.6.1.4.1.4203.666.1.55.0.1.2
50  *
51  * Subsystems monitor objectclasses	1.3.6.1.4.1.4203.666.3.16.0
52  * Databases monitor objectclasses	1.3.6.1.4.1.4203.666.3.16.0.1
53  * LDAP database monitor objectclasses	1.3.6.1.4.1.4203.666.3.16.0.1.2
54  */
55 
56 static struct {
57 	char			*name;
58 	char			*oid;
59 }		s_oid[] = {
60 	{ "olmLDAPAttributes",			"olmDatabaseAttributes:2" },
61 	{ "olmLDAPObjectClasses",		"olmDatabaseObjectClasses:2" },
62 
63 	{ NULL }
64 };
65 
66 static struct {
67 	char			*desc;
68 	AttributeDescription	**ad;
69 }		s_at[] = {
70 	{ "( olmLDAPAttributes:1 "
71 		"NAME ( 'olmDbURIList' ) "
72 		"DESC 'List of URIs a proxy is serving; can be modified run-time' "
73 		"SUP managedInfo )",
74 		&ad_olmDbURIList },
75 
76 	{ NULL }
77 };
78 
79 static struct {
80 	char		*desc;
81 	ObjectClass	**oc;
82 }		s_oc[] = {
83 	/* augments an existing object, so it must be AUXILIARY
84 	 * FIXME: derive from some ABSTRACT "monitoredEntity"? */
85 	{ "( olmLDAPObjectClasses:1 "
86 		"NAME ( 'olmLDAPDatabase' ) "
87 		"SUP top AUXILIARY "
88 		"MAY ( "
89 			"olmDbURIList "
90 			") )",
91 		&oc_olmLDAPDatabase },
92 
93 	{ NULL }
94 };
95 
96 static int
97 ldap_back_monitor_info_destroy( ldapinfo_t * li )
98 {
99 	if ( !BER_BVISNULL( &li->li_monitor_info.lmi_rdn ) )
100 		ch_free( li->li_monitor_info.lmi_rdn.bv_val );
101 	if ( !BER_BVISNULL( &li->li_monitor_info.lmi_nrdn ) )
102 		ch_free( li->li_monitor_info.lmi_nrdn.bv_val );
103 	if ( !BER_BVISNULL( &li->li_monitor_info.lmi_filter ) )
104 		ch_free( li->li_monitor_info.lmi_filter.bv_val );
105 	if ( !BER_BVISNULL( &li->li_monitor_info.lmi_more_filter ) )
106 		ch_free( li->li_monitor_info.lmi_more_filter.bv_val );
107 
108 	memset( &li->li_monitor_info, 0, sizeof( li->li_monitor_info ) );
109 
110 	return 0;
111 }
112 
113 static int
114 ldap_back_monitor_update(
115 	Operation	*op,
116 	SlapReply	*rs,
117 	Entry		*e,
118 	void		*priv )
119 {
120 	ldapinfo_t		*li = (ldapinfo_t *)priv;
121 
122 	Attribute		*a;
123 
124 	/* update olmDbURIList */
125 	a = attr_find( e->e_attrs, ad_olmDbURIList );
126 	if ( a != NULL ) {
127 		struct berval	bv;
128 
129 		assert( a->a_vals != NULL );
130 		assert( !BER_BVISNULL( &a->a_vals[ 0 ] ) );
131 		assert( BER_BVISNULL( &a->a_vals[ 1 ] ) );
132 
133 		ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
134 		if ( li->li_uri ) {
135 			ber_str2bv( li->li_uri, 0, 0, &bv );
136 			if ( !bvmatch( &a->a_vals[ 0 ], &bv ) ) {
137 				ber_bvreplace( &a->a_vals[ 0 ], &bv );
138 			}
139 		}
140 		ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
141 	}
142 
143 	return SLAP_CB_CONTINUE;
144 }
145 
146 static int
147 ldap_back_monitor_modify(
148 	Operation	*op,
149 	SlapReply	*rs,
150 	Entry		*e,
151 	void		*priv )
152 {
153 	ldapinfo_t		*li = (ldapinfo_t *) priv;
154 
155 	Attribute		*save_attrs = NULL;
156 	Modifications		*ml,
157 				*ml_olmDbURIList = NULL;
158 	struct berval		ul = BER_BVNULL;
159 	int			got = 0;
160 
161 	for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
162 		if ( ml->sml_desc == ad_olmDbURIList ) {
163 			if ( ml_olmDbURIList != NULL ) {
164 				rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
165 				rs->sr_text = "conflicting modifications";
166 				goto done;
167 			}
168 
169 			if ( ml->sml_op != LDAP_MOD_REPLACE ) {
170 				rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
171 				rs->sr_text = "modification not allowed";
172 				goto done;
173 			}
174 
175 			ml_olmDbURIList = ml;
176 			got++;
177 			continue;
178 		}
179 	}
180 
181 	if ( got == 0 ) {
182 		return SLAP_CB_CONTINUE;
183 	}
184 
185 	save_attrs = attrs_dup( e->e_attrs );
186 
187 	if ( ml_olmDbURIList != NULL ) {
188 		Attribute	*a = NULL;
189 		LDAPURLDesc	*ludlist = NULL;
190 		int		rc;
191 
192 		ml = ml_olmDbURIList;
193 		assert( ml->sml_nvalues != NULL );
194 
195 		if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
196 			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
197 			rs->sr_text = "no value provided";
198 			goto done;
199 		}
200 
201 		if ( !BER_BVISNULL( &ml->sml_nvalues[ 1 ] ) ) {
202 			rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
203 			rs->sr_text = "multiple values provided";
204 			goto done;
205 		}
206 
207 		rc = ldap_url_parselist_ext( &ludlist,
208 			ml->sml_nvalues[ 0 ].bv_val, NULL,
209 			LDAP_PVT_URL_PARSE_NOEMPTY_HOST
210 				| LDAP_PVT_URL_PARSE_DEF_PORT );
211 		if ( rc != LDAP_URL_SUCCESS ) {
212 			rs->sr_err = LDAP_INVALID_SYNTAX;
213 			rs->sr_text = "unable to parse URI list";
214 			goto done;
215 		}
216 
217 		ul.bv_val = ldap_url_list2urls( ludlist );
218 		ldap_free_urllist( ludlist );
219 		if ( ul.bv_val == NULL ) {
220 			rs->sr_err = LDAP_OTHER;
221 			goto done;
222 		}
223 		ul.bv_len = strlen( ul.bv_val );
224 
225 		a = attr_find( e->e_attrs, ad_olmDbURIList );
226 		if ( a != NULL ) {
227 			if ( a->a_nvals == a->a_vals ) {
228 				a->a_nvals = ch_calloc( sizeof( struct berval ), 2 );
229 			}
230 
231 			ber_bvreplace( &a->a_vals[ 0 ], &ul );
232 			ber_bvreplace( &a->a_nvals[ 0 ], &ul );
233 
234 		} else {
235 			attr_merge_normalize_one( e, ad_olmDbURIList, &ul, NULL );
236 		}
237 	}
238 
239 	/* apply changes */
240 	if ( !BER_BVISNULL( &ul ) ) {
241 		ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
242 		if ( li->li_uri ) {
243 			ch_free( li->li_uri );
244 		}
245 		li->li_uri = ul.bv_val;
246 		ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
247 
248 		BER_BVZERO( &ul );
249 	}
250 
251 done:;
252 	if ( !BER_BVISNULL( &ul ) ) {
253 		ldap_memfree( ul.bv_val );
254 	}
255 
256 	if ( rs->sr_err == LDAP_SUCCESS ) {
257 		attrs_free( save_attrs );
258 		return SLAP_CB_CONTINUE;
259 	}
260 
261 	attrs_free( e->e_attrs );
262 	e->e_attrs = save_attrs;
263 
264 	return rs->sr_err;
265 }
266 
267 static int
268 ldap_back_monitor_free(
269 	Entry		*e,
270 	void		**priv )
271 {
272 	ldapinfo_t		*li = (ldapinfo_t *)(*priv);
273 
274 	*priv = NULL;
275 
276 	if ( !slapd_shutdown && !BER_BVISNULL( &li->li_monitor_info.lmi_rdn ) ) {
277 		ldap_back_monitor_info_destroy( li );
278 	}
279 
280 	return SLAP_CB_CONTINUE;
281 }
282 
283 static int
284 ldap_back_monitor_conn_create(
285 	Operation	*op,
286 	SlapReply	*rs,
287 	struct berval	*ndn,
288 	Entry 		*e_parent,
289 	Entry		**ep )
290 {
291 	monitor_entry_t		*mp_parent;
292 	ldap_monitor_info_t	*lmi;
293 	ldapinfo_t		*li;
294 
295 	assert( e_parent->e_private != NULL );
296 
297 	mp_parent = e_parent->e_private;
298 	lmi = (ldap_monitor_info_t *)mp_parent->mp_info;
299 	li = lmi->lmi_li;
300 
301 	/* do the hard work! */
302 
303 	return 1;
304 }
305 
306 /*
307  * call from within ldap_back_initialize()
308  */
309 static int
310 ldap_back_monitor_initialize( void )
311 {
312 	int		i, code;
313 	ConfigArgs c;
314 	char	*argv[ 3 ];
315 
316 	static int	ldap_back_monitor_initialized = 0;
317 
318 	/* register schema here; if compiled as dynamic object,
319 	 * must be loaded __after__ back_monitor.la */
320 
321 	if ( ldap_back_monitor_initialized++ ) {
322 		return 0;
323 	}
324 
325 	if ( backend_info( "monitor" ) == NULL ) {
326 		return -1;
327 	}
328 
329 	argv[ 0 ] = "back-ldap monitor";
330 	c.argv = argv;
331 	c.argc = 3;
332 	c.fname = argv[0];
333 	for ( i = 0; s_oid[ i ].name; i++ ) {
334 
335 		argv[ 1 ] = s_oid[ i ].name;
336 		argv[ 2 ] = s_oid[ i ].oid;
337 
338 		if ( parse_oidm( &c, 0, NULL ) != 0 ) {
339 			Debug( LDAP_DEBUG_ANY,
340 				"ldap_back_monitor_initialize: unable to add "
341 				"objectIdentifier \"%s=%s\"\n",
342 				s_oid[ i ].name, s_oid[ i ].oid, 0 );
343 			return 1;
344 		}
345 	}
346 
347 	for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
348 		code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 );
349 		if ( code != LDAP_SUCCESS ) {
350 			Debug( LDAP_DEBUG_ANY,
351 				"ldap_back_monitor_initialize: register_at failed\n",
352 				0, 0, 0 );
353 		}
354 	}
355 
356 	for ( i = 0; s_oc[ i ].desc != NULL; i++ ) {
357 		code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 );
358 		if ( code != LDAP_SUCCESS ) {
359 			Debug( LDAP_DEBUG_ANY,
360 				"ldap_back_monitor_initialize: register_oc failed\n",
361 				0, 0, 0 );
362 		}
363 	}
364 
365 	return 0;
366 }
367 
368 /*
369  * call from within ldap_back_db_init()
370  */
371 int
372 ldap_back_monitor_db_init( BackendDB *be )
373 {
374 	int	rc;
375 
376 	rc = ldap_back_monitor_initialize();
377 	if ( rc != LDAP_SUCCESS ) {
378 		return rc;
379 	}
380 
381 #if 0	/* uncomment to turn monitoring on by default */
382 	SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
383 #endif
384 
385 	return 0;
386 }
387 
388 /*
389  * call from within ldap_back_db_open()
390  */
391 int
392 ldap_back_monitor_db_open( BackendDB *be )
393 {
394 	ldapinfo_t		*li = (ldapinfo_t *) be->be_private;
395 	char			buf[ BACKMONITOR_BUFSIZE ];
396 	Entry			*e = NULL;
397 	monitor_callback_t	*cb = NULL;
398 	struct berval		suffix, *filter, *base;
399 	char			*ptr;
400 	time_t			now;
401 	char			timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
402 	struct berval 		timestamp;
403 	int			rc = 0;
404 	BackendInfo		*mi;
405 	monitor_extra_t		*mbe;
406 
407 	if ( !SLAP_DBMONITORING( be ) ) {
408 		return 0;
409 	}
410 
411 	/* check if monitor is configured and usable */
412 	mi = backend_info( "monitor" );
413 	if ( !mi || !mi->bi_extra ) {
414 		SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
415 		return 0;
416  	}
417  	mbe = mi->bi_extra;
418 
419 	/* don't bother if monitor is not configured */
420 	if ( !mbe->is_configured() ) {
421 		static int warning = 0;
422 
423 		if ( warning++ == 0 ) {
424 			Debug( LDAP_DEBUG_ANY, "ldap_back_monitor_db_open: "
425 				"monitoring disabled; "
426 				"configure monitor database to enable\n",
427 				0, 0, 0 );
428 		}
429 
430 		return 0;
431 	}
432 
433 	/* set up the fake subsystem that is used to create
434 	 * the volatile connection entries */
435 	li->li_monitor_info.lmi_mss.mss_name = "back-ldap";
436 	li->li_monitor_info.lmi_mss.mss_flags = MONITOR_F_VOLATILE_CH;
437 	li->li_monitor_info.lmi_mss.mss_create = ldap_back_monitor_conn_create;
438 
439 	li->li_monitor_info.lmi_li = li;
440 	li->li_monitor_info.lmi_scope = LDAP_SCOPE_SUBORDINATE;
441 	base = &li->li_monitor_info.lmi_base;
442 	BER_BVSTR( base, "cn=databases,cn=monitor" );
443 	filter = &li->li_monitor_info.lmi_filter;
444 	BER_BVZERO( filter );
445 
446 	suffix.bv_len = ldap_bv2escaped_filter_value_len( &be->be_nsuffix[ 0 ] );
447 	if ( suffix.bv_len == be->be_nsuffix[ 0 ].bv_len ) {
448 		suffix = be->be_nsuffix[ 0 ];
449 
450 	} else {
451 		ldap_bv2escaped_filter_value( &be->be_nsuffix[ 0 ], &suffix );
452 	}
453 
454 	filter->bv_len = STRLENOF( "(&" )
455 		+ li->li_monitor_info.lmi_more_filter.bv_len
456 		+ STRLENOF( "(monitoredInfo=" )
457 		+ strlen( be->bd_info->bi_type )
458 		+ STRLENOF( ")(!(monitorOverlay=" )
459 		+ strlen( be->bd_info->bi_type )
460 		+ STRLENOF( "))(namingContexts:distinguishedNameMatch:=" )
461 		+ suffix.bv_len + STRLENOF( "))" );
462 	ptr = filter->bv_val = ch_malloc( filter->bv_len + 1 );
463 	ptr = lutil_strcopy( ptr, "(&" );
464 	ptr = lutil_strncopy( ptr, li->li_monitor_info.lmi_more_filter.bv_val,
465 		li->li_monitor_info.lmi_more_filter.bv_len );
466 	ptr = lutil_strcopy( ptr, "(monitoredInfo=" );
467 	ptr = lutil_strcopy( ptr, be->bd_info->bi_type );
468 	ptr = lutil_strcopy( ptr, ")(!(monitorOverlay=" );
469 	ptr = lutil_strcopy( ptr, be->bd_info->bi_type );
470 	ptr = lutil_strcopy( ptr, "))(namingContexts:distinguishedNameMatch:=" );
471 	ptr = lutil_strncopy( ptr, suffix.bv_val, suffix.bv_len );
472 	ptr = lutil_strcopy( ptr, "))" );
473 	ptr[ 0 ] = '\0';
474 	assert( ptr == &filter->bv_val[ filter->bv_len ] );
475 
476 	if ( suffix.bv_val != be->be_nsuffix[ 0 ].bv_val ) {
477 		ch_free( suffix.bv_val );
478 	}
479 
480 	now = slap_get_time();
481 	timestamp.bv_val = timebuf;
482 	timestamp.bv_len = sizeof( timebuf );
483 	slap_timestamp( &now, &timestamp );
484 
485 	/* caller (e.g. an overlay based on back-ldap) may want to use
486 	 * a different RDN... */
487 	if ( BER_BVISNULL( &li->li_monitor_info.lmi_rdn ) ) {
488 		ber_str2bv( "cn=Connections", 0, 1, &li->li_monitor_info.lmi_rdn );
489 	}
490 
491 	ptr = ber_bvchr( &li->li_monitor_info.lmi_rdn, '=' );
492 	assert( ptr != NULL );
493 	ptr[ 0 ] = '\0';
494 	ptr++;
495 
496 	snprintf( buf, sizeof( buf ),
497 		"dn: %s=%s\n"
498 		"objectClass: monitorContainer\n"
499 		"%s: %s\n"
500 		"creatorsName: %s\n"
501 		"createTimestamp: %s\n"
502 		"modifiersName: %s\n"
503 		"modifyTimestamp: %s\n",
504 		li->li_monitor_info.lmi_rdn.bv_val,
505 			ptr,
506 		li->li_monitor_info.lmi_rdn.bv_val,
507 			ptr,
508 		BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val,
509 		timestamp.bv_val,
510 		BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val,
511 		timestamp.bv_val );
512 	e = str2entry( buf );
513 	if ( e == NULL ) {
514 		rc = -1;
515 		goto cleanup;
516 	}
517 
518 	ptr[ -1 ] = '=';
519 
520 	/* add labeledURI and special, modifiable URI value */
521 	if ( li->li_uri != NULL ) {
522 		struct berval	bv;
523 		LDAPURLDesc	*ludlist = NULL;
524 		int		rc;
525 
526 		rc = ldap_url_parselist_ext( &ludlist,
527 			li->li_uri, NULL,
528 			LDAP_PVT_URL_PARSE_NOEMPTY_HOST
529 				| LDAP_PVT_URL_PARSE_DEF_PORT );
530 		if ( rc != LDAP_URL_SUCCESS ) {
531 			Debug( LDAP_DEBUG_ANY,
532 				"ldap_back_monitor_db_open: "
533 				"unable to parse URI list (ignored)\n",
534 				0, 0, 0 );
535 		} else {
536 			for ( ; ludlist != NULL; ) {
537 				LDAPURLDesc	*next = ludlist->lud_next;
538 
539 				bv.bv_val = ldap_url_desc2str( ludlist );
540 				assert( bv.bv_val != NULL );
541 				ldap_free_urldesc( ludlist );
542 				bv.bv_len = strlen( bv.bv_val );
543 				attr_merge_normalize_one( e, slap_schema.si_ad_labeledURI,
544 					&bv, NULL );
545 				ch_free( bv.bv_val );
546 
547 				ludlist = next;
548 			}
549 		}
550 
551 		ber_str2bv( li->li_uri, 0, 0, &bv );
552 		attr_merge_normalize_one( e, ad_olmDbURIList,
553 			&bv, NULL );
554 	}
555 
556 	ber_dupbv( &li->li_monitor_info.lmi_nrdn, &e->e_nname );
557 
558 	cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
559 	cb->mc_update = ldap_back_monitor_update;
560 	cb->mc_modify = ldap_back_monitor_modify;
561 	cb->mc_free = ldap_back_monitor_free;
562 	cb->mc_private = (void *)li;
563 
564 	rc = mbe->register_entry_parent( e, cb,
565 		(monitor_subsys_t *)&li->li_monitor_info,
566 		MONITOR_F_VOLATILE_CH,
567 		base, LDAP_SCOPE_SUBORDINATE, filter );
568 
569 cleanup:;
570 	if ( rc != 0 ) {
571 		if ( cb != NULL ) {
572 			ch_free( cb );
573 			cb = NULL;
574 		}
575 
576 		if ( e != NULL ) {
577 			entry_free( e );
578 			e = NULL;
579 		}
580 
581 		if ( !BER_BVISNULL( filter ) ) {
582 			ch_free( filter->bv_val );
583 			BER_BVZERO( filter );
584 		}
585 	}
586 
587 	/* store for cleanup */
588 	li->li_monitor_info.lmi_cb = (void *)cb;
589 
590 	if ( e != NULL ) {
591 		entry_free( e );
592 	}
593 
594 	return rc;
595 }
596 
597 /*
598  * call from within ldap_back_db_close()
599  */
600 int
601 ldap_back_monitor_db_close( BackendDB *be )
602 {
603 	ldapinfo_t		*li = (ldapinfo_t *) be->be_private;
604 
605 	if ( li && !BER_BVISNULL( &li->li_monitor_info.lmi_filter ) ) {
606 		BackendInfo		*mi;
607 		monitor_extra_t		*mbe;
608 
609 		/* check if monitor is configured and usable */
610 		mi = backend_info( "monitor" );
611 		if ( mi && mi->bi_extra ) {
612  			mbe = mi->bi_extra;
613 
614 			mbe->unregister_entry_parent(
615 				&li->li_monitor_info.lmi_nrdn,
616 				(monitor_callback_t *)li->li_monitor_info.lmi_cb,
617 				&li->li_monitor_info.lmi_base,
618 				li->li_monitor_info.lmi_scope,
619 				&li->li_monitor_info.lmi_filter );
620 		}
621 	}
622 
623 	return 0;
624 }
625 
626 /*
627  * call from within ldap_back_db_destroy()
628  */
629 int
630 ldap_back_monitor_db_destroy( BackendDB *be )
631 {
632 	ldapinfo_t		*li = (ldapinfo_t *) be->be_private;
633 
634 	if ( li ) {
635 		(void)ldap_back_monitor_info_destroy( li );
636 	}
637 
638 	return 0;
639 }
640 
641