]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb2/search.c
Server timing as a private feature of the bdb2 backend.
[openldap] / servers / slapd / back-bdb2 / search.c
1 /* search.c - bdb2 backend search function */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/string.h>
8 #include <ac/socket.h>
9
10 #include "slap.h"
11 #include "back-bdb2.h"
12 #include "proto-back-bdb2.h"
13
14 static ID_BLOCK *base_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
15 static ID_BLOCK *onelevel_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
16 static ID_BLOCK *subtree_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, Entry *e, int *err, int lookupbase);
17
18 #define GRABSIZE        BUFSIZ
19
20 #define MAKE_SPACE( n ) { \
21         if ( rcur + n > rbuf + rmaxsize ) { \
22                 int     offset = rcur - rbuf; \
23                 rbuf =  ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
24                 rmaxsize += GRABSIZE; \
25                 rcur = rbuf + offset; \
26         } \
27 }
28
29 static int
30 bdb2i_back_search_internal(
31     BackendDB   *be,
32     Connection  *conn,
33     Operation   *op,
34     char        *base,
35     int         scope,
36     int         deref,
37     int         slimit,
38     int         tlimit,
39     Filter      *filter,
40     char        *filterstr,
41     char        **attrs,
42     int         attrsonly
43 )
44 {
45         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
46         int             err;
47         time_t          stoptime;
48         ID_BLOCK                *candidates;
49         ID              id;
50         Entry           *e;
51         Attribute       *ref;
52         char            *matched = NULL;
53         int             rmaxsize, nrefs;
54         char            *rbuf, *rcur, *r;
55         int             nentries = 0;
56         char            *realBase;
57
58         Debug(LDAP_DEBUG_ARGS, "=> bdb2i_back_search\n", 0, 0, 0);
59
60         if ( tlimit == 0 && be_isroot( be, op->o_ndn ) ) {
61                 tlimit = -1;    /* allow root to set no limit */
62         } else {
63                 tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
64                     be->be_timelimit : tlimit;
65                 stoptime = op->o_time + tlimit;
66         }
67         if ( slimit == 0 && be_isroot( be, op->o_ndn ) ) {
68                 slimit = -1;    /* allow root to set no limit */
69         } else {
70                 slimit = (slimit > be->be_sizelimit || slimit < 1) ?
71                     be->be_sizelimit : slimit;
72         }
73
74         /*
75          * check and apply aliasing where the dereferencing applies to
76          * the subordinates of the base
77          */
78
79         switch ( deref ) {
80         case LDAP_DEREF_FINDING:
81         case LDAP_DEREF_ALWAYS:
82                 realBase = bdb2i_derefDN ( be, conn, op, base );
83                 break;
84         default:
85                 realBase = ch_strdup(base);
86         }
87
88         (void) dn_normalize_case( realBase );
89
90         Debug( LDAP_DEBUG_TRACE, "using base \"%s\"\n",
91                 realBase, 0, 0 );
92
93         switch ( scope ) {
94         case LDAP_SCOPE_BASE:
95                 candidates = base_candidates( be, conn, op, realBase, filter,
96                     attrs, attrsonly, &matched, &err );
97                 break;
98
99         case LDAP_SCOPE_ONELEVEL:
100                 candidates = onelevel_candidates( be, conn, op, realBase, filter,
101                     attrs, attrsonly, &matched, &err );
102                 break;
103
104         case LDAP_SCOPE_SUBTREE:
105                 candidates = subtree_candidates( be, conn, op, realBase, filter,
106                     attrs, attrsonly, &matched, NULL, &err, 1 );
107                 break;
108
109         default:
110                 send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, "",
111                     "Bad scope" );
112                 if( realBase != NULL) {
113                         free( realBase );
114                 }
115                 return( -1 );
116         }
117
118         /* null candidates means we could not find the base object */
119         if ( candidates == NULL ) {
120                 send_ldap_result( conn, op, err, matched, "" );
121                 if ( matched != NULL ) {
122                         free( matched );
123                 }
124                 if( realBase != NULL) {
125                         free( realBase );
126                 }
127                 return( -1 );
128         }
129
130         if ( matched != NULL ) {
131                 free( matched );
132         }
133
134         rmaxsize = 0;
135         nrefs = 0;
136         rbuf = rcur = NULL;
137         MAKE_SPACE( sizeof("Referral:") + 1 );
138         strcpy( rbuf, "Referral:" );
139         rcur = strchr( rbuf, '\0' );
140         for ( id = bdb2i_idl_firstid( candidates ); id != NOID;
141             id = bdb2i_idl_nextid( candidates, id ) ) {
142                 /* check for abandon */
143                 ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
144                 if ( op->o_abandon ) {
145                         ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
146                         bdb2i_idl_free( candidates );
147                         free( rbuf );
148                         if( realBase != NULL) {
149                                 free( realBase );
150                         }
151                         return( 0 );
152                 }
153                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
154
155                 /* check time limit */
156                 ldap_pvt_thread_mutex_lock( &currenttime_mutex );
157                 time( &currenttime );
158                 if ( tlimit != -1 && currenttime > stoptime ) {
159                         ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
160                         send_ldap_search_result( conn, op,
161                             LDAP_TIMELIMIT_EXCEEDED, NULL, nrefs > 0 ? rbuf :
162                             NULL, nentries );
163                         bdb2i_idl_free( candidates );
164                         free( rbuf );
165                         if( realBase != NULL) {
166                                 free( realBase );
167                         }
168                         return( 0 );
169                 }
170                 ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
171
172                 /* get the entry with reader lock */
173                 if ( (e = bdb2i_id2entry_r( be, id )) == NULL ) {
174                         Debug( LDAP_DEBUG_ARGS, "candidate %ld not found\n",
175                                id, 0, 0 );
176                         continue;
177                 }
178
179                 /*
180                  * if it's a referral, add it to the list of referrals. only do
181                  * this for subtree searches, and don't check the filter explicitly
182                  * here since it's only a candidate anyway.
183                  */
184                 if ( scope == LDAP_SCOPE_SUBTREE &&
185                         e->e_ndn != NULL &&
186                         strncmp( e->e_ndn, "REF=", 4 ) == 0 &&
187                         (ref = attr_find( e->e_attrs, "ref" )) != NULL )
188                 {
189                         int     i, len;
190
191                         if ( ref->a_vals == NULL ) {
192                                 Debug( LDAP_DEBUG_ANY, "null ref in (%s)\n", 
193                                         e->e_dn, 0, 0 );
194                         } else {
195                                 for ( i = 0; ref->a_vals[i] != NULL; i++ ) {
196                                         /* referral + newline + null */
197                                         MAKE_SPACE( ref->a_vals[i]->bv_len + 2 );
198                                         *rcur++ = '\n';
199                                         strncpy( rcur, ref->a_vals[i]->bv_val,
200                                                 ref->a_vals[i]->bv_len );
201                                         rcur = rcur + ref->a_vals[i]->bv_len;
202                                         *rcur = '\0';
203                                         nrefs++;
204                                 }
205                         }
206
207                 /* otherwise it's an entry - see if it matches the filter */
208                 } else {
209                         /* if it matches the filter and scope, send it */
210                         if ( test_filter( be, conn, op, e, filter ) == 0 ) {
211                                 int             scopeok;
212                                 char    *dn;
213
214                                 /* check scope */
215                                 scopeok = 1;
216                                 if ( scope == LDAP_SCOPE_ONELEVEL ) {
217                                         if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
218                                                 (void) dn_normalize_case( dn );
219                                                 scopeok = (dn == realBase)
220                                                         ? 1
221                                                         : (strcmp( dn, realBase ) ? 0 : 1 );
222                                                 free( dn );
223                                         } else {
224                                                 scopeok = (realBase == NULL || *realBase == '\0');
225                                         }
226                                 } else if ( scope == LDAP_SCOPE_SUBTREE ) {
227                                         dn = ch_strdup( e->e_ndn );
228                                         scopeok = dn_issuffix( dn, realBase );
229                                         free( dn );
230                                 }
231
232                                 if ( scopeok ) {
233                                         /* check size limit */
234                                         if ( --slimit == -1 ) {
235                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
236                                                 send_ldap_search_result( conn, op,
237                                                         LDAP_SIZELIMIT_EXCEEDED, NULL,
238                                                         nrefs > 0 ? rbuf : NULL, nentries );
239                                                 bdb2i_idl_free( candidates );
240                                                 free( rbuf );
241
242                                                 if( realBase != NULL) {
243                                                         free( realBase );
244                                                 }
245                                                 return( 0 );
246                                         }
247
248                                         /*
249                                          * check and apply aliasing where the dereferencing applies to
250                                          * the subordinates of the base
251                                          */
252                                         switch ( deref ) {
253                                         case LDAP_DEREF_SEARCHING:
254                                         case LDAP_DEREF_ALWAYS:
255                                                 {
256                                                         Entry *newe = bdb2i_derefAlias_r( be, conn, op, e );
257                                                         if ( newe == NULL ) { /* problem with the alias */
258                                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
259                                                                 e = NULL;
260                                                         }
261                                                         else if ( newe != e ) { /* reassign e */
262                                                                 bdb2i_cache_return_entry_r( &li->li_cache, e );
263                                                                 e = newe;
264                                                         }
265                                                 }
266                                                 break;
267                                         }
268
269                                         if (e) {
270                                                 switch ( send_search_entry( be, conn, op, e,
271                                                         attrs, attrsonly ) ) {
272                                                 case 0:         /* entry sent ok */
273                                                         nentries++;
274                                                         break;
275                                                 case 1:         /* entry not sent */
276                                                         break;
277                                                 case -1:        /* connection closed */
278                                                         bdb2i_cache_return_entry_r( &li->li_cache, e );
279                                                         bdb2i_idl_free( candidates );
280                                                         free( rbuf );
281
282                                                         if( realBase != NULL) {
283                                                                 free( realBase );
284                                                         }
285                                                         return( 0 );
286                                                 }
287                                         }
288                                 }
289                         }
290                 }
291
292                 if( e != NULL ) {
293                         /* free reader lock */
294                         bdb2i_cache_return_entry_r( &li->li_cache, e );
295                 }
296
297                 ldap_pvt_thread_yield();
298         }
299         bdb2i_idl_free( candidates );
300         if ( nrefs > 0 ) {
301                 send_ldap_search_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
302                     rbuf, nentries );
303         } else {
304                 send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL,
305                     nentries );
306         }
307         free( rbuf );
308
309         if( realBase != NULL) {
310                 free( realBase );
311         }
312
313         return( 0 );
314 }
315
316
317 int
318 bdb2_back_search(
319     BackendDB   *be,
320     Connection  *conn,
321     Operation   *op,
322     char        *base,
323     int         scope,
324     int         deref,
325     int         slimit,
326     int         tlimit,
327     Filter      *filter,
328     char        *filterstr,
329     char        **attrs,
330     int         attrsonly
331 )
332 {
333         DB_LOCK         lock;
334         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
335         struct timeval  time1;
336         int             ret;
337
338         bdb2i_start_timing( be->be_private, &time1 );
339
340         if ( bdb2i_enter_backend_r( get_dbenv( be ), &lock ) != 0 ) {
341
342                 send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
343                 return( -1 );
344
345         }
346
347         ret = bdb2i_back_search_internal( be, conn, op, base, scope, deref,
348                                         slimit, tlimit, filter, filterstr, attrs, attrsonly );
349
350         (void) bdb2i_leave_backend( get_dbenv( be ), lock );
351         bdb2i_stop_timing( be->be_private, time1, "SRCH", conn, op );
352
353         return( ret );
354 }
355
356
357 static ID_BLOCK *
358 base_candidates(
359     BackendDB   *be,
360     Connection  *conn,
361     Operation   *op,
362     char        *base,
363     Filter      *filter,
364     char        **attrs,
365     int         attrsonly,
366     char        **matched,
367     int         *err
368 )
369 {
370         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
371         int             rc;
372         ID              id;
373         ID_BLOCK                *idl;
374         Entry           *e;
375
376         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n", base, 0, 0);
377
378         *err = LDAP_SUCCESS;
379
380         /* get entry with reader lock */
381         if ( (e = bdb2i_dn2entry_r( be, base, matched )) == NULL ) {
382                 *err = LDAP_NO_SUCH_OBJECT;
383                 return( NULL );
384         }
385
386         /* check for deleted */
387
388         idl = bdb2i_idl_alloc( 1 );
389         bdb2i_idl_insert( &idl, e->e_id, 1 );
390
391
392         /* free reader lock */
393         bdb2i_cache_return_entry_r( &li->li_cache, e );
394
395         return( idl );
396 }
397
398 static ID_BLOCK *
399 onelevel_candidates(
400     BackendDB   *be,
401     Connection  *conn,
402     Operation   *op,
403     char        *base,
404     Filter      *filter,
405     char        **attrs,
406     int         attrsonly,
407     char        **matched,
408     int         *err
409 )
410 {
411         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
412         Entry           *e = NULL;
413         Filter          *f;
414         char            buf[20];
415         ID_BLOCK                *candidates;
416
417         Debug(LDAP_DEBUG_TRACE, "onelevel_candidates: base: \"%s\"\n", base, 0, 0);
418
419         *err = LDAP_SUCCESS;
420
421         /* get the base object with reader lock */
422         if ( base != NULL && *base != '\0' &&
423                 (e = bdb2i_dn2entry_r( be, base, matched )) == NULL )
424         {
425                 *err = LDAP_NO_SUCH_OBJECT;
426                 return( NULL );
427         }
428
429         /*
430          * modify the filter to be something like this:
431          *
432          *      parent=baseobject & originalfilter
433          */
434
435         f = (Filter *) ch_malloc( sizeof(Filter) );
436         f->f_next = NULL;
437         f->f_choice = LDAP_FILTER_AND;
438         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
439         f->f_and->f_choice = LDAP_FILTER_EQUALITY;
440         f->f_and->f_ava.ava_type = ch_strdup( "id2children" );
441         sprintf( buf, "%ld", e != NULL ? e->e_id : 0 );
442         f->f_and->f_ava.ava_value.bv_val = ch_strdup( buf );
443         f->f_and->f_ava.ava_value.bv_len = strlen( buf );
444         f->f_and->f_next = filter;
445
446         /* from here, it's just like subtree_candidates */
447         candidates = subtree_candidates( be, conn, op, base, f, attrs,
448             attrsonly, matched, e, err, 0 );
449
450         /* free up just the filter stuff we allocated above */
451         f->f_and->f_next = NULL;
452         filter_free( f );
453
454         /* free entry and reader lock */
455         if( e != NULL ) {
456                 bdb2i_cache_return_entry_r( &li->li_cache, e );
457         }
458         return( candidates );
459 }
460
461 static ID_BLOCK *
462 subtree_candidates(
463     BackendDB   *be,
464     Connection  *conn,
465     Operation   *op,
466     char        *base,
467     Filter      *filter,
468     char        **attrs,
469     int         attrsonly,
470     char        **matched,
471     Entry       *e,
472     int         *err,
473     int         lookupbase
474 )
475 {
476         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
477         Filter          *f, **filterarg_ptr;
478         ID_BLOCK                *candidates;
479
480         Debug(LDAP_DEBUG_TRACE, "subtree_candidates: base: \"%s\" %s\n",
481                 base ? base : "NULL", lookupbase ? "lookupbase" : "", 0);
482
483         /*
484          * get the base object - unless we already have it (from one-level).
485          * also, unless this is a one-level search or a subtree search
486          * starting at the very top of our subtree, we need to modify the
487          * filter to be something like this:
488          *
489          *      dn=*baseobjectdn & (originalfilter | ref=*)
490          *
491          * the "objectclass=referral" part is used to select referrals to return
492          */
493
494         *err = LDAP_SUCCESS;
495         f = NULL;
496         if ( lookupbase ) {
497                 e = NULL;
498
499                 if ( base != NULL && *base != '\0' &&
500                         (e = bdb2i_dn2entry_r( be, base, matched )) == NULL )
501                 {
502                         *err = LDAP_NO_SUCH_OBJECT;
503                         return( NULL );
504                 }
505
506                 if (e) {
507                         bdb2i_cache_return_entry_r( &li->li_cache, e );
508                 }
509
510                 f = (Filter *) ch_malloc( sizeof(Filter) );
511                 f->f_next = NULL;
512                 f->f_choice = LDAP_FILTER_OR;
513                 f->f_or = (Filter *) ch_malloc( sizeof(Filter) );
514                 f->f_or->f_choice = LDAP_FILTER_EQUALITY;
515                 f->f_or->f_avtype = ch_strdup( "objectclass" );
516                 /* Patch to use normalized uppercase */
517                 f->f_or->f_avvalue.bv_val = ch_strdup( "REFERRAL" );
518                 f->f_or->f_avvalue.bv_len = strlen( "REFERRAL" );
519                 filterarg_ptr = &f->f_or->f_next;
520                 *filterarg_ptr = filter;
521                 filter = f;
522
523                 if ( ! be_issuffix( be, base ) ) {
524                         f = (Filter *) ch_malloc( sizeof(Filter) );
525                         f->f_next = NULL;
526                         f->f_choice = LDAP_FILTER_AND;
527                         f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
528                         f->f_and->f_choice = LDAP_FILTER_SUBSTRINGS;
529                         f->f_and->f_sub_type = ch_strdup( "dn" );
530                         f->f_and->f_sub_initial = NULL;
531                         f->f_and->f_sub_any = NULL;
532                         f->f_and->f_sub_final = ch_strdup( base );
533                         value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
534                         f->f_and->f_next = filter;
535                         filter = f;
536                 }
537         }
538
539         candidates = bdb2i_filter_candidates( be, filter );
540
541         /* free up just the parts we allocated above */
542         if ( f != NULL ) {
543                 *filterarg_ptr = NULL;
544                 filter_free( f );
545         }
546
547         return( candidates );
548 }