root/source3/lib/gencache.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gencache_init
  2. gencache_shutdown
  3. gencache_set
  4. gencache_del
  5. gencache_get
  6. gencache_get_data_blob
  7. gencache_set_data_blob
  8. gencache_iterate_fn
  9. gencache_iterate
  10. gencache_lock_entry
  11. gencache_unlock_entry

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, const DATA_BLOB *blob, time_t timeout)
     /* [<][>][^][v][top][bottom][index][help] */
 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 struct gencache_iterate_state {
 400         void (*fn)(const char *key, const char *value, time_t timeout,
 401                    void *priv);
 402         const char *pattern;
 403         void *priv;
 404 };
 405 
 406 static int gencache_iterate_fn(struct tdb_context *tdb, TDB_DATA key,
     /* [<][>][^][v][top][bottom][index][help] */
 407                                TDB_DATA value, void *priv)
 408 {
 409         struct gencache_iterate_state *state =
 410                 (struct gencache_iterate_state *)priv;
 411         char *keystr;
 412         char *free_key = NULL;
 413         char *valstr;
 414         char *free_val = NULL;
 415         unsigned long u;
 416         time_t timeout;
 417         char *timeout_endp;
 418 
 419         if (key.dptr[key.dsize-1] == '\0') {
 420                 keystr = (char *)key.dptr;
 421         } else {
 422                 /* ensure 0-termination */
 423                 keystr = SMB_STRNDUP((char *)key.dptr, key.dsize);
 424                 free_key = keystr;
 425         }
 426 
 427         if ((value.dptr == NULL) || (value.dsize <= TIMEOUT_LEN)) {
 428                 goto done;
 429         }
 430 
 431         if (fnmatch(state->pattern, keystr, 0) != 0) {
 432                 goto done;
 433         }
 434 
 435         if (value.dptr[value.dsize-1] == '\0') {
 436                 valstr = (char *)value.dptr;
 437         } else {
 438                 /* ensure 0-termination */
 439                 valstr = SMB_STRNDUP((char *)value.dptr, value.dsize);
 440                 free_val = valstr;
 441         }
 442 
 443         u = strtoul(valstr, &timeout_endp, 10);
 444 
 445         if ((*timeout_endp != '/') || ((timeout_endp-valstr) != TIMEOUT_LEN)) {
 446                 goto done;
 447         }
 448 
 449         timeout = u;
 450         timeout_endp += 1;
 451 
 452         DEBUG(10, ("Calling function with arguments "
 453                    "(key = %s, value = %s, timeout = %s)\n",
 454                    keystr, timeout_endp, ctime(&timeout)));
 455         state->fn(keystr, timeout_endp, timeout, state->priv);
 456 
 457  done:
 458         SAFE_FREE(free_key);
 459         SAFE_FREE(free_val);
 460         return 0;
 461 }
 462 
 463 void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
     /* [<][>][^][v][top][bottom][index][help] */
 464                       void* data, const char* keystr_pattern)
 465 {
 466         struct gencache_iterate_state state;
 467 
 468         /* fail completely if get null pointers passed */
 469         SMB_ASSERT(fn && keystr_pattern);
 470 
 471         if (!gencache_init()) return;
 472 
 473         DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
 474 
 475         state.fn = fn;
 476         state.pattern = keystr_pattern;
 477         state.priv = data;
 478         tdb_traverse(cache, gencache_iterate_fn, &state);
 479 }
 480 
 481 /********************************************************************
 482  lock a key
 483 ********************************************************************/
 484 
 485 int gencache_lock_entry( const char *key )
     /* [<][>][^][v][top][bottom][index][help] */
 486 {
 487         if (!gencache_init())
 488                 return -1;
 489 
 490         return tdb_lock_bystring(cache, key);
 491 }
 492 
 493 /********************************************************************
 494  unlock a key
 495 ********************************************************************/
 496 
 497 void gencache_unlock_entry( const char *key )
     /* [<][>][^][v][top][bottom][index][help] */
 498 {
 499         if (!gencache_init())
 500                 return;
 501 
 502         tdb_unlock_bystring(cache, key);
 503         return;
 504 }

/* [<][>][^][v][top][bottom][index][help] */