]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/tools.c
Merge branch 'mdb.master' of ssh://git-master.openldap.org/~git/git/openldap
[openldap] / servers / slapd / back-mdb / 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 2011 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-mdb.h"
25 #include "idl.h"
26
27 static MDB_txn *txn = NULL, *txi = NULL;
28 static MDB_cursor *cursor = NULL, *idcursor = NULL;
29 static MDB_val key, data;
30 static ID previd = NOID;
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 struct berval    *tool_base;
43 static int              tool_scope;
44 static Filter           *tool_filter;
45 static Entry            *tool_next_entry;
46
47 #if 0
48 static ID mdb_tool_ix_id;
49 static Operation *mdb_tool_ix_op;
50 static int *mdb_tool_index_threads, mdb_tool_index_tcount;
51 static void *mdb_tool_index_rec;
52 static struct mdb_info *mdb_tool_info;
53 static ldap_pvt_thread_mutex_t mdb_tool_index_mutex;
54 static ldap_pvt_thread_cond_t mdb_tool_index_cond_main;
55 static ldap_pvt_thread_cond_t mdb_tool_index_cond_work;
56 static void * mdb_tool_index_task( void *ctx, void *ptr );
57 #endif
58
59 static int      mdb_writes, mdb_writes_per_commit;
60
61 static int
62 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep );
63
64 int mdb_tool_entry_open(
65         BackendDB *be, int mode )
66 {
67         /* In Quick mode, commit once per 1000 entries */
68         mdb_writes = 0;
69         if ( slapMode & SLAP_TOOL_QUICK )
70                 mdb_writes_per_commit = 1000;
71         else
72                 mdb_writes_per_commit = 1;
73
74 #if 0
75         /* Set up for threaded slapindex */
76         if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
77                 if ( !mdb_tool_info ) {
78                         ldap_pvt_thread_mutex_init( &mdb_tool_index_mutex );
79                         ldap_pvt_thread_cond_init( &mdb_tool_index_cond_main );
80                         ldap_pvt_thread_cond_init( &mdb_tool_index_cond_work );
81                         if ( mdb->bi_nattrs ) {
82                                 int i;
83                                 mdb_tool_index_threads = ch_malloc( slap_tool_thread_max * sizeof( int ));
84                                 mdb_tool_index_rec = ch_malloc( mdb->bi_nattrs * sizeof( IndexRec ));
85                                 mdb_tool_index_tcount = slap_tool_thread_max - 1;
86                                 for (i=1; i<slap_tool_thread_max; i++) {
87                                         int *ptr = ch_malloc( sizeof( int ));
88                                         *ptr = i;
89                                         ldap_pvt_thread_pool_submit( &connection_pool,
90                                                 mdb_tool_index_task, ptr );
91                                 }
92                         }
93                         mdb_tool_info = mdb;
94                 }
95         }
96 #endif
97
98         return 0;
99 }
100
101 int mdb_tool_entry_close(
102         BackendDB *be )
103 {
104 #if 0
105         if ( mdb_tool_info ) {
106                 slapd_shutdown = 1;
107                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
108
109                 /* There might still be some threads starting */
110                 while ( mdb_tool_index_tcount ) {
111                         ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
112                                         &mdb_tool_index_mutex );
113                 }
114
115                 mdb_tool_index_tcount = slap_tool_thread_max - 1;
116                 ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
117
118                 /* Make sure all threads are stopped */
119                 while ( mdb_tool_index_tcount ) {
120                         ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
121                                 &mdb_tool_index_mutex );
122                 }
123                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
124
125                 mdb_tool_info = NULL;
126                 slapd_shutdown = 0;
127                 ch_free( mdb_tool_index_threads );
128                 ch_free( mdb_tool_index_rec );
129                 mdb_tool_index_tcount = slap_tool_thread_max - 1;
130         }
131 #endif
132
133         if( idcursor ) {
134                 mdb_cursor_close( idcursor );
135                 idcursor = NULL;
136         }
137         if( cursor ) {
138                 mdb_cursor_close( cursor );
139                 cursor = NULL;
140         }
141         if( txn ) {
142                 if ( mdb_txn_commit( txn ))
143                         return -1;
144                 txn = NULL;
145         }
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                 nholes = 0;
155                 return -1;
156         }
157
158         return 0;
159 }
160
161 ID
162 mdb_tool_entry_first_x(
163         BackendDB *be,
164         struct berval *base,
165         int scope,
166         Filter *f )
167 {
168         tool_base = base;
169         tool_scope = scope;
170         tool_filter = f;
171
172         return mdb_tool_entry_next( be );
173 }
174
175 ID mdb_tool_entry_next(
176         BackendDB *be )
177 {
178         int rc;
179         ID id;
180         struct mdb_info *mdb;
181
182         assert( be != NULL );
183         assert( slapMode & SLAP_TOOL_MODE );
184
185         mdb = (struct mdb_info *) be->be_private;
186         assert( mdb != NULL );
187
188         if ( !txn ) {
189                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &txn );
190                 if ( rc )
191                         return NOID;
192                 rc = mdb_cursor_open( txn, mdb->mi_id2entry, &cursor );
193                 if ( rc ) {
194                         mdb_txn_abort( txn );
195                         return NOID;
196                 }
197         }
198
199 next:;
200         rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT );
201
202         if( rc ) {
203                 return NOID;
204         }
205
206         previd = *(ID *)key.mv_data;
207         id = previd;
208
209         if ( tool_filter || tool_base ) {
210                 static Operation op = {0};
211                 static Opheader ohdr = {0};
212
213                 op.o_hdr = &ohdr;
214                 op.o_bd = be;
215                 op.o_tmpmemctx = NULL;
216                 op.o_tmpmfuncs = &ch_mfuncs;
217
218                 if ( tool_next_entry ) {
219                         mdb_entry_release( &op, tool_next_entry, 0 );
220                         tool_next_entry = NULL;
221                 }
222
223                 rc = mdb_tool_entry_get_int( be, id, &tool_next_entry );
224                 if ( rc == LDAP_NO_SUCH_OBJECT ) {
225                         goto next;
226                 }
227
228                 assert( tool_next_entry != NULL );
229
230                 if ( tool_filter && test_filter( NULL, tool_next_entry, tool_filter ) != LDAP_COMPARE_TRUE )
231                 {
232                         mdb_entry_release( &op, tool_next_entry, 0 );
233                         tool_next_entry = NULL;
234                         goto next;
235                 }
236         }
237
238         return id;
239 }
240
241 ID mdb_tool_dn2id_get(
242         Backend *be,
243         struct berval *dn
244 )
245 {
246         struct mdb_info *mdb;
247         Operation op = {0};
248         Opheader ohdr = {0};
249         ID id;
250         int rc;
251
252         if ( BER_BVISEMPTY(dn) )
253                 return 0;
254
255         mdb = (struct mdb_info *) be->be_private;
256
257         if ( !txn ) {
258                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, (slapMode & SLAP_TOOL_READONLY) != 0 ?
259                         MDB_RDONLY : 0, &txn );
260                 if ( rc )
261                         return NOID;
262         }
263
264         op.o_hdr = &ohdr;
265         op.o_bd = be;
266         op.o_tmpmemctx = NULL;
267         op.o_tmpmfuncs = &ch_mfuncs;
268
269         rc = mdb_dn2id( &op, txn, dn, &id, NULL, NULL );
270         if ( rc == MDB_NOTFOUND )
271                 return NOID;
272
273         return id;
274 }
275
276 static int
277 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
278 {
279         Operation op = {0};
280         Opheader ohdr = {0};
281
282         Entry *e = NULL;
283         struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
284         int rc;
285
286         assert( be != NULL );
287         assert( slapMode & SLAP_TOOL_MODE );
288
289         if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
290                 *ep = tool_next_entry;
291                 tool_next_entry = NULL;
292                 return LDAP_SUCCESS;
293         }
294
295         if ( id != previd ) {
296                 key.mv_size = sizeof(ID);
297                 key.mv_data = &id;
298                 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
299                 if ( rc ) {
300                         rc = LDAP_OTHER;
301                         goto done;
302                 }
303         }
304
305         op.o_hdr = &ohdr;
306         op.o_bd = be;
307         op.o_tmpmemctx = NULL;
308         op.o_tmpmfuncs = &ch_mfuncs;
309         if ( slapMode & SLAP_TOOL_READONLY ) {
310                 rc = mdb_id2name( &op, txn, &idcursor, id, &dn, &ndn );
311                 if ( rc  ) {
312                         rc = LDAP_OTHER;
313                         mdb_entry_return( &op, e );
314                         e = NULL;
315                         goto done;
316                 }
317                 if ( tool_base != NULL ) {
318                         if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
319                                 ch_free( dn.bv_val );
320                                 ch_free( ndn.bv_val );
321                                 rc = LDAP_NO_SUCH_OBJECT;
322                         }
323                 }
324         }
325         rc = mdb_entry_decode( &op, &data, &e );
326         e->e_id = id;
327         if ( !BER_BVISNULL( &dn )) {
328                 e->e_name = dn;
329                 e->e_nname = ndn;
330         } else {
331                 e->e_name.bv_val = NULL;
332                 e->e_nname.bv_val = NULL;
333         }
334
335 done:
336         if ( e != NULL ) {
337                 *ep = e;
338         }
339
340         return rc;
341 }
342
343 Entry*
344 mdb_tool_entry_get( BackendDB *be, ID id )
345 {
346         Entry *e = NULL;
347
348         (void)mdb_tool_entry_get_int( be, id, &e );
349         return e;
350 }
351
352 static int mdb_tool_next_id(
353         Operation *op,
354         MDB_txn *tid,
355         Entry *e,
356         struct berval *text,
357         int hole )
358 {
359         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
360         struct berval dn = e->e_name;
361         struct berval ndn = e->e_nname;
362         struct berval pdn, npdn, nmatched;
363         ID id, pid = 0;
364         int rc;
365
366         if (ndn.bv_len == 0) {
367                 e->e_id = 0;
368                 return 0;
369         }
370
371         rc = mdb_dn2id( op, tid, &ndn, &id, NULL, &nmatched );
372         if ( rc == MDB_NOTFOUND ) {
373                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
374                         ID eid = e->e_id;
375                         dnParent( &ndn, &npdn );
376                         if ( nmatched.bv_len != npdn.bv_len ) {
377                                 dnParent( &dn, &pdn );
378                                 e->e_name = pdn;
379                                 e->e_nname = npdn;
380                                 rc = mdb_tool_next_id( op, tid, e, text, 1 );
381                                 e->e_name = dn;
382                                 e->e_nname = ndn;
383                                 if ( rc ) {
384                                         return rc;
385                                 }
386                                 /* If parent didn't exist, it was created just now
387                                  * and its ID is now in e->e_id. Make sure the current
388                                  * entry gets added under the new parent ID.
389                                  */
390                                 if ( eid != e->e_id ) {
391                                         pid = e->e_id;
392                                 }
393                         } else {
394                                 pid = id;
395                         }
396                 }
397                 rc = mdb_next_id( op->o_bd, tid, &e->e_id );
398                 if ( rc ) {
399                         snprintf( text->bv_val, text->bv_len,
400                                 "next_id failed: %s (%d)",
401                                 mdb_strerror(rc), rc );
402                 Debug( LDAP_DEBUG_ANY,
403                         "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
404                         return rc;
405                 }
406                 rc = mdb_dn2id_add( op, tid, pid, e );
407                 if ( rc ) {
408                         snprintf( text->bv_val, text->bv_len,
409                                 "dn2id_add failed: %s (%d)",
410                                 mdb_strerror(rc), rc );
411                         Debug( LDAP_DEBUG_ANY,
412                                 "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
413                 } else if ( hole ) {
414                         MDB_val key, data;
415                         if ( nholes == nhmax - 1 ) {
416                                 if ( holes == hbuf ) {
417                                         holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
418                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
419                                 } else {
420                                         holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
421                                 }
422                                 nhmax *= 2;
423                         }
424                         ber_dupbv( &holes[nholes].dn, &ndn );
425                         holes[nholes++].id = e->e_id;
426                         key.mv_size = sizeof(ID);
427                         key.mv_data = &e->e_id;
428                         data.mv_size = 0;
429                         data.mv_data = NULL;
430                         rc = mdb_put( tid, mdb->mi_id2entry, &key, &data, MDB_NOOVERWRITE );
431                         if ( rc == MDB_KEYEXIST )
432                                 rc = 0;
433                         if ( rc ) {
434                                 snprintf( text->bv_val, text->bv_len,
435                                         "dummy id2entry add failed: %s (%d)",
436                                         mdb_strerror(rc), rc );
437                                 Debug( LDAP_DEBUG_ANY,
438                                         "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
439                         }
440                 }
441         } else if ( !hole ) {
442                 unsigned i, j;
443
444                 e->e_id = id;
445
446                 for ( i=0; i<nholes; i++) {
447                         if ( holes[i].id == e->e_id ) {
448                                 free(holes[i].dn.bv_val);
449                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
450                                 holes[j].id = 0;
451                                 nholes--;
452                                 break;
453                         } else if ( holes[i].id > e->e_id ) {
454                                 break;
455                         }
456                 }
457         }
458         return rc;
459 }
460
461 static int
462 mdb_tool_index_add(
463         Operation *op,
464         MDB_txn *txn,
465         Entry *e )
466 {
467         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
468
469         if ( !mdb->mi_nattrs )
470                 return 0;
471
472 #if 0
473         if ( slapMode & SLAP_TOOL_QUICK ) {
474                 IndexRec *ir;
475                 int i, rc;
476                 Attribute *a;
477
478                 ir = mdb_tool_index_rec;
479                 memset(ir, 0, mdb->bi_nattrs * sizeof( IndexRec ));
480
481                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
482                         rc = mdb_index_recset( mdb, a, a->a_desc->ad_type,
483                                 &a->a_desc->ad_tags, ir );
484                         if ( rc )
485                                 return rc;
486                 }
487                 mdb_tool_ix_id = e->e_id;
488                 mdb_tool_ix_op = op;
489                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
490                 /* Wait for all threads to be ready */
491                 while ( mdb_tool_index_tcount ) {
492                         ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
493                                 &mdb_tool_index_mutex );
494                 }
495                 for ( i=1; i<slap_tool_thread_max; i++ )
496                         mdb_tool_index_threads[i] = LDAP_BUSY;
497                 mdb_tool_index_tcount = slap_tool_thread_max - 1;
498                 ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
499                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
500                 rc = mdb_index_recrun( op, mdb, ir, e->e_id, 0 );
501                 if ( rc )
502                         return rc;
503                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
504                 for ( i=1; i<slap_tool_thread_max; i++ ) {
505                         if ( mdb_tool_index_threads[i] == LDAP_BUSY ) {
506                                 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
507                                         &mdb_tool_index_mutex );
508                                 i--;
509                                 continue;
510                         }
511                         if ( mdb_tool_index_threads[i] ) {
512                                 rc = mdb_tool_index_threads[i];
513                                 break;
514                         }
515                 }
516                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
517                 return rc;
518         } else
519 #endif
520         {
521                 return mdb_index_entry_add( op, txn, e );
522         }
523 }
524
525 ID mdb_tool_entry_put(
526         BackendDB *be,
527         Entry *e,
528         struct berval *text )
529 {
530         int rc;
531         struct mdb_info *mdb;
532         Operation op = {0};
533         Opheader ohdr = {0};
534
535         assert( be != NULL );
536         assert( slapMode & SLAP_TOOL_MODE );
537
538         assert( text != NULL );
539         assert( text->bv_val != NULL );
540         assert( text->bv_val[0] == '\0' );      /* overconservative? */
541
542         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_tool_entry_put)
543                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
544
545         mdb = (struct mdb_info *) be->be_private;
546
547         if ( !txn ) {
548         rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
549         if( rc != 0 ) {
550                 snprintf( text->bv_val, text->bv_len,
551                         "txn_begin failed: %s (%d)",
552                         mdb_strerror(rc), rc );
553                 Debug( LDAP_DEBUG_ANY,
554                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
555                          text->bv_val, 0, 0 );
556                 return NOID;
557         }
558         }
559
560         op.o_hdr = &ohdr;
561         op.o_bd = be;
562         op.o_tmpmemctx = NULL;
563         op.o_tmpmfuncs = &ch_mfuncs;
564
565         /* add dn2id indices */
566         rc = mdb_tool_next_id( &op, txn, e, text, 0 );
567         if( rc != 0 ) {
568                 goto done;
569         }
570
571         rc = mdb_tool_index_add( &op, txn, e );
572         if( rc != 0 ) {
573                 snprintf( text->bv_val, text->bv_len,
574                                 "index_entry_add failed: err=%d", rc );
575                 Debug( LDAP_DEBUG_ANY,
576                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
577                         text->bv_val, 0, 0 );
578                 goto done;
579         }
580
581
582         /* id2entry index */
583         rc = mdb_id2entry_add( &op, txn, e );
584         if( rc != 0 ) {
585                 snprintf( text->bv_val, text->bv_len,
586                                 "id2entry_add failed: err=%d", rc );
587                 Debug( LDAP_DEBUG_ANY,
588                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
589                         text->bv_val, 0, 0 );
590                 goto done;
591         }
592
593 done:
594         if( rc == 0 ) {
595                 mdb_writes++;
596                 if ( mdb_writes >= mdb_writes_per_commit ) {
597                         rc = mdb_txn_commit( txn );
598                         mdb_writes = 0;
599                         txn = NULL;
600                         if( rc != 0 ) {
601                                 snprintf( text->bv_val, text->bv_len,
602                                                 "txn_commit failed: %s (%d)",
603                                                 mdb_strerror(rc), rc );
604                                 Debug( LDAP_DEBUG_ANY,
605                                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
606                                         text->bv_val, 0, 0 );
607                                 e->e_id = NOID;
608                         }
609                 }
610
611         } else {
612                 mdb_txn_abort( txn );
613                 txn = NULL;
614                 snprintf( text->bv_val, text->bv_len,
615                         "txn_aborted! %s (%d)",
616                         rc == LDAP_OTHER ? "Internal error" :
617                         mdb_strerror(rc), rc );
618                 Debug( LDAP_DEBUG_ANY,
619                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
620                         text->bv_val, 0, 0 );
621                 e->e_id = NOID;
622         }
623
624         return e->e_id;
625 }
626
627 int mdb_tool_entry_reindex(
628         BackendDB *be,
629         ID id,
630         AttributeDescription **adv )
631 {
632         struct mdb_info *mi = (struct mdb_info *) be->be_private;
633         int rc;
634         Entry *e;
635         Operation op = {0};
636         Opheader ohdr = {0};
637
638         Debug( LDAP_DEBUG_ARGS,
639                 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
640                 (long) id, 0, 0 );
641         assert( tool_base == NULL );
642         assert( tool_filter == NULL );
643
644         /* No indexes configured, nothing to do. Could return an
645          * error here to shortcut things.
646          */
647         if (!mi->mi_attrs) {
648                 return 0;
649         }
650
651         /* Check for explicit list of attrs to index */
652         if ( adv ) {
653                 int i, j, n;
654
655                 if ( mi->mi_attrs[0]->ai_desc != adv[0] ) {
656                         /* count */
657                         for ( n = 0; adv[n]; n++ ) ;
658
659                         /* insertion sort */
660                         for ( i = 0; i < n; i++ ) {
661                                 AttributeDescription *ad = adv[i];
662                                 for ( j = i-1; j>=0; j--) {
663                                         if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
664                                         adv[j+1] = adv[j];
665                                 }
666                                 adv[j+1] = ad;
667                         }
668                 }
669
670                 for ( i = 0; adv[i]; i++ ) {
671                         if ( mi->mi_attrs[i]->ai_desc != adv[i] ) {
672                                 for ( j = i+1; j < mi->mi_nattrs; j++ ) {
673                                         if ( mi->mi_attrs[j]->ai_desc == adv[i] ) {
674                                                 AttrInfo *ai = mi->mi_attrs[i];
675                                                 mi->mi_attrs[i] = mi->mi_attrs[j];
676                                                 mi->mi_attrs[j] = ai;
677                                                 break;
678                                         }
679                                 }
680                                 if ( j == mi->mi_nattrs ) {
681                                         Debug( LDAP_DEBUG_ANY,
682                                                 LDAP_XSTRING(mdb_tool_entry_reindex)
683                                                 ": no index configured for %s\n",
684                                                 adv[i]->ad_cname.bv_val, 0, 0 );
685                                         return -1;
686                                 }
687                         }
688                 }
689                 mi->mi_nattrs = i;
690         }
691
692         if ( slapMode & SLAP_TRUNCATE_MODE ) {
693                 int i;
694                 for ( i=0; i < mi->mi_nattrs; i++ ) {
695                         rc = mdb_drop( txn, mi->mi_attrs[i]->ai_dbi, 0 );
696                         if ( rc ) {
697                                 Debug( LDAP_DEBUG_ANY,
698                                         LDAP_XSTRING(mdb_tool_entry_reindex)
699                                         ": (Truncate) mdb_drop(%s) failed: %s (%d)\n",
700                                         mi->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
701                                         mdb_strerror(rc), rc );
702                                 return -1;
703                         }
704                 }
705                 slapMode ^= SLAP_TRUNCATE_MODE;
706         }
707
708         e = mdb_tool_entry_get( be, id );
709
710         if( e == NULL ) {
711                 Debug( LDAP_DEBUG_ANY,
712                         LDAP_XSTRING(mdb_tool_entry_reindex)
713                         ": could not locate id=%ld\n",
714                         (long) id, 0, 0 );
715                 return -1;
716         }
717
718         if ( !txi ) {
719                 rc = mdb_txn_begin( mi->mi_dbenv, NULL, 0, &txi );
720                 if( rc != 0 ) {
721                         Debug( LDAP_DEBUG_ANY,
722                                 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) ": "
723                                 "txn_begin failed: %s (%d)\n",
724                                 mdb_strerror(rc), rc, 0 );
725                         goto done;
726                 }
727         }
728
729         /*
730          * just (re)add them for now
731          * assume that some other routine (not yet implemented)
732          * will zap index databases
733          *
734          */
735
736         Debug( LDAP_DEBUG_TRACE,
737                 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
738                 (long) id, e->e_dn, 0 );
739
740         op.o_hdr = &ohdr;
741         op.o_bd = be;
742         op.o_tmpmemctx = NULL;
743         op.o_tmpmfuncs = &ch_mfuncs;
744
745         rc = mdb_tool_index_add( &op, txi, e );
746
747 done:
748         if( rc == 0 ) {
749                 mdb_writes++;
750                 if ( mdb_writes >= mdb_writes_per_commit ) {
751                         rc = mdb_txn_commit( txi );
752                         if( rc != 0 ) {
753                                 Debug( LDAP_DEBUG_ANY,
754                                         "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
755                                         ": txn_commit failed: %s (%d)\n",
756                                         mdb_strerror(rc), rc, 0 );
757                                 e->e_id = NOID;
758                         }
759                         txi = NULL;
760                 }
761
762         } else {
763                 mdb_txn_abort( txi );
764                 Debug( LDAP_DEBUG_ANY,
765                         "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
766                         ": txn_aborted! err=%d\n",
767                         rc, 0, 0 );
768                 e->e_id = NOID;
769                 txi = NULL;
770         }
771         mdb_entry_release( &op, e, 0 );
772
773         return rc;
774 }
775
776 ID mdb_tool_entry_modify(
777         BackendDB *be,
778         Entry *e,
779         struct berval *text )
780 {
781         int rc;
782         struct mdb_info *mdb;
783         MDB_txn *tid;
784         Operation op = {0};
785         Opheader ohdr = {0};
786
787         assert( be != NULL );
788         assert( slapMode & SLAP_TOOL_MODE );
789
790         assert( text != NULL );
791         assert( text->bv_val != NULL );
792         assert( text->bv_val[0] == '\0' );      /* overconservative? */
793
794         assert ( e->e_id != NOID );
795
796         Debug( LDAP_DEBUG_TRACE,
797                 "=> " LDAP_XSTRING(mdb_tool_entry_modify) "( %ld, \"%s\" )\n",
798                 (long) e->e_id, e->e_dn, 0 );
799
800         mdb = (struct mdb_info *) be->be_private;
801
802         if( cursor ) {
803                 mdb_cursor_close( cursor );
804                 cursor = NULL;
805         }
806         rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &tid );
807         if( rc != 0 ) {
808                 snprintf( text->bv_val, text->bv_len,
809                         "txn_begin failed: %s (%d)",
810                         mdb_strerror(rc), rc );
811                 Debug( LDAP_DEBUG_ANY,
812                         "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
813                          text->bv_val, 0, 0 );
814                 return NOID;
815         }
816
817         op.o_hdr = &ohdr;
818         op.o_bd = be;
819         op.o_tmpmemctx = NULL;
820         op.o_tmpmfuncs = &ch_mfuncs;
821
822         /* id2entry index */
823         rc = mdb_id2entry_update( &op, tid, e );
824         if( rc != 0 ) {
825                 snprintf( text->bv_val, text->bv_len,
826                                 "id2entry_update failed: err=%d", rc );
827                 Debug( LDAP_DEBUG_ANY,
828                         "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
829                         text->bv_val, 0, 0 );
830                 goto done;
831         }
832
833 done:
834         if( rc == 0 ) {
835                 rc = mdb_txn_commit( tid );
836                 if( rc != 0 ) {
837                         snprintf( text->bv_val, text->bv_len,
838                                         "txn_commit failed: %s (%d)",
839                                         mdb_strerror(rc), rc );
840                         Debug( LDAP_DEBUG_ANY,
841                                 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": "
842                                 "%s\n", text->bv_val, 0, 0 );
843                         e->e_id = NOID;
844                 }
845
846         } else {
847                 mdb_txn_abort( tid );
848                 snprintf( text->bv_val, text->bv_len,
849                         "txn_aborted! %s (%d)",
850                         mdb_strerror(rc), rc );
851                 Debug( LDAP_DEBUG_ANY,
852                         "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
853                         text->bv_val, 0, 0 );
854                 e->e_id = NOID;
855         }
856
857         return e->e_id;
858 }
859
860 #if 0
861 static void *
862 mdb_tool_index_task( void *ctx, void *ptr )
863 {
864         int base = *(int *)ptr;
865
866         free( ptr );
867         while ( 1 ) {
868                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
869                 mdb_tool_index_tcount--;
870                 if ( !mdb_tool_index_tcount )
871                         ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
872                 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_work,
873                         &mdb_tool_index_mutex );
874                 if ( slapd_shutdown ) {
875                         mdb_tool_index_tcount--;
876                         if ( !mdb_tool_index_tcount )
877                                 ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
878                         ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
879                         break;
880                 }
881                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
882
883                 mdb_tool_index_threads[base] = mdb_index_recrun( mdb_tool_ix_op,
884                         mdb_tool_info, mdb_tool_index_rec, mdb_tool_ix_id, base );
885         }
886
887         return NULL;
888 }
889 #endif