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