]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/dbcache.c
Preliminary ldapAdd support
[openldap] / servers / slapd / back-ldbm / dbcache.c
1 /* ldbmcache.c - maintain a cache of open ldbm files */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2005 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/errno.h>
22 #include <ac/socket.h>
23 #include <ac/string.h>
24 #include <ac/time.h>
25 #include <ac/unistd.h>
26 #include <sys/stat.h>
27
28 #include "slap.h"
29 #include "back-ldbm.h"
30
31 DBCache *
32 ldbm_cache_open(
33     Backend     *be,
34     const char  *name,
35     const char  *suffix,
36     int         flags
37 )
38 {
39         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
40         int             i, lru, empty;
41         time_t          oldtime;
42         char            buf[MAXPATHLEN];
43 #ifdef HAVE_ST_BLKSIZE
44         struct stat     st;
45 #endif
46
47         if (li->li_envdirok)
48                 sprintf( buf, "%s%s", name, suffix );
49         else
50                 sprintf( buf, "%s" LDAP_DIRSEP "%s%s",
51                         li->li_directory, name, suffix );
52
53         if( li->li_dblocking ) {
54                 flags |= LDBM_LOCKING;
55         } else {
56                 flags |= LDBM_NOLOCKING;
57         }
58         
59         if( li->li_dbwritesync ) {
60                 flags |= LDBM_SYNC;
61         } else {
62                 flags |= LDBM_NOSYNC;
63         }
64         
65         Debug( LDAP_DEBUG_TRACE, "=> ldbm_cache_open( \"%s\", %d, %o )\n", buf,
66             flags, li->li_mode );
67
68
69         empty = MAXDBCACHE;
70
71         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
72         do {
73                 lru = 0;
74                 oldtime = 1;
75                 for ( i = 0; i < MAXDBCACHE; i++ ) {
76                         /* see if this slot is free */
77                         if ( li->li_dbcache[i].dbc_name == NULL) {
78                                 if (empty == MAXDBCACHE)
79                                         empty = i;
80                                 continue;
81                         }
82
83                         if ( strcmp( li->li_dbcache[i].dbc_name, buf ) == 0 ) {
84                                 /* already open - return it */
85                                 if (li->li_dbcache[i].dbc_flags != flags
86                                         && li->li_dbcache[i].dbc_refcnt == 0)
87                                 {
88                                         /* we don't want to use an open cache with different
89                                          * permissions (esp. if we need write but the open
90                                          * cache is read-only).  So close this one if
91                                          * possible, and re-open below.
92                                          *
93                                          * FIXME:  what about the case where the refcount
94                                          * is > 0?  right now, we're using it anyway and
95                                          * just praying.  Can there be more than one open
96                                          * cache to the same db?
97                                          *
98                                          * Also, it's really only necessary to compare the
99                                          * read-only flag, instead of all of the flags,
100                                          * but for now I'm checking all of them.
101                                          */
102                                         lru = i;
103                                         empty = MAXDBCACHE;
104                                         break;
105                                 }
106                                 li->li_dbcache[i].dbc_refcnt++;
107                                 Debug( LDAP_DEBUG_TRACE,
108                                     "<= ldbm_cache_open (cache %d)\n", i, 0, 0 );
109
110                                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
111                                 return( &li->li_dbcache[i] );
112                         }
113
114                         /* keep track of lru db */
115                         if (( li->li_dbcache[i].dbc_refcnt == 0 ) &&
116                               (( oldtime == 1 ) ||
117                               ( li->li_dbcache[i].dbc_lastref < oldtime )) )
118                         {
119                                 lru = i;
120                                 oldtime = li->li_dbcache[i].dbc_lastref;
121                         }
122                 }
123
124                 i = empty;
125                 if ( i == MAXDBCACHE ) {
126                         /* no empty slots, not already open - close lru and use that slot */
127                         if ( li->li_dbcache[lru].dbc_refcnt == 0 ) {
128                                 i = lru;
129                                 ldbm_close( li->li_dbcache[i].dbc_db );
130                                 free( li->li_dbcache[i].dbc_name );
131                                 li->li_dbcache[i].dbc_name = NULL;
132                         } else {
133                                 Debug( LDAP_DEBUG_ANY,
134                                     "ldbm_cache_open no unused db to close - waiting\n",
135                                     0, 0, 0 );
136
137                                 ldap_pvt_thread_cond_wait( &li->li_dbcache_cv,
138                                             &li->li_dbcache_mutex );
139                                 /* after waiting for a free slot, go back to square
140                                  * one: look for an open cache for this db, or an
141                                  * empty slot, or an unref'ed cache, or wait again.
142                                  */
143                         }
144                 }
145         } while (i == MAXDBCACHE);
146
147         if ( (li->li_dbcache[i].dbc_db = ldbm_open( li->li_dbenv, buf, flags, li->li_mode,
148             li->li_dbcachesize )) == NULL )
149         {
150                 int err = errno;
151                 Debug( LDAP_DEBUG_TRACE,
152                     "<= ldbm_cache_open NULL \"%s\" errno=%d reason=\"%s\")\n",
153                     buf, err, err > -1 && err < sys_nerr ?
154                     sys_errlist[err] : "unknown" );
155
156                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
157                 return( NULL );
158         }
159         li->li_dbcache[i].dbc_name = ch_strdup( buf );
160         li->li_dbcache[i].dbc_refcnt = 1;
161         li->li_dbcache[i].dbc_lastref = slap_get_time();
162         li->li_dbcache[i].dbc_flags = flags;
163         li->li_dbcache[i].dbc_dirty = 0;
164 #ifdef HAVE_ST_BLKSIZE
165         if ( stat( buf, &st ) == 0 ) {
166                 li->li_dbcache[i].dbc_blksize = st.st_blksize;
167         } else
168 #endif
169         {
170                 li->li_dbcache[i].dbc_blksize = DEFAULT_BLOCKSIZE;
171         }
172         li->li_dbcache[i].dbc_maxids = (li->li_dbcache[i].dbc_blksize /
173             sizeof(ID)) - ID_BLOCK_IDS_OFFSET;
174         li->li_dbcache[i].dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
175             li->li_dbcache[i].dbc_maxids ) + 1;
176
177         assert( li->li_dbcache[i].dbc_maxindirect < 256 );
178
179         Debug( LDAP_DEBUG_ARGS,
180             "ldbm_cache_open (blksize %ld) (maxids %d) (maxindirect %d)\n",
181             li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
182             li->li_dbcache[i].dbc_maxindirect );
183
184         Debug( LDAP_DEBUG_TRACE, "<= ldbm_cache_open (opened %d)\n", i, 0, 0 );
185
186         ldap_pvt_thread_mutex_init( &li->li_dbcache[i].dbc_write_mutex );
187
188         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
189         return( &li->li_dbcache[i] );
190 }
191
192 void
193 ldbm_cache_close( Backend *be, DBCache *db )
194 {
195         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
196
197         if( li->li_dbwritesync && db->dbc_dirty ) {
198                 ldbm_sync( db->dbc_db );
199                 db->dbc_dirty = 0;
200         }
201
202         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
203         if ( --db->dbc_refcnt <= 0 ) {
204                 db->dbc_refcnt = 0;
205                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
206         }
207         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
208 }
209
210 void
211 ldbm_cache_really_close( Backend *be, DBCache *db )
212 {
213         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
214
215         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
216         if ( --db->dbc_refcnt <= 0 ) {
217                 db->dbc_refcnt = 0;
218                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
219                 ldbm_close( db->dbc_db );
220                 free( db->dbc_name );
221                 db->dbc_name = NULL;
222                 ldap_pvt_thread_mutex_destroy( &db->dbc_write_mutex );
223         }
224         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
225 }
226
227 void
228 ldbm_cache_flush_all( Backend *be )
229 {
230         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
231         int             i;
232
233         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
234         for ( i = 0; i < MAXDBCACHE; i++ ) {
235                 if ( li->li_dbcache[i].dbc_name != NULL ) {
236                         Debug( LDAP_DEBUG_TRACE, "ldbm flushing db (%s)\n",
237                             li->li_dbcache[i].dbc_name, 0, 0 );
238
239                         ldbm_sync( li->li_dbcache[i].dbc_db );
240                         li->li_dbcache[i].dbc_dirty = 0;
241                         if ( li->li_dbcache[i].dbc_refcnt != 0 ) {
242                                 Debug( LDAP_DEBUG_TRACE,
243                                        "refcnt = %d, couldn't close db (%s)\n",
244                                        li->li_dbcache[i].dbc_refcnt,
245                                        li->li_dbcache[i].dbc_name, 0 );
246
247                         } else {
248                                 Debug( LDAP_DEBUG_TRACE,
249                                        "ldbm closing db (%s)\n",
250                                        li->li_dbcache[i].dbc_name, 0, 0 );
251
252                                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
253                                 ldbm_close( li->li_dbcache[i].dbc_db );
254                                 free( li->li_dbcache[i].dbc_name );
255                                 li->li_dbcache[i].dbc_name = NULL;
256                         }
257                 }
258         }
259         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
260 }
261
262 void
263 ldbm_cache_sync( Backend *be )
264 {
265         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
266         int             i;
267
268         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
269         for ( i = 0; i < MAXDBCACHE; i++ ) {
270                 if ( li->li_dbcache[i].dbc_name != NULL && li->li_dbcache[i].dbc_dirty ) {
271                         Debug(  LDAP_DEBUG_TRACE, "ldbm syncing db (%s)\n",
272                                 li->li_dbcache[i].dbc_name, 0, 0 );
273                         ldbm_sync( li->li_dbcache[i].dbc_db );
274                         li->li_dbcache[i].dbc_dirty = 0;
275                 }
276         }
277         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
278 }
279
280 #if 0 /* macro in proto-back-ldbm.h */
281 Datum
282 ldbm_cache_fetch(
283     DBCache     *db,
284     Datum               key
285 )
286 {
287         return ldbm_fetch( db->dbc_db, key );
288 }
289 #endif /* 0 */
290
291 int
292 ldbm_cache_store(
293     DBCache     *db,
294     Datum               key,
295     Datum               data,
296     int                 flags
297 )
298 {
299         int     rc;
300
301         db->dbc_dirty = 1;
302         rc = ldbm_store( db->dbc_db, key, data, flags );
303
304         return( rc );
305 }
306
307 int
308 ldbm_cache_delete(
309     DBCache     *db,
310     Datum               key
311 )
312 {
313         int     rc;
314
315         db->dbc_dirty = 1;
316         rc = ldbm_delete( db->dbc_db, key );
317
318         return( rc );
319 }
320
321 void *
322 ldbm_cache_sync_daemon(
323         void *be_ptr
324 )
325 {
326         Backend *be = (Backend *)be_ptr;
327         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
328
329         Debug( LDAP_DEBUG_ANY, "synchronizer starting for %s\n", li->li_directory, 0, 0 );
330   
331         while (!li->li_dbshutdown) {
332                 int i = li->li_dbsyncwaitn;
333
334                 sleep( li->li_dbsyncfreq );
335
336                 while (i && ldap_pvt_thread_pool_backload(&connection_pool) != 0) {
337                         Debug( LDAP_DEBUG_TRACE, "delay syncing %s\n", li->li_directory, 0, 0 );
338                         sleep(li->li_dbsyncwaitinterval);
339                         i--;
340                 }
341
342                 if (!li->li_dbshutdown) {
343                         Debug( LDAP_DEBUG_TRACE, "syncing %s\n", li->li_directory, 0, 0 );
344                         ldbm_cache_sync( be );
345                 }
346         }
347
348         Debug( LDAP_DEBUG_ANY, "synchronizer stopping\n", 0, 0, 0 );
349   
350         return NULL;
351 }