1 /* tools.c - tools for slap tools */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2011 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
20 #include <ac/string.h>
27 static MDB_txn *txn = NULL, *txi = NULL;
28 static MDB_cursor *cursor = NULL, *idcursor = NULL;
29 static MDB_val key, data;
30 static EntryHeader eh;
31 static ID previd = NOID;
33 typedef struct dn_id {
38 #define HOLE_SIZE 4096
39 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
40 static unsigned nhmax = HOLE_SIZE;
41 static unsigned nholes;
43 static struct berval *tool_base;
44 static int tool_scope;
45 static Filter *tool_filter;
46 static Entry *tool_next_entry;
49 static ID mdb_tool_ix_id;
50 static Operation *mdb_tool_ix_op;
51 static int *mdb_tool_index_threads, mdb_tool_index_tcount;
52 static void *mdb_tool_index_rec;
53 static struct mdb_info *mdb_tool_info;
54 static ldap_pvt_thread_mutex_t mdb_tool_index_mutex;
55 static ldap_pvt_thread_cond_t mdb_tool_index_cond_main;
56 static ldap_pvt_thread_cond_t mdb_tool_index_cond_work;
57 static void * mdb_tool_index_task( void *ctx, void *ptr );
60 static int mdb_writes, mdb_writes_per_commit;
63 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep );
65 int mdb_tool_entry_open(
66 BackendDB *be, int mode )
68 /* In Quick mode, commit once per 1000 entries */
70 if ( slapMode & SLAP_TOOL_QUICK )
71 mdb_writes_per_commit = 1000;
73 mdb_writes_per_commit = 1;
76 /* Set up for threaded slapindex */
77 if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
78 if ( !mdb_tool_info ) {
79 ldap_pvt_thread_mutex_init( &mdb_tool_index_mutex );
80 ldap_pvt_thread_cond_init( &mdb_tool_index_cond_main );
81 ldap_pvt_thread_cond_init( &mdb_tool_index_cond_work );
82 if ( mdb->bi_nattrs ) {
84 mdb_tool_index_threads = ch_malloc( slap_tool_thread_max * sizeof( int ));
85 mdb_tool_index_rec = ch_malloc( mdb->bi_nattrs * sizeof( IndexRec ));
86 mdb_tool_index_tcount = slap_tool_thread_max - 1;
87 for (i=1; i<slap_tool_thread_max; i++) {
88 int *ptr = ch_malloc( sizeof( int ));
90 ldap_pvt_thread_pool_submit( &connection_pool,
91 mdb_tool_index_task, ptr );
102 int mdb_tool_entry_close(
106 if ( mdb_tool_info ) {
108 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
110 /* There might still be some threads starting */
111 while ( mdb_tool_index_tcount ) {
112 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
113 &mdb_tool_index_mutex );
116 mdb_tool_index_tcount = slap_tool_thread_max - 1;
117 ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
119 /* Make sure all threads are stopped */
120 while ( mdb_tool_index_tcount ) {
121 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
122 &mdb_tool_index_mutex );
124 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
126 mdb_tool_info = NULL;
128 ch_free( mdb_tool_index_threads );
129 ch_free( mdb_tool_index_rec );
130 mdb_tool_index_tcount = slap_tool_thread_max - 1;
135 mdb_cursor_close( idcursor );
139 mdb_cursor_close( cursor );
143 if ( mdb_txn_commit( txn ))
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);
161 mdb_tool_entry_first_x(
171 return mdb_tool_entry_next( be );
174 ID mdb_tool_entry_next(
179 struct mdb_info *mdb;
181 assert( be != NULL );
182 assert( slapMode & SLAP_TOOL_MODE );
184 mdb = (struct mdb_info *) be->be_private;
185 assert( mdb != NULL );
188 rc = mdb_txn_begin( mdb->mi_dbenv, 1, &txn );
191 rc = mdb_cursor_open( txn, mdb->mi_id2entry, &cursor );
193 mdb_txn_abort( txn );
199 rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT );
205 previd = *(ID *)key.mv_data;
208 if ( tool_filter || tool_base ) {
209 static Operation op = {0};
210 static Opheader ohdr = {0};
214 op.o_tmpmemctx = NULL;
215 op.o_tmpmfuncs = &ch_mfuncs;
217 if ( tool_next_entry ) {
218 mdb_entry_release( &op, tool_next_entry, 0 );
219 tool_next_entry = NULL;
222 rc = mdb_tool_entry_get_int( be, id, &tool_next_entry );
223 if ( rc == LDAP_NO_SUCH_OBJECT ) {
227 assert( tool_next_entry != NULL );
229 if ( tool_filter && test_filter( NULL, tool_next_entry, tool_filter ) != LDAP_COMPARE_TRUE )
231 mdb_entry_release( &op, tool_next_entry, 0 );
232 tool_next_entry = NULL;
240 ID mdb_tool_dn2id_get(
250 if ( BER_BVISEMPTY(dn) )
255 op.o_tmpmemctx = NULL;
256 op.o_tmpmfuncs = &ch_mfuncs;
258 rc = mdb_dn2id( &op, txn, dn, &id, NULL, NULL );
259 if ( rc == MDB_NOTFOUND )
266 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
269 struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
272 assert( be != NULL );
273 assert( slapMode & SLAP_TOOL_MODE );
275 if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
276 *ep = tool_next_entry;
277 tool_next_entry = NULL;
281 if ( id != previd ) {
282 key.mv_size = sizeof(ID);
284 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
291 if ( slapMode & SLAP_TOOL_READONLY ) {
297 op.o_tmpmemctx = NULL;
298 op.o_tmpmfuncs = &ch_mfuncs;
300 rc = mdb_id2name( &op, txn, &idcursor, id, &dn, &ndn );
303 mdb_entry_return( e );
307 if ( tool_base != NULL ) {
308 if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
309 ch_free( dn.bv_val );
310 ch_free( ndn.bv_val );
311 rc = LDAP_NO_SUCH_OBJECT;
316 eh.bv.bv_val = data.mv_data;
317 eh.bv.bv_len = data.mv_size;
319 rc = entry_header( &eh );
324 eh.bv.bv_len = eh.nvals * sizeof( struct berval );
325 eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
326 rc = entry_decode( &eh, &e );
328 if ( !BER_BVISNULL( &dn )) {
332 e->e_name.bv_val = NULL;
333 e->e_nname.bv_val = NULL;
346 mdb_tool_entry_get( BackendDB *be, ID id )
350 (void)mdb_tool_entry_get_int( be, id, &e );
354 static int mdb_tool_next_id(
361 struct berval dn = e->e_name;
362 struct berval ndn = e->e_nname;
363 struct berval pdn, npdn, nmatched;
367 if (ndn.bv_len == 0) {
372 rc = mdb_dn2id( op, tid, &ndn, &id, NULL, &nmatched );
373 if ( rc == MDB_NOTFOUND ) {
374 if ( !be_issuffix( op->o_bd, &ndn ) ) {
376 dnParent( &ndn, &npdn );
377 if ( nmatched.bv_len != npdn.bv_len ) {
378 dnParent( &dn, &pdn );
381 rc = mdb_tool_next_id( op, tid, e, text, 1 );
387 /* If parent didn't exist, it was created just now
388 * and its ID is now in e->e_id. Make sure the current
389 * entry gets added under the new parent ID.
391 if ( eid != e->e_id ) {
398 rc = mdb_next_id( op->o_bd, tid, &e->e_id );
400 snprintf( text->bv_val, text->bv_len,
401 "next_id failed: %s (%d)",
402 mdb_strerror(rc), rc );
403 Debug( LDAP_DEBUG_ANY,
404 "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
407 rc = mdb_dn2id_add( op, tid, pid, e );
409 snprintf( text->bv_val, text->bv_len,
410 "dn2id_add failed: %s (%d)",
411 mdb_strerror(rc), rc );
412 Debug( LDAP_DEBUG_ANY,
413 "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
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) );
420 holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
424 ber_dupbv( &holes[nholes].dn, &ndn );
425 holes[nholes++].id = e->e_id;
427 } else if ( !hole ) {
432 for ( i=0; i<nholes; i++) {
433 if ( holes[i].id == e->e_id ) {
434 free(holes[i].dn.bv_val);
435 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
439 } else if ( holes[i].id > e->e_id ) {
453 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
455 if ( !mdb->mi_nattrs )
459 if ( slapMode & SLAP_TOOL_QUICK ) {
464 ir = mdb_tool_index_rec;
465 memset(ir, 0, mdb->bi_nattrs * sizeof( IndexRec ));
467 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
468 rc = mdb_index_recset( mdb, a, a->a_desc->ad_type,
469 &a->a_desc->ad_tags, ir );
473 mdb_tool_ix_id = e->e_id;
475 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
476 /* Wait for all threads to be ready */
477 while ( mdb_tool_index_tcount ) {
478 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
479 &mdb_tool_index_mutex );
481 for ( i=1; i<slap_tool_thread_max; i++ )
482 mdb_tool_index_threads[i] = LDAP_BUSY;
483 mdb_tool_index_tcount = slap_tool_thread_max - 1;
484 ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
485 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
486 rc = mdb_index_recrun( op, mdb, ir, e->e_id, 0 );
489 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
490 for ( i=1; i<slap_tool_thread_max; i++ ) {
491 if ( mdb_tool_index_threads[i] == LDAP_BUSY ) {
492 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
493 &mdb_tool_index_mutex );
497 if ( mdb_tool_index_threads[i] ) {
498 rc = mdb_tool_index_threads[i];
502 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
507 return mdb_index_entry_add( op, txn, e );
511 ID mdb_tool_entry_put(
514 struct berval *text )
517 struct mdb_info *mdb;
521 assert( be != NULL );
522 assert( slapMode & SLAP_TOOL_MODE );
524 assert( text != NULL );
525 assert( text->bv_val != NULL );
526 assert( text->bv_val[0] == '\0' ); /* overconservative? */
528 Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_tool_entry_put)
529 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
531 mdb = (struct mdb_info *) be->be_private;
534 rc = mdb_txn_begin( mdb->mi_dbenv, 0, &txn );
536 snprintf( text->bv_val, text->bv_len,
537 "txn_begin failed: %s (%d)",
538 mdb_strerror(rc), rc );
539 Debug( LDAP_DEBUG_ANY,
540 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
541 text->bv_val, 0, 0 );
548 op.o_tmpmemctx = NULL;
549 op.o_tmpmfuncs = &ch_mfuncs;
551 /* add dn2id indices */
552 rc = mdb_tool_next_id( &op, txn, e, text, 0 );
557 rc = mdb_tool_index_add( &op, txn, e );
559 snprintf( text->bv_val, text->bv_len,
560 "index_entry_add failed: %s (%d)",
561 rc == LDAP_OTHER ? "Internal error" :
562 mdb_strerror(rc), rc );
563 Debug( LDAP_DEBUG_ANY,
564 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
565 text->bv_val, 0, 0 );
571 rc = mdb_id2entry_add( &op, txn, e );
573 snprintf( text->bv_val, text->bv_len,
574 "id2entry_add failed: %s (%d)",
575 mdb_strerror(rc), rc );
576 Debug( LDAP_DEBUG_ANY,
577 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
578 text->bv_val, 0, 0 );
585 if ( mdb_writes >= mdb_writes_per_commit ) {
586 rc = mdb_txn_commit( txn );
590 snprintf( text->bv_val, text->bv_len,
591 "txn_commit failed: %s (%d)",
592 mdb_strerror(rc), rc );
593 Debug( LDAP_DEBUG_ANY,
594 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
595 text->bv_val, 0, 0 );
601 mdb_txn_abort( txn );
603 snprintf( text->bv_val, text->bv_len,
604 "txn_aborted! %s (%d)",
605 rc == LDAP_OTHER ? "Internal error" :
606 mdb_strerror(rc), rc );
607 Debug( LDAP_DEBUG_ANY,
608 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
609 text->bv_val, 0, 0 );
616 int mdb_tool_entry_reindex(
619 AttributeDescription **adv )
621 struct mdb_info *mi = (struct mdb_info *) be->be_private;
627 Debug( LDAP_DEBUG_ARGS,
628 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
630 assert( tool_base == NULL );
631 assert( tool_filter == NULL );
633 /* No indexes configured, nothing to do. Could return an
634 * error here to shortcut things.
640 /* Check for explicit list of attrs to index */
644 if ( mi->mi_attrs[0]->ai_desc != adv[0] ) {
646 for ( n = 0; adv[n]; n++ ) ;
649 for ( i = 0; i < n; i++ ) {
650 AttributeDescription *ad = adv[i];
651 for ( j = i-1; j>=0; j--) {
652 if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
659 for ( i = 0; adv[i]; i++ ) {
660 if ( mi->mi_attrs[i]->ai_desc != adv[i] ) {
661 for ( j = i+1; j < mi->mi_nattrs; j++ ) {
662 if ( mi->mi_attrs[j]->ai_desc == adv[i] ) {
663 AttrInfo *ai = mi->mi_attrs[i];
664 mi->mi_attrs[i] = mi->mi_attrs[j];
665 mi->mi_attrs[j] = ai;
669 if ( j == mi->mi_nattrs ) {
670 Debug( LDAP_DEBUG_ANY,
671 LDAP_XSTRING(mdb_tool_entry_reindex)
672 ": no index configured for %s\n",
673 adv[i]->ad_cname.bv_val, 0, 0 );
681 e = mdb_tool_entry_get( be, id );
684 Debug( LDAP_DEBUG_ANY,
685 LDAP_XSTRING(mdb_tool_entry_reindex)
686 ": could not locate id=%ld\n",
692 rc = mdb_txn_begin( mi->mi_dbenv, 0, &txi );
694 Debug( LDAP_DEBUG_ANY,
695 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) ": "
696 "txn_begin failed: %s (%d)\n",
697 mdb_strerror(rc), rc, 0 );
703 * just (re)add them for now
704 * assume that some other routine (not yet implemented)
705 * will zap index databases
709 Debug( LDAP_DEBUG_TRACE,
710 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
711 (long) id, e->e_dn, 0 );
715 op.o_tmpmemctx = NULL;
716 op.o_tmpmfuncs = &ch_mfuncs;
718 rc = mdb_tool_index_add( &op, txi, e );
723 if ( mdb_writes >= mdb_writes_per_commit ) {
724 rc = mdb_txn_commit( txi );
726 Debug( LDAP_DEBUG_ANY,
727 "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
728 ": txn_commit failed: %s (%d)\n",
729 mdb_strerror(rc), rc, 0 );
736 mdb_txn_abort( txi );
737 Debug( LDAP_DEBUG_ANY,
738 "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
739 ": txn_aborted! %s (%d)\n",
740 mdb_strerror(rc), rc, 0 );
744 mdb_entry_release( &op, e, 0 );
749 ID mdb_tool_entry_modify(
752 struct berval *text )
755 struct mdb_info *mdb;
760 assert( be != NULL );
761 assert( slapMode & SLAP_TOOL_MODE );
763 assert( text != NULL );
764 assert( text->bv_val != NULL );
765 assert( text->bv_val[0] == '\0' ); /* overconservative? */
767 assert ( e->e_id != NOID );
769 Debug( LDAP_DEBUG_TRACE,
770 "=> " LDAP_XSTRING(mdb_tool_entry_modify) "( %ld, \"%s\" )\n",
771 (long) e->e_id, e->e_dn, 0 );
773 mdb = (struct mdb_info *) be->be_private;
776 mdb_cursor_close( cursor );
779 rc = mdb_txn_begin( mdb->mi_dbenv, 0, &tid );
781 snprintf( text->bv_val, text->bv_len,
782 "txn_begin failed: %s (%d)",
783 mdb_strerror(rc), rc );
784 Debug( LDAP_DEBUG_ANY,
785 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
786 text->bv_val, 0, 0 );
792 op.o_tmpmemctx = NULL;
793 op.o_tmpmfuncs = &ch_mfuncs;
796 rc = mdb_id2entry_update( &op, tid, e );
798 snprintf( text->bv_val, text->bv_len,
799 "id2entry_add failed: %s (%d)",
800 mdb_strerror(rc), rc );
801 Debug( LDAP_DEBUG_ANY,
802 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
803 text->bv_val, 0, 0 );
809 rc = mdb_txn_commit( tid );
811 snprintf( text->bv_val, text->bv_len,
812 "txn_commit failed: %s (%d)",
813 mdb_strerror(rc), rc );
814 Debug( LDAP_DEBUG_ANY,
815 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": "
816 "%s\n", text->bv_val, 0, 0 );
821 mdb_txn_abort( tid );
822 snprintf( text->bv_val, text->bv_len,
823 "txn_aborted! %s (%d)",
824 mdb_strerror(rc), rc );
825 Debug( LDAP_DEBUG_ANY,
826 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
827 text->bv_val, 0, 0 );
836 mdb_tool_index_task( void *ctx, void *ptr )
838 int base = *(int *)ptr;
842 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
843 mdb_tool_index_tcount--;
844 if ( !mdb_tool_index_tcount )
845 ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
846 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_work,
847 &mdb_tool_index_mutex );
848 if ( slapd_shutdown ) {
849 mdb_tool_index_tcount--;
850 if ( !mdb_tool_index_tcount )
851 ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
852 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
855 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
857 mdb_tool_index_threads[base] = mdb_index_recrun( mdb_tool_ix_op,
858 mdb_tool_info, mdb_tool_index_rec, mdb_tool_ix_id, base );