mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
811 lines
18 KiB
C
811 lines
18 KiB
C
/* txn.c - TP support functions of the bdb2 backend */
|
|
|
|
#include "txn.h"
|
|
|
|
|
|
int
|
|
bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
|
|
{
|
|
int dbFile;
|
|
BDB2_TXN_FILES **fileNodeH;
|
|
|
|
/* for each fixed DB file allocate a file descriptor node and
|
|
initialize the file's name */
|
|
fileNodeH = &head->dbFiles;
|
|
for ( dbFile = BDB2_DB_DN_FILE; dbFile <= BDB2_DB_OC_IDX_FILE; dbFile++ ) {
|
|
|
|
char fileName[MAXPATHLEN];
|
|
|
|
*fileNodeH = (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
|
|
if ( *fileNodeH == NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY, "bdb2i_txn_head_init(): out of memory!\n",
|
|
0, 0, 0 );
|
|
return( 1 );
|
|
|
|
}
|
|
|
|
sprintf( fileName, "%s%s", bdb2i_fixed_filenames[dbFile], BDB2_SUFFIX );
|
|
(*fileNodeH)->dbc_name = ch_strdup( fileName );
|
|
|
|
fileNodeH = &(*fileNodeH)->next;
|
|
|
|
}
|
|
|
|
/* set defaults for checkpointing */
|
|
head->txn_log = BDB2_TXN_CHKP_MAX_LOG;
|
|
head->txn_time = BDB2_TXN_CHKP_MAX_TIME;
|
|
|
|
/* initialize the txn_dirty_mutex */
|
|
ldap_pvt_thread_mutex_init( &txn_dirty_mutex );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
|
|
{
|
|
struct stat st;
|
|
char buf[MAXPATHLEN];
|
|
|
|
fileinfo->dbc_refcnt = 1;
|
|
|
|
sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
|
|
fileinfo->dbc_name );
|
|
if ( stat( buf, &st ) == 0 ) {
|
|
fileinfo->dbc_blksize = st.st_blksize;
|
|
} else {
|
|
fileinfo->dbc_blksize = DEFAULT_BLOCKSIZE;
|
|
}
|
|
|
|
fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) -
|
|
ID_BLOCK_IDS_OFFSET;
|
|
fileinfo->dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
|
|
fileinfo->dbc_maxids ) + 1;
|
|
|
|
}
|
|
|
|
|
|
/* create a DB file cache entry for a specified index attribute
|
|
(if not already done); the function is called during config
|
|
file read for all index'ed attributes; if "default" index with
|
|
a non-none selection is given, this is remembered for run-time
|
|
extension of the list of index files; the function is also
|
|
called before add or modify operations to check for putative
|
|
new "default" index files; at that time, files are also opened
|
|
*/
|
|
void
|
|
bdb2i_txn_attr_config(
|
|
struct ldbminfo *li,
|
|
char *attr,
|
|
int open )
|
|
{
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
|
|
/* the "attribute" 'default' is special */
|
|
if ( strcasecmp( attr, "default" )) {
|
|
|
|
/* create a new index file node, if the index is not known already */
|
|
BDB2_TXN_FILES **fileNodeH;
|
|
char fileName[MAXPATHLEN];
|
|
|
|
sprintf( fileName, "%s%s", attr, BDB2_SUFFIX );
|
|
|
|
/* search for the end of the list or a node describing
|
|
the current attribute */
|
|
for ( fileNodeH = &head->dbFiles;
|
|
( *fileNodeH && strcasecmp( (*fileNodeH)->dbc_name, fileName ));
|
|
fileNodeH = &(*fileNodeH)->next ) {
|
|
|
|
}
|
|
|
|
/* unless we have that attribute already... */
|
|
if ( *fileNodeH == NULL ) {
|
|
BDB2_TXN_FILES *p;
|
|
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"bdb2i_txn_attr_config(): adding node for \"%s\"\n",
|
|
fileName, 0, 0 );
|
|
|
|
/* if we're out of memory, we have to see, how to exit... */
|
|
if ( ( *fileNodeH = p = (BDB2_TXN_FILES *)
|
|
ch_calloc( 1, sizeof( BDB2_TXN_FILES )) ) == NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_txn_attr_config(): out of memory -- FATAL.\n",
|
|
0, 0, 0 );
|
|
|
|
/* during configuration (no files are opened)
|
|
we can just exit, otherwise we kill ourself and
|
|
hope to shutdown cleanly... */
|
|
if ( open ) {
|
|
pthread_kill( pthread_self(), LDAP_SIGUSR1 );
|
|
} else {
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
p->dbc_name = ch_strdup( fileName );
|
|
|
|
/* if requested for, we have to open the DB file */
|
|
/* BUT NOT "objectclass", 'cause that's a default index ! */
|
|
if ( open && strcasecmp( fileName, "objectclass" )) {
|
|
|
|
/* re-use filename to get the complete path */
|
|
sprintf( fileName, "%s%s%s",
|
|
li->li_directory, DEFAULT_DIRSEP, p->dbc_name );
|
|
|
|
/* since we have an mpool, we should not define a cache size */
|
|
p->dbc_db = bdb2i_db_open( fileName, DB_TYPE,
|
|
LDBM_WRCREAT, li->li_mode, 0 );
|
|
|
|
/* if the files could not be opened, something is wrong;
|
|
complain */
|
|
if ( p->dbc_db == NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
|
|
p->dbc_name, 0, 0 );
|
|
pthread_kill( pthread_self(), LDAP_SIGUSR1 );
|
|
|
|
}
|
|
|
|
bdb2i_init_db_file_cache( li, p );
|
|
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"bdb2i_txn_attr_config(): NEW INDEX FILE \"%s\"\n",
|
|
p->dbc_name, 0, 0 );
|
|
|
|
}
|
|
}
|
|
|
|
} else { /* it is "attribute" 'default' */
|
|
|
|
head->withDefIDX = BDB2_WITH_DEF_IDX;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* open the NEXTID file for read/write; if it does not exist,
|
|
create it (access to the file must be preceeded by a rewind)
|
|
*/
|
|
static int
|
|
bdb2i_open_nextid( BackendDB *be )
|
|
{
|
|
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
LDBM db = NULL;
|
|
DB_INFO dbinfo;
|
|
char fileName[MAXPATHLEN];
|
|
|
|
sprintf( fileName, "%s%s%s",
|
|
li->li_directory, DEFAULT_DIRSEP, NEXTID_NAME );
|
|
|
|
/* try to open the file for read and write */
|
|
memset( &dbinfo, 0, sizeof( dbinfo ));
|
|
dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
|
|
dbinfo.db_malloc = ldbm_malloc;
|
|
|
|
(void) db_open( fileName, DB_RECNO, DB_CREATE | DB_THREAD,
|
|
li->li_mode, &bdb2i_dbEnv, &dbinfo, &db );
|
|
|
|
if ( db == NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_open_nextid: could not open \"%s\"\n",
|
|
NEXTID_NAME, 0, 0 );
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
/* the file is open for read/write */
|
|
head->nextidFile = db;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
/* open all DB during startup of the backend (necessary due to TP)
|
|
additional files may be opened during slapd life-time due to
|
|
default indexes (must be configured in slapd.conf;
|
|
see bdb2i_txn_attr_config)
|
|
also, set the counter and timer for TP checkpointing
|
|
*/
|
|
int
|
|
bdb2i_txn_open_files( BackendDB *be )
|
|
{
|
|
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
BDB2_TXN_FILES *dbFile;
|
|
int rc;
|
|
|
|
for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
|
|
char fileName[MAXPATHLEN];
|
|
|
|
sprintf( fileName, "%s%s%s",
|
|
li->li_directory, DEFAULT_DIRSEP, dbFile->dbc_name );
|
|
|
|
/* since we have an mpool, we should not define a cache size */
|
|
dbFile->dbc_db = bdb2i_db_open( fileName, DB_TYPE,
|
|
LDBM_WRCREAT, li->li_mode, 0 );
|
|
|
|
/* if the files could not be opened, something is wrong; complain */
|
|
if ( dbFile->dbc_db == NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
|
|
dbFile->dbc_name, 0, 0 );
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
/* initialize the file info */
|
|
bdb2i_init_db_file_cache( li, dbFile );
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "bdb2i_txn_open_files(): OPEN INDEX \"%s\"\n",
|
|
dbFile->dbc_name, 0, 0 );
|
|
|
|
}
|
|
|
|
rc = bdb2i_open_nextid( be );
|
|
|
|
txn_max_pending_log = head->txn_log;
|
|
txn_max_pending_time = head->txn_time;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* close all DB files during shutdown of the backend */
|
|
void
|
|
bdb2i_txn_close_files( BackendDB *be )
|
|
{
|
|
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
BDB2_TXN_FILES *dbFile;
|
|
|
|
for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
|
|
|
|
ldbm_close( dbFile->dbc_db );
|
|
|
|
}
|
|
|
|
ldbm_close( head->nextidFile );
|
|
|
|
}
|
|
|
|
|
|
/* get the db_cache structure associated with a specified
|
|
DB file (replaces the on-the-fly opening of files in cache_open()
|
|
*/
|
|
BDB2_TXN_FILES *
|
|
bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
|
|
{
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
BDB2_TXN_FILES *dbFile;
|
|
int dbFileNum;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "bdb2i_get_db_file_cache(): looking for file %s\n",
|
|
name, 0, 0 );
|
|
|
|
for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
|
|
|
|
/* we've got it */
|
|
if ( !strcasecmp( dbFile->dbc_name, name )) return( dbFile );
|
|
|
|
}
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_get_db_file_cache(): UPS, could't find \"%s\" \n", name, 0, 0 );
|
|
|
|
/* ups, we couldn't find the file */
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
|
/* check for new attribute indexes, that might have been created
|
|
during former runs of slapd */
|
|
/* this is called during startup of the slapd server */
|
|
int
|
|
bdb2i_check_additional_attr_index( struct ldbminfo *li )
|
|
{
|
|
DIR *datadir;
|
|
struct dirent *file;
|
|
|
|
if ( ( datadir = opendir( li->li_directory ) ) == NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_check_additional_attr_index(): ERROR while opening datadir: %s\n",
|
|
strerror( errno ), 0, 0 );
|
|
return( 1 );
|
|
|
|
}
|
|
|
|
for ( file = readdir( datadir ); file; file = readdir( datadir )) {
|
|
char filename[MAXPATHLEN];
|
|
int namelen;
|
|
|
|
strcpy( filename, file->d_name );
|
|
namelen = strlen( filename );
|
|
|
|
if ( namelen > strlen( BDB2_SUFFIX )) {
|
|
|
|
if ( !strcasecmp( filename + namelen - strlen( BDB2_SUFFIX ),
|
|
BDB2_SUFFIX )) {
|
|
|
|
*(filename + namelen - strlen( BDB2_SUFFIX )) = '\0';
|
|
bdb2i_txn_attr_config( li, filename, 0 );
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "INDEX FILE: %s\n", filename, 0, 0 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir( datadir );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* check for the addition of new attribute indexes during add */
|
|
/* this is called after startup of the slapd server */
|
|
/* DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
|
|
FROM ADDING ATTRIBUTES LATER ON */
|
|
void
|
|
bdb2i_check_default_attr_index_add( struct ldbminfo *li, Entry *e )
|
|
{
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
|
|
if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
|
|
Attribute *ap;
|
|
|
|
for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
|
|
if ( strcasecmp( ap->a_type, "objectclass" ))
|
|
bdb2i_txn_attr_config( li, ap->a_type, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* check for the addition of new attribute indexes during modify */
|
|
/* this is called after startup of the slapd server */
|
|
/* DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
|
|
FROM ADDING ATTRIBUTES LATER ON */
|
|
void
|
|
bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
|
|
{
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
|
|
if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
|
|
LDAPModList *ml;
|
|
char *default_attrs[] = { "modifytimestamp", "modifiersname", NULL };
|
|
int attr;
|
|
|
|
for ( ml = modlist; ml != NULL; ml = ml->ml_next ) {
|
|
LDAPMod *mod = &ml->ml_mod;
|
|
|
|
if (( mod->mod_op & ~LDAP_MOD_BVALUES ) == LDAP_MOD_ADD )
|
|
if ( strcasecmp( mod->mod_type, "objectclass" ))
|
|
bdb2i_txn_attr_config( li, mod->mod_type, 1 );
|
|
}
|
|
|
|
/* these attributes are default when modifying */
|
|
for ( attr = 0; default_attrs[attr]; attr++ ) {
|
|
bdb2i_txn_attr_config( li, default_attrs[attr], 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* get the next ID from the NEXTID file */
|
|
ID
|
|
bdb2i_get_nextid( BackendDB *be )
|
|
{
|
|
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
ID id;
|
|
Datum key;
|
|
Datum data;
|
|
db_recno_t rec = NEXTID_RECNO;
|
|
|
|
ldbm_datum_init( key );
|
|
ldbm_datum_init( data );
|
|
|
|
key.data = &rec;
|
|
key.size = sizeof( rec );
|
|
|
|
data = bdb2i_db_fetch( head->nextidFile, key );
|
|
if ( data.data == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"next_id_read: could not get nextid from \"%s\"\n",
|
|
NEXTID_NAME, 0, 0 );
|
|
return NOID;
|
|
}
|
|
|
|
id = atol( data.data );
|
|
ldbm_datum_free( head->nextidFile, data );
|
|
|
|
if ( id < 1 ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"next_id_read %ld: return non-positive integer\n",
|
|
id, 0, 0 );
|
|
return NOID;
|
|
}
|
|
|
|
return( id );
|
|
}
|
|
|
|
|
|
int
|
|
bdb2i_put_nextid( BackendDB *be, ID id )
|
|
{
|
|
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
|
|
BDB2_TXN_HEAD *head = &li->li_txn_head;
|
|
int rc, flags;
|
|
Datum key;
|
|
Datum data;
|
|
db_recno_t rec = NEXTID_RECNO;
|
|
char buf[20];
|
|
|
|
sprintf( buf, "%ld\n", id );
|
|
|
|
ldbm_datum_init( key );
|
|
ldbm_datum_init( data );
|
|
|
|
key.data = &rec;
|
|
key.size = sizeof( rec );
|
|
|
|
data.data = &buf;
|
|
data.size = sizeof( buf );
|
|
|
|
flags = LDBM_REPLACE;
|
|
if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
|
|
|
|
if (( rc = bdb2i_db_store( head->nextidFile, key, data, flags )) != 0 ) {
|
|
Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): store failed (%d)\n",
|
|
id, rc, 0 );
|
|
return( -1 );
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
/* BDB2 backend-private functions of libldbm */
|
|
LDBM
|
|
bdb2i_db_open(
|
|
char *name,
|
|
int type,
|
|
int rw,
|
|
int mode,
|
|
int dbcachesize )
|
|
{
|
|
LDBM ret = NULL;
|
|
DB_INFO dbinfo;
|
|
|
|
memset( &dbinfo, 0, sizeof( dbinfo ));
|
|
if ( bdb2i_dbEnv.mp_info == NULL )
|
|
dbinfo.db_cachesize = dbcachesize;
|
|
dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
|
|
dbinfo.db_malloc = ldbm_malloc;
|
|
|
|
(void) db_open( name, type, rw, mode, &bdb2i_dbEnv, &dbinfo, &ret );
|
|
|
|
return( ret );
|
|
}
|
|
|
|
|
|
int
|
|
bdb2i_db_store( LDBM ldbm, Datum key, Datum data, int flags )
|
|
{
|
|
int rc;
|
|
|
|
rc = (*ldbm->put)( ldbm, txnid, &key, &data, flags & ~LDBM_SYNC );
|
|
rc = (-1 ) * rc;
|
|
|
|
if ( txnid != NULL ) {
|
|
|
|
/* if the store was OK, set the dirty flag,
|
|
otherwise set the abort flag */
|
|
if ( rc == 0 ) {
|
|
|
|
txn_dirty = 1;
|
|
|
|
} else {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_db_store: transaction failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
int
|
|
bdb2i_db_delete( LDBM ldbm, Datum key )
|
|
{
|
|
int rc;
|
|
|
|
rc = (*ldbm->del)( ldbm, txnid, &key, 0 );
|
|
rc = (-1 ) * rc;
|
|
|
|
if ( txnid != NULL ) {
|
|
|
|
/* if the delete was OK, set the dirty flag,
|
|
otherwise set the abort flag */
|
|
if ( rc == 0 ) {
|
|
|
|
txn_dirty = 1;
|
|
|
|
} else {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_db_delete: transaction failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
Datum
|
|
bdb2i_db_fetch( LDBM ldbm, Datum key )
|
|
{
|
|
Datum data;
|
|
int rc;
|
|
|
|
ldbm_datum_init( data );
|
|
data.flags = DB_DBT_MALLOC;
|
|
|
|
if ( (rc = (*ldbm->get)( ldbm, txnid, &key, &data, 0 )) != 0 ) {
|
|
if (( txnid != NULL ) && ( rc != DB_NOTFOUND )) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_db_fetch: transaction failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
|
|
}
|
|
if ( data.dptr ) free( data.dptr );
|
|
data.dptr = NULL;
|
|
data.dsize = 0;
|
|
}
|
|
|
|
return( data );
|
|
}
|
|
|
|
|
|
Datum
|
|
bdb2i_db_firstkey( LDBM ldbm, DBC **dbch )
|
|
{
|
|
Datum key, data;
|
|
int rc;
|
|
DBC *dbci;
|
|
|
|
ldbm_datum_init( key );
|
|
ldbm_datum_init( data );
|
|
|
|
key.flags = data.flags = DB_DBT_MALLOC;
|
|
|
|
#if defined( DB_VERSION_MAJOR ) && defined( DB_VERSION_MINOR ) && \
|
|
DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6
|
|
|
|
if ( (*ldbm->cursor)( ldbm, txnid, &dbci ))
|
|
|
|
#else
|
|
|
|
if ( (*ldbm->cursor)( ldbm, txnid, &dbci, 0 ))
|
|
|
|
#endif
|
|
{
|
|
if ( txnid != NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_db_firstkey: transaction failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
|
|
}
|
|
key.flags = 0;
|
|
return( key );
|
|
} else {
|
|
*dbch = dbci;
|
|
if ( (*dbci->c_get)( dbci, &key, &data, DB_NEXT ) == 0 ) {
|
|
ldbm_datum_free( ldbm, data );
|
|
} else {
|
|
if ( txnid != NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_db_firstkey: transaction failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
|
|
}
|
|
ldbm_datum_free( ldbm, key );
|
|
key.flags = 0;
|
|
key.dptr = NULL;
|
|
key.dsize = 0;
|
|
}
|
|
}
|
|
|
|
return( key );
|
|
}
|
|
|
|
|
|
Datum
|
|
bdb2i_db_nextkey( LDBM ldbm, Datum key, DBC *dbcp )
|
|
{
|
|
Datum data;
|
|
int rc;
|
|
|
|
ldbm_datum_init( data );
|
|
key.flags = data.flags = DB_DBT_MALLOC;
|
|
|
|
if ( (*dbcp->c_get)( dbcp, &key, &data, DB_NEXT ) == 0 ) {
|
|
ldbm_datum_free( ldbm, data );
|
|
} else {
|
|
if ( txnid != NULL ) {
|
|
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_db_nextkey: transaction failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
|
|
}
|
|
key.flags = 0;
|
|
key.dptr = NULL;
|
|
key.dsize = 0;
|
|
}
|
|
|
|
return( key );
|
|
}
|
|
|
|
|
|
/* Transaction control of write access */
|
|
/* Since these functions are only used by one writer at a time,
|
|
we do not have any concurrency (locking) problem */
|
|
|
|
/* initialize a new transaction */
|
|
int
|
|
bdb2i_start_transction( DB_TXNMGR *txmgr )
|
|
{
|
|
int rc;
|
|
|
|
txnid = NULL;
|
|
txn_do_abort = 0;
|
|
|
|
if (( rc = txn_begin( txmgr, NULL, &txnid )) != 0 ) {
|
|
Debug( LDAP_DEBUG_ANY, "bdb2i_start_transction failed: %d: errno=%s\n",
|
|
rc, strerror( errno ), 0 );
|
|
|
|
if ( txnid != NULL )
|
|
(void) txn_abort( txnid );
|
|
return( -1 );
|
|
}
|
|
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"bdb2i_start_transaction: transaction started.\n",
|
|
0, 0, 0 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
/* finish the transaction */
|
|
int
|
|
bdb2i_finish_transaction()
|
|
{
|
|
int rc = 0;
|
|
|
|
/* if transaction was NOT selected, just return */
|
|
if ( txnid == NULL ) return( 0 );
|
|
|
|
/* if nothing was wrong so far, we can try to commit the transaction */
|
|
/* complain, if the commit fails */
|
|
if (( txn_do_abort == 0 ) && ( txn_commit( txnid )) != 0 ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_finish_transaction: transaction commit failed: aborted.\n",
|
|
0, 0, 0 );
|
|
txn_do_abort = 1;
|
|
}
|
|
|
|
/* if anything went wrong, we have to abort the transaction */
|
|
if ( txn_do_abort ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_finish_transaction: transaction aborted.\n",
|
|
0, 0, 0 );
|
|
(void) txn_abort( txnid );
|
|
rc = -1;
|
|
} else {
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"bdb2i_finish_transaction: transaction commited.\n",
|
|
0, 0, 0 );
|
|
}
|
|
|
|
/* XXX do NOT free the txnid memory !!! */
|
|
txnid = NULL;
|
|
txn_do_abort = 0;
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
/* set a checkpoint
|
|
either forced (during shutdown) or when logsize or time are exceeded
|
|
(is called by reader and writer, so protect txn_dirty)
|
|
*/
|
|
int
|
|
bdb2i_set_txn_checkpoint( DB_TXNMGR *txmgr, int forced )
|
|
{
|
|
int rc = 0;
|
|
|
|
/* set dirty mutex */
|
|
ldap_pvt_thread_mutex_lock( &txn_dirty_mutex );
|
|
|
|
if ( txn_dirty ) {
|
|
int rc;
|
|
u_int32_t logsize;
|
|
u_int32_t mins;
|
|
time_t now;
|
|
|
|
logsize = forced ? (u_int32_t) 0 : txn_max_pending_log;
|
|
mins = forced ? (u_int32_t) 0 : txn_max_pending_time;
|
|
|
|
ldap_pvt_thread_mutex_lock( ¤ttime_mutex );
|
|
time( ¤ttime );
|
|
now = currenttime;
|
|
ldap_pvt_thread_mutex_unlock( ¤ttime_mutex );
|
|
|
|
rc = txn_checkpoint( txmgr, logsize, mins );
|
|
|
|
/* if checkpointing was successful, reset txn_dirty */
|
|
if ( rc == 0 ) {
|
|
DB_TXN_STAT *statp = NULL;
|
|
|
|
/* check whether the checkpoint was actually written;
|
|
if so, unset the txn_dirty flag */
|
|
if (( rc = txn_stat( txmgr, &statp, ldbm_malloc )) == 0 ) {
|
|
|
|
if ( statp && ( statp->st_time_ckp >= now )) {
|
|
|
|
Debug( LDAP_DEBUG_TRACE,
|
|
"bdb2i_set_txn_checkpoint succeded.\n",
|
|
0, 0, 0 );
|
|
txn_dirty = 0;
|
|
|
|
}
|
|
|
|
if ( statp ) free( statp );
|
|
|
|
} else {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"bdb2i_set_txn_checkpoint: txn_stat failed: %d\n",
|
|
rc, 0, 0 );
|
|
}
|
|
} else {
|
|
Debug( LDAP_DEBUG_ANY, "bdb2i_set_txn_checkpoint failed: %d\n",
|
|
rc, 0, 0 );
|
|
}
|
|
}
|
|
|
|
/* release dirty mutex */
|
|
ldap_pvt_thread_mutex_unlock( &txn_dirty_mutex );
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|