]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/tools.c
Add missing return from void *bdb_tool_trickle_task()
[openldap] / servers / slapd / back-bdb / tools.c
1 /* tools.c - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2007 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21 #include <ac/errno.h>
22
23 #define AVL_INTERNAL
24 #include "back-bdb.h"
25 #include "idl.h"
26
27 static DBC *cursor = NULL;
28 static DBT key, data;
29 static EntryHeader eh;
30 static int eoff;
31
32 typedef struct dn_id {
33         ID id;
34         struct berval dn;
35 } dn_id;
36
37 #define HOLE_SIZE       4096
38 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
39 static unsigned nhmax = HOLE_SIZE;
40 static unsigned nholes;
41
42 static int index_nattrs;
43
44 #ifdef BDB_TOOL_IDL_CACHING
45 #define bdb_tool_idl_cmp                BDB_SYMBOL(tool_idl_cmp)
46 #define bdb_tool_idl_flush_one          BDB_SYMBOL(tool_idl_flush_one)
47 #define bdb_tool_idl_flush              BDB_SYMBOL(tool_idl_flush)
48
49 static int bdb_tool_idl_flush( BackendDB *be );
50
51 #define IDBLOCK 1024
52
53 typedef struct bdb_tool_idl_cache_entry {
54         struct bdb_tool_idl_cache_entry *next;
55         ID ids[IDBLOCK];
56 } bdb_tool_idl_cache_entry;
57  
58 typedef struct bdb_tool_idl_cache {
59         struct berval kstr;
60         bdb_tool_idl_cache_entry *head, *tail;
61         ID first, last;
62         int count;
63 } bdb_tool_idl_cache;
64
65 static bdb_tool_idl_cache_entry *bdb_tool_idl_free_list;
66 #endif  /* BDB_TOOL_IDL_CACHING */
67
68 static ID bdb_tool_ix_id;
69 static Operation *bdb_tool_ix_op;
70 static int *bdb_tool_index_threads, bdb_tool_index_tcount;
71 static void *bdb_tool_index_rec;
72 static struct bdb_info *bdb_tool_info;
73 static ldap_pvt_thread_mutex_t bdb_tool_index_mutex;
74 static ldap_pvt_thread_cond_t bdb_tool_index_cond_main;
75 static ldap_pvt_thread_cond_t bdb_tool_index_cond_work;
76
77 static ldap_pvt_thread_mutex_t bdb_tool_trickle_mutex;
78 static ldap_pvt_thread_cond_t bdb_tool_trickle_cond;
79
80 static void * bdb_tool_index_task( void *ctx, void *ptr );
81 static void * bdb_tool_trickle_task( void *ctx, void *ptr );
82
83 int bdb_tool_entry_open(
84         BackendDB *be, int mode )
85 {
86         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
87
88         /* initialize key and data thangs */
89         DBTzero( &key );
90         DBTzero( &data );
91         key.flags = DB_DBT_REALLOC;
92         data.flags = DB_DBT_USERMEM;
93
94         if (cursor == NULL) {
95                 int rc = bdb->bi_id2entry->bdi_db->cursor(
96                         bdb->bi_id2entry->bdi_db, NULL, &cursor,
97                         bdb->bi_db_opflags );
98                 if( rc != 0 ) {
99                         return -1;
100                 }
101         }
102
103         /* Set up for threaded slapindex */
104         if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
105                 if ( !bdb_tool_info ) {
106                         ldap_pvt_thread_mutex_init( &bdb_tool_trickle_mutex );
107                         ldap_pvt_thread_cond_init( &bdb_tool_trickle_cond );
108                         ldap_pvt_thread_pool_submit( &connection_pool, bdb_tool_trickle_task, bdb->bi_dbenv );
109
110                         ldap_pvt_thread_mutex_init( &bdb_tool_index_mutex );
111                         ldap_pvt_thread_cond_init( &bdb_tool_index_cond_main );
112                         ldap_pvt_thread_cond_init( &bdb_tool_index_cond_work );
113                         if ( bdb->bi_nattrs ) {
114                                 int i;
115                                 bdb_tool_index_threads = ch_malloc( slap_tool_thread_max * sizeof( int ));
116                                 bdb_tool_index_rec = ch_malloc( bdb->bi_nattrs * sizeof( IndexRec ));
117                                 bdb_tool_index_tcount = slap_tool_thread_max - 1;
118                                 for (i=1; i<slap_tool_thread_max; i++) {
119                                         int *ptr = ch_malloc( sizeof( int ));
120                                         *ptr = i;
121                                         ldap_pvt_thread_pool_submit( &connection_pool,
122                                                 bdb_tool_index_task, ptr );
123                                 }
124                         }
125                         bdb_tool_info = bdb;
126                 }
127         }
128
129         return 0;
130 }
131
132 int bdb_tool_entry_close(
133         BackendDB *be )
134 {
135         if ( bdb_tool_info ) {
136                 slapd_shutdown = 1;
137                 ldap_pvt_thread_mutex_lock( &bdb_tool_trickle_mutex );
138                 ldap_pvt_thread_cond_signal( &bdb_tool_trickle_cond );
139                 ldap_pvt_thread_mutex_unlock( &bdb_tool_trickle_mutex );
140                 ldap_pvt_thread_mutex_lock( &bdb_tool_index_mutex );
141                 bdb_tool_index_tcount = slap_tool_thread_max - 1;
142                 ldap_pvt_thread_cond_broadcast( &bdb_tool_index_cond_work );
143                 ldap_pvt_thread_mutex_unlock( &bdb_tool_index_mutex );
144         }
145
146         if( key.data ) {
147                 ch_free( key.data );
148                 key.data = NULL;
149         }
150         if( eh.bv.bv_val ) {
151                 ch_free( eh.bv.bv_val );
152                 eh.bv.bv_val = NULL;
153         }
154
155         if( cursor ) {
156                 cursor->c_close( cursor );
157                 cursor = NULL;
158         }
159
160 #ifdef BDB_TOOL_IDL_CACHING
161         bdb_tool_idl_flush( be );
162 #endif
163
164         if( nholes ) {
165                 unsigned i;
166                 fprintf( stderr, "Error, entries missing!\n");
167                 for (i=0; i<nholes; i++) {
168                         fprintf(stderr, "  entry %ld: %s\n",
169                                 holes[i].id, holes[i].dn.bv_val);
170                 }
171                 return -1;
172         }
173                         
174         return 0;
175 }
176
177 ID bdb_tool_entry_next(
178         BackendDB *be )
179 {
180         int rc;
181         ID id;
182         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
183         char buf[16], *dptr;
184
185         assert( be != NULL );
186         assert( slapMode & SLAP_TOOL_MODE );
187         assert( bdb != NULL );
188         
189         /* Get the header */
190         data.ulen = data.dlen = sizeof( buf );
191         data.data = buf;
192         data.flags |= DB_DBT_PARTIAL;
193         rc = cursor->c_get( cursor, &key, &data, DB_NEXT );
194
195         if( rc ) {
196                 /* If we're doing linear indexing and there are more attrs to
197                  * index, and we're at the end of the database, start over.
198                  */
199                 if ( index_nattrs && rc == DB_NOTFOUND ) {
200                         /* optional - do a checkpoint here? */
201                         bdb_attr_info_free( bdb->bi_attrs[0] );
202                         bdb->bi_attrs[0] = bdb->bi_attrs[index_nattrs];
203                         index_nattrs--;
204                         rc = cursor->c_get( cursor, &key, &data, DB_FIRST );
205                         if ( rc ) {
206                                 return NOID;
207                         }
208                 } else {
209                         return NOID;
210                 }
211         }
212
213         dptr = eh.bv.bv_val;
214         eh.bv.bv_val = buf;
215         eh.bv.bv_len = data.size;
216         rc = entry_header( &eh );
217         eoff = eh.data - eh.bv.bv_val;
218         eh.bv.bv_val = dptr;
219         if( rc ) {
220                 return NOID;
221         }
222
223         BDB_DISK2ID( key.data, &id );
224         return id;
225 }
226
227 ID bdb_tool_dn2id_get(
228         Backend *be,
229         struct berval *dn
230 )
231 {
232         Operation op = {0};
233         Opheader ohdr = {0};
234         EntryInfo *ei = NULL;
235         int rc;
236
237         if ( BER_BVISEMPTY(dn) )
238                 return 0;
239
240         op.o_hdr = &ohdr;
241         op.o_bd = be;
242         op.o_tmpmemctx = NULL;
243         op.o_tmpmfuncs = &ch_mfuncs;
244
245         rc = bdb_cache_find_ndn( &op, NULL, dn, &ei );
246         if ( ei ) bdb_cache_entryinfo_unlock( ei );
247         if ( rc == DB_NOTFOUND )
248                 return NOID;
249         
250         return ei->bei_id;
251 }
252
253 int bdb_tool_id2entry_get(
254         Backend *be,
255         ID id,
256         Entry **e
257 )
258 {
259         int rc = bdb_id2entry( be, NULL, 0, id, e );
260
261         if ( rc == DB_NOTFOUND && id == 0 ) {
262                 Entry *dummy = ch_calloc( 1, sizeof(Entry) );
263                 struct berval gluebv = BER_BVC("glue");
264                 dummy->e_name.bv_val = ch_strdup( "" );
265                 dummy->e_nname.bv_val = ch_strdup( "" );
266                 attr_merge_one( dummy, slap_schema.si_ad_objectClass, &gluebv, NULL );
267                 attr_merge_one( dummy, slap_schema.si_ad_structuralObjectClass,
268                         &gluebv, NULL );
269                 *e = dummy;
270                 rc = LDAP_SUCCESS;
271         }
272         return rc;
273 }
274
275 Entry* bdb_tool_entry_get( BackendDB *be, ID id )
276 {
277         int rc;
278         Entry *e = NULL;
279
280         assert( be != NULL );
281         assert( slapMode & SLAP_TOOL_MODE );
282
283         /* Get the size */
284         data.flags ^= DB_DBT_PARTIAL;
285         data.ulen = 0;
286     rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
287         if ( rc != DB_BUFFER_SMALL ) goto leave;
288
289         /* Allocate a block and retrieve the data */
290         eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size;
291         eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len );
292         eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
293         data.data = eh.data;
294         data.ulen = data.size;
295
296         /* Skip past already parsed nattr/nvals */
297         eh.data += eoff;
298
299     rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
300         if ( rc ) goto leave;
301
302 #ifdef SLAP_ZONE_ALLOC
303         /* FIXME: will add ctx later */
304         rc = entry_decode( &eh, &e, NULL );
305 #else
306         rc = entry_decode( &eh, &e );
307 #endif
308
309         if( rc == LDAP_SUCCESS ) {
310                 e->e_id = id;
311 #ifdef BDB_HIER
312                 if ( slapMode & SLAP_TOOL_READONLY ) {
313                         EntryInfo *ei = NULL;
314                         Operation op = {0};
315                         Opheader ohdr = {0};
316
317                         op.o_hdr = &ohdr;
318                         op.o_bd = be;
319                         op.o_tmpmemctx = NULL;
320                         op.o_tmpmfuncs = &ch_mfuncs;
321
322                         rc = bdb_cache_find_parent( &op, NULL, cursor->locker, id, &ei );
323                         if ( rc == LDAP_SUCCESS ) {
324                                 bdb_cache_entryinfo_unlock( ei );
325                                 e->e_private = ei;
326                                 ei->bei_e = e;
327                                 bdb_fix_dn( e, 0 );
328                                 ei->bei_e = NULL;
329                                 e->e_private = NULL;
330                         }
331                 }
332 #endif
333         }
334 leave:
335         return e;
336 }
337
338 static int bdb_tool_next_id(
339         Operation *op,
340         DB_TXN *tid,
341         Entry *e,
342         struct berval *text,
343         int hole )
344 {
345         struct berval dn = e->e_name;
346         struct berval ndn = e->e_nname;
347         struct berval pdn, npdn;
348         EntryInfo *ei = NULL, eidummy;
349         int rc;
350
351         if (ndn.bv_len == 0) {
352                 e->e_id = 0;
353                 return 0;
354         }
355
356         rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
357         if ( ei ) bdb_cache_entryinfo_unlock( ei );
358         if ( rc == DB_NOTFOUND ) {
359                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
360                         ID eid = e->e_id;
361                         dnParent( &dn, &pdn );
362                         dnParent( &ndn, &npdn );
363                         e->e_name = pdn;
364                         e->e_nname = npdn;
365                         rc = bdb_tool_next_id( op, tid, e, text, 1 );
366                         e->e_name = dn;
367                         e->e_nname = ndn;
368                         if ( rc ) {
369                                 return rc;
370                         }
371                         /* If parent didn't exist, it was created just now
372                          * and its ID is now in e->e_id. Make sure the current
373                          * entry gets added under the new parent ID.
374                          */
375                         if ( eid != e->e_id ) {
376                                 eidummy.bei_id = e->e_id;
377                                 ei = &eidummy;
378                         }
379                 }
380                 rc = bdb_next_id( op->o_bd, tid, &e->e_id );
381                 if ( rc ) {
382                         snprintf( text->bv_val, text->bv_len,
383                                 "next_id failed: %s (%d)",
384                                 db_strerror(rc), rc );
385                 Debug( LDAP_DEBUG_ANY,
386                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
387                         return rc;
388                 }
389                 rc = bdb_dn2id_add( op, tid, ei, e );
390                 if ( rc ) {
391                         snprintf( text->bv_val, text->bv_len, 
392                                 "dn2id_add failed: %s (%d)",
393                                 db_strerror(rc), rc );
394                 Debug( LDAP_DEBUG_ANY,
395                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
396                 } else if ( hole ) {
397                         if ( nholes == nhmax - 1 ) {
398                                 if ( holes == hbuf ) {
399                                         holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
400                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
401                                 } else {
402                                         holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
403                                 }
404                                 nhmax *= 2;
405                         }
406                         ber_dupbv( &holes[nholes].dn, &ndn );
407                         holes[nholes++].id = e->e_id;
408                 }
409         } else if ( !hole ) {
410                 unsigned i;
411
412                 e->e_id = ei->bei_id;
413
414                 for ( i=0; i<nholes; i++) {
415                         if ( holes[i].id == e->e_id ) {
416                                 int j;
417                                 free(holes[i].dn.bv_val);
418                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
419                                 holes[j].id = 0;
420                                 nholes--;
421                                 break;
422                         } else if ( holes[i].id > e->e_id ) {
423                                 break;
424                         }
425                 }
426         }
427         return rc;
428 }
429
430 static int
431 bdb_tool_index_add(
432         Operation *op,
433         DB_TXN *txn,
434         Entry *e )
435 {
436         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
437
438         if ( !bdb->bi_nattrs )
439                 return 0;
440
441         if ( slapMode & SLAP_TOOL_QUICK ) {
442                 IndexRec *ir;
443                 int i, rc;
444                 Attribute *a;
445                 
446                 ir = bdb_tool_index_rec;
447                 memset(ir, 0, bdb->bi_nattrs * sizeof( IndexRec ));
448
449                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
450                         rc = bdb_index_recset( bdb, a, a->a_desc->ad_type, 
451                                 &a->a_desc->ad_tags, ir );
452                         if ( rc )
453                                 return rc;
454                 }
455                 bdb_tool_ix_id = e->e_id;
456                 bdb_tool_ix_op = op;
457                 ldap_pvt_thread_mutex_lock( &bdb_tool_index_mutex );
458                 /* Wait for all threads to be ready */
459                 while ( bdb_tool_index_tcount ) {
460                         ldap_pvt_thread_cond_wait( &bdb_tool_index_cond_main, 
461                                 &bdb_tool_index_mutex );
462                 }
463                 for ( i=1; i<slap_tool_thread_max; i++ )
464                         bdb_tool_index_threads[i] = LDAP_BUSY;
465                 bdb_tool_index_tcount = slap_tool_thread_max - 1;
466                 ldap_pvt_thread_cond_broadcast( &bdb_tool_index_cond_work );
467                 ldap_pvt_thread_mutex_unlock( &bdb_tool_index_mutex );
468                 rc = bdb_index_recrun( op, bdb, ir, e->e_id, 0 );
469                 if ( rc )
470                         return rc;
471                 ldap_pvt_thread_mutex_lock( &bdb_tool_index_mutex );
472                 for ( i=1; i<slap_tool_thread_max; i++ ) {
473                         if ( bdb_tool_index_threads[i] == LDAP_BUSY ) {
474                                 ldap_pvt_thread_cond_wait( &bdb_tool_index_cond_main, 
475                                         &bdb_tool_index_mutex );
476                                 i--;
477                                 continue;
478                         }
479                         if ( bdb_tool_index_threads[i] ) {
480                                 rc = bdb_tool_index_threads[i];
481                                 break;
482                         }
483                 }
484                 ldap_pvt_thread_mutex_unlock( &bdb_tool_index_mutex );
485                 return rc;
486         } else {
487                 return bdb_index_entry_add( op, txn, e );
488         }
489 }
490
491 ID bdb_tool_entry_put(
492         BackendDB *be,
493         Entry *e,
494         struct berval *text )
495 {
496         int rc;
497         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
498         DB_TXN *tid = NULL;
499         Operation op = {0};
500         Opheader ohdr = {0};
501
502         assert( be != NULL );
503         assert( slapMode & SLAP_TOOL_MODE );
504
505         assert( text != NULL );
506         assert( text->bv_val != NULL );
507         assert( text->bv_val[0] == '\0' );      /* overconservative? */
508
509         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_tool_entry_put)
510                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
511
512         if (! (slapMode & SLAP_TOOL_QUICK)) {
513         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
514                 bdb->bi_db_opflags );
515         if( rc != 0 ) {
516                 snprintf( text->bv_val, text->bv_len,
517                         "txn_begin failed: %s (%d)",
518                         db_strerror(rc), rc );
519                 Debug( LDAP_DEBUG_ANY,
520                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
521                          text->bv_val, 0, 0 );
522                 return NOID;
523         }
524         }
525
526         op.o_hdr = &ohdr;
527         op.o_bd = be;
528         op.o_tmpmemctx = NULL;
529         op.o_tmpmfuncs = &ch_mfuncs;
530
531         /* add dn2id indices */
532         rc = bdb_tool_next_id( &op, tid, e, text, 0 );
533         if( rc != 0 ) {
534                 goto done;
535         }
536
537         if (( slapMode & SLAP_TOOL_QUICK ) && (( e->e_id & 0xfff ) == 0xfff )) {
538                 ldap_pvt_thread_mutex_lock( &bdb_tool_trickle_mutex );
539                 ldap_pvt_thread_cond_signal( &bdb_tool_trickle_cond );
540                 ldap_pvt_thread_mutex_unlock( &bdb_tool_trickle_mutex );
541         }
542
543         if ( !bdb->bi_linear_index )
544                 rc = bdb_tool_index_add( &op, tid, e );
545         if( rc != 0 ) {
546                 snprintf( text->bv_val, text->bv_len,
547                                 "index_entry_add failed: %s (%d)",
548                                 db_strerror(rc), rc );
549                 Debug( LDAP_DEBUG_ANY,
550                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
551                         text->bv_val, 0, 0 );
552                 goto done;
553         }
554
555         /* id2entry index */
556         rc = bdb_id2entry_add( be, tid, e );
557         if( rc != 0 ) {
558                 snprintf( text->bv_val, text->bv_len,
559                                 "id2entry_add failed: %s (%d)",
560                                 db_strerror(rc), rc );
561                 Debug( LDAP_DEBUG_ANY,
562                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
563                         text->bv_val, 0, 0 );
564                 goto done;
565         }
566
567 done:
568         if( rc == 0 ) {
569                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
570                 rc = TXN_COMMIT( tid, 0 );
571                 if( rc != 0 ) {
572                         snprintf( text->bv_val, text->bv_len,
573                                         "txn_commit failed: %s (%d)",
574                                         db_strerror(rc), rc );
575                         Debug( LDAP_DEBUG_ANY,
576                                 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
577                                 text->bv_val, 0, 0 );
578                         e->e_id = NOID;
579                 }
580                 }
581
582         } else {
583                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
584                 TXN_ABORT( tid );
585                 snprintf( text->bv_val, text->bv_len,
586                         "txn_aborted! %s (%d)",
587                         db_strerror(rc), rc );
588                 Debug( LDAP_DEBUG_ANY,
589                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
590                         text->bv_val, 0, 0 );
591                 }
592                 e->e_id = NOID;
593         }
594
595         return e->e_id;
596 }
597
598 int bdb_tool_entry_reindex(
599         BackendDB *be,
600         ID id,
601         AttributeDescription **adv )
602 {
603         struct bdb_info *bi = (struct bdb_info *) be->be_private;
604         int rc;
605         Entry *e;
606         DB_TXN *tid = NULL;
607         Operation op = {0};
608         Opheader ohdr = {0};
609
610         Debug( LDAP_DEBUG_ARGS,
611                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld )\n",
612                 (long) id, 0, 0 );
613
614         /* No indexes configured, nothing to do. Could return an
615          * error here to shortcut things.
616          */
617         if (!bi->bi_attrs) {
618                 return 0;
619         }
620
621         /* Check for explicit list of attrs to index */
622         if ( adv ) {
623                 int i, j, n;
624
625                 if ( bi->bi_attrs[0]->ai_desc != adv[0] ) {
626                         /* count */
627                         for ( n = 0; adv[n]; n++ ) ;
628
629                         /* insertion sort */
630                         for ( i = 0; i < n; i++ ) {
631                                 AttributeDescription *ad = adv[i];
632                                 for ( j = i-1; j>=0; j--) {
633                                         if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
634                                         adv[j+1] = adv[j];
635                                 }
636                                 adv[j+1] = ad;
637                         }
638                 }
639
640                 for ( i = 0; adv[i]; i++ ) {
641                         if ( bi->bi_attrs[i]->ai_desc != adv[i] ) {
642                                 for ( j = i+1; j < bi->bi_nattrs; j++ ) {
643                                         if ( bi->bi_attrs[j]->ai_desc == adv[i] ) {
644                                                 AttrInfo *ai = bi->bi_attrs[i];
645                                                 bi->bi_attrs[i] = bi->bi_attrs[j];
646                                                 bi->bi_attrs[j] = ai;
647                                                 break;
648                                         }
649                                 }
650                                 if ( j == bi->bi_nattrs ) {
651                                         Debug( LDAP_DEBUG_ANY,
652                                                 LDAP_XSTRING(bdb_tool_entry_reindex)
653                                                 ": no index configured for %s\n",
654                                                 adv[i]->ad_cname.bv_val, 0, 0 );
655                                         return -1;
656                                 }
657                         }
658                 }
659                 bi->bi_nattrs = i;
660         }
661
662         /* Get the first attribute to index */
663         if (bi->bi_linear_index && !index_nattrs) {
664                 index_nattrs = bi->bi_nattrs - 1;
665                 bi->bi_nattrs = 1;
666         }
667
668         e = bdb_tool_entry_get( be, id );
669
670         if( e == NULL ) {
671                 Debug( LDAP_DEBUG_ANY,
672                         LDAP_XSTRING(bdb_tool_entry_reindex)
673                         ": could not locate id=%ld\n",
674                         (long) id, 0, 0 );
675                 return -1;
676         }
677
678         if (! (slapMode & SLAP_TOOL_QUICK)) {
679         rc = TXN_BEGIN( bi->bi_dbenv, NULL, &tid, bi->bi_db_opflags );
680         if( rc != 0 ) {
681                 Debug( LDAP_DEBUG_ANY,
682                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex) ": "
683                         "txn_begin failed: %s (%d)\n",
684                         db_strerror(rc), rc, 0 );
685                 goto done;
686         }
687         }
688         
689         /*
690          * just (re)add them for now
691          * assume that some other routine (not yet implemented)
692          * will zap index databases
693          *
694          */
695
696         Debug( LDAP_DEBUG_TRACE,
697                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
698                 (long) id, e->e_dn, 0 );
699
700         op.o_hdr = &ohdr;
701         op.o_bd = be;
702         op.o_tmpmemctx = NULL;
703         op.o_tmpmfuncs = &ch_mfuncs;
704
705         rc = bdb_tool_index_add( &op, tid, e );
706
707 done:
708         if( rc == 0 ) {
709                 if (! (slapMode & SLAP_TOOL_QUICK)) {
710                 rc = TXN_COMMIT( tid, 0 );
711                 if( rc != 0 ) {
712                         Debug( LDAP_DEBUG_ANY,
713                                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
714                                 ": txn_commit failed: %s (%d)\n",
715                                 db_strerror(rc), rc, 0 );
716                         e->e_id = NOID;
717                 }
718                 }
719
720         } else {
721                 if (! (slapMode & SLAP_TOOL_QUICK)) {
722                 TXN_ABORT( tid );
723                 Debug( LDAP_DEBUG_ANY,
724                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
725                         ": txn_aborted! %s (%d)\n",
726                         db_strerror(rc), rc, 0 );
727                 }
728                 e->e_id = NOID;
729         }
730         bdb_entry_release( &op, e, 0 );
731
732         return rc;
733 }
734
735 ID bdb_tool_entry_modify(
736         BackendDB *be,
737         Entry *e,
738         struct berval *text )
739 {
740         int rc;
741         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
742         DB_TXN *tid = NULL;
743         Operation op = {0};
744         Opheader ohdr = {0};
745
746         assert( be != NULL );
747         assert( slapMode & SLAP_TOOL_MODE );
748
749         assert( text != NULL );
750         assert( text->bv_val != NULL );
751         assert( text->bv_val[0] == '\0' );      /* overconservative? */
752
753         assert ( e->e_id != NOID );
754
755         Debug( LDAP_DEBUG_TRACE,
756                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) "( %ld, \"%s\" )\n",
757                 (long) e->e_id, e->e_dn, 0 );
758
759         if (! (slapMode & SLAP_TOOL_QUICK)) {
760         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
761                 bdb->bi_db_opflags );
762         if( rc != 0 ) {
763                 snprintf( text->bv_val, text->bv_len,
764                         "txn_begin failed: %s (%d)",
765                         db_strerror(rc), rc );
766                 Debug( LDAP_DEBUG_ANY,
767                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
768                          text->bv_val, 0, 0 );
769                 return NOID;
770         }
771         }
772
773         op.o_hdr = &ohdr;
774         op.o_bd = be;
775         op.o_tmpmemctx = NULL;
776         op.o_tmpmfuncs = &ch_mfuncs;
777
778         /* id2entry index */
779         rc = bdb_id2entry_update( be, tid, e );
780         if( rc != 0 ) {
781                 snprintf( text->bv_val, text->bv_len,
782                                 "id2entry_add failed: %s (%d)",
783                                 db_strerror(rc), rc );
784                 Debug( LDAP_DEBUG_ANY,
785                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
786                         text->bv_val, 0, 0 );
787                 goto done;
788         }
789
790 done:
791         if( rc == 0 ) {
792                 if (! (slapMode & SLAP_TOOL_QUICK)) {
793                 rc = TXN_COMMIT( tid, 0 );
794                 if( rc != 0 ) {
795                         snprintf( text->bv_val, text->bv_len,
796                                         "txn_commit failed: %s (%d)",
797                                         db_strerror(rc), rc );
798                         Debug( LDAP_DEBUG_ANY,
799                                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": "
800                                 "%s\n", text->bv_val, 0, 0 );
801                         e->e_id = NOID;
802                 }
803                 }
804
805         } else {
806                 if (! (slapMode & SLAP_TOOL_QUICK)) {
807                 TXN_ABORT( tid );
808                 snprintf( text->bv_val, text->bv_len,
809                         "txn_aborted! %s (%d)",
810                         db_strerror(rc), rc );
811                 Debug( LDAP_DEBUG_ANY,
812                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
813                         text->bv_val, 0, 0 );
814                 }
815                 e->e_id = NOID;
816         }
817
818         return e->e_id;
819 }
820
821 #ifdef BDB_TOOL_IDL_CACHING
822 static int
823 bdb_tool_idl_cmp( const void *v1, const void *v2 )
824 {
825         const bdb_tool_idl_cache *c1 = v1, *c2 = v2;
826         int rc;
827
828         if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
829         return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
830 }
831
832 static int
833 bdb_tool_idl_flush_one( void *v1, void *arg )
834 {
835         bdb_tool_idl_cache *ic = v1;
836         DB *db = arg;
837         struct bdb_info *bdb = bdb_tool_info;
838         bdb_tool_idl_cache_entry *ice;
839         DBC *curs;
840         DBT key, data;
841         int i, rc;
842         ID id, nid;
843
844         /* Freshly allocated, ignore it */
845         if ( !ic->head && ic->count <= BDB_IDL_DB_SIZE ) {
846                 return 0;
847         }
848
849         rc = db->cursor( db, NULL, &curs, 0 );
850         if ( rc )
851                 return -1;
852
853         DBTzero( &key );
854         DBTzero( &data );
855
856         bv2DBT( &ic->kstr, &key );
857
858         data.size = data.ulen = sizeof( ID );
859         data.flags = DB_DBT_USERMEM;
860         data.data = &nid;
861
862         rc = curs->c_get( curs, &key, &data, DB_SET );
863         /* If key already exists and we're writing a range... */
864         if ( rc == 0 && ic->count > BDB_IDL_DB_SIZE ) {
865                 /* If it's not currently a range, must delete old info */
866                 if ( nid ) {
867                         /* Skip lo */
868                         while ( curs->c_get( curs, &key, &data, DB_NEXT_DUP ) == 0 )
869                                 curs->c_del( curs, 0 );
870
871                         nid = 0;
872                         /* Store range marker */
873                         curs->c_put( curs, &key, &data, DB_KEYFIRST );
874                 } else {
875                         
876                         /* Skip lo */
877                         rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
878
879                         /* Get hi */
880                         rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
881
882                         /* Delete hi */
883                         curs->c_del( curs, 0 );
884                 }
885                 BDB_ID2DISK( ic->last, &nid );
886                 curs->c_put( curs, &key, &data, DB_KEYLAST );
887                 rc = 0;
888         } else if ( rc && rc != DB_NOTFOUND ) {
889                 rc = -1;
890         } else if ( ic->count > BDB_IDL_DB_SIZE ) {
891                 /* range, didn't exist before */
892                 nid = 0;
893                 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
894                 if ( rc == 0 ) {
895                         BDB_ID2DISK( ic->first, &nid );
896                         rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
897                         if ( rc == 0 ) {
898                                 BDB_ID2DISK( ic->last, &nid );
899                                 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
900                         }
901                 }
902                 if ( rc ) {
903                         rc = -1;
904                 }
905         } else {
906                 int n;
907
908                 /* Just a normal write */
909                 rc = 0;
910                 for ( ice = ic->head, n=0; ice; ice = ice->next, n++ ) {
911                         int end;
912                         if ( ice->next ) {
913                                 end = IDBLOCK;
914                         } else {
915                                 end = ic->count & (IDBLOCK-1);
916                                 if ( !end )
917                                         end = IDBLOCK;
918                         }
919                         for ( i=0; i<end; i++ ) {
920                                 if ( !ice->ids[i] ) continue;
921                                 BDB_ID2DISK( ice->ids[i], &nid );
922                                 rc = curs->c_put( curs, &key, &data, DB_NODUPDATA );
923                                 if ( rc ) {
924                                         if ( rc == DB_KEYEXIST ) {
925                                                 rc = 0;
926                                                 continue;
927                                         }
928                                         rc = -1;
929                                         break;
930                                 }
931                         }
932                         if ( rc ) {
933                                 rc = -1;
934                                 break;
935                         }
936                 }
937                 if ( ic->head ) {
938                         ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
939                         ic->tail->next = bdb_tool_idl_free_list;
940                         bdb_tool_idl_free_list = ic->head;
941                         bdb->bi_idl_cache_size -= n;
942                         ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
943                 }
944         }
945         if ( ic != db->app_private ) {
946                 ch_free( ic );
947         } else {
948                 ic->head = ic->tail = NULL;
949         }
950         curs->c_close( curs );
951         return rc;
952 }
953
954 static int
955 bdb_tool_idl_flush_db( DB *db, bdb_tool_idl_cache *ic )
956 {
957         Avlnode *root = db->app_private;
958         int rc;
959
960         db->app_private = ic;
961         rc = avl_apply( root, bdb_tool_idl_flush_one, db, -1, AVL_INORDER );
962         avl_free( root, NULL );
963         db->app_private = NULL;
964         if ( rc != -1 )
965                 rc = 0;
966         return rc;
967 }
968
969 static int
970 bdb_tool_idl_flush( BackendDB *be )
971 {
972         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
973         DB *db;
974         Avlnode *root;
975         int i, rc = 0;
976
977         for ( i=BDB_NDB; i < bdb->bi_ndatabases; i++ ) {
978                 db = bdb->bi_databases[i]->bdi_db;
979                 if ( !db->app_private ) continue;
980                 rc = bdb_tool_idl_flush_db( db, NULL );
981                 if ( rc )
982                         break;
983         }
984         if ( !rc ) {
985                 bdb->bi_idl_cache_size = 0;
986         }
987         return rc;
988 }
989
990 int bdb_tool_idl_add(
991         BackendDB *be,
992         DB *db,
993         DB_TXN *txn,
994         DBT *key,
995         ID id )
996 {
997         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
998         bdb_tool_idl_cache *ic, itmp;
999         bdb_tool_idl_cache_entry *ice;
1000         int rc;
1001
1002         if ( !bdb->bi_idl_cache_max_size )
1003                 return bdb_idl_insert_key( be, db, txn, key, id );
1004
1005         DBT2bv( key, &itmp.kstr );
1006
1007         ic = avl_find( (Avlnode *)db->app_private, &itmp, bdb_tool_idl_cmp );
1008
1009         /* No entry yet, create one */
1010         if ( !ic ) {
1011                 DBC *curs;
1012                 DBT data;
1013                 ID nid;
1014                 int rc;
1015
1016                 ic = ch_malloc( sizeof( bdb_tool_idl_cache ) + itmp.kstr.bv_len );
1017                 ic->kstr.bv_len = itmp.kstr.bv_len;
1018                 ic->kstr.bv_val = (char *)(ic+1);
1019                 AC_MEMCPY( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
1020                 ic->head = ic->tail = NULL;
1021                 ic->last = 0;
1022                 ic->count = 0;
1023                 avl_insert( (Avlnode **)&db->app_private, ic, bdb_tool_idl_cmp,
1024                         avl_dup_error );
1025
1026                 /* load existing key count here */
1027                 rc = db->cursor( db, NULL, &curs, 0 );
1028                 if ( rc ) return rc;
1029
1030                 data.ulen = sizeof( ID );
1031                 data.flags = DB_DBT_USERMEM;
1032                 data.data = &nid;
1033                 rc = curs->c_get( curs, key, &data, DB_SET );
1034                 if ( rc == 0 ) {
1035                         if ( nid == 0 ) {
1036                                 ic->count = BDB_IDL_DB_SIZE+1;
1037                         } else {
1038                                 db_recno_t count;
1039
1040                                 curs->c_count( curs, &count, 0 );
1041                                 ic->count = count;
1042                                 BDB_DISK2ID( &nid, &ic->first );
1043                         }
1044                 }
1045                 curs->c_close( curs );
1046         }
1047         /* are we a range already? */
1048         if ( ic->count > BDB_IDL_DB_SIZE ) {
1049                 ic->last = id;
1050                 return 0;
1051         /* Are we at the limit, and converting to a range? */
1052         } else if ( ic->count == BDB_IDL_DB_SIZE ) {
1053                 int n;
1054                 for ( ice = ic->head, n=0; ice; ice = ice->next, n++ )
1055                         /* counting */ ;
1056                 if ( n ) {
1057                         ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
1058                         ic->tail->next = bdb_tool_idl_free_list;
1059                         bdb_tool_idl_free_list = ic->head;
1060                         bdb->bi_idl_cache_size -= n;
1061                         ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
1062                 }
1063                 ic->head = ic->tail = NULL;
1064                 ic->last = id;
1065                 ic->count++;
1066                 return 0;
1067         }
1068         /* No free block, create that too */
1069         if ( !ic->tail || ( ic->count & (IDBLOCK-1)) == 0) {
1070                 ice = NULL;
1071                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
1072                 if ( bdb->bi_idl_cache_size >= bdb->bi_idl_cache_max_size ) {
1073                         ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
1074                         rc = bdb_tool_idl_flush_db( db, ic );
1075                         if ( rc )
1076                                 return rc;
1077                         avl_insert( (Avlnode **)&db->app_private, ic, bdb_tool_idl_cmp,
1078                                 avl_dup_error );
1079                         ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
1080                 }
1081                 bdb->bi_idl_cache_size++;
1082                 if ( bdb_tool_idl_free_list ) {
1083                         ice = bdb_tool_idl_free_list;
1084                         bdb_tool_idl_free_list = ice->next;
1085                 }
1086                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
1087                 if ( !ice ) {
1088                         ice = ch_malloc( sizeof( bdb_tool_idl_cache_entry ));
1089                 }
1090                 memset( ice, 0, sizeof( *ice ));
1091                 if ( !ic->head ) {
1092                         ic->head = ice;
1093                 } else {
1094                         ic->tail->next = ice;
1095                 }
1096                 ic->tail = ice;
1097                 if ( !ic->count )
1098                         ic->first = id;
1099         }
1100         ice = ic->tail;
1101         ice->ids[ ic->count & (IDBLOCK-1) ] = id;
1102         ic->count++;
1103
1104         return 0;
1105 }
1106 #endif
1107
1108 static void *
1109 bdb_tool_trickle_task( void *ctx, void *ptr )
1110 {
1111         DB_ENV *env = ptr;
1112         int wrote;
1113
1114         ldap_pvt_thread_mutex_lock( &bdb_tool_trickle_mutex );
1115         while ( 1 ) {
1116                 ldap_pvt_thread_cond_wait( &bdb_tool_trickle_cond,
1117                         &bdb_tool_trickle_mutex );
1118                 if ( slapd_shutdown )
1119                         break;
1120                 env->memp_trickle( env, 30, &wrote );
1121         }
1122         ldap_pvt_thread_mutex_unlock( &bdb_tool_trickle_mutex );
1123
1124         return NULL;
1125 }
1126
1127 static void *
1128 bdb_tool_index_task( void *ctx, void *ptr )
1129 {
1130         int base = *(int *)ptr;
1131
1132         free( ptr );
1133         while ( 1 ) {
1134                 ldap_pvt_thread_mutex_lock( &bdb_tool_index_mutex );
1135                 bdb_tool_index_tcount--;
1136                 if ( !bdb_tool_index_tcount )
1137                         ldap_pvt_thread_cond_signal( &bdb_tool_index_cond_main );
1138                 ldap_pvt_thread_cond_wait( &bdb_tool_index_cond_work,
1139                         &bdb_tool_index_mutex );
1140                 ldap_pvt_thread_mutex_unlock( &bdb_tool_index_mutex );
1141                 if ( slapd_shutdown )
1142                         break;
1143
1144                 bdb_tool_index_threads[base] = bdb_index_recrun( bdb_tool_ix_op,
1145                         bdb_tool_info, bdb_tool_index_rec, bdb_tool_ix_id, base );
1146         }
1147
1148         return NULL;
1149 }