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