1 /* txn.c - TP support functions of the bdb2 backend */
7 bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
10 BDB2_TXN_FILES **fileNodeH;
12 /* for each fixed DB file allocate a file descriptor node and
13 initialize the file's name */
14 fileNodeH = &head->dbFiles;
15 for ( dbFile = BDB2_DB_DN_FILE; dbFile <= BDB2_DB_OC_IDX_FILE; dbFile++ ) {
17 char fileName[MAXPATHLEN];
19 *fileNodeH = (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
20 if ( *fileNodeH == NULL ) {
22 Debug( LDAP_DEBUG_ANY, "bdb2i_txn_head_init(): out of memory!\n",
28 sprintf( fileName, "%s%s", bdb2i_fixed_filenames[dbFile], BDB2_SUFFIX );
29 (*fileNodeH)->dbc_name = ch_strdup( fileName );
31 fileNodeH = &(*fileNodeH)->next;
35 /* set defaults for checkpointing */
36 head->txn_log = BDB2_TXN_CHKP_MAX_LOG;
37 head->txn_time = BDB2_TXN_CHKP_MAX_TIME;
39 /* initialize the txn_dirty_mutex */
40 ldap_pvt_thread_mutex_init( &txn_dirty_mutex );
47 bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
52 fileinfo->dbc_refcnt = 1;
54 sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
56 if ( stat( buf, &st ) == 0 ) {
57 fileinfo->dbc_blksize = st.st_blksize;
59 fileinfo->dbc_blksize = DEFAULT_BLOCKSIZE;
62 fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) -
64 fileinfo->dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
65 fileinfo->dbc_maxids ) + 1;
70 /* create a DB file cache entry for a specified index attribute
71 (if not already done); the function is called during config
72 file read for all index'ed attributes; if "default" index with
73 a non-none selection is given, this is remembered for run-time
74 extension of the list of index files; the function is also
75 called before add or modify operations to check for putative
76 new "default" index files; at that time, files are also opened
79 bdb2i_txn_attr_config(
84 BDB2_TXN_HEAD *head = &li->li_txn_head;
86 /* the "attribute" 'default' is special */
87 if ( strcasecmp( attr, "default" )) {
89 /* create a new index file node, if the index is not known already */
90 BDB2_TXN_FILES **fileNodeH;
91 char fileName[MAXPATHLEN];
93 sprintf( fileName, "%s%s", attr, BDB2_SUFFIX );
95 /* search for the end of the list or a node describing
96 the current attribute */
97 for ( fileNodeH = &head->dbFiles;
98 ( *fileNodeH && strcasecmp( (*fileNodeH)->dbc_name, fileName ));
99 fileNodeH = &(*fileNodeH)->next ) {
103 /* unless we have that attribute already... */
104 if ( *fileNodeH == NULL ) {
107 Debug( LDAP_DEBUG_TRACE,
108 "bdb2i_txn_attr_config(): adding node for \"%s\"\n",
111 /* if we're out of memory, we have to see, how to exit... */
112 if ( ( *fileNodeH = p = (BDB2_TXN_FILES *)
113 ch_calloc( 1, sizeof( BDB2_TXN_FILES )) ) == NULL ) {
115 Debug( LDAP_DEBUG_ANY,
116 "bdb2i_txn_attr_config(): out of memory -- FATAL.\n",
119 /* during configuration (no files are opened)
120 we can just exit, otherwise we kill ourself and
121 hope to shutdown cleanly... */
123 pthread_kill( pthread_self(), LDAP_SIGUSR1 );
129 p->dbc_name = ch_strdup( fileName );
131 /* if requested for, we have to open the DB file */
132 /* BUT NOT "objectclass", 'cause that's a default index ! */
133 if ( open && strcasecmp( fileName, "objectclass" )) {
135 /* re-use filename to get the complete path */
136 sprintf( fileName, "%s%s%s",
137 li->li_directory, DEFAULT_DIRSEP, p->dbc_name );
139 /* since we have an mpool, we should not define a cache size */
140 p->dbc_db = ldbm_open( fileName, LDBM_WRCREAT, li->li_mode, 0 );
142 /* if the files could not be opened, something is wrong;
144 if ( p->dbc_db == NULL ) {
146 Debug( LDAP_DEBUG_ANY,
147 "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
149 pthread_kill( pthread_self(), LDAP_SIGUSR1 );
153 bdb2i_init_db_file_cache( li, p );
155 Debug( LDAP_DEBUG_TRACE,
156 "bdb2i_txn_attr_config(): NEW INDEX FILE \"%s\"\n",
162 } else { /* it is "attribute" 'default' */
164 head->withDefIDX = BDB2_WITH_DEF_IDX;
170 /* open the NEXTID file for read/write; if it does not exist,
171 create it (access to the file must be preceeded by a rewind)
174 bdb2i_open_nextid( BackendDB *be )
176 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
177 BDB2_TXN_HEAD *head = &li->li_txn_head;
180 DB_ENV *dbenv = get_dbenv( be );
181 char fileName[MAXPATHLEN];
183 sprintf( fileName, "%s%s%s",
184 li->li_directory, DEFAULT_DIRSEP, NEXTID_NAME );
186 /* try to open the file for read and write */
187 memset( &dbinfo, 0, sizeof( dbinfo ));
188 dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
189 dbinfo.db_malloc = ldbm_malloc;
191 (void) db_open( fileName, DB_RECNO, DB_CREATE | DB_THREAD,
192 li->li_mode, dbenv, &dbinfo, &db );
196 Debug( LDAP_DEBUG_ANY,
197 "bdb2i_open_nextid: could not open \"%s\"\n",
203 /* the file is open for read/write */
204 head->nextidFile = db;
210 /* open all DB during startup of the backend (necessary due to TP)
211 additional files may be opened during slapd life-time due to
212 default indexes (must be configured in slapd.conf;
213 see bdb2i_txn_attr_config)
214 also, set the counter and timer for TP checkpointing
217 bdb2i_txn_open_files( BackendDB *be )
219 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
220 BDB2_TXN_HEAD *head = &li->li_txn_head;
221 BDB2_TXN_FILES *dbFile;
224 for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
225 char fileName[MAXPATHLEN];
227 sprintf( fileName, "%s%s%s",
228 li->li_directory, DEFAULT_DIRSEP, dbFile->dbc_name );
230 /* since we have an mpool, we should not define a cache size */
231 dbFile->dbc_db = ldbm_open( fileName, LDBM_WRCREAT, li->li_mode, 0 );
233 /* if the files could not be opened, something is wrong; complain */
234 if ( dbFile->dbc_db == NULL ) {
236 Debug( LDAP_DEBUG_ANY,
237 "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
238 dbFile->dbc_name, 0, 0 );
243 /* initialize the file info */
244 bdb2i_init_db_file_cache( li, dbFile );
246 Debug( LDAP_DEBUG_TRACE, "bdb2i_txn_open_files(): OPEN INDEX \"%s\"\n",
247 dbFile->dbc_name, 0, 0 );
251 rc = bdb2i_open_nextid( be );
253 txn_max_pending_log = head->txn_log;
254 txn_max_pending_time = head->txn_time;
260 /* close all DB files during shutdown of the backend */
262 bdb2i_txn_close_files( BackendDB *be )
264 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
265 BDB2_TXN_HEAD *head = &li->li_txn_head;
266 BDB2_TXN_FILES *dbFile;
268 for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
270 ldbm_close( dbFile->dbc_db );
274 ldbm_close( head->nextidFile );
279 /* get the db_cache structure associated with a specified
280 DB file (replaces the on-the-fly opening of files in cache_open()
283 bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
285 BDB2_TXN_HEAD *head = &li->li_txn_head;
286 BDB2_TXN_FILES *dbFile;
289 Debug( LDAP_DEBUG_TRACE, "bdb2i_get_db_file_cache(): looking for file %s\n",
292 for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
295 if ( !strcasecmp( dbFile->dbc_name, name )) return( dbFile );
299 Debug( LDAP_DEBUG_ANY,
300 "bdb2i_get_db_file_cache(): UPS, could't find \"%s\" \n", name, 0, 0 );
302 /* ups, we couldn't find the file */
308 /* check for new attribute indexes, that might have been created
309 during former runs of slapd */
310 /* this is called during startup of the slapd server */
312 bdb2i_check_additional_attr_index( struct ldbminfo *li )
317 if ( ( datadir = opendir( li->li_directory ) ) == NULL ) {
319 Debug( LDAP_DEBUG_ANY,
320 "bdb2i_check_additional_attr_index(): ERROR while opening datadir: %s\n",
321 strerror( errno ), 0, 0 );
326 for ( file = readdir( datadir ); file; file = readdir( datadir )) {
327 char filename[MAXPATHLEN];
330 strcpy( filename, file->d_name );
331 namelen = strlen( filename );
333 if ( namelen > strlen( BDB2_SUFFIX )) {
335 if ( !strcasecmp( filename + namelen - strlen( BDB2_SUFFIX ),
338 *(filename + namelen - strlen( BDB2_SUFFIX )) = '\0';
339 bdb2i_txn_attr_config( li, filename, 0 );
341 Debug( LDAP_DEBUG_TRACE, "INDEX FILE: %s\n", filename, 0, 0 );
355 /* check for the addition of new attribute indexes during add */
356 /* this is called after startup of the slapd server */
357 /* DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
358 FROM ADDING ATTRIBUTES LATER ON */
360 bdb2i_check_default_attr_index_add( struct ldbminfo *li, Entry *e )
362 BDB2_TXN_HEAD *head = &li->li_txn_head;
364 if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
367 for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
368 if ( strcasecmp( ap->a_type, "objectclass" ))
369 bdb2i_txn_attr_config( li, ap->a_type, 1 );
375 /* check for the addition of new attribute indexes during modify */
376 /* this is called after startup of the slapd server */
377 /* DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
378 FROM ADDING ATTRIBUTES LATER ON */
380 bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
382 BDB2_TXN_HEAD *head = &li->li_txn_head;
384 if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
386 char *default_attrs[] = { "modifytimestamp", "modifiersname", NULL };
389 for ( ml = modlist; ml != NULL; ml = ml->ml_next ) {
390 LDAPMod *mod = &ml->ml_mod;
392 if (( mod->mod_op & ~LDAP_MOD_BVALUES ) == LDAP_MOD_ADD )
393 if ( strcasecmp( mod->mod_type, "objectclass" ))
394 bdb2i_txn_attr_config( li, mod->mod_type, 1 );
397 /* these attributes are default when modifying */
398 for ( attr = 0; default_attrs[attr]; attr++ ) {
399 bdb2i_txn_attr_config( li, default_attrs[attr], 1 );
405 /* get the next ID from the NEXTID file */
407 bdb2i_get_nextid( BackendDB *be )
409 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
410 BDB2_TXN_HEAD *head = &li->li_txn_head;
414 db_recno_t rec = NEXTID_RECNO;
416 ldbm_datum_init( key );
417 ldbm_datum_init( data );
420 key.size = sizeof( rec );
422 data = bdb2i_db_fetch( head->nextidFile, key );
423 if ( data.data == NULL ) {
424 Debug( LDAP_DEBUG_ANY,
425 "next_id_read: could not get nextid from \"%s\"\n",
430 id = atol( data.data );
431 ldbm_datum_free( head->nextidFile, data );
434 Debug( LDAP_DEBUG_ANY,
435 "next_id_read %ld: return non-positive integer\n",
445 bdb2i_put_nextid( BackendDB *be, ID id )
447 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
448 BDB2_TXN_HEAD *head = &li->li_txn_head;
452 db_recno_t rec = NEXTID_RECNO;
455 sprintf( buf, "%ld\n", id );
457 ldbm_datum_init( key );
458 ldbm_datum_init( data );
461 key.size = sizeof( rec );
464 data.size = sizeof( buf );
466 flags = LDBM_REPLACE;
467 if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
469 if (( rc = bdb2i_db_store( head->nextidFile, key, data, flags )) != 0 ) {
470 Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): store failed (%d)\n",
479 /* BDB2 backend-private functions of ldbm_store and ldbm_delete */
481 bdb2i_db_store( LDBM ldbm, Datum key, Datum data, int flags )
485 rc = (*ldbm->put)( ldbm, txnid, &key, &data, flags & ~LDBM_SYNC );
488 if ( txnid != NULL ) {
490 /* if the store was OK, set the dirty flag,
491 otherwise set the abort flag */
498 Debug( LDAP_DEBUG_ANY,
499 "bdb2i_db_store: transaction failed: aborted.\n",
511 bdb2i_db_delete( LDBM ldbm, Datum key )
515 rc = (*ldbm->del)( ldbm, txnid, &key, 0 );
518 if ( txnid != NULL ) {
520 /* if the delete was OK, set the dirty flag,
521 otherwise set the abort flag */
528 Debug( LDAP_DEBUG_ANY,
529 "bdb2i_db_delete: transaction failed: aborted.\n",
541 bdb2i_db_fetch( LDBM ldbm, Datum key )
546 ldbm_datum_init( data );
547 data.flags = DB_DBT_MALLOC;
549 if ( (rc = (*ldbm->get)( ldbm, txnid, &key, &data, 0 )) != 0 ) {
550 if (( txnid != NULL ) && ( rc != DB_NOTFOUND )) {
552 Debug( LDAP_DEBUG_ANY,
553 "bdb2i_db_fetch: transaction failed: aborted.\n",
558 if ( data.dptr ) free( data.dptr );
568 bdb2i_db_firstkey( LDBM ldbm, DBC **dbch )
574 ldbm_datum_init( key );
575 ldbm_datum_init( data );
577 key.flags = data.flags = DB_DBT_MALLOC;
579 #if defined( DB_VERSION_MAJOR ) && defined( DB_VERSION_MINOR ) && \
580 DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6
582 if ( (*ldbm->cursor)( ldbm, txnid, &dbci ))
586 if ( (*ldbm->cursor)( ldbm, txnid, &dbci, 0 ))
590 if ( txnid != NULL ) {
592 Debug( LDAP_DEBUG_ANY,
593 "bdb2i_db_firstkey: transaction failed: aborted.\n",
601 if ( (*dbci->c_get)( dbci, &key, &data, DB_NEXT ) == 0 ) {
602 if ( data.dptr ) free( data.dptr );
604 if ( txnid != NULL ) {
606 Debug( LDAP_DEBUG_ANY,
607 "bdb2i_db_firstkey: transaction failed: aborted.\n",
612 if ( key.dptr ) free( key.dptr );
623 bdb2i_db_nextkey( LDBM ldbm, Datum key, DBC *dbcp )
627 void *oldKey = key.dptr;
629 ldbm_datum_init( data );
630 data.flags = DB_DBT_MALLOC;
632 if ( (*dbcp->c_get)( dbcp, &key, &data, DB_NEXT ) == 0 ) {
633 if ( data.dptr ) free( data.dptr );
635 if ( txnid != NULL ) {
637 Debug( LDAP_DEBUG_ANY,
638 "bdb2i_db_nextkey: transaction failed: aborted.\n",
647 if ( oldKey ) free( oldKey );
653 /* Transaction control of write access */
654 /* Since these functions are only used by one writer at a time,
655 we do not have any concurrency (locking) problem */
657 /* initialize a new transaction */
659 bdb2i_start_transction( DB_TXNMGR *txmgr )
666 if (( rc = txn_begin( txmgr, NULL, &txnid )) != 0 ) {
667 Debug( LDAP_DEBUG_ANY, "bdb2i_start_transction failed: %d: errno=%s\n",
668 rc, strerror( errno ), 0 );
671 (void) txn_abort( txnid );
675 Debug( LDAP_DEBUG_TRACE,
676 "bdb2i_start_transaction: transaction started.\n",
683 /* finish the transaction */
685 bdb2i_finish_transaction()
689 /* if transaction was NOT selected, just return */
690 if ( txnid == NULL ) return( 0 );
692 /* if nothing was wrong so far, we can try to commit the transaction */
693 /* complain, if the commit fails */
694 if (( txn_do_abort == 0 ) && ( txn_commit( txnid )) != 0 ) {
695 Debug( LDAP_DEBUG_ANY,
696 "bdb2i_finish_transaction: transaction commit failed: aborted.\n",
701 /* if anything went wrong, we have to abort the transaction */
702 if ( txn_do_abort ) {
703 Debug( LDAP_DEBUG_ANY,
704 "bdb2i_finish_transaction: transaction aborted.\n",
706 (void) txn_abort( txnid );
709 Debug( LDAP_DEBUG_TRACE,
710 "bdb2i_finish_transaction: transaction commited.\n",
714 /* XXX do NOT free the txnid memory !!! */
723 either forced (during shutdown) or when logsize or time are exceeded
724 (is called by reader and writer, so protect txn_dirty)
727 bdb2i_set_txn_checkpoint( DB_TXNMGR *txmgr, int forced )
731 /* set dirty mutex */
732 ldap_pvt_thread_mutex_lock( &txn_dirty_mutex );
740 logsize = forced ? (u_int32_t) 0 : txn_max_pending_log;
741 mins = forced ? (u_int32_t) 0 : txn_max_pending_time;
743 ldap_pvt_thread_mutex_lock( ¤ttime_mutex );
744 time( ¤ttime );
746 ldap_pvt_thread_mutex_unlock( ¤ttime_mutex );
748 rc = txn_checkpoint( txmgr, logsize, mins );
750 /* if checkpointing was successful, reset txn_dirty */
752 DB_TXN_STAT *statp = NULL;
754 /* check whether the checkpoint was actually written;
755 if so, unset the txn_dirty flag */
756 if (( rc = txn_stat( txmgr, &statp, ldbm_malloc )) == 0 ) {
758 if ( statp && ( statp->st_time_ckp >= now )) {
760 Debug( LDAP_DEBUG_TRACE,
761 "bdb2i_set_txn_checkpoint succeded.\n",
767 if ( statp ) free( statp );
770 Debug( LDAP_DEBUG_ANY,
771 "bdb2i_set_txn_checkpoint: txn_stat failed: %d\n",
775 Debug( LDAP_DEBUG_ANY, "bdb2i_set_txn_checkpoint failed: %d\n",
780 /* release dirty mutex */
781 ldap_pvt_thread_mutex_unlock( &txn_dirty_mutex );