]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb2/txn.c
Rename
[openldap] / servers / slapd / back-bdb2 / txn.c
1 /* txn.c - TP support functions of the bdb2 backend */
2
3 #include "txn.h"
4
5
6 int
7 bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
8 {
9         int             dbFile;
10         BDB2_TXN_FILES  **fileNodeH;
11
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++ ) {
16
17                 char fileName[MAXPATHLEN];
18
19                 *fileNodeH = (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
20                 if ( *fileNodeH == NULL ) {
21
22                         Debug( LDAP_DEBUG_ANY, "bdb2i_txn_head_init(): out of memory!\n",
23                                         0, 0, 0 );
24                         return( 1 );
25
26                 }
27
28                 sprintf( fileName, "%s%s", bdb2i_fixed_filenames[dbFile], BDB2_SUFFIX );
29                 (*fileNodeH)->dbc_name = ch_strdup( fileName );
30
31                 fileNodeH = &(*fileNodeH)->next;
32
33         }
34
35         /*  set defaults for checkpointing  */
36         head->txn_log  = BDB2_TXN_CHKP_MAX_LOG;
37         head->txn_time = BDB2_TXN_CHKP_MAX_TIME;
38
39         /*  initialize the txn_dirty_mutex  */
40         ldap_pvt_thread_mutex_init( &txn_dirty_mutex );
41
42         return 0;
43 }
44
45
46 static void
47 bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
48 {
49         struct stat st;
50         char        buf[MAXPATHLEN];
51
52         fileinfo->dbc_refcnt = 1;
53
54         sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
55                                         fileinfo->dbc_name );
56         if ( stat( buf, &st ) == 0 ) {
57                 fileinfo->dbc_blksize = st.st_blksize;
58         } else {
59                 fileinfo->dbc_blksize = DEFAULT_BLOCKSIZE;
60         }
61
62         fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) -
63                         ID_BLOCK_IDS_OFFSET;
64         fileinfo->dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
65                 fileinfo->dbc_maxids ) + 1;
66
67 }
68
69
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
77 */
78 void
79 bdb2i_txn_attr_config(
80         struct ldbminfo  *li,
81         char             *attr,
82         int              open )
83 {
84         BDB2_TXN_HEAD  *head = &li->li_txn_head;
85
86         /*  the "attribute" 'default' is special  */
87         if ( strcasecmp( attr, "default" )) {
88
89                 /*  create a new index file node, if the index is not known  already  */
90                 BDB2_TXN_FILES  **fileNodeH;
91                 char            fileName[MAXPATHLEN];
92
93                 sprintf( fileName, "%s%s", attr,  BDB2_SUFFIX );
94
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 ) {
100
101                 }
102
103                 /*  unless we have that attribute already...  */
104                 if ( *fileNodeH == NULL ) {
105                         BDB2_TXN_FILES *p;
106
107                         Debug( LDAP_DEBUG_TRACE,
108                                         "bdb2i_txn_attr_config(): adding node for \"%s\"\n",
109                                         fileName, 0, 0 );
110
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 ) {
114
115                                 Debug( LDAP_DEBUG_ANY,
116                                                 "bdb2i_txn_attr_config(): out of memory -- FATAL.\n",
117                                                 0, 0, 0 );
118
119                                 /*  during configuration (no files are opened)
120                                         we can just exit, otherwise we kill ourself and
121                                         hope to shutdown cleanly...  */
122                                 if ( open ) {
123                                         pthread_kill( pthread_self(), LDAP_SIGUSR1 );
124                                 } else {
125                                         exit( 1 );
126                                 }
127                         }
128
129                         p->dbc_name = ch_strdup( fileName );
130
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" )) {
134
135                                 /*  re-use filename to get the complete path  */
136                                 sprintf( fileName, "%s%s%s",
137                                                         li->li_directory, DEFAULT_DIRSEP, p->dbc_name );
138
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 );
141
142                                 /*  if the files could not be opened, something is wrong;
143                                         complain  */
144                                 if ( p->dbc_db == NULL ) {
145
146                                         Debug( LDAP_DEBUG_ANY,
147                                 "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
148                                                 p->dbc_name, 0, 0 );
149                                         pthread_kill( pthread_self(), LDAP_SIGUSR1 );
150
151                                 }
152
153                                 bdb2i_init_db_file_cache( li, p );
154
155                                 Debug( LDAP_DEBUG_TRACE,
156                                         "bdb2i_txn_attr_config(): NEW INDEX FILE \"%s\"\n",
157                                         p->dbc_name, 0, 0 );
158
159                         }
160                 }
161
162         } else {  /*  it is "attribute" 'default'  */
163
164                 head->withDefIDX = BDB2_WITH_DEF_IDX;
165
166         }
167 }
168
169
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)
172 */
173 static int
174 bdb2i_open_nextid( BackendDB *be )
175 {
176         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
177         BDB2_TXN_HEAD   *head = &li->li_txn_head;
178         LDBM            db = NULL;
179         DB_INFO                 dbinfo;
180         DB_ENV                  *dbenv = get_dbenv( be );
181         char            fileName[MAXPATHLEN];
182
183         sprintf( fileName, "%s%s%s",
184                                 li->li_directory, DEFAULT_DIRSEP, NEXTID_NAME );
185
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;
190
191         (void) db_open( fileName, DB_RECNO, DB_CREATE | DB_THREAD,
192                                         li->li_mode, dbenv, &dbinfo, &db );
193
194         if ( db == NULL ) {
195
196                         Debug( LDAP_DEBUG_ANY,
197                                 "bdb2i_open_nextid: could not open \"%s\"\n",
198                                 NEXTID_NAME, 0, 0 );
199                         return( -1 );
200
201         }
202
203         /*  the file is open for read/write  */
204         head->nextidFile = db;
205
206         return( 0 );
207 }
208
209
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
215 */
216 int
217 bdb2i_txn_open_files( BackendDB *be )
218 {
219         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
220         BDB2_TXN_HEAD   *head = &li->li_txn_head;
221         BDB2_TXN_FILES  *dbFile;
222         int             rc;
223
224         for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
225                 char   fileName[MAXPATHLEN];
226
227                 sprintf( fileName, "%s%s%s",
228                                         li->li_directory, DEFAULT_DIRSEP, dbFile->dbc_name );
229
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 );
232
233                 /*  if the files could not be opened, something is wrong; complain  */
234                 if ( dbFile->dbc_db == NULL ) {
235
236                         Debug( LDAP_DEBUG_ANY,
237                                 "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
238                                 dbFile->dbc_name, 0, 0 );
239                         return( -1 );
240
241                 }
242
243                 /*  initialize the file info  */
244                 bdb2i_init_db_file_cache( li, dbFile );
245
246                 Debug( LDAP_DEBUG_TRACE, "bdb2i_txn_open_files(): OPEN INDEX \"%s\"\n",
247                                 dbFile->dbc_name, 0, 0 );
248
249         }
250
251         rc = bdb2i_open_nextid( be );
252
253         txn_max_pending_log  = head->txn_log;
254         txn_max_pending_time = head->txn_time;
255
256         return rc;
257 }
258
259
260 /*  close all DB files during shutdown of the backend  */
261 void
262 bdb2i_txn_close_files( BackendDB *be )
263 {
264         struct ldbminfo  *li = (struct ldbminfo *) be->be_private;
265         BDB2_TXN_HEAD    *head = &li->li_txn_head;
266         BDB2_TXN_FILES   *dbFile;
267
268         for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
269
270                 ldbm_close( dbFile->dbc_db );
271
272         }
273
274         ldbm_close( head->nextidFile );
275
276 }
277
278
279 /*  get the db_cache structure associated with a specified
280         DB file (replaces the on-the-fly opening of files in cache_open()
281 */
282 BDB2_TXN_FILES *
283 bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
284 {
285         BDB2_TXN_HEAD  *head = &li->li_txn_head;
286         BDB2_TXN_FILES *dbFile;
287         int            dbFileNum;
288
289         Debug( LDAP_DEBUG_TRACE, "bdb2i_get_db_file_cache(): looking for file %s\n",
290                         name, 0, 0 );
291
292         for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
293
294                 /*  we've got it  */
295                 if ( !strcasecmp( dbFile->dbc_name, name )) return( dbFile );
296
297         }
298
299         Debug( LDAP_DEBUG_ANY,
300                 "bdb2i_get_db_file_cache(): UPS, could't find \"%s\" \n", name, 0, 0 );
301
302         /*  ups, we couldn't find the file  */
303         return( NULL );
304
305 }
306
307
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  */
311 int
312 bdb2i_check_additional_attr_index( struct ldbminfo *li )
313 {
314         DIR            *datadir;
315         struct dirent  *file;
316
317         if ( ( datadir = opendir( li->li_directory ) ) == NULL ) {
318
319                 Debug( LDAP_DEBUG_ANY,
320         "bdb2i_check_additional_attr_index(): ERROR while opening datadir: %s\n",
321                                 strerror( errno ), 0, 0 );
322                 return( 1 );
323
324         }
325
326         for ( file = readdir( datadir ); file; file = readdir( datadir )) {
327                 char  filename[MAXPATHLEN];
328                 int   namelen;
329
330                 strcpy( filename, file->d_name );
331                 namelen = strlen( filename );
332
333                 if ( namelen > strlen( BDB2_SUFFIX )) {
334
335                         if ( !strcasecmp( filename + namelen - strlen( BDB2_SUFFIX ),
336                                                         BDB2_SUFFIX )) {
337
338                                 *(filename + namelen - strlen( BDB2_SUFFIX )) = '\0';
339                                 bdb2i_txn_attr_config( li, filename, 0 );
340
341                                 Debug( LDAP_DEBUG_TRACE, "INDEX FILE: %s\n", filename, 0, 0 );
342
343                         }
344
345                 }
346
347         }
348
349         closedir( datadir );
350
351         return 0;
352 }
353
354
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  */
359 void
360 bdb2i_check_default_attr_index_add( struct ldbminfo *li, Entry *e )
361 {
362         BDB2_TXN_HEAD  *head = &li->li_txn_head;
363
364         if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
365                 Attribute   *ap;
366
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 );
370                 }
371         }
372 }
373
374
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  */
379 void
380 bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
381 {
382         BDB2_TXN_HEAD  *head = &li->li_txn_head;
383
384         if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
385                 LDAPModList *ml;
386                 char  *default_attrs[] = { "modifytimestamp", "modifiersname", NULL };
387                 int   attr;
388
389                 for ( ml = modlist; ml != NULL; ml = ml->ml_next ) {
390                         LDAPMod *mod = &ml->ml_mod;
391
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 );
395                 }
396
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 );
400                 }
401         }
402 }
403
404
405 /*  get the next ID from the NEXTID file  */
406 ID
407 bdb2i_get_nextid( BackendDB *be )
408 {
409         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
410         BDB2_TXN_HEAD   *head = &li->li_txn_head;
411         ID                              id;
412         Datum                   key;
413         Datum                   data;
414         db_recno_t              rec = NEXTID_RECNO;
415
416         ldbm_datum_init( key );
417         ldbm_datum_init( data );
418
419         key.data = &rec;
420         key.size = sizeof( rec );
421
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",
426                         NEXTID_NAME, 0, 0 );
427                 return NOID;
428         }
429
430         id = atol( data.data );
431         ldbm_datum_free( head->nextidFile, data );
432
433         if ( id < 1 ) {
434                 Debug( LDAP_DEBUG_ANY,
435                         "next_id_read %ld: return non-positive integer\n",
436                         id, 0, 0 );
437                 return NOID;
438         }
439
440         return( id );
441 }
442
443
444 int
445 bdb2i_put_nextid( BackendDB *be, ID id )
446 {
447         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
448         BDB2_TXN_HEAD   *head = &li->li_txn_head;
449         int                             rc, flags;
450         Datum                   key;
451         Datum                   data;
452         db_recno_t              rec = NEXTID_RECNO;
453         char                    buf[20];
454
455         sprintf( buf, "%ld\n", id );
456
457         ldbm_datum_init( key );
458         ldbm_datum_init( data );
459
460         key.data = &rec;
461         key.size = sizeof( rec );
462
463         data.data = &buf;
464         data.size = sizeof( buf );
465
466         flags = LDBM_REPLACE;
467         if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
468
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",
471                         id, rc, 0 );
472                 return( -1 );
473         }
474
475         return( rc );
476 }
477
478
479 /*  BDB2 backend-private functions of ldbm_store and ldbm_delete  */
480 int
481 bdb2i_db_store( LDBM ldbm, Datum key, Datum data, int flags )
482 {
483         int     rc;
484
485         rc = (*ldbm->put)( ldbm, txnid, &key, &data, flags & ~LDBM_SYNC );
486         rc = (-1 ) * rc;
487
488         if ( txnid != NULL ) {
489
490                 /*  if the store was OK, set the dirty flag,
491                         otherwise set the abort flag  */
492                 if ( rc == 0 ) {
493
494                         txn_dirty = 1;
495
496                 } else {
497
498                         Debug( LDAP_DEBUG_ANY,
499                                 "bdb2i_db_store: transaction failed: aborted.\n",
500                                 0, 0, 0 );
501                         txn_do_abort = 1;
502
503                 }
504         }
505
506         return( rc );
507 }
508
509
510 int
511 bdb2i_db_delete( LDBM ldbm, Datum key )
512 {
513         int     rc;
514
515         rc = (*ldbm->del)( ldbm, txnid, &key, 0 );
516         rc = (-1 ) * rc;
517
518         if ( txnid != NULL ) {
519
520                 /*  if the delete was OK, set the dirty flag,
521                         otherwise set the abort flag  */
522                 if ( rc == 0 ) {
523
524                         txn_dirty = 1;
525
526                 } else {
527
528                         Debug( LDAP_DEBUG_ANY,
529                                 "bdb2i_db_delete: transaction failed: aborted.\n",
530                                 0, 0, 0 );
531                         txn_do_abort = 1;
532
533                 }
534         }
535
536         return( rc );
537 }
538
539
540 Datum
541 bdb2i_db_fetch( LDBM ldbm, Datum key )
542 {
543         Datum   data;
544         int     rc;
545
546         ldbm_datum_init( data );
547         data.flags = DB_DBT_MALLOC;
548
549         if ( (rc = (*ldbm->get)( ldbm, txnid, &key, &data, 0 )) != 0 ) {
550                 if (( txnid != NULL ) && ( rc != DB_NOTFOUND )) {
551
552                         Debug( LDAP_DEBUG_ANY,
553                                 "bdb2i_db_fetch: transaction failed: aborted.\n",
554                                 0, 0, 0 );
555                         txn_do_abort = 1;
556
557                 }
558                 if ( data.dptr ) free( data.dptr );
559                 data.dptr = NULL;
560                 data.dsize = 0;
561         }
562
563         return( data );
564 }
565
566
567 Datum
568 bdb2i_db_firstkey( LDBM ldbm, DBC **dbch )
569 {
570         Datum   key, data;
571         int             rc;
572         DBC             *dbci;
573
574         ldbm_datum_init( key );
575         ldbm_datum_init( data );
576
577         key.flags = data.flags = DB_DBT_MALLOC;
578
579 #if defined( DB_VERSION_MAJOR ) && defined( DB_VERSION_MINOR ) && \
580    DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6
581
582         if ( (*ldbm->cursor)( ldbm, txnid, &dbci ))
583
584 #else
585
586         if ( (*ldbm->cursor)( ldbm, txnid, &dbci, 0 ))
587
588 #endif
589         {
590                 if ( txnid != NULL ) {
591
592                         Debug( LDAP_DEBUG_ANY,
593                                 "bdb2i_db_firstkey: transaction failed: aborted.\n",
594                                 0, 0, 0 );
595                         txn_do_abort = 1;
596
597                 }
598                 return( key );
599         } else {
600                 *dbch = dbci;
601                 if ( (*dbci->c_get)( dbci, &key, &data, DB_NEXT ) == 0 ) {
602                         if ( data.dptr ) free( data.dptr );
603                 } else {
604                         if ( txnid != NULL ) {
605
606                                 Debug( LDAP_DEBUG_ANY,
607                                         "bdb2i_db_firstkey: transaction failed: aborted.\n",
608                                         0, 0, 0 );
609                                 txn_do_abort = 1;
610
611                         }
612                         if ( key.dptr ) free( key.dptr );
613                         key.dptr = NULL;
614                         key.dsize = 0;
615                 }
616         }
617
618         return( key );
619 }
620
621
622 Datum
623 bdb2i_db_nextkey( LDBM ldbm, Datum key, DBC *dbcp )
624 {
625         Datum   data;
626         int             rc;
627         void    *oldKey = key.dptr;
628
629         ldbm_datum_init( data );
630         data.flags = DB_DBT_MALLOC;
631
632         if ( (*dbcp->c_get)( dbcp, &key, &data, DB_NEXT ) == 0 ) {
633                 if ( data.dptr ) free( data.dptr );
634         } else {
635                 if ( txnid != NULL ) {
636
637                         Debug( LDAP_DEBUG_ANY,
638                                 "bdb2i_db_nextkey: transaction failed: aborted.\n",
639                                 0, 0, 0 );
640                         txn_do_abort = 1;
641
642                 }
643                 key.dptr = NULL;
644                 key.dsize = 0;
645         }
646
647         if ( oldKey ) free( oldKey );
648
649         return( key );
650 }
651
652
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  */
656
657 /*  initialize a new transaction  */
658 int
659 bdb2i_start_transction( DB_TXNMGR *txmgr )
660 {
661         int             rc;
662
663         txnid        = NULL;
664         txn_do_abort = 0;
665
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 );
669
670                 if ( txnid != NULL )
671                         (void) txn_abort( txnid );
672                 return( -1 );
673         }
674
675         Debug( LDAP_DEBUG_TRACE,
676                         "bdb2i_start_transaction: transaction started.\n",
677                         0, 0, 0 );
678
679         return( 0 );
680 }
681
682
683 /*  finish the transaction  */
684 int
685 bdb2i_finish_transaction()
686 {
687         int             rc = 0;
688
689         /*  if transaction was NOT selected, just return  */
690         if ( txnid == NULL ) return( 0 );
691
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",
697                         0, 0, 0 );
698                 txn_do_abort = 1;
699         }
700
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",
705                         0, 0, 0 );
706                 (void) txn_abort( txnid );
707                 rc = -1;
708         } else {
709                 Debug( LDAP_DEBUG_TRACE,
710                         "bdb2i_finish_transaction: transaction commited.\n",
711                         0, 0, 0 );
712         }
713
714         /*  XXX do NOT free the txnid memory !!!  */
715         txnid        = NULL;
716         txn_do_abort = 0;
717
718         return( rc );
719 }
720
721
722 /*  set a checkpoint
723         either forced (during shutdown) or when logsize or time are exceeded
724         (is called by reader and writer, so protect txn_dirty)
725 */
726 int
727 bdb2i_set_txn_checkpoint( DB_TXNMGR *txmgr, int forced )
728 {
729         int   rc = 0;
730
731         /*  set dirty mutex  */
732         ldap_pvt_thread_mutex_lock( &txn_dirty_mutex );
733
734         if ( txn_dirty ) {
735                 int  rc;
736                 u_int32_t   logsize;
737                 u_int32_t   mins;
738                 time_t      now;
739
740                 logsize = forced ? (u_int32_t) 0 : txn_max_pending_log;
741                 mins    = forced ? (u_int32_t) 0 : txn_max_pending_time;
742
743                 ldap_pvt_thread_mutex_lock( &currenttime_mutex );
744                 time( &currenttime );
745                 now = currenttime;
746                 ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
747
748                 rc = txn_checkpoint( txmgr, logsize, mins );
749
750                 /*  if checkpointing was successful, reset txn_dirty  */
751                 if ( rc == 0 ) {
752                         DB_TXN_STAT  *statp = NULL;
753
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 ) {
757
758                                 if ( statp && ( statp->st_time_ckp >= now )) {
759
760                                         Debug( LDAP_DEBUG_TRACE,
761                                                 "bdb2i_set_txn_checkpoint succeded.\n",
762                                                 0, 0, 0 );
763                                         txn_dirty = 0;
764
765                                 }
766
767                                 if ( statp ) free( statp );
768
769                         } else {
770                                 Debug( LDAP_DEBUG_ANY,
771                                                 "bdb2i_set_txn_checkpoint: txn_stat failed: %d\n",
772                                                 rc, 0, 0 );
773                         }
774                 } else {
775                         Debug( LDAP_DEBUG_ANY, "bdb2i_set_txn_checkpoint failed: %d\n",
776                                         rc, 0, 0 );
777                 }
778         }
779
780         /*  release dirty mutex  */
781         ldap_pvt_thread_mutex_unlock( &txn_dirty_mutex );
782
783         return( rc );
784 }
785
786