1 /* txn.c - TP support functions of the bdb2 backend */
6 #ifdef HAVE_SYS_PARAM_H
11 /* default DB files */
12 char *bdb2i_fixed_filenames[] = {
21 bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
24 BDB2_TXN_FILES **fileNodeH;
26 /* for each fixed DB file allocate a file descriptor node and
27 initialize the file's name */
28 fileNodeH = &head->dbFiles;
30 for ( dbFile = 0; bdb2i_fixed_filenames[dbFile] != NULL; dbFile++ ) {
31 char fileName[MAXPATHLEN];
33 *fileNodeH = (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
35 if ( *fileNodeH == NULL ) {
36 Debug( LDAP_DEBUG_ANY, "bdb2i_txn_head_init(): out of memory!\n",
41 sprintf( fileName, "%s" BDB2_SUFFIX, bdb2i_fixed_filenames[dbFile] );
42 (*fileNodeH)->dbc_name = ch_strdup( fileName );
44 fileNodeH = &(*fileNodeH)->next;
48 /* set defaults for checkpointing */
49 head->txn_log = BDB2_TXN_CHKP_MAX_LOG;
50 head->txn_time = BDB2_TXN_CHKP_MAX_TIME;
52 /* initialize the txn_dirty_mutex */
53 ldap_pvt_thread_mutex_init( &txn_dirty_mutex );
60 bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
65 fileinfo->dbc_refcnt = 1;
67 sprintf( buf, "%s" LDAP_DIRSEP "%s",
68 li->li_directory, fileinfo->dbc_name );
70 if ( stat( buf, &st ) == 0 ) {
71 fileinfo->dbc_blksize = st.st_blksize;
73 fileinfo->dbc_blksize = DEFAULT_BLOCKSIZE;
76 fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) -
78 fileinfo->dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
79 fileinfo->dbc_maxids ) + 1;
84 /* create a DB file cache entry for a specified index attribute
85 (if not already done); the function is called during config
86 file read for all index'ed attributes; if "default" index with
87 a non-none selection is given, this is remembered for run-time
88 extension of the list of index files; the function is also
89 called before add or modify operations to check for putative
90 new "default" index files; at that time, files are also opened
93 bdb2i_txn_attr_config(
98 BDB2_TXN_HEAD *head = &li->li_txn_head;
100 /* the "attribute" 'default' is special */
101 if ( strcasecmp( attr, "default" )) {
103 /* create a new index file node, if the index is not known already */
104 BDB2_TXN_FILES **fileNodeH;
105 char fileName[MAXPATHLEN];
107 sprintf( fileName, "%s%s", attr, BDB2_SUFFIX );
109 /* search for the end of the list or a node describing
110 the current attribute */
111 for ( fileNodeH = &head->dbFiles;
112 ( *fileNodeH && strcasecmp( (*fileNodeH)->dbc_name, fileName ));
113 fileNodeH = &(*fileNodeH)->next ) {
117 /* unless we have that attribute already... */
118 if ( *fileNodeH == NULL ) {
121 Debug( LDAP_DEBUG_TRACE,
122 "bdb2i_txn_attr_config(): adding node for \"%s\"\n",
125 /* if we're out of memory, we have to see, how to exit... */
126 if ( ( *fileNodeH = p = (BDB2_TXN_FILES *)
127 ch_calloc( 1, sizeof( BDB2_TXN_FILES )) ) == NULL ) {
129 Debug( LDAP_DEBUG_ANY,
130 "bdb2i_txn_attr_config(): out of memory -- FATAL.\n",
133 /* during configuration (no files are opened)
134 we can just exit, otherwise we kill ourself and
135 hope to shutdown cleanly... */
137 pthread_kill( pthread_self(), LDAP_SIGUSR1 );
139 exit( EXIT_FAILURE );
143 p->dbc_name = ch_strdup( fileName );
145 /* if requested for, we have to open the DB file */
146 /* BUT NOT "objectclass", 'cause that's a default index ! */
147 if ( open && strcasecmp( fileName, "objectclass" )) {
149 /* re-use filename to get the complete path */
150 sprintf( fileName, "%s" LDAP_DIRSEP "%s",
151 li->li_directory, p->dbc_name );
153 /* since we have an mpool, we should not define a cache size */
154 p->dbc_db = bdb2i_db_open( fileName, DB_TYPE,
155 LDBM_WRCREAT, li->li_mode, 0 );
157 /* if the files could not be opened, something is wrong;
159 if ( p->dbc_db == NULL ) {
161 Debug( LDAP_DEBUG_ANY,
162 "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
164 pthread_kill( pthread_self(), LDAP_SIGUSR1 );
168 bdb2i_init_db_file_cache( li, p );
170 Debug( LDAP_DEBUG_TRACE,
171 "bdb2i_txn_attr_config(): NEW INDEX FILE \"%s\"\n",
177 } else { /* it is "attribute" 'default' */
179 head->withDefIDX = BDB2_WITH_DEF_IDX;
185 /* open the NEXTID file for read/write; if it does not exist,
186 create it (access to the file must be preceeded by a rewind)
189 bdb2i_open_nextid( BackendDB *be )
191 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
192 BDB2_TXN_HEAD *head = &li->li_txn_head;
195 char fileName[MAXPATHLEN];
197 sprintf( fileName, "%s" LDAP_DIRSEP "%s",
198 li->li_directory, NEXTID_NAME );
200 /* try to open the file for read and write */
201 memset( &dbinfo, 0, sizeof( dbinfo ));
202 dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
203 dbinfo.db_malloc = ldbm_malloc;
205 (void) db_open( fileName, DB_RECNO, DB_CREATE | DB_THREAD,
206 li->li_mode, &bdb2i_dbEnv, &dbinfo, &db );
210 Debug( LDAP_DEBUG_ANY,
211 "bdb2i_open_nextid: could not open \"%s\"\n",
217 /* the file is open for read/write */
218 head->nextidFile = db;
224 /* open all DB during startup of the backend (necessary due to TP)
225 additional files may be opened during slapd life-time due to
226 default indexes (must be configured in slapd.conf;
227 see bdb2i_txn_attr_config)
228 also, set the counter and timer for TP checkpointing
231 bdb2i_txn_open_files( BackendDB *be )
233 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
234 BDB2_TXN_HEAD *head = &li->li_txn_head;
235 BDB2_TXN_FILES *dbFile;
238 for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
239 char fileName[MAXPATHLEN];
241 sprintf( fileName, "%s" LDAP_DIRSEP "%s",
242 li->li_directory, dbFile->dbc_name );
244 /* since we have an mpool, we should not define a cache size */
245 dbFile->dbc_db = bdb2i_db_open( fileName, DB_TYPE,
246 LDBM_WRCREAT, li->li_mode, 0 );
248 /* if the files could not be opened, something is wrong; complain */
249 if ( dbFile->dbc_db == NULL ) {
251 Debug( LDAP_DEBUG_ANY,
252 "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
253 dbFile->dbc_name, 0, 0 );
258 /* initialize the file info */
259 bdb2i_init_db_file_cache( li, dbFile );
261 Debug( LDAP_DEBUG_TRACE, "bdb2i_txn_open_files(): OPEN INDEX \"%s\"\n",
262 dbFile->dbc_name, 0, 0 );
266 rc = bdb2i_open_nextid( be );
268 txn_max_pending_log = head->txn_log;
269 txn_max_pending_time = head->txn_time;
275 /* close all DB files during shutdown of the backend */
277 bdb2i_txn_close_files( BackendDB *be )
279 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
280 BDB2_TXN_HEAD *head = &li->li_txn_head;
281 BDB2_TXN_FILES *dbFile;
283 for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
285 ldbm_close( dbFile->dbc_db );
289 if ( head->nextidFile )
290 ldbm_close( head->nextidFile );
295 /* get the db_cache structure associated with a specified
296 DB file (replaces the on-the-fly opening of files in cache_open()
299 bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
301 BDB2_TXN_HEAD *head = &li->li_txn_head;
302 BDB2_TXN_FILES *dbFile;
305 Debug( LDAP_DEBUG_TRACE, "bdb2i_get_db_file_cache(): looking for file %s\n",
308 for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
311 if ( !strcasecmp( dbFile->dbc_name, name )) return( dbFile );
315 Debug( LDAP_DEBUG_ANY,
316 "bdb2i_get_db_file_cache(): UPS, could't find \"%s\" \n", name, 0, 0 );
318 /* ups, we couldn't find the file */
324 /* check for new attribute indexes, that might have been created
325 during former runs of slapd */
326 /* this is called during startup of the slapd server */
328 bdb2i_check_additional_attr_index( struct ldbminfo *li )
333 if ( ( datadir = opendir( li->li_directory ) ) == NULL ) {
336 Debug( LDAP_DEBUG_ANY,
337 "bdb2i_check_additional_attr_index(): ERROR while opening datadir: %s\n",
338 strerror( err ), 0, 0 );
343 for ( file = readdir( datadir ); file; file = readdir( datadir )) {
344 char filename[MAXPATHLEN];
347 strcpy( filename, file->d_name );
348 namelen = strlen( filename );
350 if ( namelen > strlen( BDB2_SUFFIX )) {
352 if ( !strcasecmp( filename + namelen - strlen( BDB2_SUFFIX ),
355 *(filename + namelen - strlen( BDB2_SUFFIX )) = '\0';
356 bdb2i_txn_attr_config( li, filename, 0 );
358 Debug( LDAP_DEBUG_TRACE, "INDEX FILE: %s\n", filename, 0, 0 );
372 /* check for the addition of new attribute indexes during add */
373 /* this is called after startup of the slapd server */
374 /* DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
375 FROM ADDING ATTRIBUTES LATER ON */
377 bdb2i_check_default_attr_index_add( struct ldbminfo *li, Entry *e )
379 BDB2_TXN_HEAD *head = &li->li_txn_head;
381 if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
384 for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
385 if ( strcasecmp( ap->a_type, "objectclass" ))
386 bdb2i_txn_attr_config( li, ap->a_type, 1 );
392 /* check for the addition of new attribute indexes during modify */
393 /* this is called after startup of the slapd server */
394 /* DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
395 FROM ADDING ATTRIBUTES LATER ON */
397 bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
399 BDB2_TXN_HEAD *head = &li->li_txn_head;
401 if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
403 char *default_attrs[] = { "modifytimestamp", "modifiersname", NULL };
406 for ( ml = modlist; ml != NULL; ml = ml->ml_next ) {
407 LDAPMod *mod = &ml->ml_mod;
409 if (( mod->mod_op & ~LDAP_MOD_BVALUES ) == LDAP_MOD_ADD )
410 if ( strcasecmp( mod->mod_type, "objectclass" ))
411 bdb2i_txn_attr_config( li, mod->mod_type, 1 );
414 /* these attributes are default when modifying */
415 for ( attr = 0; default_attrs[attr]; attr++ ) {
416 bdb2i_txn_attr_config( li, default_attrs[attr], 1 );
422 /* get the next ID from the NEXTID file */
424 bdb2i_get_nextid( BackendDB *be )
426 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
427 BDB2_TXN_HEAD *head = &li->li_txn_head;
431 db_recno_t rec = NEXTID_RECNO;
433 ldbm_datum_init( key );
434 ldbm_datum_init( data );
437 key.size = sizeof( rec );
439 data = bdb2i_db_fetch( head->nextidFile, key );
440 if ( data.data == NULL ) {
441 Debug( LDAP_DEBUG_ANY,
442 "next_id_read: could not get nextid from \"%s\"\n",
447 id = atol( data.data );
448 ldbm_datum_free( head->nextidFile, data );
451 Debug( LDAP_DEBUG_ANY,
452 "next_id_read %ld: return non-positive integer\n",
462 bdb2i_put_nextid( BackendDB *be, ID id )
464 struct ldbminfo *li = (struct ldbminfo *) be->be_private;
465 BDB2_TXN_HEAD *head = &li->li_txn_head;
469 db_recno_t rec = NEXTID_RECNO;
472 sprintf( buf, "%ld\n", id );
474 ldbm_datum_init( key );
475 ldbm_datum_init( data );
478 key.size = sizeof( rec );
481 data.size = sizeof( buf );
483 flags = LDBM_REPLACE;
484 if (( rc = bdb2i_db_store( head->nextidFile, key, data, flags )) != 0 ) {
485 Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): store failed (%d)\n",
494 /* BDB2 backend-private functions of libldbm */
506 memset( &dbinfo, 0, sizeof( dbinfo ));
507 if ( bdb2i_dbEnv.mp_info == NULL )
508 dbinfo.db_cachesize = dbcachesize;
509 dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
510 dbinfo.db_malloc = ldbm_malloc;
512 (void) db_open( name, type, rw, mode, &bdb2i_dbEnv, &dbinfo, &ret );
519 bdb2i_db_store( LDBM ldbm, Datum key, Datum data, int flags )
523 rc = (*ldbm->put)( ldbm, txnid, &key, &data, flags );
526 if ( txnid != NULL ) {
528 /* if the store was OK, set the dirty flag,
529 otherwise set the abort flag */
536 Debug( LDAP_DEBUG_ANY,
537 "bdb2i_db_store: transaction failed: aborted.\n",
549 bdb2i_db_delete( LDBM ldbm, Datum key )
553 rc = (*ldbm->del)( ldbm, txnid, &key, 0 );
556 if ( txnid != NULL ) {
558 /* if the delete was OK, set the dirty flag,
559 otherwise set the abort flag */
566 Debug( LDAP_DEBUG_ANY,
567 "bdb2i_db_delete: transaction failed: aborted.\n",
579 bdb2i_db_fetch( LDBM ldbm, Datum key )
584 ldbm_datum_init( data );
585 data.flags = DB_DBT_MALLOC;
587 if ( (rc = (*ldbm->get)( ldbm, txnid, &key, &data, 0 )) != 0 ) {
588 if (( txnid != NULL ) && ( rc != DB_NOTFOUND )) {
590 Debug( LDAP_DEBUG_ANY,
591 "bdb2i_db_fetch: transaction failed: aborted.\n",
596 if ( data.dptr ) free( data.dptr );
606 bdb2i_db_firstkey( LDBM ldbm, DBC **dbch )
612 ldbm_datum_init( key );
613 ldbm_datum_init( data );
615 key.flags = data.flags = DB_DBT_MALLOC;
617 #if defined( DB_VERSION_MAJOR ) && defined( DB_VERSION_MINOR ) && \
618 DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6
620 if ( (*ldbm->cursor)( ldbm, txnid, &dbci ))
624 if ( (*ldbm->cursor)( ldbm, txnid, &dbci, 0 ))
628 if ( txnid != NULL ) {
630 Debug( LDAP_DEBUG_ANY,
631 "bdb2i_db_firstkey: transaction failed: aborted.\n",
640 if ( (*dbci->c_get)( dbci, &key, &data, DB_NEXT ) == 0 ) {
641 ldbm_datum_free( ldbm, data );
643 if ( txnid != NULL ) {
645 Debug( LDAP_DEBUG_ANY,
646 "bdb2i_db_firstkey: transaction failed: aborted.\n",
651 ldbm_datum_free( ldbm, key );
663 bdb2i_db_nextkey( LDBM ldbm, Datum key, DBC *dbcp )
668 ldbm_datum_init( data );
669 ldbm_datum_free( ldbm, key );
670 key.flags = data.flags = DB_DBT_MALLOC;
672 if ( (*dbcp->c_get)( dbcp, &key, &data, DB_NEXT ) == 0 ) {
673 ldbm_datum_free( ldbm, data );
675 if ( txnid != NULL ) {
677 Debug( LDAP_DEBUG_ANY,
678 "bdb2i_db_nextkey: transaction failed: aborted.\n",
692 /* Transaction control of write access */
693 /* Since these functions are only used by one writer at a time,
694 we do not have any concurrency (locking) problem */
696 /* initialize a new transaction */
698 bdb2i_start_transction( DB_TXNMGR *txmgr )
705 if (( rc = txn_begin( txmgr, NULL, &txnid )) != 0 ) {
707 Debug( LDAP_DEBUG_ANY, "bdb2i_start_transction failed: %d: errno=%s\n",
708 rc, strerror( err ), 0 );
711 (void) txn_abort( txnid );
715 Debug( LDAP_DEBUG_TRACE,
716 "bdb2i_start_transaction: transaction started.\n",
723 /* finish the transaction */
725 bdb2i_finish_transaction()
729 /* if transaction was NOT selected, just return */
730 if ( txnid == NULL ) return( 0 );
732 /* if nothing was wrong so far, we can try to commit the transaction */
733 /* complain, if the commit fails */
734 if (( txn_do_abort == 0 ) && ( txn_commit( txnid )) != 0 ) {
735 Debug( LDAP_DEBUG_ANY,
736 "bdb2i_finish_transaction: transaction commit failed: aborted.\n",
741 /* if anything went wrong, we have to abort the transaction */
742 if ( txn_do_abort ) {
743 Debug( LDAP_DEBUG_ANY,
744 "bdb2i_finish_transaction: transaction aborted.\n",
746 (void) txn_abort( txnid );
749 Debug( LDAP_DEBUG_TRACE,
750 "bdb2i_finish_transaction: transaction commited.\n",
754 /* XXX do NOT free the txnid memory !!! */
763 either forced (during shutdown) or when logsize or time are exceeded
764 (is called by reader and writer, so protect txn_dirty)
767 bdb2i_set_txn_checkpoint( DB_TXNMGR *txmgr, int forced )
771 /* set dirty mutex */
772 ldap_pvt_thread_mutex_lock( &txn_dirty_mutex );
780 logsize = forced ? (u_int32_t) 0 : txn_max_pending_log;
781 mins = forced ? (u_int32_t) 0 : txn_max_pending_time;
783 now = slap_get_time();
785 rc = txn_checkpoint( txmgr, logsize, mins );
787 /* if checkpointing was successful, reset txn_dirty */
789 DB_TXN_STAT *statp = NULL;
791 /* check whether the checkpoint was actually written;
792 if so, unset the txn_dirty flag */
793 if (( rc = txn_stat( txmgr, &statp, ldbm_malloc )) == 0 ) {
795 if ( statp && ( statp->st_time_ckp >= now )) {
797 Debug( LDAP_DEBUG_TRACE,
798 "bdb2i_set_txn_checkpoint succeded.\n",
804 if ( statp ) free( statp );
807 Debug( LDAP_DEBUG_ANY,
808 "bdb2i_set_txn_checkpoint: txn_stat failed: %d\n",
812 Debug( LDAP_DEBUG_ANY, "bdb2i_set_txn_checkpoint failed: %d\n",
817 /* release dirty mutex */
818 ldap_pvt_thread_mutex_unlock( &txn_dirty_mutex );