2002-09-11 18:07:21 +04:00
/*
Unix SMB / CIFS implementation .
Generic , persistent and shared between processes cache mechanism for use
by various parts of the Samba code
Copyright ( C ) Rafal Szczesniak 2002
2009-09-20 20:10:01 +04:00
Copyright ( C ) Volker Lendecke 2009
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2002-09-11 18:07:21 +04:00
( at your option ) any later version .
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
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 .
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2002-09-11 18:07:21 +04:00
*/
# include "includes.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2011-02-25 18:34:46 +03:00
# include "system/glob.h"
2011-05-05 13:25:29 +04:00
# include "util_tdb.h"
2002-09-11 18:07:21 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_TDB
# define TIMEOUT_LEN 12
2009-07-14 13:33:04 +04:00
# define CACHE_DATA_FMT "%12u / "
2006-06-16 01:03:40 +04:00
# define READ_CACHE_DATA_FMT_TEMPLATE "%%12u / %%%us"
2007-08-28 16:40:01 +04:00
# define BLOB_TYPE "DATA_BLOB"
# define BLOB_TYPE_LEN 9
2002-09-11 18:07:21 +04:00
2009-07-10 13:00:24 +04:00
static struct tdb_context * cache ;
2009-07-13 19:04:29 +04:00
static struct tdb_context * cache_notrans ;
2002-09-11 18:07:21 +04:00
/**
* @ file gencache . c
* @ brief Generic , persistent and shared between processes cache mechanism
* for use by various parts of the Samba code
*
* */
/**
* Cache initialisation function . Opens cache tdb file or creates
* it if does not exist .
*
* @ return true on successful initialisation of the cache or
* false on failure
* */
2009-07-10 14:24:56 +04:00
static bool gencache_init ( void )
2002-09-11 18:07:21 +04:00
{
char * cache_fname = NULL ;
2009-07-13 19:04:29 +04:00
int open_flags = O_RDWR | O_CREAT ;
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
/* skip file open if it's already opened */
if ( cache ) return True ;
2013-03-28 14:00:27 +04:00
cache_fname = cache_path ( " gencache.tdb " ) ;
2002-09-11 18:07:21 +04:00
2006-09-10 01:05:51 +04:00
DEBUG ( 5 , ( " Opening cache file at %s \n " , cache_fname ) ) ;
2010-09-27 16:46:07 +04:00
cache = tdb_open_log ( cache_fname , 0 , TDB_DEFAULT | TDB_INCOMPATIBLE_HASH , open_flags , 0644 ) ;
2009-12-04 18:46:34 +03:00
if ( cache ) {
int ret ;
ret = tdb_check ( cache , NULL , NULL ) ;
if ( ret ! = 0 ) {
tdb_close ( cache ) ;
2013-06-11 23:03:22 +04:00
/*
* Retry with CLEAR_IF_FIRST .
*
* Warning : Converting this to dbwrap won ' t work
* directly . gencache . c does transactions on this tdb ,
* and dbwrap forbids this for CLEAR_IF_FIRST
* databases . tdb does allow transactions on
* CLEAR_IF_FIRST databases , so lets use it here to
* clean up a broken database .
*/
cache = tdb_open_log ( cache_fname , 0 ,
TDB_DEFAULT |
TDB_INCOMPATIBLE_HASH |
TDB_CLEAR_IF_FIRST ,
open_flags , 0644 ) ;
2009-12-04 18:46:34 +03:00
}
}
2002-09-11 18:07:21 +04:00
2006-10-03 03:34:03 +04:00
if ( ! cache & & ( errno = = EACCES ) ) {
2009-07-13 19:04:29 +04:00
open_flags = O_RDONLY ;
2010-09-27 16:46:07 +04:00
cache = tdb_open_log ( cache_fname , 0 , TDB_DEFAULT | TDB_INCOMPATIBLE_HASH , open_flags ,
2009-07-13 19:04:29 +04:00
0644 ) ;
2006-10-03 03:34:03 +04:00
if ( cache ) {
DEBUG ( 5 , ( " gencache_init: Opening cache file %s read-only. \n " , cache_fname ) ) ;
}
}
2002-09-11 18:07:21 +04:00
if ( ! cache ) {
2003-02-12 04:20:56 +03:00
DEBUG ( 5 , ( " Attempt to open gencache.tdb has failed. \n " ) ) ;
2002-09-11 18:07:21 +04:00
return False ;
}
2009-07-13 19:04:29 +04:00
cache_fname = lock_path ( " gencache_notrans.tdb " ) ;
DEBUG ( 5 , ( " Opening cache file at %s \n " , cache_fname ) ) ;
2012-11-20 13:02:07 +04:00
cache_notrans = tdb_open_log ( cache_fname , 0 ,
TDB_CLEAR_IF_FIRST |
TDB_INCOMPATIBLE_HASH |
TDB_NOSYNC ,
2009-07-13 19:04:29 +04:00
open_flags , 0644 ) ;
if ( cache_notrans = = NULL ) {
DEBUG ( 5 , ( " Opening %s failed: %s \n " , cache_fname ,
strerror ( errno ) ) ) ;
tdb_close ( cache ) ;
2009-12-04 18:46:34 +03:00
cache = NULL ;
2009-07-13 19:04:29 +04:00
return false ;
}
2002-09-11 18:07:21 +04:00
return True ;
}
2009-07-13 19:04:29 +04:00
static TDB_DATA last_stabilize_key ( void )
{
TDB_DATA result ;
2011-05-06 00:42:05 +04:00
result . dptr = discard_const_p ( uint8_t , " @LAST_STABILIZED " ) ;
2009-07-13 19:04:29 +04:00
result . dsize = 17 ;
return result ;
}
2002-09-11 18:07:21 +04:00
2012-11-20 12:50:57 +04:00
struct gencache_have_val_state {
time_t new_timeout ;
const DATA_BLOB * data ;
bool gotit ;
} ;
static void gencache_have_val_parser ( time_t old_timeout , DATA_BLOB data ,
void * private_data )
{
struct gencache_have_val_state * state =
( struct gencache_have_val_state * ) private_data ;
time_t now = time ( NULL ) ;
int cache_time_left , new_time_left , additional_time ;
/*
* Excuse the many variables , but these time calculations are
* confusing to me . We do not want to write to gencache with a
* possibly expensive transaction if we are about to write the same
* value , just extending the remaining timeout by less than 10 % .
*/
cache_time_left = old_timeout - now ;
if ( cache_time_left < = 0 ) {
/*
* timed out , write new value
*/
return ;
}
new_time_left = state - > new_timeout - now ;
if ( new_time_left < = 0 ) {
/*
* Huh - - no new timeout ? ? Write it .
*/
return ;
}
if ( new_time_left < cache_time_left ) {
/*
* Someone wants to shorten the timeout . Let it happen .
*/
return ;
}
/*
* By how much does the new timeout extend the remaining cache time ?
*/
additional_time = new_time_left - cache_time_left ;
if ( additional_time * 10 < 0 ) {
/*
* Integer overflow . We extend by so much that we have to write it .
*/
return ;
}
/*
* The comparison below is essentially equivalent to
*
* new_time_left > cache_time_left * 1.10
*
* but without floating point calculations .
*/
if ( additional_time * 10 > cache_time_left ) {
/*
* We extend the cache timeout by more than 10 % . Do it .
*/
return ;
}
/*
* Now the more expensive data compare .
*/
if ( data_blob_cmp ( state - > data , & data ) ! = 0 ) {
/*
* Write a new value . Certainly do it .
*/
return ;
}
/*
* Extending the timeout by less than 10 % for the same cache value is
* not worth the trouble writing a value into gencache under a
* possibly expensive transaction .
*/
state - > gotit = true ;
}
static bool gencache_have_val ( const char * keystr , const DATA_BLOB * data ,
time_t timeout )
{
struct gencache_have_val_state state ;
state . new_timeout = timeout ;
state . data = data ;
state . gotit = false ;
if ( ! gencache_parse ( keystr , gencache_have_val_parser , & state ) ) {
return false ;
}
return state . gotit ;
}
2002-09-11 18:07:21 +04:00
/**
2003-01-04 11:48:15 +03:00
* Set an entry in the cache file . If there ' s no such
* one , then add it .
2002-09-11 18:07:21 +04:00
*
2003-04-14 06:18:10 +04:00
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ param blob DATA_BLOB value being cached
2002-09-11 18:07:21 +04:00
* @ param timeout time when the value is expired
*
2003-04-14 06:18:10 +04:00
* @ retval true when entry is successfuly stored
* @ retval false on failure
2002-09-11 18:07:21 +04:00
* */
2009-01-19 02:01:08 +03:00
2009-07-14 13:33:04 +04:00
bool gencache_set_data_blob ( const char * keystr , const DATA_BLOB * blob ,
time_t timeout )
2002-09-11 18:07:21 +04:00
{
int ret ;
2010-03-26 15:15:53 +03:00
TDB_DATA databuf ;
2009-07-14 13:33:04 +04:00
char * val ;
2009-07-13 19:04:29 +04:00
time_t last_stabilize ;
2009-07-14 20:31:28 +04:00
static int writecount ;
2009-07-13 19:04:29 +04:00
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 ;
}
2009-01-19 02:01:08 +03:00
2009-07-14 13:33:04 +04:00
if ( ( keystr = = NULL ) | | ( blob = = NULL ) ) {
2009-07-10 12:54:33 +04:00
return false ;
}
2002-09-11 18:07:21 +04:00
if ( ! gencache_init ( ) ) return False ;
2009-01-19 02:01:08 +03:00
2012-11-20 12:50:57 +04:00
if ( gencache_have_val ( keystr , blob , timeout ) ) {
DEBUG ( 10 , ( " Did not store value for %s, we already got it \n " ,
keystr ) ) ;
return true ;
}
2009-07-14 13:33:04 +04:00
val = talloc_asprintf ( talloc_tos ( ) , CACHE_DATA_FMT , ( int ) timeout ) ;
if ( val = = NULL ) {
2003-03-23 02:32:50 +03:00
return False ;
2008-02-25 17:24:49 +03:00
}
2009-07-14 13:33:04 +04:00
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 ;
}
2003-03-23 02:32:50 +03:00
2013-02-11 16:41:12 +04:00
DEBUG ( 10 , ( " Adding cache entry with key=[%s] and timeout= "
" [%s] (%d seconds %s) \n " , keystr ,
2013-02-13 12:46:33 +04:00
timestring ( talloc_tos ( ) , timeout ) ,
2003-09-08 05:28:48 +04:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2009-07-14 13:33:04 +04:00
ret = tdb_store_bystring (
cache_notrans , keystr ,
make_tdb_data ( ( uint8_t * ) val , talloc_array_length ( val ) ) ,
0 ) ;
TALLOC_FREE ( val ) ;
2009-01-19 02:01:08 +03:00
2009-07-13 19:04:29 +04:00
if ( ret ! = 0 ) {
return false ;
}
2009-07-14 20:31:28 +04:00
/*
* 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 ;
}
2009-07-13 19:04:29 +04:00
/*
* Every 5 minutes , call gencache_stabilize ( ) to not let grow
* gencache_notrans . tdb too large .
*/
2010-03-26 15:15:53 +03:00
last_stabilize = 0 ;
2011-06-20 13:10:31 +04:00
databuf = tdb_fetch_compat ( cache_notrans , last_stabilize_key ( ) ) ;
2010-03-26 15:15:53 +03:00
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 ( ) ;
2009-07-13 19:04:29 +04:00
}
2009-07-14 20:31:28 +04:00
done :
2003-02-20 01:50:29 +03:00
return ret = = 0 ;
2002-09-11 18:07:21 +04:00
}
/**
* Delete one entry from the cache file .
*
2003-04-14 06:18:10 +04:00
* @ param keystr string that represents a key of this entry
2002-09-11 18:07:21 +04:00
*
2003-04-14 06:18:10 +04:00
* @ retval true upon successful deletion
* @ retval false in case of failure
2002-09-11 18:07:21 +04:00
* */
2007-10-19 04:40:25 +04:00
bool gencache_del ( const char * keystr )
2002-09-11 18:07:21 +04:00
{
2009-09-23 17:47:05 +04:00
bool exists , was_expired ;
2009-07-13 19:04:29 +04:00
bool ret = false ;
2009-09-23 17:47:05 +04:00
DATA_BLOB value ;
2009-01-19 02:01:08 +03:00
2009-07-10 12:54:33 +04:00
if ( keystr = = NULL ) {
return false ;
}
2002-09-11 18:07:21 +04:00
if ( ! gencache_init ( ) ) return False ;
2009-01-19 02:01:08 +03:00
2013-02-11 16:43:32 +04:00
DEBUG ( 10 , ( " Deleting cache entry (key=[%s]) \n " , keystr ) ) ;
2009-01-19 02:01:08 +03:00
2009-07-13 19:04:29 +04:00
/*
* 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 .
*/
2013-09-04 10:22:43 +04:00
exists = gencache_get_data_blob ( keystr , NULL , & value , NULL ,
& was_expired ) ;
2009-09-23 17:47:05 +04:00
if ( ! exists & & was_expired ) {
/*
* gencache_get_data_blob has implicitly deleted this
* entry , so we have to return success here .
*/
return true ;
}
2009-07-13 19:04:29 +04:00
if ( exists ) {
2009-09-23 17:47:05 +04:00
data_blob_free ( & value ) ;
2009-07-13 19:04:29 +04:00
ret = gencache_set ( keystr , " " , 0 ) ;
}
return ret ;
2002-09-11 18:07:21 +04:00
}
2009-07-13 19:04:29 +04:00
static bool gencache_pull_timeout ( char * val , time_t * pres , char * * pendptr )
{
time_t res ;
char * endptr ;
2010-11-27 17:48:21 +03:00
if ( val = = NULL ) {
return false ;
}
2009-07-13 19:04:29 +04:00
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 ;
}
2002-09-11 18:07:21 +04:00
2010-11-27 02:40:25 +03:00
struct gencache_parse_state {
void ( * parser ) ( time_t timeout , DATA_BLOB blob , void * private_data ) ;
void * private_data ;
} ;
static int gencache_parse_fn ( TDB_DATA key , TDB_DATA data , void * private_data )
{
struct gencache_parse_state * state ;
DATA_BLOB blob ;
time_t t ;
char * endptr ;
bool ret ;
if ( data . dptr = = NULL ) {
return - 1 ;
}
ret = gencache_pull_timeout ( ( char * ) data . dptr , & t , & endptr ) ;
if ( ! ret ) {
return - 1 ;
}
state = ( struct gencache_parse_state * ) private_data ;
blob = data_blob_const (
endptr + 1 , data . dsize - PTR_DIFF ( endptr + 1 , data . dptr ) ) ;
state - > parser ( t , blob , state - > private_data ) ;
return 0 ;
}
bool gencache_parse ( const char * keystr ,
void ( * parser ) ( time_t timeout , DATA_BLOB blob ,
void * private_data ) ,
void * private_data )
{
struct gencache_parse_state state ;
2013-09-08 15:29:33 +04:00
TDB_DATA key = string_term_tdb_data ( keystr ) ;
2010-11-27 02:40:25 +03:00
int ret ;
if ( keystr = = NULL ) {
return false ;
}
2013-09-08 15:29:33 +04:00
if ( tdb_data_cmp ( key , last_stabilize_key ( ) ) = = 0 ) {
2010-11-27 02:40:25 +03:00
return false ;
}
if ( ! gencache_init ( ) ) {
return false ;
}
state . parser = parser ;
state . private_data = private_data ;
ret = tdb_parse_record ( cache_notrans , key , gencache_parse_fn , & state ) ;
2011-06-20 13:10:32 +04:00
if ( ret = = 0 ) {
2010-11-27 02:40:25 +03:00
return true ;
}
ret = tdb_parse_record ( cache , key , gencache_parse_fn , & state ) ;
2011-06-20 13:10:32 +04:00
return ( ret = = 0 ) ;
2010-11-27 02:40:25 +03:00
}
2010-11-27 13:36:52 +03:00
struct gencache_get_data_blob_state {
2013-09-04 10:22:43 +04:00
TALLOC_CTX * mem_ctx ;
2010-11-27 13:36:52 +03:00
DATA_BLOB * blob ;
time_t timeout ;
bool result ;
} ;
static void gencache_get_data_blob_parser ( time_t timeout , DATA_BLOB blob ,
void * private_data )
{
struct gencache_get_data_blob_state * state =
( struct gencache_get_data_blob_state * ) private_data ;
if ( timeout = = 0 ) {
state - > result = false ;
return ;
}
state - > timeout = timeout ;
if ( state - > blob = = NULL ) {
state - > result = true ;
return ;
}
2013-09-04 10:22:43 +04:00
* state - > blob = data_blob_talloc ( state - > mem_ctx , blob . data ,
blob . length ) ;
2010-11-27 13:36:52 +03:00
if ( state - > blob - > data = = NULL ) {
state - > result = false ;
return ;
}
state - > result = true ;
}
2002-09-11 18:07:21 +04:00
/**
* Get existing entry from the cache file .
*
2003-04-14 06:18:10 +04:00
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ param blob DATA_BLOB that is filled with entry ' s blob
2008-07-11 19:44:15 +04:00
* @ param timeout pointer to a time_t that is filled with entry ' s
* timeout
2002-09-11 18:07:21 +04:00
*
2003-04-14 06:18:10 +04:00
* @ retval true when entry is successfuly fetched
* @ retval False for failure
2002-09-11 18:07:21 +04:00
* */
2013-09-04 10:22:43 +04:00
bool gencache_get_data_blob ( const char * keystr , TALLOC_CTX * mem_ctx ,
DATA_BLOB * blob ,
2009-09-23 17:21:40 +04:00
time_t * timeout , bool * was_expired )
2002-09-11 18:07:21 +04:00
{
2010-11-27 13:36:52 +03:00
struct gencache_get_data_blob_state state ;
2009-09-23 17:21:40 +04:00
bool expired = false ;
2002-09-11 18:07:21 +04:00
2010-11-27 13:36:52 +03:00
state . result = false ;
2013-09-04 10:22:43 +04:00
state . mem_ctx = mem_ctx ;
2010-11-27 13:36:52 +03:00
state . blob = blob ;
2009-07-13 19:04:29 +04:00
2010-11-27 13:36:52 +03:00
if ( ! gencache_parse ( keystr , gencache_get_data_blob_parser , & state ) ) {
2009-09-23 17:21:40 +04:00
goto fail ;
2006-09-10 01:31:56 +04:00
}
2010-11-27 13:36:52 +03:00
if ( ! state . result ) {
2009-09-23 17:21:40 +04:00
goto fail ;
2006-09-10 01:31:56 +04:00
}
2010-11-27 13:36:52 +03:00
if ( state . timeout < = time ( NULL ) ) {
2009-07-13 19:04:29 +04:00
/*
* 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 .
*/
gencache_set ( keystr , " " , 0 ) ;
2009-09-23 17:21:40 +04:00
expired = true ;
goto fail ;
2006-09-10 01:40:47 +04:00
}
2008-07-11 19:44:15 +04:00
if ( timeout ) {
2010-11-27 13:36:52 +03:00
* timeout = state . timeout ;
2006-06-16 01:03:40 +04:00
}
2003-01-25 00:25:12 +03:00
2006-09-10 01:40:47 +04:00
return True ;
2009-09-23 17:21:40 +04:00
fail :
if ( was_expired ! = NULL ) {
* was_expired = expired ;
}
2010-12-17 15:26:16 +03:00
if ( state . result & & state . blob ) {
data_blob_free ( state . blob ) ;
}
2009-09-23 17:21:40 +04:00
return false ;
2008-07-11 19:44:15 +04:00
}
2006-09-10 01:31:56 +04:00
2009-07-13 19:04:29 +04:00
struct stabilize_state {
bool written ;
bool error ;
} ;
static int stabilize_fn ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA val ,
void * priv ) ;
/**
* Stabilize gencache
*
* Migrate the clear - if - first gencache data to the stable ,
* transaction - based gencache . tdb
*/
bool gencache_stabilize ( void )
{
struct stabilize_state state ;
int res ;
char * now ;
if ( ! gencache_init ( ) ) {
return false ;
}
2010-03-26 15:18:52 +03:00
res = tdb_transaction_start_nonblock ( cache ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2011-06-20 13:10:32 +04:00
if ( tdb_error ( cache ) = = TDB_ERR_NOLOCK )
{
2010-03-26 15:18:52 +03:00
/*
* Someone else already does the stabilize ,
* this does not have to be done twice
*/
return true ;
}
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " Could not start transaction on gencache.tdb: "
2011-06-20 13:10:32 +04:00
" %s \n " , tdb_errorstr_compat ( cache ) ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
res = tdb_transaction_start ( cache_notrans ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
tdb_transaction_cancel ( cache ) ;
DEBUG ( 10 , ( " Could not start transaction on "
" gencache_notrans.tdb: %s \n " ,
2011-06-20 13:10:32 +04:00
tdb_errorstr_compat ( cache_notrans ) ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
state . error = false ;
state . written = false ;
res = tdb_traverse ( cache_notrans , stabilize_fn , & state ) ;
2011-06-20 13:10:31 +04:00
if ( ( res < 0 ) | | state . error ) {
2011-06-20 13:10:31 +04:00
tdb_transaction_cancel ( cache_notrans ) ;
tdb_transaction_cancel ( cache ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
if ( ! state . written ) {
2011-06-20 13:10:31 +04:00
tdb_transaction_cancel ( cache_notrans ) ;
tdb_transaction_cancel ( cache ) ;
2009-07-13 19:04:29 +04:00
return true ;
}
res = tdb_transaction_commit ( cache ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " tdb_transaction_commit on gencache.tdb failed: "
2011-06-20 13:10:32 +04:00
" %s \n " , tdb_errorstr_compat ( cache ) ) ) ;
2011-06-20 13:10:31 +04:00
tdb_transaction_cancel ( cache_notrans ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
res = tdb_transaction_commit ( cache_notrans ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " tdb_transaction_commit on gencache.tdb failed: "
2011-06-20 13:10:32 +04:00
" %s \n " , tdb_errorstr_compat ( cache ) ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
2010-03-26 15:15:53 +03:00
now = talloc_asprintf ( talloc_tos ( ) , " %d " , ( int ) time ( NULL ) ) ;
2009-07-13 19:04:29 +04:00
if ( now ! = NULL ) {
tdb_store ( cache_notrans , last_stabilize_key ( ) ,
string_term_tdb_data ( now ) , 0 ) ;
TALLOC_FREE ( now ) ;
}
return 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 ( ! 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 ) ;
2011-06-20 13:10:31 +04:00
if ( ( res ! = 0 ) & & ( tdb_error ( cache ) = = TDB_ERR_NOEXIST ) ) {
2009-07-13 19:04:29 +04:00
res = 0 ;
} else {
state - > written = true ;
}
} else {
res = tdb_store ( cache , key , val , 0 ) ;
if ( res = = 0 ) {
state - > written = true ;
}
}
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " Transfer to gencache.tdb failed: %s \n " ,
2011-06-20 13:10:32 +04:00
tdb_errorstr_compat ( cache ) ) ) ;
2009-07-13 19:04:29 +04:00
state - > error = true ;
return - 1 ;
}
2011-06-20 13:10:31 +04:00
if ( tdb_delete ( cache_notrans , key ) ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " tdb_delete from gencache_notrans.tdb failed: "
2011-06-20 13:10:32 +04:00
" %s \n " , tdb_errorstr_compat ( cache_notrans ) ) ) ;
2009-07-13 19:04:29 +04:00
state - > error = true ;
return - 1 ;
}
return 0 ;
}
2007-08-28 16:40:01 +04:00
/**
* Get existing entry from the cache file .
*
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ 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
2007-08-28 16:40:01 +04:00
*
* @ retval true when entry is successfuly fetched
* @ retval False for failure
* */
2013-09-04 10:56:23 +04:00
bool gencache_get ( const char * keystr , TALLOC_CTX * mem_ctx , char * * value ,
time_t * ptimeout )
2007-08-28 16:40:01 +04:00
{
2009-07-14 13:33:04 +04:00
DATA_BLOB blob ;
2007-10-19 04:40:25 +04:00
bool ret = False ;
2007-08-28 16:40:01 +04:00
2013-09-04 10:57:59 +04:00
ret = gencache_get_data_blob ( keystr , mem_ctx , & blob , ptimeout , NULL ) ;
2009-07-14 13:33:04 +04:00
if ( ! ret ) {
2009-07-10 12:54:33 +04:00
return false ;
}
2009-07-14 13:33:04 +04:00
if ( ( blob . data = = NULL ) | | ( blob . length = = 0 ) ) {
2013-09-04 10:46:34 +04:00
data_blob_free ( & blob ) ;
2009-07-14 13:33:04 +04:00
return false ;
2007-08-28 16:40:01 +04:00
}
2009-07-14 13:33:04 +04:00
if ( blob . data [ blob . length - 1 ] ! = ' \0 ' ) {
/* Not NULL terminated, can't be a string */
2013-09-04 10:46:34 +04:00
data_blob_free ( & blob ) ;
2009-07-14 13:33:04 +04:00
return false ;
2007-08-28 16:40:01 +04:00
}
2009-11-02 15:01:58 +03:00
if ( value ) {
2013-12-16 15:42:46 +04:00
/*
* talloc_move generates a type - punned warning here . As we
* leave the function immediately , do a simple talloc_steal .
*/
* value = ( char * ) talloc_steal ( mem_ctx , blob . data ) ;
2009-11-02 15:01:58 +03:00
return true ;
2007-08-28 16:40:01 +04:00
}
2009-11-02 15:01:58 +03:00
data_blob_free ( & blob ) ;
2009-07-14 13:33:04 +04:00
return true ;
2007-08-28 16:40:01 +04:00
}
/**
* 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
2009-07-14 13:33:04 +04:00
* @ param value text representation value being cached
2007-08-28 16:40:01 +04:00
* @ param timeout time when the value is expired
*
* @ retval true when entry is successfuly stored
* @ retval false on failure
* */
2009-07-14 13:33:04 +04:00
bool gencache_set ( const char * keystr , const char * value , time_t timeout )
2007-08-28 16:40:01 +04:00
{
2009-07-14 13:33:04 +04:00
DATA_BLOB blob = data_blob_const ( value , strlen ( value ) + 1 ) ;
return gencache_set_data_blob ( keystr , & blob , timeout ) ;
2007-08-28 16:40:01 +04:00
}
2002-09-11 18:07:21 +04:00
2010-11-27 17:48:21 +03:00
struct gencache_iterate_blobs_state {
void ( * fn ) ( const char * key , DATA_BLOB value ,
time_t timeout , void * private_data ) ;
2009-01-19 02:01:08 +03:00
const char * pattern ;
2010-11-27 17:48:21 +03:00
void * private_data ;
2009-07-13 19:04:29 +04:00
bool in_persistent ;
2009-01-19 02:01:08 +03:00
} ;
2010-11-27 17:48:21 +03:00
static int gencache_iterate_blobs_fn ( struct tdb_context * tdb , TDB_DATA key ,
TDB_DATA data , void * priv )
2009-01-19 02:01:08 +03:00
{
2010-11-27 17:48:21 +03:00
struct gencache_iterate_blobs_state * state =
( struct gencache_iterate_blobs_state * ) priv ;
2009-01-19 02:01:08 +03:00
char * keystr ;
char * free_key = NULL ;
time_t timeout ;
2010-11-27 17:48:21 +03:00
char * endptr ;
2009-01-19 02:01:08 +03:00
2009-07-13 19:04:29 +04:00
if ( tdb_data_cmp ( key , last_stabilize_key ( ) ) = = 0 ) {
return 0 ;
}
if ( state - > in_persistent & & tdb_exists ( cache_notrans , key ) ) {
return 0 ;
}
2009-01-19 02:01:08 +03:00
if ( key . dptr [ key . dsize - 1 ] = = ' \0 ' ) {
keystr = ( char * ) key . dptr ;
} else {
/* ensure 0-termination */
2013-09-04 10:57:59 +04:00
keystr = talloc_strndup ( talloc_tos ( ) , ( char * ) key . dptr , key . dsize ) ;
2009-01-19 02:01:08 +03:00
free_key = keystr ;
2013-09-04 10:57:59 +04:00
if ( keystr = = NULL ) {
goto done ;
}
2009-01-19 02:01:08 +03:00
}
2010-11-27 17:48:21 +03:00
if ( ! gencache_pull_timeout ( ( char * ) data . dptr , & timeout , & endptr ) ) {
2009-01-19 02:01:08 +03:00
goto done ;
}
2010-11-27 17:48:21 +03:00
endptr + = 1 ;
2009-01-19 02:01:08 +03:00
if ( fnmatch ( state - > pattern , keystr , 0 ) ! = 0 ) {
goto done ;
}
2013-02-11 16:42:28 +04:00
DEBUG ( 10 , ( " Calling function with arguments "
" (key=[%s], timeout=[%s]) \n " ,
2013-02-13 12:43:19 +04:00
keystr , timestring ( talloc_tos ( ) , timeout ) ) ) ;
2009-01-19 02:01:08 +03:00
2010-11-27 17:48:21 +03:00
state - > fn ( keystr ,
data_blob_const ( endptr ,
data . dsize - PTR_DIFF ( endptr , data . dptr ) ) ,
timeout , state - > private_data ) ;
2009-01-19 02:01:08 +03:00
done :
2013-09-04 10:57:59 +04:00
TALLOC_FREE ( free_key ) ;
2009-01-19 02:01:08 +03:00
return 0 ;
}
2010-11-27 17:48:21 +03:00
void gencache_iterate_blobs ( void ( * fn ) ( const char * key , DATA_BLOB value ,
time_t timeout , void * private_data ) ,
void * private_data , const char * pattern )
2002-09-11 18:07:21 +04:00
{
2010-11-27 17:48:21 +03:00
struct gencache_iterate_blobs_state state ;
2002-09-11 18:07:21 +04:00
2010-11-27 17:48:21 +03:00
if ( ( fn = = NULL ) | | ( pattern = = NULL ) | | ! gencache_init ( ) ) {
2009-07-10 12:54:33 +04:00
return ;
}
2002-09-11 18:07:21 +04:00
2010-11-27 17:48:21 +03:00
DEBUG ( 5 , ( " Searching cache keys with pattern %s \n " , pattern ) ) ;
2006-06-16 01:03:40 +04:00
2009-01-19 02:01:08 +03:00
state . fn = fn ;
2010-11-27 17:48:21 +03:00
state . pattern = pattern ;
state . private_data = private_data ;
2009-07-13 19:04:29 +04:00
state . in_persistent = false ;
2010-11-27 17:48:21 +03:00
tdb_traverse ( cache_notrans , gencache_iterate_blobs_fn , & state ) ;
2009-07-13 19:04:29 +04:00
state . in_persistent = true ;
2010-11-27 17:48:21 +03:00
tdb_traverse ( cache , gencache_iterate_blobs_fn , & state ) ;
}
/**
* Iterate through all entries which key matches to specified pattern
*
* @ param fn pointer to the function that will be supplied with each single
* matching cache entry ( key , value and timeout ) as an arguments
* @ param data void pointer to an arbitrary data that is passed directly to the fn
* function on each call
* @ param keystr_pattern pattern the existing entries ' keys are matched to
*
* */
struct gencache_iterate_state {
void ( * fn ) ( const char * key , const char * value , time_t timeout ,
void * priv ) ;
void * private_data ;
} ;
static void gencache_iterate_fn ( const char * key , DATA_BLOB value ,
time_t timeout , void * private_data )
{
struct gencache_iterate_state * state =
( struct gencache_iterate_state * ) private_data ;
char * valstr ;
char * free_val = NULL ;
if ( value . data [ value . length - 1 ] = = ' \0 ' ) {
valstr = ( char * ) value . data ;
} else {
/* ensure 0-termination */
2013-09-04 10:57:59 +04:00
valstr = talloc_strndup ( talloc_tos ( ) , ( char * ) value . data , value . length ) ;
2010-11-27 17:48:21 +03:00
free_val = valstr ;
2013-09-04 10:57:59 +04:00
if ( valstr = = NULL ) {
goto done ;
}
2010-11-27 17:48:21 +03:00
}
DEBUG ( 10 , ( " Calling function with arguments "
2013-02-11 16:42:58 +04:00
" (key=[%s], value=[%s], timeout=[%s]) \n " ,
2013-02-13 12:45:09 +04:00
key , valstr , timestring ( talloc_tos ( ) , timeout ) ) ) ;
2010-11-27 17:48:21 +03:00
state - > fn ( key , valstr , timeout , state - > private_data ) ;
2013-09-04 10:57:59 +04:00
done :
TALLOC_FREE ( free_val ) ;
2010-11-27 17:48:21 +03:00
}
void gencache_iterate ( void ( * fn ) ( const char * key , const char * value ,
time_t timeout , void * dptr ) ,
void * private_data , const char * pattern )
{
struct gencache_iterate_state state ;
if ( fn = = NULL ) {
return ;
}
state . fn = fn ;
state . private_data = private_data ;
gencache_iterate_blobs ( gencache_iterate_fn , & state , pattern ) ;
2002-09-11 18:07:21 +04:00
}