Remove gencache_[un]lock_key
[obnox/samba/samba-obnox.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 struct 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         if ((keystr == NULL) || (value == NULL)) {
119                 return false;
120         }
121
122         if (!gencache_init()) return False;
123
124         if (asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value) == -1) {
125                 return False;
126         }
127
128         databuf = string_term_tdb_data(valstr);
129         DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
130                    " %s (%d seconds %s)\n", keystr, value,ctime(&timeout),
131                    (int)(timeout - time(NULL)), 
132                    timeout > time(NULL) ? "ahead" : "in the past"));
133
134         ret = tdb_store_bystring(cache, keystr, databuf, 0);
135         SAFE_FREE(valstr);
136
137         return ret == 0;
138 }
139
140 /**
141  * Delete one entry from the cache file.
142  *
143  * @param keystr string that represents a key of this entry
144  *
145  * @retval true upon successful deletion
146  * @retval false in case of failure
147  **/
148
149 bool gencache_del(const char *keystr)
150 {
151         int ret;
152
153         if (keystr == NULL) {
154                 return false;
155         }
156
157         if (!gencache_init()) return False;     
158
159         DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
160         ret = tdb_delete_bystring(cache, keystr);
161
162         return ret == 0;
163 }
164
165
166 /**
167  * Get existing entry from the cache file.
168  *
169  * @param keystr string that represents a key of this entry
170  * @param valstr buffer that is allocated and filled with the entry value
171  *        buffer's disposing must be done outside
172  * @param timeout pointer to a time_t that is filled with entry's
173  *        timeout
174  *
175  * @retval true when entry is successfuly fetched
176  * @retval False for failure
177  **/
178
179 bool gencache_get(const char *keystr, char **valstr, time_t *timeout)
180 {
181         TDB_DATA databuf;
182         time_t t;
183         char *endptr;
184
185         if (keystr == NULL) {
186                 return false;
187         }
188
189         if (!gencache_init()) {
190                 return False;
191         }
192
193         databuf = tdb_fetch_bystring(cache, keystr);
194
195         if (databuf.dptr == NULL) {
196                 DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
197                            keystr));
198                 return False;
199         }
200
201         t = strtol((const char *)databuf.dptr, &endptr, 10);
202
203         if ((endptr == NULL) || (*endptr != '/')) {
204                 DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
205                 SAFE_FREE(databuf.dptr);
206                 return False;
207         }
208
209         DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
210                    "timeout = %s", t > time(NULL) ? "valid" :
211                    "expired", keystr, endptr+1, ctime(&t)));
212
213         if (t <= time(NULL)) {
214
215                 /* We're expired, delete the entry */
216                 tdb_delete_bystring(cache, keystr);
217
218                 SAFE_FREE(databuf.dptr);
219                 return False;
220         }
221
222         if (valstr) {
223                 *valstr = SMB_STRDUP(endptr+1);
224                 if (*valstr == NULL) {
225                         SAFE_FREE(databuf.dptr);
226                         DEBUG(0, ("strdup failed\n"));
227                         return False;
228                 }
229         }
230
231         SAFE_FREE(databuf.dptr);
232
233         if (timeout) {
234                 *timeout = t;
235         }
236
237         return True;
238
239
240 /**
241  * Get existing entry from the cache file.
242  *
243  * @param keystr string that represents a key of this entry
244  * @param blob DATA_BLOB that is filled with entry's blob
245  * @param expired pointer to a bool that indicates whether the entry is expired
246  *
247  * @retval true when entry is successfuly fetched
248  * @retval False for failure
249  **/
250
251 bool gencache_get_data_blob(const char *keystr, DATA_BLOB *blob, bool *expired)
252 {
253         TDB_DATA databuf;
254         time_t t;
255         char *blob_type;
256         unsigned char *buf = NULL;
257         bool ret = False;
258         fstring valstr;
259         int buflen = 0, len = 0, blob_len = 0;
260         unsigned char *blob_buf = NULL;
261
262         if (keystr == NULL) {
263                 return false;
264         }
265
266         if (!gencache_init()) {
267                 return False;
268         }
269
270         databuf = tdb_fetch_bystring(cache, keystr);
271         if (!databuf.dptr) {
272                 DEBUG(10,("Cache entry with key = %s couldn't be found\n",
273                           keystr));
274                 return False;
275         }
276
277         buf = (unsigned char *)databuf.dptr;
278         buflen = databuf.dsize;
279
280         len += tdb_unpack(buf+len, buflen-len, "fB",
281                           &valstr,
282                           &blob_len, &blob_buf);
283         if (len == -1) {
284                 goto out;
285         }
286
287         t = strtol(valstr, &blob_type, 10);
288
289         if (strcmp(blob_type+1, BLOB_TYPE) != 0) {
290                 goto out;
291         }
292
293         DEBUG(10,("Returning %s cache entry: key = %s, "
294                   "timeout = %s", t > time(NULL) ? "valid" :
295                   "expired", keystr, ctime(&t)));
296
297         if (t <= time(NULL)) {
298                 /* We're expired */
299                 if (expired) {
300                         *expired = True;
301                 }
302         }
303
304         if (blob) {
305                 *blob = data_blob(blob_buf, blob_len);
306                 if (!blob->data) {
307                         goto out;
308                 }
309         }
310
311         ret = True;
312  out:
313         SAFE_FREE(blob_buf);
314         SAFE_FREE(databuf.dptr);
315
316         return ret;
317 }
318
319 /**
320  * Set an entry in the cache file. If there's no such
321  * one, then add it.
322  *
323  * @param keystr string that represents a key of this entry
324  * @param blob DATA_BLOB value being cached
325  * @param timeout time when the value is expired
326  *
327  * @retval true when entry is successfuly stored
328  * @retval false on failure
329  **/
330
331 bool gencache_set_data_blob(const char *keystr, const DATA_BLOB *blob, time_t timeout)
332 {
333         bool ret = False;
334         int tdb_ret;
335         TDB_DATA databuf;
336         char *valstr = NULL;
337         unsigned char *buf = NULL;
338         int len = 0, buflen = 0;
339
340         if ((keystr == NULL) || (blob == NULL)) {
341                 return false;
342         }
343
344         if (!gencache_init()) {
345                 return False;
346         }
347
348         if (asprintf(&valstr, "%12u/%s", (int)timeout, BLOB_TYPE) == -1) {
349                 return False;
350         }
351
352  again:
353         len = 0;
354
355         len += tdb_pack(buf+len, buflen-len, "fB",
356                         valstr,
357                         blob->length, blob->data);
358
359         if (len == -1) {
360                 goto out;
361         }
362
363         if (buflen < len) {
364                 SAFE_FREE(buf);
365                 buf = SMB_MALLOC_ARRAY(unsigned char, len);
366                 if (!buf) {
367                         goto out;
368                 }
369                 buflen = len;
370                 goto again;
371         }
372
373         databuf = make_tdb_data(buf, len);
374
375         DEBUG(10,("Adding cache entry with key = %s; "
376                   "blob size = %d and timeout = %s"
377                   "(%d seconds %s)\n", keystr, (int)databuf.dsize,
378                   ctime(&timeout), (int)(timeout - time(NULL)),
379                   timeout > time(NULL) ? "ahead" : "in the past"));
380
381         tdb_ret = tdb_store_bystring(cache, keystr, databuf, 0);
382         if (tdb_ret == 0) {
383                 ret = True;
384         }
385
386  out:
387         SAFE_FREE(valstr);
388         SAFE_FREE(buf);
389
390         return ret;
391 }
392
393 /**
394  * Iterate through all entries which key matches to specified pattern
395  *
396  * @param fn pointer to the function that will be supplied with each single
397  *        matching cache entry (key, value and timeout) as an arguments
398  * @param data void pointer to an arbitrary data that is passed directly to the fn
399  *        function on each call
400  * @param keystr_pattern pattern the existing entries' keys are matched to
401  *
402  **/
403
404 struct gencache_iterate_state {
405         void (*fn)(const char *key, const char *value, time_t timeout,
406                    void *priv);
407         const char *pattern;
408         void *priv;
409 };
410
411 static int gencache_iterate_fn(struct tdb_context *tdb, TDB_DATA key,
412                                TDB_DATA value, void *priv)
413 {
414         struct gencache_iterate_state *state =
415                 (struct gencache_iterate_state *)priv;
416         char *keystr;
417         char *free_key = NULL;
418         char *valstr;
419         char *free_val = NULL;
420         unsigned long u;
421         time_t timeout;
422         char *timeout_endp;
423
424         if (key.dptr[key.dsize-1] == '\0') {
425                 keystr = (char *)key.dptr;
426         } else {
427                 /* ensure 0-termination */
428                 keystr = SMB_STRNDUP((char *)key.dptr, key.dsize);
429                 free_key = keystr;
430         }
431
432         if ((value.dptr == NULL) || (value.dsize <= TIMEOUT_LEN)) {
433                 goto done;
434         }
435
436         if (fnmatch(state->pattern, keystr, 0) != 0) {
437                 goto done;
438         }
439
440         if (value.dptr[value.dsize-1] == '\0') {
441                 valstr = (char *)value.dptr;
442         } else {
443                 /* ensure 0-termination */
444                 valstr = SMB_STRNDUP((char *)value.dptr, value.dsize);
445                 free_val = valstr;
446         }
447
448         u = strtoul(valstr, &timeout_endp, 10);
449
450         if ((*timeout_endp != '/') || ((timeout_endp-valstr) != TIMEOUT_LEN)) {
451                 goto done;
452         }
453
454         timeout = u;
455         timeout_endp += 1;
456
457         DEBUG(10, ("Calling function with arguments "
458                    "(key = %s, value = %s, timeout = %s)\n",
459                    keystr, timeout_endp, ctime(&timeout)));
460         state->fn(keystr, timeout_endp, timeout, state->priv);
461
462  done:
463         SAFE_FREE(free_key);
464         SAFE_FREE(free_val);
465         return 0;
466 }
467
468 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
469                       void* data, const char* keystr_pattern)
470 {
471         struct gencache_iterate_state state;
472
473         if ((fn == NULL) || (keystr_pattern == NULL)) {
474                 return;
475         }
476
477         if (!gencache_init()) return;
478
479         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
480
481         state.fn = fn;
482         state.pattern = keystr_pattern;
483         state.priv = data;
484         tdb_traverse(cache, gencache_iterate_fn, &state);
485 }