r18310: Add a little test for some gencache routines
[vlendec/samba-autobuild/.git] / source3 / lib / 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
26 #undef  DBGC_CLASS
27 #define DBGC_CLASS DBGC_TDB
28
29 #define TIMEOUT_LEN 12
30 #define CACHE_DATA_FMT  "%12u/%s"
31 #define READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us"
32
33 static TDB_CONTEXT *cache;
34
35 /**
36  * @file gencache.c
37  * @brief Generic, persistent and shared between processes cache mechanism
38  *        for use by various parts of the Samba code
39  *
40  **/
41
42
43 /**
44  * Cache initialisation function. Opens cache tdb file or creates
45  * it if does not exist.
46  *
47  * @return true on successful initialisation of the cache or
48  *         false on failure
49  **/
50
51 BOOL gencache_init(void)
52 {
53         char* cache_fname = NULL;
54         
55         /* skip file open if it's already opened */
56         if (cache) return True;
57
58         asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb");
59         if (cache_fname == NULL) {
60                 DEBUG(0, ("Filename allocation failed.\n"));
61                 return False;
62         }
63
64         DEBUG(5, ("Opening cache file at %s\n", cache_fname));
65
66         cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
67                              O_RDWR|O_CREAT, 0644);
68
69         SAFE_FREE(cache_fname);
70         if (!cache) {
71                 DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
72                 return False;
73         }
74         return True;
75 }
76
77
78 /**
79  * Cache shutdown function. Closes opened cache tdb file.
80  *
81  * @return true on successful closing the cache or
82  *         false on failure during cache shutdown
83  **/
84  
85 BOOL gencache_shutdown(void)
86 {
87         int ret;
88         /* tdb_close routine returns -1 on error */
89         if (!cache) return False;
90         DEBUG(5, ("Closing cache file\n"));
91         ret = tdb_close(cache);
92         cache = NULL;
93         return ret != -1;
94 }
95
96
97 /**
98  * Set an entry in the cache file. If there's no such
99  * one, then add it.
100  *
101  * @param keystr string that represents a key of this entry
102  * @param value text representation value being cached
103  * @param timeout time when the value is expired
104  *
105  * @retval true when entry is successfuly stored
106  * @retval false on failure
107  **/
108  
109 BOOL gencache_set(const char *keystr, const char *value, time_t timeout)
110 {
111         int ret;
112         TDB_DATA keybuf, databuf;
113         char* valstr = NULL;
114         
115         /* fail completely if get null pointers passed */
116         SMB_ASSERT(keystr && value);
117
118         if (!gencache_init()) return False;
119         
120         asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
121         if (!valstr)
122                 return False;
123
124         keybuf.dptr = CONST_DISCARD(char *, keystr);
125         keybuf.dsize = strlen(keystr)+1;
126         databuf.dptr = valstr;
127         databuf.dsize = strlen(valstr)+1;
128         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
129                    " %s (%d seconds %s)\n", keybuf.dptr, value,ctime(&timeout),
130                    (int)(timeout - time(NULL)), 
131                    timeout > time(NULL) ? "ahead" : "in the past"));
132
133         ret = tdb_store(cache, keybuf, databuf, 0);
134         SAFE_FREE(valstr);
135         
136         return ret == 0;
137 }
138
139 /**
140  * Delete one entry from the cache file.
141  *
142  * @param keystr string that represents a key of this entry
143  *
144  * @retval true upon successful deletion
145  * @retval false in case of failure
146  **/
147
148 BOOL gencache_del(const char *keystr)
149 {
150         int ret;
151         TDB_DATA keybuf;
152         
153         /* fail completely if get null pointers passed */
154         SMB_ASSERT(keystr);
155
156         if (!gencache_init()) return False;     
157         
158         keybuf.dptr = CONST_DISCARD(char *, keystr);
159         keybuf.dsize = strlen(keystr)+1;
160         DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
161         ret = tdb_delete(cache, keybuf);
162         
163         return ret == 0;
164 }
165
166
167 /**
168  * Get existing entry from the cache file.
169  *
170  * @param keystr string that represents a key of this entry
171  * @param valstr buffer that is allocated and filled with the entry value
172  *        buffer's disposing must be done outside
173  * @param timeout pointer to a time_t that is filled with entry's
174  *        timeout
175  *
176  * @retval true when entry is successfuly fetched
177  * @retval False for failure
178  **/
179
180 BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
181 {
182         TDB_DATA keybuf, databuf;
183
184         /* fail completely if get null pointers passed */
185         SMB_ASSERT(keystr);
186
187         if (!gencache_init()) {
188                 return False;
189         }
190         
191         keybuf.dptr = CONST_DISCARD(char *, keystr);
192         keybuf.dsize = strlen(keystr)+1;
193         databuf = tdb_fetch(cache, keybuf);
194         
195         if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
196                 char* entry_buf = SMB_STRNDUP(databuf.dptr, databuf.dsize);
197                 char *v;
198                 time_t t;
199                 unsigned u;
200                 int status;
201                 char *fmt;
202
203                 v = (char *)SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
204                 if (!v) {
205                         return False;
206                 }
207
208                 SAFE_FREE(databuf.dptr);
209
210                 asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE, (unsigned int)databuf.dsize - TIMEOUT_LEN);
211                 if (!fmt) {
212                         SAFE_FREE(v);
213                         return False;
214                 }
215
216                 status = sscanf(entry_buf, fmt, &u, v);
217                 SAFE_FREE(fmt);
218
219                 if ( status != 2 ) {
220                         DEBUG(0, ("gencache_get: Invalid return %d from sscanf\n", status ));
221                 }
222                 t = u;
223                 SAFE_FREE(entry_buf);
224
225                 DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
226                            "timeout = %s", t > time(NULL) ? "valid" :
227                            "expired", keystr, v, ctime(&t)));
228
229                 if (valstr) {
230                         *valstr = v;
231                 } else {
232                         SAFE_FREE(v);
233                 }
234
235                 if (timeout) {
236                         *timeout = t;
237                 }
238
239                 return t > time(NULL);
240
241         } 
242
243         SAFE_FREE(databuf.dptr);
244
245         if (valstr) {
246                 *valstr = NULL;
247         }
248         if (timeout) {
249                 timeout = NULL;
250         }
251
252         DEBUG(10, ("Cache entry with key = %s couldn't be found\n", keystr));
253         return False;
254 }
255
256 /**
257  * Iterate through all entries which key matches to specified pattern
258  *
259  * @param fn pointer to the function that will be supplied with each single
260  *        matching cache entry (key, value and timeout) as an arguments
261  * @param data void pointer to an arbitrary data that is passed directly to the fn
262  *        function on each call
263  * @param keystr_pattern pattern the existing entries' keys are matched to
264  *
265  **/
266
267 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
268                       void* data, const char* keystr_pattern)
269 {
270         TDB_LIST_NODE *node, *first_node;
271         TDB_DATA databuf;
272         char *keystr = NULL, *valstr = NULL, *entry = NULL;
273         time_t timeout = 0;
274         int status;
275         unsigned u;
276
277         /* fail completely if get null pointers passed */
278         SMB_ASSERT(fn && keystr_pattern);
279
280         if (!gencache_init()) return;
281
282         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
283         node = tdb_search_keys(cache, keystr_pattern);
284         first_node = node;
285         
286         while (node) {
287                 char *fmt;
288
289                 /* ensure null termination of the key string */
290                 keystr = SMB_STRNDUP(node->node_key.dptr, node->node_key.dsize);
291                 if (!keystr) {
292                         break;
293                 }
294                 
295                 /* 
296                  * We don't use gencache_get function, because we need to iterate through
297                  * all of the entries. Validity verification is up to fn routine.
298                  */
299                 databuf = tdb_fetch(cache, node->node_key);
300                 if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
301                         SAFE_FREE(databuf.dptr);
302                         SAFE_FREE(keystr);
303                         node = node->next;
304                         continue;
305                 }
306                 entry = SMB_STRNDUP(databuf.dptr, databuf.dsize);
307                 if (!entry) {
308                         SAFE_FREE(databuf.dptr);
309                         SAFE_FREE(keystr);
310                         break;
311                 }
312
313                 SAFE_FREE(databuf.dptr);
314
315                 valstr = (char *)SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
316                 if (!valstr) {
317                         SAFE_FREE(entry);
318                         SAFE_FREE(keystr);
319                         break;
320                 }
321
322                 asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE, (unsigned int)databuf.dsize - TIMEOUT_LEN);
323                 if (!fmt) {
324                         SAFE_FREE(valstr);
325                         SAFE_FREE(entry);
326                         SAFE_FREE(keystr);
327                         break;
328                 }
329                 status = sscanf(entry, fmt, &u, valstr);
330                 SAFE_FREE(fmt);
331
332                 if ( status != 2 ) {
333                         DEBUG(0,("gencache_iterate: invalid return from sscanf %d\n",status));
334                 }
335                 timeout = u;
336                 
337                 DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
338                            keystr, valstr, ctime(&timeout)));
339                 fn(keystr, valstr, timeout, data);
340                 
341                 SAFE_FREE(valstr);
342                 SAFE_FREE(entry);
343                 SAFE_FREE(keystr);
344                 node = node->next;
345         }
346         
347         tdb_search_list_free(first_node);
348 }
349
350 /********************************************************************
351  lock a key
352 ********************************************************************/
353
354 int gencache_lock_entry( const char *key )
355 {
356         if (!gencache_init())
357                 return -1;
358         
359         return tdb_lock_bystring(cache, key);
360 }
361
362 /********************************************************************
363  unlock a key
364 ********************************************************************/
365
366 void gencache_unlock_entry( const char *key )
367 {
368         if (!gencache_init())
369                 return;
370         
371         tdb_unlock_bystring(cache, key);
372         return;
373 }