by various parts of the Samba code
Copyright (C) Rafal Szczesniak 2002
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DBGC_CLASS DBGC_TDB
#define TIMEOUT_LEN 12
-#define CACHE_DATA_FMT "%12u/%s"
+#define CACHE_DATA_FMT "%12u/"
#define READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us"
#define BLOB_TYPE "DATA_BLOB"
#define BLOB_TYPE_LEN 9
-static TDB_CONTEXT *cache;
+static struct tdb_context *cache;
+static struct tdb_context *cache_notrans;
/**
* @file gencache.c
* false on failure
**/
-bool gencache_init(void)
+static bool gencache_init(void)
{
char* cache_fname = NULL;
-
+ int open_flags = O_RDWR|O_CREAT;
+
/* skip file open if it's already opened */
if (cache) return True;
DEBUG(5, ("Opening cache file at %s\n", cache_fname));
- cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
- O_RDWR|O_CREAT, 0644);
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, open_flags, 0644);
if (!cache && (errno == EACCES)) {
- cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDONLY, 0644);
+ open_flags = O_RDONLY;
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, open_flags,
+ 0644);
if (cache) {
DEBUG(5, ("gencache_init: Opening cache file %s read-only.\n", cache_fname));
}
DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
return False;
}
+
+ cache_fname = lock_path("gencache_notrans.tdb");
+
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+
+ cache_notrans = tdb_open_log(cache_fname, 0, TDB_CLEAR_IF_FIRST,
+ open_flags, 0644);
+ if (cache_notrans == NULL) {
+ DEBUG(5, ("Opening %s failed: %s\n", cache_fname,
+ strerror(errno)));
+ tdb_close(cache);
+ return false;
+ }
+
return True;
}
-
-/**
- * Cache shutdown function. Closes opened cache tdb file.
- *
- * @return true on successful closing the cache or
- * false on failure during cache shutdown
- **/
-
-bool gencache_shutdown(void)
+static TDB_DATA last_stabilize_key(void)
{
- int ret;
- /* tdb_close routine returns -1 on error */
- if (!cache) return False;
- DEBUG(5, ("Closing cache file\n"));
- ret = tdb_close(cache);
- cache = NULL;
- return ret != -1;
+ TDB_DATA result;
+ result.dptr = (uint8_t *)"@LAST_STABILIZED";
+ result.dsize = 17;
+ return result;
}
-
/**
* Set an entry in the cache file. If there's no such
* one, then add it.
*
* @param keystr string that represents a key of this entry
- * @param value text representation value being cached
+ * @param blob DATA_BLOB value being cached
* @param timeout time when the value is expired
*
* @retval true when entry is successfuly stored
* @retval false on failure
**/
-
-bool gencache_set(const char *keystr, const char *value, time_t timeout)
+
+bool gencache_set_data_blob(const char *keystr, const DATA_BLOB *blob,
+ time_t timeout)
{
int ret;
TDB_DATA databuf;
- char* valstr = NULL;
-
- /* fail completely if get null pointers passed */
- SMB_ASSERT(keystr && value);
+ char* val;
+ time_t last_stabilize;
+ static int writecount;
+
+ if (tdb_data_cmp(string_term_tdb_data(keystr),
+ last_stabilize_key()) == 0) {
+ DEBUG(10, ("Can't store %s as a key\n", keystr));
+ return false;
+ }
+
+ if ((keystr == NULL) || (blob == NULL)) {
+ return false;
+ }
if (!gencache_init()) return False;
-
- if (asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value) == -1) {
+
+ val = talloc_asprintf(talloc_tos(), CACHE_DATA_FMT, (int)timeout);
+ if (val == NULL) {
return False;
}
+ val = talloc_realloc(NULL, val, char, talloc_array_length(val)-1);
+ if (val == NULL) {
+ return false;
+ }
+ val = (char *)talloc_append_blob(NULL, val, *blob);
+ if (val == NULL) {
+ return false;
+ }
- databuf = string_term_tdb_data(valstr);
- DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
- " %s (%d seconds %s)\n", keystr, value,ctime(&timeout),
+ DEBUG(10, ("Adding cache entry with key = %s and timeout ="
+ " %s (%d seconds %s)\n", keystr, ctime(&timeout),
(int)(timeout - time(NULL)),
timeout > time(NULL) ? "ahead" : "in the past"));
- ret = tdb_store_bystring(cache, keystr, databuf, 0);
- SAFE_FREE(valstr);
-
+ ret = tdb_store_bystring(
+ cache_notrans, keystr,
+ make_tdb_data((uint8_t *)val, talloc_array_length(val)),
+ 0);
+ TALLOC_FREE(val);
+
+ if (ret != 0) {
+ return false;
+ }
+
+ /*
+ * Every 100 writes within a single process, stabilize the cache with
+ * a transaction. This is done to prevent a single transaction to
+ * become huge and chew lots of memory.
+ */
+ writecount += 1;
+ if (writecount > lp_parm_int(-1, "gencache", "stabilize_count", 100)) {
+ gencache_stabilize();
+ writecount = 0;
+ goto done;
+ }
+
+ /*
+ * Every 5 minutes, call gencache_stabilize() to not let grow
+ * gencache_notrans.tdb too large.
+ */
+
+ last_stabilize = 0;
+ databuf = tdb_fetch(cache_notrans, last_stabilize_key());
+ if ((databuf.dptr != NULL)
+ && (databuf.dptr[databuf.dsize-1] == '\0')) {
+ last_stabilize = atoi((char *)databuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ }
+ if ((last_stabilize
+ + lp_parm_int(-1, "gencache", "stabilize_interval", 300))
+ < time(NULL)) {
+ gencache_stabilize();
+ }
+
+done:
return ret == 0;
}
bool gencache_del(const char *keystr)
{
- int ret;
-
- /* fail completely if get null pointers passed */
- SMB_ASSERT(keystr);
+ bool exists;
+ bool ret = false;
+ char *value;
+
+ if (keystr == NULL) {
+ return false;
+ }
if (!gencache_init()) return False;
-
+
DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
- ret = tdb_delete_bystring(cache, keystr);
-
- return ret == 0;
+
+ /*
+ * We delete an element by setting its timeout to 0. This way we don't
+ * have to do a transaction on gencache.tdb every time we delete an
+ * element.
+ */
+
+ exists = gencache_get(keystr, &value, NULL);
+ if (exists) {
+ SAFE_FREE(value);
+ ret = gencache_set(keystr, "", 0);
+ }
+ return ret;
}
+static bool gencache_pull_timeout(char *val, time_t *pres, char **pendptr)
+{
+ time_t res;
+ char *endptr;
+
+ res = strtol(val, &endptr, 10);
+
+ if ((endptr == NULL) || (*endptr != '/')) {
+ DEBUG(2, ("Invalid gencache data format: %s\n", val));
+ return false;
+ }
+ if (pres != NULL) {
+ *pres = res;
+ }
+ if (pendptr != NULL) {
+ *pendptr = endptr;
+ }
+ return true;
+}
/**
* Get existing entry from the cache file.
*
* @param keystr string that represents a key of this entry
- * @param valstr buffer that is allocated and filled with the entry value
- * buffer's disposing must be done outside
- * @param timeout If == NULL, the caller is not interested in timed out
- * entries. If != NULL, return the timeout timestamp, the
- * caller must figure out itself if this entry is timed out.
+ * @param blob DATA_BLOB that is filled with entry's blob
+ * @param timeout pointer to a time_t that is filled with entry's
+ * timeout
*
* @retval true when entry is successfuly fetched
* @retval False for failure
**/
-bool gencache_get(const char *keystr, char **valstr, time_t *ptimeout)
+bool gencache_get_data_blob(const char *keystr, DATA_BLOB *blob,
+ time_t *timeout, bool *was_expired)
{
TDB_DATA databuf;
time_t t;
char *endptr;
+ bool expired = false;
+
+ if (keystr == NULL) {
+ goto fail;
+ }
- /* fail completely if get null pointers passed */
- SMB_ASSERT(keystr);
+ if (tdb_data_cmp(string_term_tdb_data(keystr),
+ last_stabilize_key()) == 0) {
+ DEBUG(10, ("Can't get %s as a key\n", keystr));
+ goto fail;
+ }
if (!gencache_init()) {
- return False;
+ goto fail;
}
- databuf = tdb_fetch_bystring(cache, keystr);
+ databuf = tdb_fetch_bystring(cache_notrans, keystr);
if (databuf.dptr == NULL) {
- DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
- keystr));
- return False;
+ databuf = tdb_fetch_bystring(cache, keystr);
}
- t = strtol((const char *)databuf.dptr, &endptr, 10);
+ if (databuf.dptr == NULL) {
+ DEBUG(10, ("Cache entry with key = %s couldn't be found \n",
+ keystr));
+ goto fail;
+ }
- if ((endptr == NULL) || (*endptr != '/')) {
- DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
+ if (!gencache_pull_timeout((char *)databuf.dptr, &t, &endptr)) {
SAFE_FREE(databuf.dptr);
- return False;
+ goto fail;
}
DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
"timeout = %s", t > time(NULL) ? "valid" :
"expired", keystr, endptr+1, ctime(&t)));
- if ((t <= time(NULL)) && (ptimeout == NULL)) {
+ if (t == 0) {
+ /* Deleted */
+ SAFE_FREE(databuf.dptr);
+ goto fail;
+ }
+
+ if (t <= time(NULL)) {
/*
- * The entry is expired, and the caller isn't interested in
- * timed out ones. Delete it.
+ * We're expired, delete the entry. We can't use gencache_del
+ * here, because that uses gencache_get_data_blob for checking
+ * the existence of a record. We know the thing exists and
+ * directly store an empty value with 0 timeout.
*/
-
- tdb_delete_bystring(cache, keystr);
+ gencache_set(keystr, "", 0);
SAFE_FREE(databuf.dptr);
- return False;
+
+ expired = true;
+ goto fail;
}
- if (valstr) {
- *valstr = SMB_STRDUP(endptr+1);
- if (*valstr == NULL) {
+ if (blob != NULL) {
+ *blob = data_blob(
+ endptr+1,
+ databuf.dsize - PTR_DIFF(endptr+1, databuf.dptr));
+ if (blob->data == NULL) {
SAFE_FREE(databuf.dptr);
- DEBUG(0, ("strdup failed\n"));
- return False;
+ DEBUG(0, ("memdup failed\n"));
+ goto fail;
}
}
SAFE_FREE(databuf.dptr);
- if (ptimeout) {
- *ptimeout = t;
+ if (timeout) {
+ *timeout = t;
}
return True;
-}
+
+fail:
+ if (was_expired != NULL) {
+ *was_expired = expired;
+ }
+ return false;
+}
+
+struct stabilize_state {
+ bool written;
+ bool error;
+};
+static int stabilize_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val,
+ void *priv);
/**
- * Get existing entry from the cache file.
- *
- * @param keystr string that represents a key of this entry
- * @param blob DATA_BLOB that is filled with entry's blob
- * @param expired pointer to a bool that indicates whether the entry is expired
+ * Stabilize gencache
*
- * @retval true when entry is successfuly fetched
- * @retval False for failure
- **/
+ * Migrate the clear-if-first gencache data to the stable,
+ * transaction-based gencache.tdb
+ */
-bool gencache_get_data_blob(const char *keystr, DATA_BLOB *blob, bool *expired)
+bool gencache_stabilize(void)
{
- TDB_DATA databuf;
- time_t t;
- char *blob_type;
- unsigned char *buf = NULL;
- bool ret = False;
- fstring valstr;
- int buflen = 0, len = 0, blob_len = 0;
- unsigned char *blob_buf = NULL;
-
- /* fail completely if get null pointers passed */
- SMB_ASSERT(keystr);
+ struct stabilize_state state;
+ int res;
+ char *now;
if (!gencache_init()) {
- return False;
+ return false;
}
- databuf = tdb_fetch_bystring(cache, keystr);
- if (!databuf.dptr) {
- DEBUG(10,("Cache entry with key = %s couldn't be found\n",
- keystr));
- return False;
+ res = tdb_transaction_start(cache);
+ if (res == -1) {
+ DEBUG(10, ("Could not start transaction on gencache.tdb: "
+ "%s\n", tdb_errorstr(cache)));
+ return false;
+ }
+ res = tdb_transaction_start(cache_notrans);
+ if (res == -1) {
+ tdb_transaction_cancel(cache);
+ DEBUG(10, ("Could not start transaction on "
+ "gencache_notrans.tdb: %s\n",
+ tdb_errorstr(cache_notrans)));
+ return false;
+ }
+
+ state.error = false;
+ state.written = false;
+
+ res = tdb_traverse(cache_notrans, stabilize_fn, &state);
+ if ((res == -1) || state.error) {
+ if ((tdb_transaction_cancel(cache_notrans) == -1)
+ || (tdb_transaction_cancel(cache) == -1)) {
+ smb_panic("tdb_transaction_cancel failed\n");
+ }
+ return false;
}
- buf = (unsigned char *)databuf.dptr;
- buflen = databuf.dsize;
+ if (!state.written) {
+ if ((tdb_transaction_cancel(cache_notrans) == -1)
+ || (tdb_transaction_cancel(cache) == -1)) {
+ smb_panic("tdb_transaction_cancel failed\n");
+ }
+ return true;
+ }
- len += tdb_unpack(buf+len, buflen-len, "fB",
- &valstr,
- &blob_len, &blob_buf);
- if (len == -1) {
- goto out;
+ res = tdb_transaction_commit(cache);
+ if (res == -1) {
+ DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
+ "%s\n", tdb_errorstr(cache)));
+ if (tdb_transaction_cancel(cache_notrans) == -1) {
+ smb_panic("tdb_transaction_cancel failed\n");
+ }
+ return false;
}
- t = strtol(valstr, &blob_type, 10);
+ res = tdb_transaction_commit(cache_notrans);
+ if (res == -1) {
+ DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
+ "%s\n", tdb_errorstr(cache)));
+ return false;
+ }
- if (strcmp(blob_type+1, BLOB_TYPE) != 0) {
- goto out;
+ now = talloc_asprintf(talloc_tos(), "%d", (int)time(NULL));
+ if (now != NULL) {
+ tdb_store(cache_notrans, last_stabilize_key(),
+ string_term_tdb_data(now), 0);
+ TALLOC_FREE(now);
}
- DEBUG(10,("Returning %s cache entry: key = %s, "
- "timeout = %s", t > time(NULL) ? "valid" :
- "expired", keystr, ctime(&t)));
+ return true;
+}
- if (t <= time(NULL)) {
- /* We're expired */
- if (expired) {
- *expired = True;
- }
+static int stabilize_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val,
+ void *priv)
+{
+ struct stabilize_state *state = (struct stabilize_state *)priv;
+ int res;
+ time_t timeout;
+
+ if (tdb_data_cmp(key, last_stabilize_key()) == 0) {
+ return 0;
}
- if (blob) {
- *blob = data_blob(blob_buf, blob_len);
- if (!blob->data) {
- goto out;
+ if (!gencache_pull_timeout((char *)val.dptr, &timeout, NULL)) {
+ DEBUG(10, ("Ignoring invalid entry\n"));
+ return 0;
+ }
+ if ((timeout < time(NULL)) || (val.dsize == 0)) {
+ res = tdb_delete(cache, key);
+ if ((res == -1) && (tdb_error(cache) == TDB_ERR_NOEXIST)) {
+ res = 0;
+ } else {
+ state->written = true;
+ }
+ } else {
+ res = tdb_store(cache, key, val, 0);
+ if (res == 0) {
+ state->written = true;
}
}
- ret = True;
- out:
- SAFE_FREE(blob_buf);
- SAFE_FREE(databuf.dptr);
+ if (res == -1) {
+ DEBUG(10, ("Transfer to gencache.tdb failed: %s\n",
+ tdb_errorstr(cache)));
+ state->error = true;
+ return -1;
+ }
- return ret;
+ if (tdb_delete(cache_notrans, key) == -1) {
+ DEBUG(10, ("tdb_delete from gencache_notrans.tdb failed: "
+ "%s\n", tdb_errorstr(cache_notrans)));
+ state->error = true;
+ return -1;
+ }
+ return 0;
}
/**
- * Set an entry in the cache file. If there's no such
- * one, then add it.
+ * Get existing entry from the cache file.
*
* @param keystr string that represents a key of this entry
- * @param blob DATA_BLOB value being cached
- * @param timeout time when the value is expired
+ * @param valstr buffer that is allocated and filled with the entry value
+ * buffer's disposing must be done outside
+ * @param timeout pointer to a time_t that is filled with entry's
+ * timeout
*
- * @retval true when entry is successfuly stored
- * @retval false on failure
+ * @retval true when entry is successfuly fetched
+ * @retval False for failure
**/
-bool gencache_set_data_blob(const char *keystr, const DATA_BLOB *blob, time_t timeout)
+bool gencache_get(const char *keystr, char **value, time_t *ptimeout)
{
+ DATA_BLOB blob;
bool ret = False;
- int tdb_ret;
- TDB_DATA databuf;
- char *valstr = NULL;
- unsigned char *buf = NULL;
- int len = 0, buflen = 0;
-
- /* fail completely if get null pointers passed */
- SMB_ASSERT(keystr && blob);
-
- if (!gencache_init()) {
- return False;
- }
- if (asprintf(&valstr, "%12u/%s", (int)timeout, BLOB_TYPE) == -1) {
- return False;
+ ret = gencache_get_data_blob(keystr, &blob, ptimeout, NULL);
+ if (!ret) {
+ return false;
}
-
- again:
- len = 0;
-
- len += tdb_pack(buf+len, buflen-len, "fB",
- valstr,
- blob->length, blob->data);
-
- if (len == -1) {
- goto out;
+ if ((blob.data == NULL) || (blob.length == 0)) {
+ SAFE_FREE(blob.data);
+ return false;
}
-
- if (buflen < len) {
- SAFE_FREE(buf);
- buf = SMB_MALLOC_ARRAY(unsigned char, len);
- if (!buf) {
- goto out;
- }
- buflen = len;
- goto again;
+ if (blob.data[blob.length-1] != '\0') {
+ /* Not NULL terminated, can't be a string */
+ SAFE_FREE(blob.data);
+ return false;
}
-
- databuf = make_tdb_data(buf, len);
-
- DEBUG(10,("Adding cache entry with key = %s; "
- "blob size = %d and timeout = %s"
- "(%d seconds %s)\n", keystr, (int)databuf.dsize,
- ctime(&timeout), (int)(timeout - time(NULL)),
- timeout > time(NULL) ? "ahead" : "in the past"));
-
- tdb_ret = tdb_store_bystring(cache, keystr, databuf, 0);
- if (tdb_ret == 0) {
- ret = True;
+ *value = SMB_STRDUP((char *)blob.data);
+ data_blob_free(&blob);
+ if (*value == NULL) {
+ return false;
}
+ return true;
+}
- out:
- SAFE_FREE(valstr);
- SAFE_FREE(buf);
+/**
+ * Set an entry in the cache file. If there's no such
+ * one, then add it.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param value text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly stored
+ * @retval false on failure
+ **/
- return ret;
+bool gencache_set(const char *keystr, const char *value, time_t timeout)
+{
+ DATA_BLOB blob = data_blob_const(value, strlen(value)+1);
+ return gencache_set_data_blob(keystr, &blob, timeout);
}
/**
*
**/
-void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
- void* data, const char* keystr_pattern)
+struct gencache_iterate_state {
+ void (*fn)(const char *key, const char *value, time_t timeout,
+ void *priv);
+ const char *pattern;
+ void *priv;
+ bool in_persistent;
+};
+
+static int gencache_iterate_fn(struct tdb_context *tdb, TDB_DATA key,
+ TDB_DATA value, void *priv)
{
- TDB_LIST_NODE *node, *first_node;
- TDB_DATA databuf;
- char *keystr = NULL, *valstr = NULL, *entry = NULL;
- time_t timeout = 0;
- int status;
- unsigned u;
+ struct gencache_iterate_state *state =
+ (struct gencache_iterate_state *)priv;
+ char *keystr;
+ char *free_key = NULL;
+ char *valstr;
+ char *free_val = NULL;
+ unsigned long u;
+ time_t timeout;
+ char *timeout_endp;
+
+ if (tdb_data_cmp(key, last_stabilize_key()) == 0) {
+ return 0;
+ }
- /* fail completely if get null pointers passed */
- SMB_ASSERT(fn && keystr_pattern);
+ if (state->in_persistent && tdb_exists(cache_notrans, key)) {
+ return 0;
+ }
- if (!gencache_init()) return;
+ if (key.dptr[key.dsize-1] == '\0') {
+ keystr = (char *)key.dptr;
+ } else {
+ /* ensure 0-termination */
+ keystr = SMB_STRNDUP((char *)key.dptr, key.dsize);
+ free_key = keystr;
+ }
- DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
- node = tdb_search_keys(cache, keystr_pattern);
- first_node = node;
-
- while (node) {
- char *fmt;
-
- /* ensure null termination of the key string */
- keystr = SMB_STRNDUP((const char *)node->node_key.dptr, node->node_key.dsize);
- if (!keystr) {
- break;
- }
-
- /*
- * We don't use gencache_get function, because we need to iterate through
- * all of the entries. Validity verification is up to fn routine.
- */
- databuf = tdb_fetch(cache, node->node_key);
- if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
- SAFE_FREE(databuf.dptr);
- SAFE_FREE(keystr);
- node = node->next;
- continue;
- }
- entry = SMB_STRNDUP((const char *)databuf.dptr, databuf.dsize);
- if (!entry) {
- SAFE_FREE(databuf.dptr);
- SAFE_FREE(keystr);
- break;
- }
+ if ((value.dptr == NULL) || (value.dsize <= TIMEOUT_LEN)) {
+ goto done;
+ }
- SAFE_FREE(databuf.dptr);
+ if (fnmatch(state->pattern, keystr, 0) != 0) {
+ goto done;
+ }
- valstr = (char *)SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
- if (!valstr) {
- SAFE_FREE(entry);
- SAFE_FREE(keystr);
- break;
- }
+ if (value.dptr[value.dsize-1] == '\0') {
+ valstr = (char *)value.dptr;
+ } else {
+ /* ensure 0-termination */
+ valstr = SMB_STRNDUP((char *)value.dptr, value.dsize);
+ free_val = valstr;
+ }
- if (asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE,
- (unsigned int)databuf.dsize - TIMEOUT_LEN)
- == -1) {
- SAFE_FREE(valstr);
- SAFE_FREE(entry);
- SAFE_FREE(keystr);
- break;
- }
- status = sscanf(entry, fmt, &u, valstr);
- SAFE_FREE(fmt);
+ u = strtoul(valstr, &timeout_endp, 10);
- if ( status != 2 ) {
- DEBUG(0,("gencache_iterate: invalid return from sscanf %d\n",status));
- }
- timeout = u;
-
- DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
- keystr, valstr, ctime(&timeout)));
- fn(keystr, valstr, timeout, data);
-
- SAFE_FREE(valstr);
- SAFE_FREE(entry);
- SAFE_FREE(keystr);
- node = node->next;
- }
-
- tdb_search_list_free(first_node);
-}
+ if ((*timeout_endp != '/') || ((timeout_endp-valstr) != TIMEOUT_LEN)) {
+ goto done;
+ }
-/********************************************************************
- lock a key
-********************************************************************/
+ timeout = u;
+ timeout_endp += 1;
-int gencache_lock_entry( const char *key )
-{
- if (!gencache_init())
- return -1;
-
- return tdb_lock_bystring(cache, key);
-}
+ DEBUG(10, ("Calling function with arguments "
+ "(key = %s, value = %s, timeout = %s)\n",
+ keystr, timeout_endp, ctime(&timeout)));
+ state->fn(keystr, timeout_endp, timeout, state->priv);
-/********************************************************************
- unlock a key
-********************************************************************/
+ done:
+ SAFE_FREE(free_key);
+ SAFE_FREE(free_val);
+ return 0;
+}
-void gencache_unlock_entry( const char *key )
+void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
+ void* data, const char* keystr_pattern)
{
- if (!gencache_init())
+ struct gencache_iterate_state state;
+
+ if ((fn == NULL) || (keystr_pattern == NULL)) {
return;
-
- tdb_unlock_bystring(cache, key);
- return;
+ }
+
+ if (!gencache_init()) return;
+
+ DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
+
+ state.fn = fn;
+ state.pattern = keystr_pattern;
+ state.priv = data;
+
+ state.in_persistent = false;
+ tdb_traverse(cache_notrans, gencache_iterate_fn, &state);
+
+ state.in_persistent = true;
+ tdb_traverse(cache, gencache_iterate_fn, &state);
}