]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/dbcache.c
rework op/rs structures to deal with opeartional attributes
[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-2004 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 #ifdef NEW_LOGGING
66         LDAP_LOG( CACHE, ENTRY, 
67                 "ldbm_cache_open: \"%s\", %d, %o\n", buf, flags, li->li_mode );
68 #else
69         Debug( LDAP_DEBUG_TRACE, "=> ldbm_cache_open( \"%s\", %d, %o )\n", buf,
70             flags, li->li_mode );
71 #endif
72
73
74         empty = MAXDBCACHE;
75
76         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
77         do {
78                 lru = 0;
79                 oldtime = 1;
80                 for ( i = 0; i < MAXDBCACHE; i++ ) {
81                         /* see if this slot is free */
82                         if ( li->li_dbcache[i].dbc_name == NULL) {
83                                 if (empty == MAXDBCACHE)
84                                         empty = i;
85                                 continue;
86                         }
87
88                         if ( strcmp( li->li_dbcache[i].dbc_name, buf ) == 0 ) {
89                                 /* already open - return it */
90                                 if (li->li_dbcache[i].dbc_flags != flags
91                                         && li->li_dbcache[i].dbc_refcnt == 0)
92                                 {
93                                         /* we don't want to use an open cache with different
94                                          * permissions (esp. if we need write but the open
95                                          * cache is read-only).  So close this one if
96                                          * possible, and re-open below.
97                                          *
98                                          * FIXME:  what about the case where the refcount
99                                          * is > 0?  right now, we're using it anyway and
100                                          * just praying.  Can there be more than one open
101                                          * cache to the same db?
102                                          *
103                                          * Also, it's really only necessary to compare the
104                                          * read-only flag, instead of all of the flags,
105                                          * but for now I'm checking all of them.
106                                          */
107                                         lru = i;
108                                         empty = MAXDBCACHE;
109                                         break;
110                                 }
111                                 li->li_dbcache[i].dbc_refcnt++;
112 #ifdef NEW_LOGGING
113                                 LDAP_LOG( CACHE, DETAIL1, 
114                                         "ldbm_cache_open: cache %d\n", i, 0, 0 );
115 #else
116                                 Debug( LDAP_DEBUG_TRACE,
117                                     "<= ldbm_cache_open (cache %d)\n", i, 0, 0 );
118 #endif
119
120                                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
121                                 return( &li->li_dbcache[i] );
122                         }
123
124                         /* keep track of lru db */
125                         if (( li->li_dbcache[i].dbc_refcnt == 0 ) &&
126                               (( oldtime == 1 ) ||
127                               ( li->li_dbcache[i].dbc_lastref < oldtime )) )
128                         {
129                                 lru = i;
130                                 oldtime = li->li_dbcache[i].dbc_lastref;
131                         }
132                 }
133
134                 i = empty;
135                 if ( i == MAXDBCACHE ) {
136                         /* no empty slots, not already open - close lru and use that slot */
137                         if ( li->li_dbcache[lru].dbc_refcnt == 0 ) {
138                                 i = lru;
139                                 ldbm_close( li->li_dbcache[i].dbc_db );
140                                 free( li->li_dbcache[i].dbc_name );
141                                 li->li_dbcache[i].dbc_name = NULL;
142                         } else {
143 #ifdef NEW_LOGGING
144                                 LDAP_LOG( CACHE, INFO,
145                                         "ldbm_cache_open: no unused db to close - waiting\n", 
146                                         0, 0, 0 );
147 #else
148                                 Debug( LDAP_DEBUG_ANY,
149                                     "ldbm_cache_open no unused db to close - waiting\n",
150                                     0, 0, 0 );
151 #endif
152
153                                 ldap_pvt_thread_cond_wait( &li->li_dbcache_cv,
154                                             &li->li_dbcache_mutex );
155                                 /* after waiting for a free slot, go back to square
156                                  * one: look for an open cache for this db, or an
157                                  * empty slot, or an unref'ed cache, or wait again.
158                                  */
159                         }
160                 }
161         } while (i == MAXDBCACHE);
162
163         if ( (li->li_dbcache[i].dbc_db = ldbm_open( li->li_dbenv, buf, flags, li->li_mode,
164             li->li_dbcachesize )) == NULL )
165         {
166                 int err = errno;
167 #ifdef NEW_LOGGING
168                 LDAP_LOG( CACHE, ERR, 
169                         "ldbm_cache_open: \"%s\" failed, errono=%d, reason=%s\n",
170                         buf, err, err > -1 && err < sys_nerr ? sys_errlist[err] :
171                         "unknown" );
172 #else
173                 Debug( LDAP_DEBUG_TRACE,
174                     "<= ldbm_cache_open NULL \"%s\" errno=%d reason=\"%s\")\n",
175                     buf, err, err > -1 && err < sys_nerr ?
176                     sys_errlist[err] : "unknown" );
177 #endif
178
179                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
180                 return( NULL );
181         }
182         li->li_dbcache[i].dbc_name = ch_strdup( buf );
183         li->li_dbcache[i].dbc_refcnt = 1;
184         li->li_dbcache[i].dbc_lastref = slap_get_time();
185         li->li_dbcache[i].dbc_flags = flags;
186         li->li_dbcache[i].dbc_dirty = 0;
187 #ifdef HAVE_ST_BLKSIZE
188         if ( stat( buf, &st ) == 0 ) {
189                 li->li_dbcache[i].dbc_blksize = st.st_blksize;
190         } else
191 #endif
192         {
193                 li->li_dbcache[i].dbc_blksize = DEFAULT_BLOCKSIZE;
194         }
195         li->li_dbcache[i].dbc_maxids = (li->li_dbcache[i].dbc_blksize /
196             sizeof(ID)) - ID_BLOCK_IDS_OFFSET;
197         li->li_dbcache[i].dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
198             li->li_dbcache[i].dbc_maxids ) + 1;
199
200         assert( li->li_dbcache[i].dbc_maxindirect < 256 );
201
202 #ifdef NEW_LOGGING
203         LDAP_LOG( CACHE, ARGS, 
204                    "ldbm_cache_open: blksize:%ld  maxids:%d  maxindirect:%d\n",
205                    li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
206                    li->li_dbcache[i].dbc_maxindirect );
207 #else
208         Debug( LDAP_DEBUG_ARGS,
209             "ldbm_cache_open (blksize %ld) (maxids %d) (maxindirect %d)\n",
210             li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
211             li->li_dbcache[i].dbc_maxindirect );
212 #endif
213
214 #ifdef NEW_LOGGING
215         LDAP_LOG( CACHE, DETAIL1, "<= ldbm_cache_open: (opened %d)\n", i, 0, 0 );
216 #else
217         Debug( LDAP_DEBUG_TRACE, "<= ldbm_cache_open (opened %d)\n", i, 0, 0 );
218 #endif
219
220         ldap_pvt_thread_mutex_init( &li->li_dbcache[i].dbc_write_mutex );
221
222         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
223         return( &li->li_dbcache[i] );
224 }
225
226 void
227 ldbm_cache_close( Backend *be, DBCache *db )
228 {
229         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
230
231         if( li->li_dbwritesync && db->dbc_dirty ) {
232                 ldbm_sync( db->dbc_db );
233                 db->dbc_dirty = 0;
234         }
235
236         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
237         if ( --db->dbc_refcnt <= 0 ) {
238                 db->dbc_refcnt = 0;
239                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
240         }
241         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
242 }
243
244 void
245 ldbm_cache_really_close( Backend *be, DBCache *db )
246 {
247         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
248
249         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
250         if ( --db->dbc_refcnt <= 0 ) {
251                 db->dbc_refcnt = 0;
252                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
253                 ldbm_close( db->dbc_db );
254                 free( db->dbc_name );
255                 db->dbc_name = NULL;
256                 ldap_pvt_thread_mutex_destroy( &db->dbc_write_mutex );
257         }
258         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
259 }
260
261 void
262 ldbm_cache_flush_all( Backend *be )
263 {
264         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
265         int             i;
266
267         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
268         for ( i = 0; i < MAXDBCACHE; i++ ) {
269                 if ( li->li_dbcache[i].dbc_name != NULL ) {
270 #ifdef NEW_LOGGING
271                         LDAP_LOG( CACHE, DETAIL1, 
272                                    "ldbm_cache_flush_all: flushing db (%s)\n",
273                                    li->li_dbcache[i].dbc_name, 0, 0 );
274 #else
275                         Debug( LDAP_DEBUG_TRACE, "ldbm flushing db (%s)\n",
276                             li->li_dbcache[i].dbc_name, 0, 0 );
277 #endif
278
279                         ldbm_sync( li->li_dbcache[i].dbc_db );
280                         li->li_dbcache[i].dbc_dirty = 0;
281                         if ( li->li_dbcache[i].dbc_refcnt != 0 ) {
282 #ifdef NEW_LOGGING
283                                 LDAP_LOG( CACHE, INFO, 
284                                         "ldbm_cache_flush_all: couldn't close db (%s), refcnt=%d\n",
285                                         li->li_dbcache[i].dbc_name, li->li_dbcache[i].dbc_refcnt,0);
286 #else
287                                 Debug( LDAP_DEBUG_TRACE,
288                                        "refcnt = %d, couldn't close db (%s)\n",
289                                        li->li_dbcache[i].dbc_refcnt,
290                                        li->li_dbcache[i].dbc_name, 0 );
291 #endif
292
293                         } else {
294 #ifdef NEW_LOGGING
295                                 LDAP_LOG( CACHE, DETAIL1, 
296                                            "ldbm_cache_flush_all: ldbm closing db (%s)\n",
297                                            li->li_dbcache[i].dbc_name, 0, 0 );
298 #else
299                                 Debug( LDAP_DEBUG_TRACE,
300                                        "ldbm closing db (%s)\n",
301                                        li->li_dbcache[i].dbc_name, 0, 0 );
302 #endif
303
304                                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
305                                 ldbm_close( li->li_dbcache[i].dbc_db );
306                                 free( li->li_dbcache[i].dbc_name );
307                                 li->li_dbcache[i].dbc_name = NULL;
308                         }
309                 }
310         }
311         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
312 }
313
314 void
315 ldbm_cache_sync( Backend *be )
316 {
317         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
318         int             i;
319
320         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
321         for ( i = 0; i < MAXDBCACHE; i++ ) {
322                 if ( li->li_dbcache[i].dbc_name != NULL && li->li_dbcache[i].dbc_dirty ) {
323 #ifdef NEW_LOGGING
324                         LDAP_LOG ( CACHE, DETAIL1, "ldbm_cache_sync: "
325                                 "ldbm syncing db (%s)\n", li->li_dbcache[i].dbc_name, 0, 0 );
326 #else
327                         Debug(  LDAP_DEBUG_TRACE, "ldbm syncing db (%s)\n",
328                                 li->li_dbcache[i].dbc_name, 0, 0 );
329 #endif
330                         ldbm_sync( li->li_dbcache[i].dbc_db );
331                         li->li_dbcache[i].dbc_dirty = 0;
332                 }
333         }
334         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
335 }
336
337 #if 0 /* macro in proto-back-ldbm.h */
338 Datum
339 ldbm_cache_fetch(
340     DBCache     *db,
341     Datum               key
342 )
343 {
344         return ldbm_fetch( db->dbc_db, key );
345 }
346 #endif /* 0 */
347
348 int
349 ldbm_cache_store(
350     DBCache     *db,
351     Datum               key,
352     Datum               data,
353     int                 flags
354 )
355 {
356         int     rc;
357
358         db->dbc_dirty = 1;
359         rc = ldbm_store( db->dbc_db, key, data, flags );
360
361         return( rc );
362 }
363
364 int
365 ldbm_cache_delete(
366     DBCache     *db,
367     Datum               key
368 )
369 {
370         int     rc;
371
372         db->dbc_dirty = 1;
373         rc = ldbm_delete( db->dbc_db, key );
374
375         return( rc );
376 }
377
378 void *
379 ldbm_cache_sync_daemon(
380         void *be_ptr
381 )
382 {
383         Backend *be = (Backend *)be_ptr;
384         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
385
386 #ifdef NEW_LOGGING
387         LDAP_LOG ( CACHE, ARGS, "ldbm_cache_sync_daemon:"
388                 " synchronizer starting for %s\n", li->li_directory, 0, 0 );
389 #else
390         Debug( LDAP_DEBUG_ANY, "synchronizer starting for %s\n", li->li_directory, 0, 0 );
391 #endif
392   
393         while (!li->li_dbshutdown) {
394                 int i = li->li_dbsyncwaitn;
395
396                 sleep( li->li_dbsyncfreq );
397
398                 while (i && ldap_pvt_thread_pool_backload(&connection_pool) != 0) {
399 #ifdef NEW_LOGGING
400                         LDAP_LOG ( CACHE, DETAIL1, "ldbm_cache_sync_daemon:"
401                                 " delay syncing %s\n", li->li_directory, 0, 0 );
402 #else
403                         Debug( LDAP_DEBUG_TRACE, "delay syncing %s\n", li->li_directory, 0, 0 );
404 #endif
405                         sleep(li->li_dbsyncwaitinterval);
406                         i--;
407                 }
408
409                 if (!li->li_dbshutdown) {
410 #ifdef NEW_LOGGING
411                         LDAP_LOG ( CACHE, DETAIL1, "ldbm_cache_sync_daemon:"
412                                 " syncing %s\n", li->li_directory, 0, 0 );
413 #else
414                         Debug( LDAP_DEBUG_TRACE, "syncing %s\n", li->li_directory, 0, 0 );
415 #endif
416                         ldbm_cache_sync( be );
417                 }
418         }
419
420 #ifdef NEW_LOGGING
421         LDAP_LOG ( CACHE, DETAIL1, "ldbm_cache_sync_daemon:"
422                                 " synchronizer stopping\n", 0, 0, 0);
423 #else
424         Debug( LDAP_DEBUG_ANY, "synchronizer stopping\n", 0, 0, 0 );
425 #endif
426   
427         return NULL;
428 }