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