r18312: Change gencache_get slightly: Delete expired keys, and only strdup the value
[sfrench/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         time_t t;
184         char *endptr;
185
186         /* fail completely if get null pointers passed */
187         SMB_ASSERT(keystr);
188
189         if (!gencache_init()) {
190                 return False;
191         }
192         
193         keybuf.dptr = CONST_DISCARD(char *, keystr);
194         keybuf.dsize = strlen(keystr)+1;
195         databuf = tdb_fetch(cache, keybuf);
196
197         if (databuf.dptr == NULL) {
198                 DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
199                            keystr));
200                 return False;
201         }
202
203         t = strtol(databuf.dptr, &endptr, 10);
204
205         if ((endptr == NULL) || (*endptr != '/')) {
206                 DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
207                 SAFE_FREE(databuf.dptr);
208                 return False;
209         }
210
211         DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
212                    "timeout = %s", t > time(NULL) ? "valid" :
213                    "expired", keystr, endptr+1, ctime(&t)));
214
215         if (t <= time(NULL)) {
216
217                 /* We're expired, delete the entry */
218                 tdb_delete(cache, keybuf);
219
220                 SAFE_FREE(databuf.dptr);
221                 return False;
222         }
223
224         if (valstr) {
225                 *valstr = SMB_STRDUP(endptr+1);
226                 if (*valstr == NULL) {
227                         SAFE_FREE(databuf.dptr);
228                         DEBUG(0, ("strdup failed\n"));
229                         return False;
230                 }
231         }
232         
233         SAFE_FREE(databuf.dptr);
234
235         if (timeout) {
236                 *timeout = t;
237         }
238
239         return True;
240
241
242
243 /**
244  * Iterate through all entries which key matches to specified pattern
245  *
246  * @param fn pointer to the function that will be supplied with each single
247  *        matching cache entry (key, value and timeout) as an arguments
248  * @param data void pointer to an arbitrary data that is passed directly to the fn
249  *        function on each call
250  * @param keystr_pattern pattern the existing entries' keys are matched to
251  *
252  **/
253
254 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
255                       void* data, const char* keystr_pattern)
256 {
257         TDB_LIST_NODE *node, *first_node;
258         TDB_DATA databuf;
259         char *keystr = NULL, *valstr = NULL, *entry = NULL;
260         time_t timeout = 0;
261         int status;
262         unsigned u;
263
264         /* fail completely if get null pointers passed */
265         SMB_ASSERT(fn && keystr_pattern);
266
267         if (!gencache_init()) return;
268
269         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
270         node = tdb_search_keys(cache, keystr_pattern);
271         first_node = node;
272         
273         while (node) {
274                 char *fmt;
275
276                 /* ensure null termination of the key string */
277                 keystr = SMB_STRNDUP(node->node_key.dptr, node->node_key.dsize);
278                 if (!keystr) {
279                         break;
280                 }
281                 
282                 /* 
283                  * We don't use gencache_get function, because we need to iterate through
284                  * all of the entries. Validity verification is up to fn routine.
285                  */
286                 databuf = tdb_fetch(cache, node->node_key);
287                 if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
288                         SAFE_FREE(databuf.dptr);
289                         SAFE_FREE(keystr);
290                         node = node->next;
291                         continue;
292                 }
293                 entry = SMB_STRNDUP(databuf.dptr, databuf.dsize);
294                 if (!entry) {
295                         SAFE_FREE(databuf.dptr);
296                         SAFE_FREE(keystr);
297                         break;
298                 }
299
300                 SAFE_FREE(databuf.dptr);
301
302                 valstr = (char *)SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
303                 if (!valstr) {
304                         SAFE_FREE(entry);
305                         SAFE_FREE(keystr);
306                         break;
307                 }
308
309                 asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE, (unsigned int)databuf.dsize - TIMEOUT_LEN);
310                 if (!fmt) {
311                         SAFE_FREE(valstr);
312                         SAFE_FREE(entry);
313                         SAFE_FREE(keystr);
314                         break;
315                 }
316                 status = sscanf(entry, fmt, &u, valstr);
317                 SAFE_FREE(fmt);
318
319                 if ( status != 2 ) {
320                         DEBUG(0,("gencache_iterate: invalid return from sscanf %d\n",status));
321                 }
322                 timeout = u;
323                 
324                 DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
325                            keystr, valstr, ctime(&timeout)));
326                 fn(keystr, valstr, timeout, data);
327                 
328                 SAFE_FREE(valstr);
329                 SAFE_FREE(entry);
330                 SAFE_FREE(keystr);
331                 node = node->next;
332         }
333         
334         tdb_search_list_free(first_node);
335 }
336
337 /********************************************************************
338  lock a key
339 ********************************************************************/
340
341 int gencache_lock_entry( const char *key )
342 {
343         if (!gencache_init())
344                 return -1;
345         
346         return tdb_lock_bystring(cache, key);
347 }
348
349 /********************************************************************
350  unlock a key
351 ********************************************************************/
352
353 void gencache_unlock_entry( const char *key )
354 {
355         if (!gencache_init())
356                 return;
357         
358         tdb_unlock_bystring(cache, key);
359         return;
360 }