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