r16267: Fix Klocwork #401, #402 - ensure format specifier
[mat/samba.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)
60                 DEBUG(5, ("Opening cache file at %s\n", cache_fname));
61         else {
62                 DEBUG(0, ("Filename allocation failed.\n"));
63                 return False;
64         }
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 = SMB_STRDUP(keystr);
125         keybuf.dsize = strlen(keystr)+1;
126         databuf.dptr = SMB_STRDUP(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         SAFE_FREE(keybuf.dptr);
136         SAFE_FREE(databuf.dptr);
137         
138         return ret == 0;
139 }
140
141
142 /**
143  * Set existing entry to the cache file.
144  *
145  * @param keystr string that represents a key of this entry
146  * @param valstr text representation value being cached
147  * @param timeout time when the value is expired
148  *
149  * @retval true when entry is successfuly set
150  * @retval false on failure
151  **/
152
153 BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout)
154 {
155         int ret = -1;
156         TDB_DATA keybuf, databuf;
157         char *old_valstr, *datastr;
158         time_t old_timeout;
159         
160         /* fail completely if get null pointers passed */
161         SMB_ASSERT(keystr && valstr);
162
163         if (!gencache_init()) return False;
164                         
165         /* 
166          * Check whether entry exists in the cache
167          * Don't verify gencache_get exit code, since the entry may be expired
168          */     
169         gencache_get(keystr, &old_valstr, &old_timeout);
170         
171         if (!(old_valstr && old_timeout)) return False;
172                 
173         DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \
174                    = %s\n", keystr, old_valstr, ctime(&old_timeout)));
175
176         asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr);
177         keybuf.dptr = SMB_STRDUP(keystr);
178         keybuf.dsize = strlen(keystr)+1;
179         databuf.dptr = SMB_STRDUP(datastr);
180         databuf.dsize = strlen(datastr)+1;
181         DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr,
182                       ctime(&timeout), (int)(timeout - time(NULL)),
183                       timeout > time(NULL) ? "ahead" : "in the past"));
184
185                 
186         ret = tdb_store(cache, keybuf, databuf, TDB_REPLACE);
187
188         SAFE_FREE(datastr);
189         SAFE_FREE(old_valstr);
190         SAFE_FREE(keybuf.dptr);
191         SAFE_FREE(databuf.dptr);
192         
193         return ret == 0;
194 }
195  
196
197 /**
198  * Delete one entry from the cache file.
199  *
200  * @param keystr string that represents a key of this entry
201  *
202  * @retval true upon successful deletion
203  * @retval false in case of failure
204  **/
205
206 BOOL gencache_del(const char *keystr)
207 {
208         int ret;
209         TDB_DATA keybuf;
210         
211         /* fail completely if get null pointers passed */
212         SMB_ASSERT(keystr);
213
214         if (!gencache_init()) return False;     
215         
216         keybuf.dptr = SMB_STRDUP(keystr);
217         keybuf.dsize = strlen(keystr)+1;
218         DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
219         ret = tdb_delete(cache, keybuf);
220         
221         SAFE_FREE(keybuf.dptr);
222         return ret == 0;
223 }
224
225
226 /**
227  * Get existing entry from the cache file.
228  *
229  * @param keystr string that represents a key of this entry
230  * @param valstr buffer that is allocated and filled with the entry value
231  *        buffer's disposing must be done outside
232  * @param timeout pointer to a time_t that is filled with entry's
233  *        timeout
234  *
235  * @retval true when entry is successfuly fetched
236  * @retval False for failure
237  **/
238
239 BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
240 {
241         TDB_DATA keybuf, databuf;
242
243         /* fail completely if get null pointers passed */
244         SMB_ASSERT(keystr);
245
246         if (!gencache_init()) {
247                 return False;
248         }
249         
250         keybuf.dptr = SMB_STRDUP(keystr);
251         keybuf.dsize = strlen(keystr)+1;
252         databuf = tdb_fetch(cache, keybuf);
253         SAFE_FREE(keybuf.dptr);
254         
255         if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
256                 char* entry_buf = SMB_STRNDUP(databuf.dptr, databuf.dsize);
257                 char *v;
258                 time_t t;
259                 unsigned u;
260                 int status;
261                 char *fmt;
262
263                 v = SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
264                 if (!v) {
265                         return False;
266                 }
267
268                 SAFE_FREE(databuf.dptr);
269
270                 asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE, (unsigned int)databuf.dsize - TIMEOUT_LEN);
271                 if (!fmt) {
272                         SAFE_FREE(v);
273                         return False;
274                 }
275
276                 status = sscanf(entry_buf, fmt, &u, v);
277                 SAFE_FREE(fmt);
278
279                 if ( status != 2 ) {
280                         DEBUG(0, ("gencache_get: Invalid return %d from sscanf\n", status ));
281                 }
282                 t = u;
283                 SAFE_FREE(entry_buf);
284
285                 DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
286                            "timeout = %s", t > time(NULL) ? "valid" :
287                            "expired", keystr, v, ctime(&t)));
288
289                 if (valstr) {
290                         *valstr = v;
291                 } else {
292                         SAFE_FREE(v);
293                 }
294
295                 if (timeout) {
296                         *timeout = t;
297                 }
298
299                 return t > time(NULL);
300
301         } 
302
303         SAFE_FREE(databuf.dptr);
304
305         if (valstr) {
306                 *valstr = NULL;
307         }
308         if (timeout) {
309                 timeout = NULL;
310         }
311
312         DEBUG(10, ("Cache entry with key = %s couldn't be found\n", keystr));
313         return False;
314 }
315
316 /**
317  * Iterate through all entries which key matches to specified pattern
318  *
319  * @param fn pointer to the function that will be supplied with each single
320  *        matching cache entry (key, value and timeout) as an arguments
321  * @param data void pointer to an arbitrary data that is passed directly to the fn
322  *        function on each call
323  * @param keystr_pattern pattern the existing entries' keys are matched to
324  *
325  **/
326
327 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
328                       void* data, const char* keystr_pattern)
329 {
330         TDB_LIST_NODE *node, *first_node;
331         TDB_DATA databuf;
332         char *keystr = NULL, *valstr = NULL, *entry = NULL;
333         time_t timeout = 0;
334         int status;
335         unsigned u;
336
337         /* fail completely if get null pointers passed */
338         SMB_ASSERT(fn && keystr_pattern);
339
340         if (!gencache_init()) return;
341
342         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
343         node = tdb_search_keys(cache, keystr_pattern);
344         first_node = node;
345         
346         while (node) {
347                 char *fmt;
348
349                 /* ensure null termination of the key string */
350                 keystr = SMB_STRNDUP(node->node_key.dptr, node->node_key.dsize);
351                 if (!keystr) {
352                         return;
353                 }
354                 
355                 /* 
356                  * We don't use gencache_get function, because we need to iterate through
357                  * all of the entries. Validity verification is up to fn routine.
358                  */
359                 databuf = tdb_fetch(cache, node->node_key);
360                 if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
361                         SAFE_FREE(databuf.dptr);
362                         SAFE_FREE(keystr);
363                         node = node->next;
364                         continue;
365                 }
366                 entry = SMB_STRNDUP(databuf.dptr, databuf.dsize);
367                 if (!entry) {
368                         SAFE_FREE(databuf.dptr);
369                         SAFE_FREE(keystr);
370                         return;
371                 }
372
373                 SAFE_FREE(databuf.dptr);
374
375                 valstr = SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
376                 if (!valstr) {
377                         SAFE_FREE(entry);
378                         SAFE_FREE(keystr);
379                         return;
380                 }
381
382                 asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE, (unsigned int)databuf.dsize - TIMEOUT_LEN);
383                 if (!fmt) {
384                         SAFE_FREE(valstr);
385                         SAFE_FREE(entry);
386                         SAFE_FREE(keystr);
387                         return;
388                 }
389                 status = sscanf(entry, fmt, &u, valstr);
390                 SAFE_FREE(fmt);
391
392                 if ( status != 2 ) {
393                         DEBUG(0,("gencache_iterate: invalid return from sscanf %d\n",status));
394                 }
395                 timeout = u;
396                 
397                 DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
398                            keystr, valstr, ctime(&timeout)));
399                 fn(keystr, valstr, timeout, data);
400                 
401                 SAFE_FREE(valstr);
402                 SAFE_FREE(entry);
403                 SAFE_FREE(keystr);
404                 node = node->next;
405         }
406         
407         tdb_search_list_free(first_node);
408 }
409
410 /********************************************************************
411  lock a key
412 ********************************************************************/
413
414 int gencache_lock_entry( const char *key )
415 {
416         if (!gencache_init())
417                 return -1;
418         
419         return tdb_lock_bystring(cache, key);
420 }
421
422 /********************************************************************
423  unlock a key
424 ********************************************************************/
425
426 void gencache_unlock_entry( const char *key )
427 {
428         if (!gencache_init())
429                 return;
430         
431         tdb_unlock_bystring(cache, key);
432         return;
433 }
434
435