r15325: Fix compiler warnings
[bbaumbach/samba-autobuild/.git] / source4 / lib / gencache / gencache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Generic, persistent and shared between processes cache mechanism for use
5    by various parts of the Samba code
6
7    Copyright (C) Rafal Szczesniak    2002
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "lib/tdb/include/tdbutil.h"
26 #include "system/time.h"
27 #include "system/filesys.h"
28 #include "db_wrap.h"
29 #include "lib/gencache/gencache.h"
30
31 #define TIMEOUT_LEN 12
32 #define CACHE_DATA_FMT  "%12u/%s"
33
34 static struct tdb_wrap *cache;
35
36 /**
37  * @file gencache.c
38  * @brief Generic, persistent and shared between processes cache mechanism
39  *        for use by various parts of the Samba code
40  *
41  **/
42
43
44 /**
45  * Cache initialisation function. Opens cache tdb file or creates
46  * it if does not exist.
47  *
48  * @return true on successful initialisation of the cache or
49  *         false on failure
50  **/
51
52 BOOL gencache_init(void)
53 {
54         char* cache_fname = NULL;
55         
56         /* skip file open if it's already opened */
57         if (cache) return True;
58
59         asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb");
60         if (cache_fname)
61                 DEBUG(5, ("Opening cache file at %s\n", cache_fname));
62         else {
63                 DEBUG(0, ("Filename allocation failed.\n"));
64                 return False;
65         }
66
67         cache = tdb_wrap_open(NULL, cache_fname, 0, TDB_DEFAULT,
68                               O_RDWR|O_CREAT, 0644);
69
70         SAFE_FREE(cache_fname);
71         if (!cache) {
72                 DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
73                 return False;
74         }
75         return True;
76 }
77
78
79 /**
80  * Cache shutdown function. Closes opened cache tdb file.
81  *
82  * @return true on successful closing the cache or
83  *         false on failure during cache shutdown
84  **/
85  
86 BOOL gencache_shutdown(void)
87 {
88         if (!cache) return False;
89         DEBUG(5, ("Closing cache file\n"));
90         talloc_free(cache);
91         return True;
92 }
93
94
95 /**
96  * Set an entry in the cache file. If there's no such
97  * one, then add it.
98  *
99  * @param keystr string that represents a key of this entry
100  * @param value text representation value being cached
101  * @param timeout time when the value is expired
102  *
103  * @retval true when entry is successfuly stored
104  * @retval false on failure
105  **/
106  
107 BOOL gencache_set(const char *keystr, const char *value, time_t timeout)
108 {
109         int ret;
110         TDB_DATA keybuf, databuf;
111         char* valstr = NULL;
112         
113         /* fail completely if get null pointers passed */
114         SMB_ASSERT(keystr && value);
115
116         if (!gencache_init()) return False;
117         
118         asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
119         if (!valstr)
120                 return False;
121
122         keybuf.dptr = (uint8_t *)strdup(keystr);
123         keybuf.dsize = strlen(keystr)+1;
124         databuf.dptr = (uint8_t *)strdup(valstr);
125         databuf.dsize = strlen(valstr)+1;
126         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout \
127                    = %s (%d seconds %s)\n", keybuf.dptr, value, ctime(&timeout),
128                    (int)(timeout - time(NULL)), timeout > time(NULL) ? "ahead" : "in the past"));
129                 
130         ret = tdb_store(cache->tdb, keybuf, databuf, 0);
131         SAFE_FREE(valstr);
132         SAFE_FREE(keybuf.dptr);
133         SAFE_FREE(databuf.dptr);
134         
135         return ret == 0;
136 }
137
138
139 /**
140  * Set existing entry to the cache file.
141  *
142  * @param keystr string that represents a key of this entry
143  * @param valstr text representation value being cached
144  * @param timeout time when the value is expired
145  *
146  * @retval true when entry is successfuly set
147  * @retval false on failure
148  **/
149
150 BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout)
151 {
152         int ret = -1;
153         TDB_DATA keybuf, databuf;
154         char *old_valstr, *datastr;
155         time_t old_timeout;
156         
157         /* fail completely if get null pointers passed */
158         SMB_ASSERT(keystr && valstr);
159
160         if (!gencache_init()) return False;
161                         
162         /* 
163          * Check whether entry exists in the cache
164          * Don't verify gencache_get exit code, since the entry may be expired
165          */     
166         gencache_get(keystr, &old_valstr, &old_timeout);
167         
168         if (!(old_valstr && old_timeout)) return False;
169                 
170         DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \
171                    = %s\n", keystr, old_valstr, ctime(&old_timeout)));
172
173         asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr);
174         keybuf.dptr = (uint8_t *)strdup(keystr);
175         keybuf.dsize = strlen(keystr)+1;
176         databuf.dptr = (uint8_t *)strdup(datastr);
177         databuf.dsize = strlen(datastr)+1;
178         DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr,
179                       ctime(&timeout), (int)(timeout - time(NULL)),
180                       timeout > time(NULL) ? "ahead" : "in the past"));
181
182                 
183         ret = tdb_store(cache->tdb, keybuf, databuf, TDB_REPLACE);
184
185         SAFE_FREE(datastr);
186         SAFE_FREE(old_valstr);
187         SAFE_FREE(keybuf.dptr);
188         SAFE_FREE(databuf.dptr);
189         
190         return ret == 0;
191 }
192  
193
194 /**
195  * Delete one entry from the cache file.
196  *
197  * @param keystr string that represents a key of this entry
198  *
199  * @retval true upon successful deletion
200  * @retval false in case of failure
201  **/
202
203 BOOL gencache_del(const char *keystr)
204 {
205         int ret;
206         TDB_DATA keybuf;
207         
208         /* fail completely if get null pointers passed */
209         SMB_ASSERT(keystr);
210
211         if (!gencache_init()) return False;     
212         
213         keybuf.dptr = (uint8_t *)strdup(keystr);
214         keybuf.dsize = strlen(keystr)+1;
215         DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
216         ret = tdb_delete(cache->tdb, keybuf);
217         
218         SAFE_FREE(keybuf.dptr);
219         return ret == 0;
220 }
221
222
223 /**
224  * Get existing entry from the cache file.
225  *
226  * @param keystr string that represents a key of this entry
227  * @param valstr buffer that is allocated and filled with the entry value
228  *        buffer's disposing must be done outside
229  * @param timeout pointer to a time_t that is filled with entry's
230  *        timeout
231  *
232  * @retval true when entry is successfuly fetched
233  * @retval False for failure
234  **/
235
236 BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
237 {
238         TDB_DATA keybuf, databuf;
239
240         /* fail completely if get null pointers passed */
241         SMB_ASSERT(keystr);
242
243         if (!gencache_init())
244                 return False;
245         
246         keybuf.dptr = (uint8_t *)strdup(keystr);
247         keybuf.dsize = strlen(keystr)+1;
248         databuf = tdb_fetch(cache->tdb, keybuf);
249         SAFE_FREE(keybuf.dptr);
250         
251         if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
252                 char* entry_buf = strndup((char *)databuf.dptr, databuf.dsize);
253                 char *v;
254                 time_t t;
255                 unsigned i;
256
257                 v = malloc_array_p(char, databuf.dsize - TIMEOUT_LEN);
258                                 
259                 SAFE_FREE(databuf.dptr);
260                 sscanf(entry_buf, CACHE_DATA_FMT, (int*)&i, v);
261                 SAFE_FREE(entry_buf);
262                 t = i;
263
264                 DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
265                            "timeout = %s\n", t > time(NULL) ? "valid" :
266                            "expired", keystr, v, ctime(&t)));
267
268                 if (valstr)
269                         *valstr = v;
270                 else
271                         SAFE_FREE(v);
272
273                 if (timeout)
274                         *timeout = t;
275
276                 return t > time(NULL);
277
278         } else {
279                 SAFE_FREE(databuf.dptr);
280
281                 if (valstr)
282                         *valstr = NULL;
283
284                 if (timeout)
285                         timeout = NULL;
286
287                 DEBUG(10, ("Cache entry with key = %s couldn't be found\n", 
288                            keystr));
289
290                 return False;
291         }
292 }
293
294
295 /**
296  * Iterate through all entries which key matches to specified pattern
297  *
298  * @param fn pointer to the function that will be supplied with each single
299  *        matching cache entry (key, value and timeout) as an arguments
300  * @param data void pointer to an arbitrary data that is passed directly to the fn
301  *        function on each call
302  * @param keystr_pattern pattern the existing entries' keys are matched to
303  *
304  **/
305
306 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
307                       void* data, const char* keystr_pattern)
308 {
309         TDB_LIST_NODE *node, *first_node;
310         TDB_DATA databuf;
311         char *keystr = NULL, *valstr = NULL, *entry = NULL;
312         time_t timeout = 0;
313         unsigned i;
314
315         /* fail completely if get null pointers passed */
316         SMB_ASSERT(fn && keystr_pattern);
317
318         if (!gencache_init()) return;
319
320         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
321         node = tdb_search_keys(cache->tdb, keystr_pattern);
322         first_node = node;
323         
324         while (node) {
325                 /* ensure null termination of the key string */
326                 keystr = strndup((char *)node->node_key.dptr, node->node_key.dsize);
327                 
328                 /* 
329                  * We don't use gencache_get function, because we need to iterate through
330                  * all of the entries. Validity verification is up to fn routine.
331                  */
332                 databuf = tdb_fetch(cache->tdb, node->node_key);
333                 if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
334                         SAFE_FREE(databuf.dptr);
335                         SAFE_FREE(keystr);
336                         node = node->next;
337                         continue;
338                 }
339                 entry = strndup((char *)databuf.dptr, databuf.dsize);
340                 SAFE_FREE(databuf.dptr);
341                 valstr = malloc_array_p(char, databuf.dsize - TIMEOUT_LEN);
342                 sscanf(entry, CACHE_DATA_FMT, (int*)(&i), valstr);
343                 timeout = i;
344                 
345                 DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
346                            keystr, valstr, ctime(&timeout)));
347                 fn(keystr, valstr, timeout, data);
348                 
349                 SAFE_FREE(valstr);
350                 SAFE_FREE(entry);
351                 SAFE_FREE(keystr);
352                 node = node->next;
353         }
354         
355         tdb_search_list_free(first_node);
356 }
357
358 /********************************************************************
359  lock a key
360 ********************************************************************/
361
362 int gencache_lock_entry( const char *key )
363 {
364         return tdb_lock_bystring(cache->tdb, key);
365 }
366
367 /********************************************************************
368  unlock a key
369 ********************************************************************/
370
371 void gencache_unlock_entry( const char *key )
372 {
373         tdb_unlock_bystring(cache->tdb, key);
374 }
375
376