2 * Copyright (c) 2003 by International Business Machines, Inc.
4 * International Business Machines, Inc. (hereinafter called IBM) grants
5 * permission under its copyrights to use, copy, modify, and distribute this
6 * Software with or without fee, provided that the above copyright notice and
7 * all paragraphs of this notice appear in all copies, and that the name of IBM
8 * not be used in connection with the marketing of any product incorporating
9 * the Software or modifications thereof, without specific, written prior
12 * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
13 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14 * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
15 * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
17 * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
20 * This software is based on the backends back-ldap and back-meta, implemented
21 * by Howard Chu <hyc@highlandsun.com>, Mark Valence
22 * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
25 * The original copyright statements follow.
26 * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
27 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
29 * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
31 * This work has been developed to fulfill the requirements
32 * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
33 * to the OpenLDAP Foundation in the hope that it may be useful
34 * to the Open Source community, but WITHOUT ANY WARRANTY.
36 * Permission is granted to anyone to use this software for any purpose
37 * on any computer system, and to alter it and redistribute it, subject
38 * to the following restrictions:
40 * 1. The author and SysNet s.n.c. are not responsible for the consequences
41 * of use of this software, no matter how awful, even if they arise from
44 * 2. The origin of this software must not be misrepresented, either by
45 * explicit claim or by omission. Since few users ever read sources,
46 * credits should appear in the documentation.
48 * 3. Altered versions must be plainly marked as such, and must not be
49 * misrepresented as being the original software. Since few users
50 * ever read sources, credits should appear in the documentation.
51 * SysNet s.n.c. cannot be responsible for the consequences of the
54 * 4. This notice may not be removed or altered.
57 * This software is based on the backend back-ldap, implemented
58 * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
59 * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
60 * contributors. The contribution of the original software to the present
61 * implementation is acknowledged in this copyright statement.
63 * A special acknowledgement goes to Howard for the overall architecture
64 * (and for borrowing large pieces of code), and to Mark, who implemented
65 * from scratch the attribute/objectclass mapping.
67 * The original copyright statement follows.
69 * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
71 * Permission is granted to anyone to use this software for any purpose
72 * on any computer system, and to alter it and redistribute it, subject
73 * to the following restrictions:
75 * 1. The author is not responsible for the consequences of use of this
76 * software, no matter how awful, even if they arise from flaws in it.
78 * 2. The origin of this software must not be misrepresented, either by
79 * explicit claim or by omission. Since few users ever read sources,
80 * credits should appear in the documentation.
82 * 3. Altered versions must be plainly marked as such, and must not be
83 * misrepresented as being the original software. Since few users
84 * ever read sources, credits should appear in the
87 * 4. This notice may not be removed or altered.
94 #include <ac/socket.h>
95 #include <ac/string.h>
101 #include "../back-ldap/back-ldap.h"
102 #include "back-meta.h"
103 #undef ldap_debug /* silence a warning in ldap-int.h */
104 #include "ldap_log.h"
105 #include "../../../libraries/libldap/ldap-int.h"
114 struct exception* result
123 static struct metaconn*
128 struct berval *nbase,
129 struct exception *result
134 AttributeName** newattrs,
135 AttributeName* attrs,
136 AttributeName* filter_attrs
144 int* msgid, Backend* be,
145 AttributeName* attrs,
149 Entry*** entry_array,
152 struct exception* result
159 struct metasingleconn* lsc,
163 struct exception* result
168 struct rewrite_info* info,
169 const char* rewriteContext,
173 struct exception* result
178 AttributeName* attrs,
185 AttributeName* attrs_in,
195 struct exception* result
201 struct berval* tempstr,
219 meta_back_cache_search(
227 struct berval *nbase,
233 struct berval *filterstr,
234 AttributeName *attributes,
238 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
240 struct metasingleconn *lsc;
241 cache_manager* cm = li->cm;
242 query_manager* qm = cm->qm;
248 int count, rc = 0, *msgid = NULL;
253 int i = -1, last = 0, candidates = 0, op_type;
255 struct berval mfilter;
256 struct berval cachebase = { 0L, NULL };
257 struct berval ncachebase = { 0L, NULL };
258 struct berval cache_suffix;
259 struct berval tempstr = { 0L, NULL };
261 AttributeName *filter_attrs = NULL;
262 AttributeName *new_attrs = NULL;
263 AttributeName *attrs = NULL;
266 Entry **entry_array = NULL;
271 int template_id = -1;
279 struct exception result[1];
281 Filter* filter = str2filter(op->ors_filterstr.bv_val);
282 slap_callback cb = {cache_back_sentry, NULL};
284 cb.sc_private = op->o_bd;
287 for ( count=0; op->ors_attrs[ count ].an_name.bv_val; count++ )
289 attrs = (AttributeName*)malloc( ( count + 1 ) *
290 sizeof(AttributeName));
291 for ( count=0; op->ors_attrs[ count ].an_name.bv_val; count++ ) {
292 ber_dupbv(&attrs[ count ].an_name,
293 &op->ors_attrs[ count ].an_name);
294 attrs[count].an_desc = op->ors_attrs[count].an_desc;
296 attrs[ count ].an_name.bv_val = NULL;
297 attrs[ count ].an_name.bv_len = 0;
301 result->type = SUCCESS;
303 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
306 LDAP_LOG( BACK_META, DETAIL1, "Threads++ = %d\n", cm->threads, 0, 0 );
307 #else /* !NEW_LOGGING */
308 Debug( LDAP_DEBUG_ANY, "Threads++ = %d\n", cm->threads, 0, 0 );
309 #endif /* !NEW_LOGGING */
310 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
312 ldap_pvt_thread_mutex_lock(&cm->cc_mutex);
313 if (!cm->cc_thread_started) {
314 oper = (Operation*)malloc(sizeof(Operation));
316 cm->cc_thread_started = 1;
317 ldap_pvt_thread_create(&(cm->cc_thread), 1, consistency_check, (void*)oper);
319 ldap_pvt_thread_mutex_unlock(&cm->cc_mutex);
321 filter2template(filter, &tempstr, &filter_attrs, &fattr_cnt, result);
322 if (result->type != SUCCESS)
326 LDAP_LOG( BACK_META, DETAIL1, "query template of incoming query = %s\n",
327 tempstr.bv_val, 0, 0 );
328 #else /* !NEW_LOGGING */
329 Debug( LDAP_DEBUG_ANY, "query template of incoming query = %s\n",
330 tempstr.bv_val, 0, 0 );
331 #endif /* !NEW_LOGGING */
332 curr_limit = cm->num_entries_limit ;
335 attr_set = get_attr_set(attrs, qm, cm->numattrsets);
337 query.filter = filter;
339 query.base = op->o_req_dn;
340 query.scope = op->ors_scope;
342 /* check for query containment */
344 for (i=0; i<cm->numtemplates; i++) {
345 /* find if template i can potentially answer tempstr */
346 if (!is_temp_answerable(attr_set, &tempstr, qm, i))
348 if (attr_set == qm->templates[i].attr_set_index) {
353 LDAP_LOG( BACK_META, DETAIL2,
354 "Entering QC, querystr = %s\n",
355 op->ors_filterstr.bv_val, 0, 0 );
356 #else /* !NEW_LOGGING */
357 Debug( LDAP_DEBUG_NONE, "Entering QC, querystr = %s\n",
358 op->ors_filterstr.bv_val, 0, 0 );
359 #endif /* !NEW_LOGGING */
360 answerable = (*(qm->qcfunc))(qm, &query, i);
371 LDAP_LOG( BACK_META, DETAIL1, "QUERY ANSWERABLE\n", 0, 0, 0 );
372 #else /* !NEW_LOGGING */
373 Debug( LDAP_DEBUG_ANY, "QUERY ANSWERABLE\n", 0, 0, 0 );
374 #endif /* !NEW_LOGGING */
375 rewriteSession(li->rwinfo, "cacheBase", op->o_req_dn.bv_val,
376 op->o_conn, &cbase, result);
377 if (result->type != SUCCESS) {
378 ldap_pvt_thread_rdwr_runlock(&qm->templates[i].t_rwlock);
381 if ( cbase == NULL ) {
382 cachebase = op->o_req_dn;
384 cachebase.bv_val = cbase;
385 cachebase.bv_len = strlen(cbase);
387 dnNormalize(0, NULL, NULL, &cachebase, &ncachebase,
390 /* FIXME: safe default? */
393 op_tmp.o_bd = li->glue_be;
394 op_tmp.o_req_dn = cachebase;
395 op_tmp.o_req_ndn = ncachebase;
397 op_tmp.o_caching_on = 1;
398 op_tmp.o_callback = &cb;
400 li->glue_be->be_search(&op_tmp, rs);
401 free( ncachebase.bv_val );
402 if ( cachebase.bv_val != op->o_req_dn.bv_val ) {
403 /* free only if rewritten */
404 free( cachebase.bv_val );
407 ldap_pvt_thread_rdwr_runlock(&qm->templates[i].t_rwlock);
412 LDAP_LOG( BACK_META, DETAIL1, "QUERY NOT ANSWERABLE\n",
414 #else /* !NEW_LOGGING */
415 Debug( LDAP_DEBUG_ANY, "QUERY NOT ANSWERABLE\n", 0, 0, 0 );
416 #endif /* !NEW_LOGGING */
418 if ( op->ors_scope == LDAP_SCOPE_BASE ) {
419 op_type = META_OP_REQUIRE_SINGLE;
421 op_type = META_OP_ALLOW_MULTIPLE;
424 lc = metaConnect(&op_tmp, rs, op_type,
425 &op->o_req_ndn, result);
427 if (result->type != SUCCESS)
430 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
431 if (cm->num_cached_queries >= cm->max_queries) {
434 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
437 add_filter_attrs(&new_attrs, attrs, filter_attrs);
445 * Array of message id of each target
447 msgid = ch_calloc( sizeof( int ), li->ntargets );
448 if ( msgid == NULL ) {
449 result->type = CONN_ERR;
454 if (slimit > 0 && (slimit <= cm->num_entries_limit))
455 slimit = cm->num_entries_limit;
462 for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
463 char *realbase = ( char * )op->o_req_dn.bv_val;
464 int realscope = op->ors_scope;
466 char *mapped_filter, **mapped_attrs;
468 /* FIXME: Check for more than one targets */
469 if ( meta_back_is_candidate(
470 &li->targets[i]->suffix,
472 lsc->candidate = META_CANDIDATE;
474 if ( lsc->candidate != META_CANDIDATE )
477 if ( op->ors_deref != -1 ) {
478 ldap_set_option( lsc->ld, LDAP_OPT_DEREF,
479 ( void * )&op->ors_deref);
481 if ( op->ors_tlimit != -1 ) {
482 ldap_set_option( lsc->ld, LDAP_OPT_TIMELIMIT,
483 ( void * )&op->ors_tlimit);
485 if ( op->ors_slimit != -1 ) {
486 ldap_set_option( lsc->ld, LDAP_OPT_SIZELIMIT,
487 ( void * )&op->ors_slimit);
491 * modifies the base according to the scope, if required
493 suffixlen = li->targets[ i ]->suffix.bv_len;
494 if ( suffixlen > op->o_req_ndn.bv_len ) {
495 switch ( op->ors_scope ) {
496 case LDAP_SCOPE_SUBTREE:
498 * make the target suffix the new base
499 * FIXME: this is very forgiving,
500 * because illegal bases may be turned
501 * into the suffix of the target.
504 &li->targets[ i ]->suffix,
507 li->targets[i]->suffix.bv_val;
510 * this target is no longer
519 case LDAP_SCOPE_ONELEVEL:
520 if ( is_one_level_rdn(
521 li->targets[ i ]->suffix.bv_val,
522 suffixlen - op->o_req_ndn.bv_len - 1 )
524 &li->targets[ i ]->suffix,
527 * if there is exactly one
528 * level, make the target suffix
529 * the new base, and make scope
533 li->targets[i]->suffix.bv_val;
534 realscope = LDAP_SCOPE_BASE;
536 } /* else continue with the next case */
538 case LDAP_SCOPE_BASE:
540 * this target is no longer candidate
542 lsc->candidate = META_NOT_CANDIDATE;
548 * Rewrite the search base, if required
551 rewriteSession(li->targets[i]->rwmap.rwm_rw,
553 realbase, op->o_conn, &mbase, result);
555 if (result->type != SUCCESS)
558 if ( mbase == NULL ) {
563 * Rewrite the search filter, if required
565 rewriteSession( li->targets[i]->rwmap.rwm_rw,
567 op->ors_filterstr.bv_val, op->o_conn,
568 &mfilter.bv_val, result);
569 if (result->type != SUCCESS)
572 if ( mfilter.bv_val != NULL && mfilter.bv_val[ 0 ]
574 mfilter.bv_len = strlen( mfilter.bv_val );
576 if ( mfilter.bv_val != NULL ) {
577 free( mfilter.bv_val );
579 mfilter = op->ors_filterstr;
584 * Maps attributes in filter
586 mapped_filter = ldap_back_map_filter(
587 &li->targets[i]->rwmap.rwm_at,
588 &li->targets[i]->rwmap.rwm_oc,
590 if ( mapped_filter == NULL ) {
591 mapped_filter = ( char * )mfilter.bv_val;
593 if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
594 free( mfilter.bv_val );
597 mfilter.bv_val = NULL;
600 mapped_filter = (char *) mfilter.bv_val;
604 * Maps required attributes
606 if ( ldap_back_map_attrs(
607 &li->targets[ i ]->rwmap.rwm_at,
608 new_attrs, 0, &mapped_attrs ) ) {
615 msgid[ i ] = ldap_search( lsc->ld, mbase, realscope,
616 mapped_filter, mapped_attrs,
619 if ( msgid[ i ] == -1 ) {
620 result->type = CONN_ERR;
623 lsc->candidate = META_NOT_CANDIDATE;
628 if ( mapped_attrs ) {
629 free( mapped_attrs );
633 if ( mapped_filter != op->ors_filterstr.bv_val ) {
634 free( mapped_filter );
635 mapped_filter = NULL;
638 if ( mbase != realbase ) {
646 num_entries = handleLdapResult(lc, &op_tmp, rs, msgid,
648 op->ors_attrsonly, candidates,
649 cacheable, &entry_array,
650 curr_limit, op->ors_slimit, result);
652 if (result->type != SUCCESS)
654 if (cacheable && (num_entries <= curr_limit)) {
657 LDAP_LOG( BACK_META, DETAIL1,
658 "QUERY CACHEABLE\n", 0, 0, 0 );
659 #else /* !NEW_LOGGING */
660 Debug( LDAP_DEBUG_ANY, "QUERY CACHEABLE\n", 0, 0, 0 );
661 #endif /* !NEW_LOGGING */
662 op_tmp.o_bd = li->glue_be;
663 uuid = cache_entries(&op_tmp, rs, entry_array,
666 LDAP_LOG( BACK_META, DETAIL1,
667 "Added query %s UUID %s ENTRIES %d\n",
668 op->ors_filterstr.bv_val,
670 #else /* !NEW_LOGGING */
671 Debug( LDAP_DEBUG_ANY,
672 "Added query %s UUID %s ENTRIES %d\n",
673 op->ors_filterstr.bv_val,
675 #endif /* !NEW_LOGGING */
677 if (result->type != SUCCESS)
679 (*(qm->addfunc))(qm, &query, template_id, uuid, result);
680 if (result->type != SUCCESS)
686 LDAP_LOG( BACK_META, DETAIL1,
687 "QUERY NOT CACHEABLE no\n",
689 #else /* !NEW_LOGGING */
690 Debug( LDAP_DEBUG_ANY, "QUERY NOT CACHEABLE no\n",
692 #endif /* !NEW_LOGGING */
697 switch (result->type) {
704 LDAP_LOG( BACK_META, DETAIL1,
705 "Invalid template error\n", 0, 0, 0 );
706 #else /* !NEW_LOGGING */
707 Debug( LDAP_DEBUG_ANY, "Invalid template error\n",
709 #endif /* !NEW_LOGGING */
715 LDAP_LOG( BACK_META, DETAIL1,
716 "Could not connect to a remote server\n",
718 #else /* !NEW_LOGGING */
719 Debug( LDAP_DEBUG_ANY,
720 "Could not connect to a remote server\n",
722 #endif /* !NEW_LOGGING */
723 send_ldap_error(op, rs, LDAP_OTHER,
724 "Connection error" );
730 LDAP_LOG( BACK_META, DETAIL1,
731 "Error in handling ldap_result\n", 0, 0, 0 );
732 #else /* !NEW_LOGGING */
733 Debug( LDAP_DEBUG_ANY,
734 "Error in handling ldap_result\n", 0, 0, 0 );
735 #endif /* !NEW_LOGGING */
740 if (result->rc == REWRITE_REGEXEC_UNWILLING) {
741 send_ldap_error( op, rs,
742 LDAP_UNWILLING_TO_PERFORM,
743 "Unwilling to perform" );
745 send_ldap_error( op, rs, LDAP_OTHER,
753 LDAP_LOG( BACK_META, DETAIL1,
754 "Error in merging entry \n", 0, 0, 0 );
755 #else /* !NEW_LOGGING */
756 Debug( LDAP_DEBUG_ANY,
757 "Error in merging entry \n", 0, 0, 0 );
758 #endif /* !NEW_LOGGING */
764 LDAP_LOG( BACK_META, DETAIL1,
765 "Error in removing query \n",
767 #else /* !NEW_LOGGING */
768 Debug( LDAP_DEBUG_ANY, "Error in removing query \n",
770 #endif /* !NEW_LOGGING */
783 for (i=0; (e = entry_array[i]); i++) {
792 if (new_attrs != attrs)
799 if (tempstr.bv_val ) {
800 free(tempstr.bv_val);
802 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
805 LDAP_LOG( BACK_META, DETAIL1, "Threads-- = %d\n", cm->threads, 0, 0 );
806 #else /* !NEW_LOGGING */
807 Debug( LDAP_DEBUG_ANY, "Threads-- = %d\n", cm->threads, 0, 0 );
808 #endif /* !NEW_LOGGING */
809 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
820 struct exception* result
823 struct metainfo *li = ( struct metainfo * )be->be_private;
824 struct berval a, mapped;
826 BerElement ber = *e->lm_ber;
827 Attribute *attr, **attrp;
828 struct berval dummy = { 0, NULL };
829 struct berval *bv, bdn;
832 if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
833 result->type = CREATE_ENTRY_ERR;
836 ent = (Entry*)malloc(sizeof(Entry));
839 * Rewrite the dn of the result, if needed
841 rewriteSession( li->targets[ target ]->rwmap.rwm_rw, "searchResult",
842 bdn.bv_val, lc->conn, &ent->e_name.bv_val, result );
844 if (result->type != SUCCESS) {
847 if ( ent->e_name.bv_val == NULL ) {
848 ber_dupbv(&(ent->e_name), &bdn);
851 LDAP_LOG( BACK_META, DETAIL1,
852 "[rw] searchResult[%d]: \"%s\" -> \"%s\"\n",
853 target, bdn.bv_val, ent->e_name.bv_val );
854 #else /* !NEW_LOGGING */
855 Debug( LDAP_DEBUG_ARGS, "rw> searchResult[%d]: \"%s\""
857 target, bdn.bv_val, ent->e_name.bv_val );
858 #endif /* !NEW_LOGGING */
859 ent->e_name.bv_len = strlen( ent->e_name.bv_val );
863 * Note: this may fail if the target host(s) schema differs
864 * from the one known to the meta, and a DN with unknown
865 * attributes is returned.
867 * FIXME: should we log anything, or delegate to dnNormalize?
869 dnNormalize( 0, NULL, NULL, &ent->e_name, &ent->e_nname, NULL );
872 if ( dnNormalize( 0, NULL, NULL, &ent->e_name, &ent->e_nname )
875 return LDAP_INVALID_DN_SYNTAX;
882 if ( li->cache.ttl != META_DNCACHE_DISABLED ) {
883 meta_dncache_update_entry( &li->cache, &ent->e_nname, target );
889 ent->e_bv.bv_val = 0;
891 attrp = &ent->e_attrs;
893 while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
894 ldap_back_map( &li->targets[ target ]->rwmap.rwm_at,
896 if ( mapped.bv_val == NULL ) {
899 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
900 if ( attr == NULL ) {
906 attr->a_nvals = NULL;
907 if ( slap_bv2ad( &mapped, &attr->a_desc, &text ) != LDAP_SUCCESS) {
908 if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text )
911 LDAP_LOG( BACK_META, DETAIL1,
912 "slap_bv2undef_ad(%s): %s\n",
913 mapped.bv_val, text, 0 );
914 #else /* !NEW_LOGGING */
915 Debug( LDAP_DEBUG_ANY,
916 "slap_bv2undef_ad(%s): "
917 "%s\n%s", mapped.bv_val, text, "" );
918 #endif /* !NEW_LOGGING */
924 /* no subschemaSubentry */
925 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
930 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
931 || attr->a_vals == NULL ) {
932 attr->a_vals = &dummy;
933 } else if ( attr->a_desc == slap_schema.si_ad_objectClass ||
935 slap_schema.si_ad_structuralObjectClass) {
937 for ( last = 0; attr->a_vals[ last ].bv_val; ++last )
939 for ( i = 0, bv = attr->a_vals; bv->bv_val; bv++,i++ ) {
940 ldap_back_map( &li->targets[ target]->rwmap.rwm_oc,
942 if ( mapped.bv_val == NULL ) {
948 *bv = attr->a_vals[ last ];
949 attr->a_vals[ last ].bv_val = NULL;
951 } else if ( mapped.bv_val != bv->bv_val ) {
953 ber_dupbv( bv, &mapped );
957 * It is necessary to try to rewrite attributes with
958 * dn syntax because they might be used in ACLs as
959 * members of groups; since ACLs are applied to the
960 * rewritten stuff, no dn-based subecj clause could
961 * be used at the ldap backend side (see
962 * http://www.OpenLDAP.org/faq/data/cache/452.html)
963 * The problem can be overcome by moving the dn-based
964 * ACLs to the target directory server, and letting
965 * everything pass thru the ldap backend.
967 } else if ( strcmp( attr->a_desc->ad_type->sat_syntax->ssyn_oid,
968 SLAPD_DN_SYNTAX ) == 0 ) {
970 for ( i = 0, bv = attr->a_vals; bv->bv_val; bv++,i++ ) {
972 rewriteSession(li->targets[ target ]->rwmap.rwm_rw,
973 "searchResult", bv->bv_val,
974 lc->conn, &newval, result);
975 if (result->type != SUCCESS) {
976 /* FIXME : Handle error */
977 result->type = SUCCESS;
980 if ( newval == NULL ) {
984 LDAP_LOG( BACK_META, DETAIL1,
985 "[rw] searchResult on "
986 "attr=%s: \"%s\" -> \"%s\"\n",
987 attr->a_desc->ad_type->
989 bv->bv_val, newval );
990 #else /* !NEW_LOGGING */
991 Debug( LDAP_DEBUG_ARGS,
992 "rw> searchResult on attr=%s:"
993 " \"%s\" -> \"%s\"\n",
994 attr->a_desc->ad_type->
996 bv->bv_val, newval );
997 #endif /* !NEW_LOGGING */
1000 bv->bv_len = strlen( newval );
1005 attrp = &attr->a_next;
1017 if ( DN_SEPARATOR( rdn[ from ] ) ) {
1024 static struct metaconn*
1029 struct berval *nbase,
1030 struct exception *result)
1032 struct metaconn *lc;
1034 result->type = SUCCESS;
1035 lc = meta_back_getconn( op, rs, op_type, nbase, NULL );
1037 result->type = CONN_ERR;
1045 AttributeName** new_attrs,
1046 AttributeName* attrs,
1047 AttributeName* filter_attrs )
1049 struct berval all_user = { sizeof(LDAP_ALL_USER_ATTRIBUTES) -1,
1050 LDAP_ALL_USER_ATTRIBUTES };
1052 struct berval all_op = { sizeof(LDAP_ALL_OPERATIONAL_ATTRIBUTES) -1,
1053 LDAP_ALL_OPERATIONAL_ATTRIBUTES};
1060 /* duplicate attrs */
1061 if (attrs == NULL) {
1064 for (count=0; attrs[count].an_name.bv_val; count++)
1067 *new_attrs = (AttributeName*)(malloc((count+1)*sizeof(AttributeName)));
1068 if (attrs == NULL) {
1069 (*new_attrs)[0].an_name.bv_val = "*";
1070 (*new_attrs)[0].an_name.bv_len = 1;
1071 (*new_attrs)[1].an_name.bv_val = NULL;
1072 (*new_attrs)[1].an_name.bv_len = 0;
1076 for (i=0; i<count; i++) {
1077 (*new_attrs)[i].an_name = attrs[i].an_name;
1078 (*new_attrs)[i].an_desc = attrs[i].an_desc;
1080 (*new_attrs)[count].an_name.bv_val = NULL;
1081 (*new_attrs)[count].an_name.bv_len = 0;
1082 alluser = an_find(*new_attrs, &all_user);
1083 allop = an_find(*new_attrs, &all_op);
1086 for ( i=0; filter_attrs[i].an_name.bv_val; i++ ) {
1087 if ( an_find(*new_attrs, &filter_attrs[i].an_name ))
1089 if ( is_at_operational(filter_attrs[i].an_desc->ad_type) ) {
1094 *new_attrs = (AttributeName*)(realloc(*new_attrs,
1095 (count+2)*sizeof(AttributeName)));
1096 (*new_attrs)[count].an_name.bv_val =
1097 filter_attrs[i].an_name.bv_val;
1098 (*new_attrs)[count].an_name.bv_len =
1099 filter_attrs[i].an_name.bv_len;
1100 (*new_attrs)[count].an_desc = filter_attrs[i].an_desc;
1101 (*new_attrs)[count+1].an_name.bv_val = NULL;
1102 (*new_attrs)[count+1].an_name.bv_len = 0;
1109 struct metaconn* lc,
1112 int* msgid, Backend* be,
1113 AttributeName* attrs,
1117 Entry*** entry_array,
1120 struct exception* result)
1123 char *match = NULL, *err = NULL, *cache_ename = NULL;
1125 int mres = LDAP_SUCCESS;
1126 int num_entries = 0, count, i, rc;
1127 struct timeval tv = {0, 0};
1128 struct metasingleconn* lsc;
1129 struct metainfo *li = ( struct metainfo * )be->be_private;
1131 result->type = SUCCESS;
1133 for ( count = 0, rc = 0; candidates > 0; ) {
1136 /* check for abandon */
1139 for ( i = 0, lsc = lc->conns; !META_LAST(lsc); lsc++, i++ ) {
1140 if ( lsc->candidate != META_CANDIDATE ) {
1145 ldap_abandon( lsc->ld, msgid[ i ] );
1146 result->type = ABANDON_ERR;
1150 if ( slimit > 0 && num_entries == slimit ) {
1151 result->type = SLIMIT_ERR;
1155 if ((entry = get_result_entry(be, lc, lsc,
1156 msgid, i, &tv, result))) {
1157 rs->sr_entry = entry;
1158 rs->sr_attrs = op->ors_attrs;
1159 send_search_entry( op, rs );
1160 rs->sr_entry = NULL;
1161 rs->sr_attrs = NULL;
1163 (num_entries < curr_limit)) {
1164 rewriteSession( li->rwinfo,
1166 entry->e_name.bv_val,
1168 &cache_ename, result );
1169 free(entry->e_name.bv_val);
1170 if (result->type != SUCCESS) {
1173 ber_str2bv(cache_ename,
1174 strlen(cache_ename),
1176 ber_dupbv(&entry->e_nname,
1178 *entry_array = (Entry**)realloc(
1180 (( num_entries+2 ) *
1182 (*entry_array)[num_entries] = entry;
1183 (*entry_array)[num_entries+1] = NULL;
1187 } else if (result->type == REWRITING_ERR) {
1189 } else if (result->type == TIMEOUT_ERR) {
1190 result->type = SUCCESS;
1192 } else if (result->type == CREATE_ENTRY_ERR) {
1194 } else if (result->rc == -1) {
1197 rs->sr_err = result->rc;
1198 sres = ldap_back_map_result(rs);
1199 if (mres == LDAP_SUCCESS &&
1200 sres != LDAP_SUCCESS) {
1202 ldap_get_option(lsc->ld,
1203 LDAP_OPT_ERROR_STRING, &err);
1204 ldap_get_option(lsc->ld,
1205 LDAP_OPT_MATCHED_DN, &match);
1207 lsc->candidate = META_NOT_CANDIDATE;
1209 result->type = SUCCESS;
1212 switch (result->type) {
1215 LDAP_LOG( BACK_META, DETAIL1,
1216 "ldap_result error, rc = -1\n",
1218 #else /* !NEW_LOGGING */
1219 Debug( LDAP_DEBUG_ANY, "ldap_result error, rc = -1\n",
1221 #endif /* !NEW_LOGGING */
1222 rs->sr_err = LDAP_OTHER;
1223 send_ldap_result( op, rs );
1226 case CREATE_ENTRY_ERR:
1228 LDAP_LOG( BACK_META, DETAIL1,
1229 "Error in parsing result \n",
1231 #else /* !NEW_LOGGING */
1232 Debug( LDAP_DEBUG_ANY, "Error in parsing result \n",
1234 #endif /* !NEW_LOGGING */
1235 rs->sr_err = LDAP_OTHER;
1236 send_ldap_result( op, rs );
1237 result->type = RESULT_ERR;
1242 LDAP_LOG( BACK_META, DETAIL1, "Size limit exceeded \n",
1244 #else /* !NEW_LOGGING */
1245 Debug( LDAP_DEBUG_ANY, "Size limit exceeded \n",
1247 #endif /* !NEW_LOGGING */
1248 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1249 send_ldap_result( op, rs );
1250 result->type = RESULT_ERR;
1255 LDAP_LOG( BACK_META, DETAIL1,
1256 "search operation abandoned \n",
1258 #else /* !NEW_LOGGING */
1259 Debug( LDAP_DEBUG_ANY, "search operation abandoned \n",
1261 #endif /* !NEW_LOGGING */
1262 result->type = RESULT_ERR;
1271 tv.tv_usec = 100000;
1272 ldap_pvt_thread_yield();
1281 rs->sr_matched = match;
1283 send_ldap_result( op, rs );
1286 rs->sr_matched = NULL;
1294 result->type = (mres == LDAP_SUCCESS) ? SUCCESS : RESULT_ERR;
1301 struct metaconn* lc,
1302 struct metasingleconn* lsc,
1306 struct exception* result)
1309 LDAPMessage *res, *e;
1311 int sres = LDAP_SUCCESS;
1313 rc = ldap_result( lsc->ld, msgid[ i ],
1317 result->type = TIMEOUT_ERR;
1319 } else if ( rc == -1 ) {
1321 result->type = RESULT_ERR;
1323 } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
1324 e = ldap_first_entry( lsc->ld, res );
1325 entry = meta_create_entry(be, lc, i, e, result);
1329 ldap_msgfree( res );
1330 result->type = SUCCESS;
1333 sres = ldap_result2error( lsc->ld,
1336 result->type = RESULT_ERR;
1343 struct rewrite_info* info,
1344 const char* rewriteContext,
1348 struct exception* result)
1350 int rc = rewrite_session(info, rewriteContext, string, cookie, base);
1351 if (rc != REWRITE_REGEXEC_OK) {
1353 result->type = REWRITING_ERR;
1355 if (strcmp(rewriteContext, "searchBase") == 0)
1357 LDAP_LOG( BACK_META, DETAIL1,
1358 "Problem in rewriting search base\n", 0, 0, 0 );
1359 #else /* !NEW_LOGGING */
1360 Debug( LDAP_DEBUG_ANY,
1361 "Problem in rewriting search base\n", 0, 0, 0 );
1362 #endif /* !NEW_LOGGING */
1363 if (strcmp(rewriteContext, "searchFilter") == 0)
1365 LDAP_LOG( BACK_META, DETAIL1,
1366 "Problem in rewriting search filter\n",
1368 #else /* !NEW_LOGGING */
1369 Debug( LDAP_DEBUG_ANY,
1370 "Problem in rewriting search filter\n",
1372 #endif /* !NEW_LOGGING */
1373 if (strcmp(rewriteContext, "searchResult") == 0)
1375 LDAP_LOG( BACK_META, DETAIL1,
1376 "Problem in rewriting DN, or DN syntax "
1377 "attributes of search result\n", 0, 0, 0 );
1378 #else /* !NEW_LOGGING */
1379 Debug( LDAP_DEBUG_ANY,
1380 "Problem in rewriting DN, or DN syntax "
1381 "attributes of search result\n", 0, 0, 0 );
1382 #endif /* !NEW_LOGGING */
1383 if (strcmp(rewriteContext, "cacheBase") == 0)
1385 LDAP_LOG( BACK_META, DETAIL1,
1386 "Problem in rewriting search base with "
1387 "cache base\n", 0, 0, 0 );
1388 #else /* !NEW_LOGGING */
1389 Debug( LDAP_DEBUG_ANY,
1390 "Problem in rewriting search base with "
1391 "cache base\n", 0, 0, 0 );
1392 #endif /* !NEW_LOGGING */
1393 if (strcmp(rewriteContext, "cacheResult") == 0)
1395 LDAP_LOG( BACK_META, DETAIL1,
1396 "Problem in rewriting DN for cached entries\n",
1398 #else /* !NEW_LOGGING */
1399 Debug( LDAP_DEBUG_ANY,
1400 "Problem in rewriting DN for cached entries\n",
1402 #endif /* !NEW_LOGGING */
1403 if (strcmp(rewriteContext, "cacheReturn") == 0)
1405 LDAP_LOG( BACK_META, DETAIL1,
1406 "Problem in rewriting DN for answerable "
1407 "entries\n", 0, 0, 0 );
1408 #else /* !NEW_LOGGING */
1409 Debug( LDAP_DEBUG_ANY,
1410 "Problem in rewriting DN for answerable "
1411 "entries\n", 0, 0, 0 );
1412 #endif /* !NEW_LOGGING */
1414 result->type = SUCCESS;
1420 AttributeName* attrs,
1425 for (i=0; i<num; i++) {
1426 if (attrscmp(attrs, qm->attr_sets[i].attrs))
1434 AttributeName* attrs_in,
1435 AttributeName* attrs)
1437 int i, count1, count2;
1438 if ( attrs_in == NULL ) {
1439 return (attrs ? 0 : 1);
1441 if ( attrs == NULL )
1445 attrs_in && attrs_in[count1].an_name.bv_val != NULL;
1449 attrs && attrs[count2].an_name.bv_val != NULL;
1452 if ( count1 != count2 )
1455 for ( i=0; i<count1; i++ ) {
1456 if ( !an_find(attrs, &attrs_in[i].an_name ))
1466 Entry** entry_array,
1468 struct exception* result)
1474 struct berval query_uuid;
1475 struct berval crp_uuid;
1476 char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ], *crpid;
1478 query_manager *qm = cm->qm;
1480 result->type = SUCCESS;
1481 query_uuid.bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
1482 query_uuid.bv_val = ch_strdup(uuidbuf);
1485 LDAP_LOG( BACK_META, DETAIL1, "UUID for query being added = %s\n",
1487 #else /* !NEW_LOGGING */
1488 Debug( LDAP_DEBUG_ANY, "UUID for query being added = %s\n",
1490 #endif /* !NEW_LOGGING */
1492 for ( i=0; ( entry_array && (e=entry_array[i]) ); i++ ) {
1494 LDAP_LOG( BACK_META, DETAIL2, "LOCKING REMOVE MUTEX\n",
1496 #else /* !NEW_LOGGING */
1497 Debug( LDAP_DEBUG_NONE, "LOCKING REMOVE MUTEX\n", 0, 0, 0 );
1498 #endif /* !NEW_LOGGING */
1499 ldap_pvt_thread_mutex_lock(&cm->remove_mutex);
1501 LDAP_LOG( BACK_META, DETAIL2, "LOCKED REMOVE MUTEX\n", 0, 0, 0);
1502 #else /* !NEW_LOGGING */
1503 Debug( LDAP_DEBUG_NONE, "LOCKED REMOVE MUTEX\n", 0, 0, 0);
1504 #endif /* !NEW_LOGGING */
1505 if ( cm->cache_size > (cm->thresh_hi) ) {
1506 while(cm->cache_size > (cm->thresh_lo)) {
1507 crpid = cache_replacement(qm);
1508 if (crpid == NULL) {
1509 result->type = REMOVE_ERR;
1511 strcpy(crpuuid, crpid);
1512 crp_uuid.bv_val = crpuuid;
1513 crp_uuid.bv_len = strlen(crpuuid);
1515 LDAP_LOG( BACK_META, DETAIL1,
1516 "Removing query UUID %s\n",
1518 #else /* !NEW_LOGGING */
1519 Debug( LDAP_DEBUG_ANY,
1520 "Removing query UUID %s\n",
1522 #endif /* !NEW_LOGGING */
1523 return_val = remove_query_data(op, rs,
1526 LDAP_LOG( BACK_META, DETAIL1,
1527 "QUERY REMOVED, SIZE=%d\n",
1529 #else /* !NEW_LOGGING */
1530 Debug( LDAP_DEBUG_ANY,
1531 "QUERY REMOVED, SIZE=%d\n",
1533 #endif /* !NEW_LOGGING */
1534 ldap_pvt_thread_mutex_lock(
1536 cm->total_entries -= result->rc;
1537 cm->num_cached_queries--;
1539 LDAP_LOG( BACK_META, DETAIL1,
1540 "STORED QUERIES = %lu\n",
1541 cm->num_cached_queries, 0, 0 );
1542 #else /* !NEW_LOGGING */
1543 Debug( LDAP_DEBUG_ANY,
1544 "STORED QUERIES = %lu\n",
1545 cm->num_cached_queries, 0, 0 );
1546 #endif /* !NEW_LOGGING */
1547 ldap_pvt_thread_mutex_unlock(
1549 cm->cache_size = (return_val >
1551 0 : (cm->cache_size-return_val);
1553 LDAP_LOG( BACK_META, DETAIL1,
1554 "QUERY REMOVED, CACHE SIZE="
1555 "%lu bytes %d entries\n",
1557 cm->total_entries, 0 );
1558 #else /* !NEW_LOGGING */
1559 Debug( LDAP_DEBUG_ANY,
1560 "QUERY REMOVED, CACHE SIZE="
1561 "%lu bytes %d entries\n",
1563 cm->total_entries, 0 );
1564 #endif /* !NEW_LOGGING */
1570 return_val = merge_entry(op, rs, &query_uuid, result);
1571 rs->sr_entry = NULL;
1572 cm->cache_size += return_val;
1574 LDAP_LOG( BACK_META, DETAIL1,
1575 "ENTRY ADDED/MERGED, CACHE SIZE=%lu bytes\n",
1576 cm->cache_size, 0, 0 );
1577 #else /* !NEW_LOGGING */
1578 Debug( LDAP_DEBUG_ANY,
1579 "ENTRY ADDED/MERGED, CACHE SIZE=%lu bytes\n",
1580 cm->cache_size, 0, 0 );
1581 #endif /* !NEW_LOGGING */
1583 LDAP_LOG( BACK_META, DETAIL2, "UNLOCKING REMOVE MUTEX\n",
1585 #else /* !NEW_LOGGING */
1586 Debug( LDAP_DEBUG_NONE, "UNLOCKING REMOVE MUTEX\n", 0, 0, 0 );
1587 #endif /* !NEW_LOGGING */
1588 ldap_pvt_thread_mutex_unlock(&cm->remove_mutex);
1590 LDAP_LOG( BACK_META, DETAIL2, "UNLOCKED REMOVE MUTEX\n",
1592 #else /* !NEW_LOGGING */
1593 Debug( LDAP_DEBUG_NONE, "UNLOCKED REMOVE MUTEX\n", 0, 0, 0 );
1594 #endif /* !NEW_LOGGING */
1595 if (result->type != SUCCESS)
1597 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1598 cm->total_entries += result->rc;
1600 LDAP_LOG( BACK_META, DETAIL1,
1601 "ENTRY ADDED/MERGED, SIZE=%d, CACHED ENTRIES=%d\n",
1602 return_val, cm->total_entries, 0 );
1603 #else /* !NEW_LOGGING */
1604 Debug( LDAP_DEBUG_ANY,
1605 "ENTRY ADDED/MERGED, SIZE=%d, CACHED ENTRIES=%d\n",
1606 return_val, cm->total_entries, 0 );
1607 #endif /* !NEW_LOGGING */
1608 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1610 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1611 cm->num_cached_queries++;
1613 LDAP_LOG( BACK_META, DETAIL1, "STORED QUERIES = %lu\n",
1614 cm->num_cached_queries, 0, 0 );
1615 #else /* !NEW_LOGGING */
1616 Debug( LDAP_DEBUG_ANY, "STORED QUERIES = %lu\n",
1617 cm->num_cached_queries, 0, 0 );
1618 #endif /* !NEW_LOGGING */
1619 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1621 return query_uuid.bv_val;
1627 struct berval* tempstr,
1635 i = qm->templates[template_id].attr_set_index;
1636 str = qm->templates[template_id].querystr;
1638 if (attr_set == i) {
1641 id_array = qm->attr_sets[attr_set].ID_array;
1643 while (*id_array != -1) {
1651 if (strcasecmp(str, tempstr->bv_val) == 0)
1657 consistency_check(void* operation)
1659 Operation* op = (Operation*)operation;
1661 SlapReply rs = {REP_RESULT};
1663 struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
1664 cache_manager* cm = li->cm;
1665 query_manager* qm = cm->qm;
1666 CachedQuery* query, *query_prev;
1669 struct exception result;
1671 QueryTemplate* templ;
1674 op->o_bd = li->glue_be;
1677 ldap_pvt_thread_sleep(cm->cc_period);
1678 for (i=0; qm->templates[i].querystr; i++) {
1679 templ = qm->templates + i;
1680 query = templ->query_last;
1681 curr_time = slap_get_time();
1682 ldap_pvt_thread_mutex_lock(&cm->remove_mutex);
1683 while (query && (query->expiry_time < curr_time)) {
1684 ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1685 remove_query(qm, query);
1686 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1688 LDAP_LOG( BACK_META, DETAIL1, "Lock CR index = %d\n",
1690 #else /* !NEW_LOGGING */
1691 Debug( LDAP_DEBUG_ANY, "Lock CR index = %d\n",
1693 #endif /* !NEW_LOGGING */
1694 ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
1695 remove_from_template(query, templ);
1697 LDAP_LOG( BACK_META, DETAIL1,
1698 "TEMPLATE %d QUERIES-- %d\n",
1699 i, templ->no_of_queries, 0 );
1700 #else /* !NEW_LOGGING */
1701 Debug( LDAP_DEBUG_ANY, "TEMPLATE %d QUERIES-- %d\n",
1702 i, templ->no_of_queries, 0 );
1703 #endif /* !NEW_LOGGING */
1705 LDAP_LOG( BACK_META, DETAIL1, "Unlock CR index = %d\n",
1707 #else /* !NEW_LOGGING */
1708 Debug( LDAP_DEBUG_ANY, "Unlock CR index = %d\n",
1710 #endif /* !NEW_LOGGING */
1711 ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
1712 uuid.bv_val = query->q_uuid;
1713 uuid.bv_len = strlen(query->q_uuid);
1714 return_val = remove_query_data(op, &rs, &uuid, &result);
1716 LDAP_LOG( BACK_META, DETAIL1,
1717 "STALE QUERY REMOVED, SIZE=%d\n",
1719 #else /* !NEW_LOGGING */
1720 Debug( LDAP_DEBUG_ANY, "STALE QUERY REMOVED, SIZE=%d\n",
1722 #endif /* !NEW_LOGGING */
1723 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1724 cm->total_entries -= result.rc;
1725 cm->num_cached_queries--;
1727 LDAP_LOG( BACK_META, DETAIL1, "STORED QUERIES = %lu\n",
1728 cm->num_cached_queries, 0, 0 );
1729 #else /* !NEW_LOGGING */
1730 Debug( LDAP_DEBUG_ANY, "STORED QUERIES = %lu\n",
1731 cm->num_cached_queries, 0, 0 );
1732 #endif /* !NEW_LOGGING */
1733 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1734 cm->cache_size = (return_val > cm->cache_size) ?
1735 0: (cm->cache_size-return_val);
1737 LDAP_LOG( BACK_META, DETAIL1,
1738 "STALE QUERY REMOVED, CACHE SIZE=%lu bytes %d "
1739 "entries\n", cm->cache_size,
1740 cm->total_entries, 0 );
1741 #else /* !NEW_LOGGING */
1742 Debug( LDAP_DEBUG_ANY,
1743 "STALE QUERY REMOVED, CACHE SIZE=%lu bytes %d "
1744 "entries\n", cm->cache_size,
1745 cm->total_entries, 0 );
1746 #endif /* !NEW_LOGGING */
1748 query = query->prev;
1749 free_query(query_prev);
1751 ldap_pvt_thread_mutex_unlock(&cm->remove_mutex);
1761 slap_callback *cb = op->o_callback;
1762 /*struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;*/
1763 Backend* be = (Backend*)(cb->sc_private);
1764 struct metainfo *li = ( struct metainfo * )be->be_private;
1767 struct exception result;
1771 if (rs->sr_type == REP_SEARCH) {
1772 dn = rs->sr_entry->e_name;
1773 ndn = rs->sr_entry->e_nname;
1775 rewriteSession( li->rwinfo, "cacheReturn",
1776 rs->sr_entry->e_name.bv_val, op->o_conn,
1778 ber_str2bv(ename, strlen(ename), 0, &rs->sr_entry->e_name);
1779 /* FIXME: should we normalize this? */
1780 ber_dupbv(&rs->sr_entry->e_nname, &rs->sr_entry->e_name);
1782 op->o_callback = NULL;
1784 send_search_entry( op, rs );
1786 rs->sr_entry->e_name = dn;
1787 rs->sr_entry->e_nname = ndn;
1789 op->o_callback = cb;
1791 } else if (rs->sr_type == REP_RESULT) {
1792 op->o_callback = NULL;
1793 send_ldap_result( op, rs );