netapi: add -f switch for DsGetDCName() example and be more verbose on output.
[kai/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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24
25 #undef  DBGC_CLASS
26 #define DBGC_CLASS DBGC_TDB
27
28 #define TIMEOUT_LEN 12
29 #define CACHE_DATA_FMT  "%12u/%s"
30 #define READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us"
31 #define BLOB_TYPE "DATA_BLOB"
32 #define BLOB_TYPE_LEN 9
33
34 static TDB_CONTEXT *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         cache_fname = lock_path("gencache.tdb");
60
61         DEBUG(5, ("Opening cache file at %s\n", cache_fname));
62
63         cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
64                              O_RDWR|O_CREAT, 0644);
65
66         if (!cache && (errno == EACCES)) {
67                 cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDONLY, 0644);
68                 if (cache) {
69                         DEBUG(5, ("gencache_init: Opening cache file %s read-only.\n", cache_fname));
70                 }
71         }
72
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         int ret;
91         /* tdb_close routine returns -1 on error */
92         if (!cache) return False;
93         DEBUG(5, ("Closing cache file\n"));
94         ret = tdb_close(cache);
95         cache = NULL;
96         return ret != -1;
97 }
98
99
100 /**
101  * Set an entry in the cache file. If there's no such
102  * one, then add it.
103  *
104  * @param keystr string that represents a key of this entry
105  * @param value text representation value being cached
106  * @param timeout time when the value is expired
107  *
108  * @retval true when entry is successfuly stored
109  * @retval false on failure
110  **/
111  
112 bool gencache_set(const char *keystr, const char *value, time_t timeout)
113 {
114         int ret;
115         TDB_DATA databuf;
116         char* valstr = NULL;
117         
118         /* fail completely if get null pointers passed */
119         SMB_ASSERT(keystr && value);
120
121         if (!gencache_init()) return False;
122         
123         if (asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value) == -1) {
124                 return False;
125         }
126
127         databuf = string_term_tdb_data(valstr);
128         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
129                    " %s (%d seconds %s)\n", keystr, value,ctime(&timeout),
130                    (int)(timeout - time(NULL)), 
131                    timeout > time(NULL) ? "ahead" : "in the past"));
132
133         ret = tdb_store_bystring(cache, keystr, 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         
152         /* fail completely if get null pointers passed */
153         SMB_ASSERT(keystr);
154
155         if (!gencache_init()) return False;     
156         
157         DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
158         ret = tdb_delete_bystring(cache, keystr);
159         
160         return ret == 0;
161 }
162
163
164 /**
165  * Get existing entry from the cache file.
166  *
167  * @param keystr string that represents a key of this entry
168  * @param valstr buffer that is allocated and filled with the entry value
169  *        buffer's disposing must be done outside
170  * @param timeout pointer to a time_t that is filled with entry's
171  *        timeout
172  *
173  * @retval true when entry is successfuly fetched
174  * @retval False for failure
175  **/
176
177 bool gencache_get(const char *keystr, char **valstr, time_t *timeout)
178 {
179         TDB_DATA databuf;
180         time_t t;
181         char *endptr;
182
183         /* fail completely if get null pointers passed */
184         SMB_ASSERT(keystr);
185
186         if (!gencache_init()) {
187                 return False;
188         }
189
190         databuf = tdb_fetch_bystring(cache, keystr);
191
192         if (databuf.dptr == NULL) {
193                 DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
194                            keystr));
195                 return False;
196         }
197
198         t = strtol((const char *)databuf.dptr, &endptr, 10);
199
200         if ((endptr == NULL) || (*endptr != '/')) {
201                 DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
202                 SAFE_FREE(databuf.dptr);
203                 return False;
204         }
205
206         DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
207                    "timeout = %s", t > time(NULL) ? "valid" :
208                    "expired", keystr, endptr+1, ctime(&t)));
209
210         if (t <= time(NULL)) {
211
212                 /* We're expired, delete the entry */
213                 tdb_delete_bystring(cache, keystr);
214
215                 SAFE_FREE(databuf.dptr);
216                 return False;
217         }
218
219         if (valstr) {
220                 *valstr = SMB_STRDUP(endptr+1);
221                 if (*valstr == NULL) {
222                         SAFE_FREE(databuf.dptr);
223                         DEBUG(0, ("strdup failed\n"));
224                         return False;
225                 }
226         }
227         
228         SAFE_FREE(databuf.dptr);
229
230         if (timeout) {
231                 *timeout = t;
232         }
233
234         return True;
235
236
237 /**
238  * Get existing entry from the cache file.
239  *
240  * @param keystr string that represents a key of this entry
241  * @param blob DATA_BLOB that is filled with entry's blob
242  * @param expired pointer to a bool that indicates whether the entry is expired
243  *
244  * @retval true when entry is successfuly fetched
245  * @retval False for failure
246  **/
247
248 bool gencache_get_data_blob(const char *keystr, DATA_BLOB *blob, bool *expired)
249 {
250         TDB_DATA databuf;
251         time_t t;
252         char *blob_type;
253         unsigned char *buf = NULL;
254         bool ret = False;
255         fstring valstr;
256         int buflen = 0, len = 0, blob_len = 0;
257         unsigned char *blob_buf = NULL;
258
259         /* fail completely if get null pointers passed */
260         SMB_ASSERT(keystr);
261
262         if (!gencache_init()) {
263                 return False;
264         }
265
266         databuf = tdb_fetch_bystring(cache, keystr);
267         if (!databuf.dptr) {
268                 DEBUG(10,("Cache entry with key = %s couldn't be found\n",
269                           keystr));
270                 return False;
271         }
272
273         buf = (unsigned char *)databuf.dptr;
274         buflen = databuf.dsize;
275
276         len += tdb_unpack(buf+len, buflen-len, "fB",
277                           &valstr,
278                           &blob_len, &blob_buf);
279         if (len == -1) {
280                 goto out;
281         }
282
283         t = strtol(valstr, &blob_type, 10);
284
285         if (strcmp(blob_type+1, BLOB_TYPE) != 0) {
286                 goto out;
287         }
288
289         DEBUG(10,("Returning %s cache entry: key = %s, "
290                   "timeout = %s", t > time(NULL) ? "valid" :
291                   "expired", keystr, ctime(&t)));
292
293         if (t <= time(NULL)) {
294                 /* We're expired */
295                 if (expired) {
296                         *expired = True;
297                 }
298         }
299
300         if (blob) {
301                 *blob = data_blob(blob_buf, blob_len);
302                 if (!blob->data) {
303                         goto out;
304                 }
305         }
306
307         ret = True;
308  out:
309         SAFE_FREE(blob_buf);
310         SAFE_FREE(databuf.dptr);
311
312         return ret;
313 }
314
315 /**
316  * Set an entry in the cache file. If there's no such
317  * one, then add it.
318  *
319  * @param keystr string that represents a key of this entry
320  * @param blob DATA_BLOB value being cached
321  * @param timeout time when the value is expired
322  *
323  * @retval true when entry is successfuly stored
324  * @retval false on failure
325  **/
326
327 bool gencache_set_data_blob(const char *keystr, DATA_BLOB *blob, time_t timeout)
328 {
329         bool ret = False;
330         int tdb_ret;
331         TDB_DATA databuf;
332         char *valstr = NULL;
333         unsigned char *buf = NULL;
334         int len = 0, buflen = 0;
335
336         /* fail completely if get null pointers passed */
337         SMB_ASSERT(keystr && blob);
338
339         if (!gencache_init()) {
340                 return False;
341         }
342
343         if (asprintf(&valstr, "%12u/%s", (int)timeout, BLOB_TYPE) == -1) {
344                 return False;
345         }
346
347  again:
348         len = 0;
349
350         len += tdb_pack(buf+len, buflen-len, "fB",
351                         valstr,
352                         blob->length, blob->data);
353
354         if (len == -1) {
355                 goto out;
356         }
357
358         if (buflen < len) {
359                 SAFE_FREE(buf);
360                 buf = SMB_MALLOC_ARRAY(unsigned char, len);
361                 if (!buf) {
362                         goto out;
363                 }
364                 buflen = len;
365                 goto again;
366         }
367
368         databuf = make_tdb_data(buf, len);
369
370         DEBUG(10,("Adding cache entry with key = %s; "
371                   "blob size = %d and timeout = %s"
372                   "(%d seconds %s)\n", keystr, (int)databuf.dsize,
373                   ctime(&timeout), (int)(timeout - time(NULL)),
374                   timeout > time(NULL) ? "ahead" : "in the past"));
375
376         tdb_ret = tdb_store_bystring(cache, keystr, databuf, 0);
377         if (tdb_ret == 0) {
378                 ret = True;
379         }
380
381  out:
382         SAFE_FREE(valstr);
383         SAFE_FREE(buf);
384
385         return ret;
386 }
387
388 /**
389  * Iterate through all entries which key matches to specified pattern
390  *
391  * @param fn pointer to the function that will be supplied with each single
392  *        matching cache entry (key, value and timeout) as an arguments
393  * @param data void pointer to an arbitrary data that is passed directly to the fn
394  *        function on each call
395  * @param keystr_pattern pattern the existing entries' keys are matched to
396  *
397  **/
398
399 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
400                       void* data, const char* keystr_pattern)
401 {
402         TDB_LIST_NODE *node, *first_node;
403         TDB_DATA databuf;
404         char *keystr = NULL, *valstr = NULL, *entry = NULL;
405         time_t timeout = 0;
406         int status;
407         unsigned u;
408
409         /* fail completely if get null pointers passed */
410         SMB_ASSERT(fn && keystr_pattern);
411
412         if (!gencache_init()) return;
413
414         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
415         node = tdb_search_keys(cache, keystr_pattern);
416         first_node = node;
417         
418         while (node) {
419                 char *fmt;
420
421                 /* ensure null termination of the key string */
422                 keystr = SMB_STRNDUP((const char *)node->node_key.dptr, node->node_key.dsize);
423                 if (!keystr) {
424                         break;
425                 }
426                 
427                 /* 
428                  * We don't use gencache_get function, because we need to iterate through
429                  * all of the entries. Validity verification is up to fn routine.
430                  */
431                 databuf = tdb_fetch(cache, node->node_key);
432                 if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
433                         SAFE_FREE(databuf.dptr);
434                         SAFE_FREE(keystr);
435                         node = node->next;
436                         continue;
437                 }
438                 entry = SMB_STRNDUP((const char *)databuf.dptr, databuf.dsize);
439                 if (!entry) {
440                         SAFE_FREE(databuf.dptr);
441                         SAFE_FREE(keystr);
442                         break;
443                 }
444
445                 SAFE_FREE(databuf.dptr);
446
447                 valstr = (char *)SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
448                 if (!valstr) {
449                         SAFE_FREE(entry);
450                         SAFE_FREE(keystr);
451                         break;
452                 }
453
454                 if (asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE,
455                              (unsigned int)databuf.dsize - TIMEOUT_LEN)
456                     == -1) {
457                         SAFE_FREE(valstr);
458                         SAFE_FREE(entry);
459                         SAFE_FREE(keystr);
460                         break;
461                 }
462                 status = sscanf(entry, fmt, &u, valstr);
463                 SAFE_FREE(fmt);
464
465                 if ( status != 2 ) {
466                         DEBUG(0,("gencache_iterate: invalid return from sscanf %d\n",status));
467                 }
468                 timeout = u;
469                 
470                 DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
471                            keystr, valstr, ctime(&timeout)));
472                 fn(keystr, valstr, timeout, data);
473                 
474                 SAFE_FREE(valstr);
475                 SAFE_FREE(entry);
476                 SAFE_FREE(keystr);
477                 node = node->next;
478         }
479         
480         tdb_search_list_free(first_node);
481 }
482
483 /********************************************************************
484  lock a key
485 ********************************************************************/
486
487 int gencache_lock_entry( const char *key )
488 {
489         if (!gencache_init())
490                 return -1;
491         
492         return tdb_lock_bystring(cache, key);
493 }
494
495 /********************************************************************
496  unlock a key
497 ********************************************************************/
498
499 void gencache_unlock_entry( const char *key )
500 {
501         if (!gencache_init())
502                 return;
503         
504         tdb_unlock_bystring(cache, key);
505         return;
506 }