]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/ldbm.c
6a8dbc81da77f38ce731896a7a2d4fc50525b174
[openldap] / servers / slapd / back-ldbm / ldbm.c
1 /* ldbm.c - ldap dbm compatibility routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2007 The OpenLDAP Foundation.
6  * Portions Copyright 1998-2003 Kurt D. Zeilenga.
7  * Portions Copyright 1998-2001 Net Boolean Incorporated.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was originally developed by the University of Michigan
20  * (as part of U-MICH LDAP).  Additional significant contributors
21  * include:
22  *   Gary Williams
23  *   Howard Chu
24  *   Juan Gomez
25  *   Kurt D. Zeilenga
26  *   Kurt Spanier
27  *   Mark Whitehouse
28  *   Randy Kundee
29  */
30
31 #include "portable.h"
32
33 #ifdef SLAPD_LDBM
34
35 #include <stdio.h>
36
37 #include <ac/stdlib.h>
38 #include <ac/string.h>
39 #include <ac/errno.h>
40
41 #include "ldbm.h"
42 #include "ldap_pvt_thread.h"
43
44 void
45 ldbm_datum_free( LDBM ldbm, Datum data )
46 {
47         if ( data.dptr ) {
48                 free( data.dptr );
49                 memset( &data, '\0', sizeof( Datum ));
50                 data.dptr = NULL;
51         }
52 }
53
54 Datum
55 ldbm_datum_dup( LDBM ldbm, Datum data )
56 {
57         Datum   dup;
58
59         ldbm_datum_init( dup );
60
61         if ( data.dsize == 0 ) {
62                 dup.dsize = 0;
63                 dup.dptr = NULL;
64
65                 return( dup );
66         }
67         dup.dsize = data.dsize;
68
69         if ( (dup.dptr = (char *) malloc( data.dsize )) != NULL ) {
70                 AC_MEMCPY( dup.dptr, data.dptr, data.dsize );
71         }
72
73         return( dup );
74 }
75
76 static int ldbm_initialized = 0;
77
78 #if defined( USE_BERKELEY_CDB )
79         /* not currently supported */
80 #define LDBM_RWLOCK_INIT  ((void) 0)
81 #define LDBM_RWLOCK_DESTROY ((void) 0)
82 #define LDBM_WLOCK              ((void) 0)
83 #define LDBM_WUNLOCK    ((void) 0)
84 #define LDBM_RLOCK              ((void) 0)
85 #define LDBM_RUNLOCK    ((void) 0)
86
87 #elif defined( HAVE_BERKELEY_DB_THREAD )
88 static ldap_pvt_thread_rdwr_t ldbm_big_rdwr;
89 #define LDBM_RWLOCK_INIT (ldap_pvt_thread_rdwr_init( &ldbm_big_rdwr ))
90 #define LDBM_RWLOCK_DESTROY (ldap_pvt_thread_rdwr_destroy( &ldbm_big_rdwr ))
91 #define LDBM_WLOCK              (ldap_pvt_thread_rdwr_wlock(&ldbm_big_rdwr))
92 #define LDBM_WUNLOCK    (ldap_pvt_thread_rdwr_wunlock(&ldbm_big_rdwr))
93 #define LDBM_RLOCK              (ldap_pvt_thread_rdwr_rlock(&ldbm_big_rdwr))
94 #define LDBM_RUNLOCK    (ldap_pvt_thread_rdwr_runlock(&ldbm_big_rdwr))
95
96 #else
97 static ldap_pvt_thread_mutex_t ldbm_big_mutex;
98 #define LDBM_RWLOCK_INIT (ldap_pvt_thread_mutex_init( &ldbm_big_mutex ))
99 #define LDBM_RWLOCK_DESTROY (ldap_pvt_thread_mutex_destroy( &ldbm_big_mutex ))
100 #define LDBM_WLOCK              (ldap_pvt_thread_mutex_lock(&ldbm_big_mutex))
101 #define LDBM_WUNLOCK    (ldap_pvt_thread_mutex_unlock(&ldbm_big_mutex))
102 #define LDBM_RLOCK              LDBM_WLOCK
103 #define LDBM_RUNLOCK    LDBM_WUNLOCK
104 #endif
105
106 #if !defined( HAVE_BERKELEY_DB ) || (DB_VERSION_MAJOR < 3)
107         /*  a dbEnv for BERKELEYv2  */
108 DB_ENV *ldbm_Env = NULL;        /* real or fake, depending on db and version */
109 #endif
110
111 /* Let's make the version comparisons a little easier... */
112 #undef DB_VERSION_X
113 #ifdef HAVE_BERKELEY_DB
114 #define DB_VERSION_X    ((DB_VERSION_MAJOR<<16)|(DB_VERSION_MINOR<<8)|DB_VERSION_PATCH)
115 #endif
116
117 /*******************************************************************
118  *                                                                 *
119  *  Create some special functions to initialize Berkeley DB for    *
120  *  versions greater than 2.                                       *
121  *                                                                 *
122  *******************************************************************/
123 #if defined( HAVE_BERKELEY_DB ) && (DB_VERSION_MAJOR >= 2)
124
125 void *
126 ldbm_malloc( size_t size )
127 {
128         /* likely should use ber_mem* routines */
129         return( calloc( 1, size ) );
130 }
131
132 #ifdef LDAP_SYSLOG
133 #include <ac/syslog.h>
134 #endif
135
136 static void
137 #if DB_VERSION_X < 0x040300
138 ldbm_db_errcall( const char *prefix, char *message )
139 #else
140 ldbm_db_errcall( const DB_ENV *env, const char *prefix, const char *message )
141 #endif
142 {
143 #ifdef LDAP_SYSLOG
144         syslog( LOG_INFO, "ldbm: %s %s", prefix, message );
145 #endif
146 }
147
148 int ldbm_initialize( const char* home )
149 {
150 #if DB_VERSION_MAJOR < 3
151         int     err;
152         u_int32_t       envFlags;
153 #endif
154
155         if(ldbm_initialized++) return 1;
156
157         {
158                 char *version;
159 #ifdef HAVE_EBCDIC
160                 char v2[1024];
161 #endif
162                 int major, minor, patch;
163                 version = db_version( &major, &minor, &patch );
164 #ifdef HAVE_EBCDIC
165                 strcpy( v2, version );
166                 __etoa( v2 );
167                 version = v2;
168 #endif
169
170                 if( major != DB_VERSION_MAJOR ||
171                         minor < DB_VERSION_MINOR )
172                 {
173 #ifdef LDAP_SYSLOG
174                         syslog( LOG_INFO,
175                                 "ldbm_initialize(): version mismatch\nexpected: %s\ngot: %s\n",
176                                 DB_VERSION_STRING, version );
177 #endif
178                         return 1;
179                 }
180         }
181
182 #if DB_VERSION_MAJOR < 3
183         ldbm_Env = calloc( 1, sizeof( DB_ENV ));
184
185         if( ldbm_Env == NULL ) return 1;
186
187         ldbm_Env->db_errcall    = ldbm_db_errcall;
188         ldbm_Env->db_errpfx             = "==>";
189
190         envFlags = DB_CREATE | DB_USE_ENVIRON;
191
192         /* add optional flags */
193 #ifdef DB_PRIVATE
194         envFlags |= DB_PRIVATE;
195 #endif
196 #ifdef HAVE_BERKELEY_DB_THREAD
197         envFlags |= DB_THREAD; 
198 #endif
199
200         err = db_appinit( home, NULL, ldbm_Env, envFlags );
201
202         if ( err ) {
203 #ifdef LDAP_SYSLOG
204                 syslog( LOG_INFO, "ldbm_initialize(): "
205                         "FATAL error (%d) in db_appinit()\n", err );
206 #endif
207                 return( 1 );
208         }
209 #endif
210
211         LDBM_RWLOCK_INIT;
212
213         return 0;
214 }
215
216 int ldbm_shutdown( void )
217 {
218         if( !ldbm_initialized ) return 1;
219
220 #if DB_VERSION_MAJOR < 3
221         db_appexit( ldbm_Env );
222 #endif
223
224         LDBM_RWLOCK_DESTROY;
225         return 0;
226 }
227
228 #else  /* some DB other than Berkeley V2 or greater */
229
230 int ldbm_initialize( const char * home )
231 {
232         if(ldbm_initialized++) return 1;
233
234         LDBM_RWLOCK_INIT;
235
236         return 0;
237 }
238
239 int ldbm_shutdown( void )
240 {
241         if( !ldbm_initialized ) return 1;
242
243         LDBM_RWLOCK_DESTROY;
244
245         return 0;
246 }
247
248 #endif /* HAVE_BERKELEY_DB */
249
250 #if defined( HAVE_BERKELEY_DB ) && (DB_VERSION_MAJOR >= 3)
251
252 DB_ENV *ldbm_initialize_env(const char *home, int dbcachesize, int *envdirok)
253 {
254         DB_ENV *env = NULL;    
255         int     err;
256         u_int32_t       envFlags;
257 #ifdef HAVE_EBCDIC
258         char n2[2048];
259 #endif
260
261         err = db_env_create( &env, 0 );
262
263         if ( err ) {
264 #ifdef LDAP_SYSLOG
265                 syslog( LOG_INFO, "ldbm_initialize_env(): "
266                         "FATAL error in db_env_create() : %s (%d)\n",
267                         db_strerror( err ), err );
268 #endif
269                 return NULL;
270         }
271
272 #if DB_VERSION_X >= 0x030300
273         /* This interface appeared in 3.3 */
274         env->set_alloc( env, ldbm_malloc, NULL, NULL );
275 #endif
276
277         env->set_errcall( env, ldbm_db_errcall );
278         env->set_errpfx( env, "==>" );
279         if (dbcachesize) {
280                 env->set_cachesize( env, 0, dbcachesize, 0 );
281         }
282
283         envFlags = DB_CREATE | DB_INIT_MPOOL | DB_USE_ENVIRON;
284 #ifdef DB_PRIVATE
285         envFlags |= DB_PRIVATE;
286 #endif
287 #ifdef DB_MPOOL_PRIVATE
288         envFlags |= DB_MPOOL_PRIVATE;
289 #endif
290 #ifdef HAVE_BERKELEY_DB_THREAD
291         envFlags |= DB_THREAD;
292 #endif
293
294 #ifdef HAVE_EBCDIC
295         strncpy(n2, home, sizeof(n2)-1);
296         n2[sizeof(n2)-1] = '\0';
297         __atoe(n2);
298         home = n2;
299 #endif
300 #if DB_VERSION_X >= 0x030100
301         err = (env->open)( env, home, envFlags, 0 );
302 #else
303         /* 3.0.x requires an extra argument */
304         err = (env->open)( env, home, NULL, envFlags, 0 );
305 #endif
306
307         if ( err != 0 ) {
308 #ifdef LDAP_SYSLOG
309                 syslog( LOG_INFO, "ldbm_initialize_env(): "
310                         "FATAL error in dbEnv->open() : %s (%d)\n",
311                         db_strerror( err ), err );
312 #endif
313                 env->close( env, 0 );
314                 return NULL;
315         }
316
317         *envdirok = 1;
318         return env;
319 }
320
321 void ldbm_shutdown_env(DB_ENV *env)
322 {
323         env->close( env, 0 );
324 }
325
326 #else
327
328 DB_ENV *ldbm_initialize_env(const char *home, int dbcachesize, int *envdirok)
329 {
330         return ldbm_Env;
331 }
332
333 void ldbm_shutdown_env(DB_ENV *env)
334 {
335 }
336
337 #endif
338
339 #if defined( LDBM_USE_DBHASH ) || defined( LDBM_USE_DBBTREE )
340
341 /*****************************************************************
342  *                                                               *
343  * use berkeley db hash or btree package                         *
344  *                                                               *
345  *****************************************************************/
346
347 LDBM
348 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
349 {
350         LDBM            ret = NULL;
351 #ifdef HAVE_EBCDIC
352         char n2[2048];
353 #endif
354
355 #if DB_VERSION_MAJOR >= 3
356         int err;
357
358         LDBM_WLOCK;
359
360         err = db_create( &ret, env, 0 );
361         if ( err != 0 ) {
362                 (void)ret->close(ret, 0);
363                 LDBM_WUNLOCK;
364
365                 return NULL;
366         }
367
368 #if DB_VERSION_X < 0x030300
369         ret->set_malloc( ret, ldbm_malloc );
370 #endif
371
372         ret->set_pagesize( ret, DEFAULT_DB_PAGE_SIZE );
373
374         /* likely should use ber_mem* routines */
375
376 #ifdef HAVE_EBCDIC
377         strncpy(n2, name, sizeof(n2)-1);
378         n2[sizeof(n2)-1] = '\0';
379         __atoe(n2);
380         name = n2;
381 #endif
382 #if DB_VERSION_X >= 0x040111
383         err = (ret->open)( ret, NULL, name, NULL, DB_TYPE, rw, mode);
384 #else
385         err = (ret->open)( ret, name, NULL, DB_TYPE, rw, mode);
386 #endif
387
388         if ( err != 0 ) {
389                 int tmp = errno;
390                 (void)ret->close(ret, 0);
391                 errno = tmp;
392
393                 LDBM_WUNLOCK;
394                 return NULL;
395         }
396
397         LDBM_WUNLOCK;
398  
399 #elif DB_VERSION_MAJOR >= 2
400         DB_INFO dbinfo;
401
402         memset( &dbinfo, '\0', sizeof( dbinfo ));
403
404 #if     DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR == 4
405         /*
406          * BerkeleyDB 2.4 do not allow db_cachesize
407          * to be specified if an DB_ENV is.
408          */
409 #else
410         /* set db_cachesize of MPOOL is NOT being used. */
411         if (( ldbm_Env == NULL ) || ( ldbm_Env->mp_info == NULL )) {
412                 dbinfo.db_cachesize = dbcachesize;
413         }
414 #endif
415
416         dbinfo.db_pagesize      = DEFAULT_DB_PAGE_SIZE;
417         dbinfo.db_malloc        = ldbm_malloc;
418
419         LDBM_WLOCK;
420         (void) db_open( name, DB_TYPE, rw, mode, ldbm_Env, &dbinfo, &ret );
421         LDBM_WUNLOCK;
422
423 #else
424         void            *info;
425         BTREEINFO       binfo;
426         HASHINFO        hinfo;
427
428         if ( DB_TYPE == DB_HASH ) {
429                 memset( (char *) &hinfo, '\0', sizeof(hinfo) );
430                 hinfo.cachesize = dbcachesize;
431                 info = &hinfo;
432         } else if ( DB_TYPE == DB_BTREE ) {
433                 memset( (char *) &binfo, '\0', sizeof(binfo) );
434                 binfo.cachesize = dbcachesize;
435                 info = &binfo;
436         } else {
437                 info = NULL;
438         }
439
440         LDBM_WLOCK;
441         ret = dbopen( name, rw, mode, DB_TYPE, info );
442         LDBM_WUNLOCK;
443 #endif
444
445         return ret;
446 }
447
448 void
449 ldbm_close( LDBM ldbm )
450 {
451         LDBM_WLOCK;
452 #if DB_VERSION_MAJOR >= 2
453         ldbm->close( ldbm, 0 );
454 #else
455         ldbm->close( ldbm );
456 #endif
457         LDBM_WUNLOCK;
458 }
459
460 void
461 ldbm_sync( LDBM ldbm )
462 {
463         LDBM_WLOCK;
464         (*ldbm->sync)( ldbm, 0 );
465         LDBM_WUNLOCK;
466 }
467
468 Datum
469 ldbm_fetch( LDBM ldbm, Datum key )
470 {
471         Datum   data;
472         int     rc;
473
474         LDBM_RLOCK;
475
476 #if DB_VERSION_MAJOR >= 2
477         ldbm_datum_init( data );
478
479         data.flags = DB_DBT_MALLOC;
480
481         if ( (rc = ldbm->get( ldbm, NULL, &key, &data, 0 )) != 0 ) {
482                 ldbm_datum_free( ldbm, data );
483                 data.dptr = NULL;
484                 data.dsize = 0;
485         }
486 #else
487         if ( (rc = ldbm->get( ldbm, &key, &data, 0 )) == 0 ) {
488                 /* Berkeley DB 1.85 don't malloc the data for us */
489                 /* duplicate it for to ensure reentrancy */
490                 data = ldbm_datum_dup( ldbm, data );
491         } else {
492                 data.dptr = NULL;
493                 data.dsize = 0;
494         }
495 #endif
496
497         LDBM_RUNLOCK;
498
499         return( data );
500 }
501
502 int
503 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
504 {
505         int     rc;
506
507         LDBM_WLOCK;
508
509 #if DB_VERSION_MAJOR >= 2
510         rc = ldbm->put( ldbm, NULL, &key, &data, flags & ~LDBM_SYNC );
511         rc = (-1) * rc;
512 #else
513         rc = ldbm->put( ldbm, &key, &data, flags & ~LDBM_SYNC );
514 #endif
515
516         if ( flags & LDBM_SYNC )
517                 ldbm->sync( ldbm, 0 );
518
519         LDBM_WUNLOCK;
520
521         return( rc );
522 }
523
524 int
525 ldbm_delete( LDBM ldbm, Datum key )
526 {
527         int     rc;
528
529         LDBM_WLOCK;
530
531 #if DB_VERSION_MAJOR >= 2
532         rc = ldbm->del( ldbm, NULL, &key, 0 );
533         rc = (-1) * rc;
534 #else
535         rc = ldbm->del( ldbm, &key, 0 );
536 #endif
537         ldbm->sync( ldbm, 0 );
538
539         LDBM_WUNLOCK;
540
541         return( rc );
542 }
543
544 Datum
545 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbch )
546 {
547         Datum   key, data;
548         int     rc;
549
550 #if DB_VERSION_MAJOR >= 2
551         LDBMCursor  *dbci;
552
553         ldbm_datum_init( key );
554         ldbm_datum_init( data );
555
556         key.flags = data.flags = DB_DBT_MALLOC;
557
558         LDBM_RLOCK;
559
560         /* acquire a cursor for the DB */
561 # if DB_VERSION_X >= 0x020600
562         rc = ldbm->cursor( ldbm, NULL, &dbci, 0 );
563 # else
564         rc = ldbm->cursor( ldbm, NULL, &dbci );
565 # endif
566
567         if( rc ) {
568                 key.dptr = NULL;
569         } else {
570                 *dbch = dbci;
571                 if ( dbci->c_get( dbci, &key, &data, DB_NEXT ) == 0 ) {
572                         ldbm_datum_free( ldbm, data );
573                 } else {
574                         key.dptr = NULL;
575                         key.dsize = 0;
576                 }
577         }
578
579         LDBM_RUNLOCK;
580
581 #else
582         LDBM_RLOCK;
583
584         rc = ldbm->seq( ldbm, &key, &data, R_FIRST );
585
586         if ( rc == 0 ) {
587                 key = ldbm_datum_dup( ldbm, key );
588         } else {
589                 key.dptr = NULL;
590                 key.dsize = 0;
591         }
592
593         LDBM_RUNLOCK;
594 #endif
595
596         return( key );
597 }
598
599 Datum
600 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
601 {
602         int     rc;
603         Datum   data;
604
605         LDBM_RLOCK;
606
607 #if DB_VERSION_MAJOR >= 2
608         ldbm_datum_init( data );
609
610         ldbm_datum_free( ldbm, key );
611         key.flags = data.flags = DB_DBT_MALLOC;
612
613         rc = dbcp->c_get( dbcp, &key, &data, DB_NEXT );
614         if ( rc == 0 ) {
615                 ldbm_datum_free( ldbm, data );
616         } else
617 #else
618         rc = ldbm->seq( ldbm, &key, &data, R_NEXT );
619
620         if ( rc == 0 ) {
621                 key = ldbm_datum_dup( ldbm, key );
622         } else
623 #endif
624         {
625                 key.dptr = NULL;
626                 key.dsize = 0;
627         }
628
629         LDBM_RUNLOCK;
630         return( key );
631 }
632
633 int
634 ldbm_errno( LDBM ldbm )
635 {
636         return( errno );
637 }
638
639 /******************************************************************
640  *                                                                *
641  *         END Berkeley section                                   *
642  *                                                                *
643  ******************************************************************/
644
645 #elif defined( HAVE_GDBM )
646
647 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
648 #include <sys/stat.h>
649 #endif
650
651 /*****************************************************************
652  *                                                               *
653  * use gdbm                                                      *
654  *                                                               *
655  *****************************************************************/
656
657 LDBM
658 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
659 {
660         LDBM            db;
661 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
662                 struct stat     st;
663 #endif
664 #ifdef HAVE_EBCDIC
665         char n2[2048];
666
667         strncpy(n2, name, sizeof(n2)-1);
668         n2[sizeof(n2)-1] = '\0';
669         __atoe(n2);
670         name = n2;
671 #endif
672
673         LDBM_WLOCK;
674
675         if ( (db = gdbm_open( name, 0, rw | GDBM_FAST, mode, 0 )) == NULL ) {
676                 LDBM_WUNLOCK;
677                 return( NULL );
678         }
679
680 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
681         if ( dbcachesize > 0 && stat( name, &st ) == 0 ) {
682                 dbcachesize /= st.st_blksize;
683                 if( dbcachesize == 0 ) dbcachesize = 1;
684                 gdbm_setopt( db, GDBM_CACHESIZE, &dbcachesize, sizeof(int) );
685         }
686 #else
687         if ( dbcachesize > 0 ) {
688                 dbcachesize /= 4096;
689                 if( dbcachesize == 0 ) dbcachesize = 1;
690                 gdbm_setopt( db, GDBM_CACHESIZE, &dbcachesize, sizeof(int) );
691         }
692 #endif
693
694         LDBM_WUNLOCK;
695
696         return( db );
697 }
698
699 void
700 ldbm_close( LDBM ldbm )
701 {
702         LDBM_WLOCK;
703         gdbm_close( ldbm );
704         LDBM_WUNLOCK;
705 }
706
707 void
708 ldbm_sync( LDBM ldbm )
709 {
710         LDBM_WLOCK;
711         gdbm_sync( ldbm );
712         LDBM_WUNLOCK;
713 }
714
715 Datum
716 ldbm_fetch( LDBM ldbm, Datum key )
717 {
718         Datum d;
719
720         LDBM_RLOCK;
721         d = gdbm_fetch( ldbm, key );
722         LDBM_RUNLOCK;
723
724         return d;
725 }
726
727 int
728 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
729 {
730         int     rc;
731
732         LDBM_WLOCK;
733         rc = gdbm_store( ldbm, key, data, flags & ~LDBM_SYNC );
734         if ( flags & LDBM_SYNC )
735                 gdbm_sync( ldbm );
736         LDBM_WUNLOCK;
737
738         return( rc );
739 }
740
741 int
742 ldbm_delete( LDBM ldbm, Datum key )
743 {
744         int     rc;
745
746         LDBM_WLOCK;
747         rc = gdbm_delete( ldbm, key );
748         gdbm_sync( ldbm );
749         LDBM_WUNLOCK;
750
751         return( rc );
752 }
753
754 Datum
755 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
756 {
757         Datum d;
758
759         LDBM_RLOCK;
760         d = gdbm_firstkey( ldbm );
761         LDBM_RUNLOCK;
762
763         if ( d.dptr != NULL ) {
764                 *dbcp = (Datum *) malloc( sizeof( Datum ) );
765                 **dbcp = ldbm_datum_dup( ldbm, d );
766         }
767
768         return d;
769 }
770
771 Datum
772 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
773 {
774         Datum d;
775
776         LDBM_RLOCK;
777         d = gdbm_nextkey( ldbm, *dbcp );
778         LDBM_RUNLOCK;
779
780         ldbm_datum_free( ldbm, *dbcp );
781
782         if ( d.dptr != NULL ) {
783                 *dbcp = ldbm_datum_dup( ldbm, d );
784         } else {
785                 free( dbcp );
786         }
787
788         return d;
789 }
790
791 int
792 ldbm_errno( LDBM ldbm )
793 {
794         int err;
795
796         LDBM_WLOCK;
797         err = gdbm_errno;
798         LDBM_WUNLOCK;
799
800         return( err );
801 }
802
803 #elif HAVE_MDBM
804
805 /* MMAPED DBM HASHING DATABASE */
806
807 #include <ac/string.h>
808
809 /* #define MDBM_DEBUG */
810
811 #ifdef MDBM_DEBUG
812 #include <stdio.h>
813 #endif
814
815 #define NO_NULL_KEY
816 /* #define MDBM_CHAIN */
817
818 #ifdef MDBM_CHAIN
819
820 /* Use chaining */
821
822 #define mdbm_store      mdbm_chain_store
823 #define mdbm_fetch      mdbm_chain_fetch
824 #define mdbm_delete     mdbm_chain_delete
825 #define mdbm_first      mdbm_chain_first
826 #define mdbm_next       mdbm_chain_next
827
828 #endif
829
830 #define MDBM_PG_SZ      (4*1024)
831
832 /*****************************************************************
833  *                                                               *
834  * use mdbm                                                      *
835  *                                                               *
836  *****************************************************************/
837
838 LDBM
839 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
840 {
841         LDBM            db;
842
843 #ifdef MDBM_DEBUG
844         fprintf( stdout,
845                  "==>(mdbm)ldbm_open(name=%s,rw=%x,mode=%x,cachesize=%d)\n",
846                  name ? name : "NULL", rw, mode, dbcachesize );
847         fflush( stdout );
848 #endif
849
850         LDBM_WLOCK;     /* We need locking here, this is the only non-thread
851                 * safe function we have.  */
852
853         if ( (db =  mdbm_open( name, rw, mode, MDBM_PG_SZ )) == NULL ) {
854                 LDBM_WUNLOCK;
855 #ifdef MDBM_DEBUG
856                 fprintf( stdout, "<==(mdbm)ldbm_open(db=NULL)\n" );
857                 fflush( stdout );
858 #endif
859                 return( NULL );
860         }
861
862 #ifdef MDBM_CHAIN
863         (void)mdbm_set_chain(db);
864 #endif
865
866         LDBM_WUNLOCK;
867
868 #ifdef MDBM_DEBUG
869         fprintf( stdout, "<==(mdbm)ldbm_open(db=%p)\n", db );
870         fflush( stdout );
871 #endif
872
873         return( db );
874 }
875
876 void
877 ldbm_close( LDBM ldbm )
878 {
879         /* Open and close are not reentrant so we need to use locks here */
880
881 #ifdef MDBM_DEBUG
882         fprintf( stdout,
883                  "==>(mdbm)ldbm_close(db=%p)\n", ldbm );
884         fflush( stdout );
885 #endif
886
887         LDBM_WLOCK;
888         mdbm_close( ldbm );
889         LDBM_WUNLOCK;
890
891 #ifdef MDBM_DEBUG
892         fprintf( stdout, "<==(mdbm)ldbm_close()\n" );
893         fflush( stdout );
894 #endif
895 }
896
897 void
898 ldbm_sync( LDBM ldbm )
899 {
900         /* XXX: Not sure if this is re-entrant need to check code, if so
901          * you can leave LOCKS out.
902          */
903
904         LDBM_WLOCK;
905         mdbm_sync( ldbm );
906         LDBM_WUNLOCK;
907 }
908
909 #define MAX_MDBM_RETRY  5
910
911 Datum
912 ldbm_fetch( LDBM ldbm, Datum key )
913 {
914         Datum   d;
915         kvpair  k;
916         int     retry = 0;
917
918         /* This hack is needed because MDBM does not take keys
919          * which begin with NULL when working in the chaining
920          * mode.
921          */
922
923 #ifdef NO_NULL_KEY
924         k.key.dsize = key.dsize + 1;                    
925         k.key.dptr = malloc(k.key.dsize);
926         *(k.key.dptr) = 'l';
927         AC_MEMCPY( (void *)(k.key.dptr + 1), key.dptr, key.dsize );     
928 #else
929         k.key = key;
930 #endif  
931
932         k.val.dptr = NULL;
933         k.val.dsize = 0;
934
935         /* LDBM_RLOCK; */
936         do {
937                 d = mdbm_fetch( ldbm, k );
938
939                 if ( d.dsize > 0 ) {
940                         if ( k.val.dptr != NULL ) {
941                                 free( k.val.dptr );
942                         }
943
944                         if ( (k.val.dptr = malloc( d.dsize )) != NULL ) {
945                                 k.val.dsize = d.dsize;
946                                 d = mdbm_fetch( ldbm, k );
947
948                         } else { 
949                                 d.dsize = 0;
950                                 break;
951                         }
952                 }/* if ( d.dsize > 0 ) */
953         } while ((d.dsize > k.val.dsize) && (++retry < MAX_MDBM_RETRY));
954         /* LDBM_RUNLOCK; */
955
956 #ifdef NO_NULL_KEY
957         free(k.key.dptr);
958 #endif
959
960         return d;
961 }
962
963 int
964 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
965 {
966         int     rc;
967         Datum   int_key;        /* Internal key */
968
969 #ifdef MDBM_DEBUG
970         fprintf( stdout,
971                  "==>(mdbm)ldbm_store(db=%p, key(dptr=%p,sz=%d), data(dptr=%p,sz=%d), flags=%x)\n",
972                  ldbm, key.dptr, key.dsize, data.dptr, data.dsize, flags );
973         fflush( stdout );
974 #endif
975
976         /* LDBM_WLOCK; */
977
978 #ifdef NO_NULL_KEY
979         int_key.dsize = key.dsize + 1;
980         int_key.dptr = malloc( int_key.dsize );
981         *(int_key.dptr) = 'l';  /* Must not be NULL !*/
982         AC_MEMCPY( (void *)(int_key.dptr + 1), key.dptr, key.dsize );
983 #else
984         int_key = key;
985 #endif
986
987         rc = mdbm_store( ldbm, int_key, data, flags );
988         if ( flags & LDBM_SYNC ) {
989                 mdbm_sync( ldbm );
990         }
991
992         /* LDBM_WUNLOCK; */
993
994 #ifdef MDBM_DEBUG
995         fprintf( stdout, "<==(mdbm)ldbm_store(rc=%d)\n", rc );
996         fflush( stdout );
997 #endif
998
999 #ifdef NO_NULL_KEY
1000         free(int_key.dptr);
1001 #endif
1002
1003         return( rc );
1004 }
1005
1006 int
1007 ldbm_delete( LDBM ldbm, Datum key )
1008 {
1009         int     rc;
1010         Datum   int_key;
1011
1012         /* LDBM_WLOCK; */
1013
1014 #ifdef NO_NULL_KEY
1015         int_key.dsize = key.dsize + 1;
1016         int_key.dptr = malloc(int_key.dsize);
1017         *(int_key.dptr) = 'l';
1018         AC_MEMCPY( (void *)(int_key.dptr + 1), key.dptr, key.dsize );   
1019 #else
1020         int_key = key;
1021 #endif
1022         
1023         rc = mdbm_delete( ldbm, int_key );
1024
1025         /* LDBM_WUNLOCK; */
1026 #ifdef NO_NULL_KEY
1027         free(int_key.dptr);
1028 #endif
1029
1030         return( rc );
1031 }
1032
1033 static Datum
1034 ldbm_get_next( LDBM ldbm, kvpair (*fptr)(MDBM *, kvpair) ) 
1035 {
1036         kvpair  out;
1037         kvpair  in;
1038         Datum   ret;
1039         size_t  sz = MDBM_PAGE_SIZE(ldbm);
1040 #ifdef NO_NULL_KEY
1041         int     delta = 1;
1042 #else
1043         int     delta = 0;
1044 #endif
1045
1046         /* LDBM_RLOCK; */
1047
1048         in.key.dsize = sz;      /* Assume first key in one pg */
1049         in.key.dptr = malloc(sz);
1050         
1051         in.val.dptr = NULL;     /* Don't need data just key */ 
1052         in.val.dsize = 0;
1053
1054         ret.dptr = NULL;
1055         ret.dsize = NULL;
1056
1057         out = fptr( ldbm, in );
1058
1059         if (out.key.dsize > 0) {
1060                 ret.dsize = out.key.dsize - delta;
1061
1062                 if ((ret.dptr = (char *)malloc(ret.dsize)) == NULL) { 
1063                         ret.dsize = 0;
1064                         ret.dptr = NULL;
1065
1066                 } else {
1067                         AC_MEMCPY(ret.dptr, (void *)(out.key.dptr + delta),
1068                                 ret.dsize );
1069             }
1070         }
1071
1072         /* LDBM_RUNLOCK; */
1073         
1074         free(in.key.dptr);
1075         return ret;
1076 }
1077
1078 Datum
1079 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
1080 {
1081         return ldbm_get_next( ldbm, mdbm_first );
1082 }
1083
1084 Datum
1085 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
1086 {
1087         /* XXX:
1088          * don't know if this will affect the LDAP server operation 
1089          * but mdbm cannot take and input key.
1090          */
1091
1092         return ldbm_get_next( ldbm, mdbm_next );
1093 }
1094
1095 int
1096 ldbm_errno( LDBM ldbm )
1097 {
1098         /* XXX: best we can do with current  mdbm interface */
1099         return( errno );
1100 }
1101
1102 #elif defined( HAVE_NDBM )
1103
1104 /*****************************************************************
1105  *                                                               *
1106  * if no gdbm or mdbm, fall back to using ndbm, the standard unix thing  *
1107  *                                                               *
1108  *****************************************************************/
1109
1110 /* ARGSUSED */
1111 LDBM
1112 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
1113 {
1114         LDBM ldbm;
1115
1116         LDBM_WLOCK;
1117         ldbm = dbm_open( name, rw, mode );
1118         LDBM_WUNLOCK;
1119
1120         return( ldbm );
1121 }
1122
1123 void
1124 ldbm_close( LDBM ldbm )
1125 {
1126         LDBM_WLOCK;
1127         dbm_close( ldbm );
1128         LDBM_WUNLOCK;
1129 }
1130
1131 /* ARGSUSED */
1132 void
1133 ldbm_sync( LDBM ldbm )
1134 {
1135         return;
1136 }
1137
1138 Datum
1139 ldbm_fetch( LDBM ldbm, Datum key )
1140 {
1141         Datum d;
1142
1143         LDBM_RLOCK;
1144         d = ldbm_datum_dup( ldbm, dbm_fetch( ldbm, key ) );
1145         LDBM_RUNLOCK;
1146
1147         return d;
1148 }
1149
1150 int
1151 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
1152 {
1153         int rc;
1154
1155         LDBM_WLOCK;
1156         rc = dbm_store( ldbm, key, data, flags );
1157         LDBM_WUNLOCK;
1158
1159         return rc;
1160 }
1161
1162 int
1163 ldbm_delete( LDBM ldbm, Datum key )
1164 {
1165         int rc;
1166
1167         LDBM_WLOCK;
1168         rc = dbm_delete( ldbm, key );
1169         LDBM_WUNLOCK;
1170
1171         return rc;
1172 }
1173
1174 Datum
1175 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
1176 {
1177         Datum d;
1178
1179         LDBM_RLOCK;
1180         d = dbm_firstkey( ldbm );
1181         LDBM_RUNLOCK;
1182
1183         return d;
1184 }
1185
1186 Datum
1187 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
1188 {
1189         Datum d;
1190
1191         LDBM_RLOCK;
1192         d = dbm_nextkey( ldbm );
1193         LDBM_RUNLOCK;
1194
1195         return d;
1196 }
1197
1198 int
1199 ldbm_errno( LDBM ldbm )
1200 {
1201         int err;
1202
1203         LDBM_WLOCK;
1204         err = dbm_error( ldbm );
1205         LDBM_WUNLOCK;
1206
1207         return err;
1208 }
1209
1210 #endif /* ndbm */
1211 #endif /* ldbm */