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
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 .
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
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"
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_TDB
# define TIMEOUT_LEN 12
# define CACHE_DATA_FMT "%12u / %s"
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
static TDB_CONTEXT * cache ;
/**
* @ 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
* */
2007-10-19 04:40:25 +04:00
bool gencache_init ( void )
2002-09-11 18:07:21 +04:00
{
char * cache_fname = NULL ;
/* skip file open if it's already opened */
if ( cache ) return True ;
2006-10-19 19:47:19 +04:00
cache_fname = lock_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 ) ) ;
2002-09-11 18:07:21 +04:00
cache = tdb_open_log ( cache_fname , 0 , TDB_DEFAULT ,
O_RDWR | O_CREAT , 0644 ) ;
2006-10-03 03:34:03 +04:00
if ( ! cache & & ( errno = = EACCES ) ) {
cache = tdb_open_log ( cache_fname , 0 , TDB_DEFAULT , O_RDONLY , 0644 ) ;
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 ;
}
return True ;
}
/**
* Cache shutdown function . Closes opened cache tdb file .
*
* @ return true on successful closing the cache or
* false on failure during cache shutdown
* */
2007-10-19 04:40:25 +04:00
bool gencache_shutdown ( void )
2002-09-11 18:07:21 +04:00
{
2006-01-13 21:45:30 +03:00
int ret ;
2003-02-20 01:50:29 +03:00
/* tdb_close routine returns -1 on error */
2002-09-11 18:07:21 +04:00
if ( ! cache ) return False ;
DEBUG ( 5 , ( " Closing cache file \n " ) ) ;
2006-01-13 21:45:30 +03:00
ret = tdb_close ( cache ) ;
cache = NULL ;
return ret ! = - 1 ;
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
2002-09-11 18:07:21 +04:00
* @ param value text representation value being cached
* @ 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
* */
2007-10-19 04:40:25 +04:00
bool gencache_set ( const char * keystr , const char * value , time_t timeout )
2002-09-11 18:07:21 +04:00
{
int ret ;
2007-03-27 13:59:32 +04:00
TDB_DATA databuf ;
2002-09-11 18:07:21 +04:00
char * valstr = NULL ;
/* fail completely if get null pointers passed */
SMB_ASSERT ( keystr & & value ) ;
if ( ! gencache_init ( ) ) return False ;
2008-02-25 17:24:49 +03:00
if ( asprintf ( & valstr , CACHE_DATA_FMT , ( int ) timeout , value ) = = - 1 ) {
2003-03-23 02:32:50 +03:00
return False ;
2008-02-25 17:24:49 +03:00
}
2003-03-23 02:32:50 +03:00
2007-03-27 13:59:32 +04:00
databuf = string_term_tdb_data ( valstr ) ;
2003-09-08 05:28:48 +04:00
DEBUG ( 10 , ( " Adding cache entry with key = %s; value = %s and timeout = "
2007-03-27 13:59:32 +04:00
" %s (%d seconds %s) \n " , keystr , value , ctime ( & timeout ) ,
2003-09-08 05:28:48 +04:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2007-03-27 13:59:32 +04:00
ret = tdb_store_bystring ( cache , keystr , databuf , 0 ) ;
2002-09-11 18:07:21 +04:00
SAFE_FREE ( valstr ) ;
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
{
int ret ;
/* fail completely if get null pointers passed */
SMB_ASSERT ( keystr ) ;
if ( ! gencache_init ( ) ) return False ;
DEBUG ( 10 , ( " Deleting cache entry (key = %s) \n " , keystr ) ) ;
2007-03-27 13:59:32 +04:00
ret = tdb_delete_bystring ( cache , keystr ) ;
2002-09-11 18:07:21 +04:00
2003-02-20 01:50:29 +03:00
return ret = = 0 ;
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
* @ param valstr buffer that is allocated and filled with the entry value
2008-07-11 19:44:15 +04:00
* buffer ' s disposing must be done outside
* @ 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
* */
2008-07-11 19:44:15 +04:00
bool gencache_get ( const char * keystr , char * * valstr , time_t * timeout )
2002-09-11 18:07:21 +04:00
{
2007-03-27 13:59:32 +04:00
TDB_DATA databuf ;
2006-09-10 01:31:56 +04:00
time_t t ;
char * endptr ;
2002-09-11 18:07:21 +04:00
/* fail completely if get null pointers passed */
2003-01-25 00:25:12 +03:00
SMB_ASSERT ( keystr ) ;
2002-09-11 18:07:21 +04:00
2006-06-16 01:03:40 +04:00
if ( ! gencache_init ( ) ) {
2002-11-23 05:52:36 +03:00
return False ;
2006-06-16 01:03:40 +04:00
}
2007-03-27 13:59:32 +04:00
databuf = tdb_fetch_bystring ( cache , keystr ) ;
2003-01-25 00:25:12 +03:00
2006-09-10 01:31:56 +04:00
if ( databuf . dptr = = NULL ) {
DEBUG ( 10 , ( " Cache entry with key = %s couldn't be found \n " ,
keystr ) ) ;
return False ;
}
2006-06-16 01:03:40 +04:00
2007-03-29 13:35:51 +04:00
t = strtol ( ( const char * ) databuf . dptr , & endptr , 10 ) ;
2002-09-11 18:07:21 +04:00
2006-09-10 01:31:56 +04:00
if ( ( endptr = = NULL ) | | ( * endptr ! = ' / ' ) ) {
DEBUG ( 2 , ( " Invalid gencache data format: %s \n " , databuf . dptr ) ) ;
SAFE_FREE ( databuf . dptr ) ;
return False ;
}
2003-01-25 00:25:12 +03:00
2006-09-10 01:31:56 +04:00
DEBUG ( 10 , ( " Returning %s cache entry: key = %s, value = %s, "
" timeout = %s " , t > time ( NULL ) ? " valid " :
" expired " , keystr , endptr + 1 , ctime ( & t ) ) ) ;
2003-01-25 00:25:12 +03:00
2008-07-11 19:44:15 +04:00
if ( t < = time ( NULL ) ) {
2006-09-10 01:40:47 +04:00
2008-07-11 19:44:15 +04:00
/* We're expired, delete the entry */
2007-03-27 13:59:32 +04:00
tdb_delete_bystring ( cache , keystr ) ;
2006-09-10 01:40:47 +04:00
SAFE_FREE ( databuf . dptr ) ;
return False ;
}
2006-09-10 01:31:56 +04:00
if ( valstr ) {
* valstr = SMB_STRDUP ( endptr + 1 ) ;
if ( * valstr = = NULL ) {
SAFE_FREE ( databuf . dptr ) ;
DEBUG ( 0 , ( " strdup failed \n " ) ) ;
return False ;
2006-06-16 01:03:40 +04:00
}
2006-09-10 01:31:56 +04:00
}
2008-07-11 19:44:15 +04:00
2006-02-04 00:19:24 +03:00
SAFE_FREE ( databuf . dptr ) ;
2003-01-25 00:25:12 +03:00
2008-07-11 19:44:15 +04:00
if ( timeout ) {
* timeout = t ;
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 ;
2008-07-11 19:44:15 +04:00
}
2006-09-10 01:31:56 +04:00
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
* @ param blob DATA_BLOB that is filled with entry ' s blob
2007-10-19 04:40:25 +04:00
* @ param expired pointer to a bool that indicates whether the entry is expired
2007-08-28 16:40:01 +04:00
*
* @ retval true when entry is successfuly fetched
* @ retval False for failure
* */
2007-10-19 04:40:25 +04:00
bool gencache_get_data_blob ( const char * keystr , DATA_BLOB * blob , bool * expired )
2007-08-28 16:40:01 +04:00
{
TDB_DATA databuf ;
time_t t ;
char * blob_type ;
unsigned char * buf = NULL ;
2007-10-19 04:40:25 +04:00
bool ret = False ;
2007-08-28 16:40:01 +04:00
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 ) ;
if ( ! gencache_init ( ) ) {
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 ;
}
buf = ( unsigned char * ) databuf . dptr ;
buflen = databuf . dsize ;
len + = tdb_unpack ( buf + len , buflen - len , " fB " ,
& valstr ,
& blob_len , & blob_buf ) ;
if ( len = = - 1 ) {
goto out ;
}
t = strtol ( valstr , & blob_type , 10 ) ;
if ( strcmp ( blob_type + 1 , BLOB_TYPE ) ! = 0 ) {
goto out ;
}
DEBUG ( 10 , ( " Returning %s cache entry: key = %s, "
" timeout = %s " , t > time ( NULL ) ? " valid " :
" expired " , keystr , ctime ( & t ) ) ) ;
if ( t < = time ( NULL ) ) {
/* We're expired */
if ( expired ) {
* expired = True ;
}
}
if ( blob ) {
* blob = data_blob ( blob_buf , blob_len ) ;
if ( ! blob - > data ) {
goto out ;
}
}
ret = True ;
out :
SAFE_FREE ( blob_buf ) ;
SAFE_FREE ( databuf . dptr ) ;
return ret ;
}
/**
* 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 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
* */
2008-05-07 23:01:46 +04:00
bool gencache_set_data_blob ( const char * keystr , const DATA_BLOB * blob , time_t timeout )
2007-08-28 16:40:01 +04:00
{
2007-10-19 04:40:25 +04:00
bool ret = False ;
2007-08-28 16:40:01 +04:00
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 ;
}
2008-02-25 17:24:49 +03:00
if ( asprintf ( & valstr , " %12u/%s " , ( int ) timeout , BLOB_TYPE ) = = - 1 ) {
2007-08-28 16:40:01 +04:00
return False ;
}
again :
len = 0 ;
len + = tdb_pack ( buf + len , buflen - len , " fB " ,
valstr ,
blob - > length , blob - > data ) ;
if ( len = = - 1 ) {
goto out ;
}
if ( buflen < len ) {
SAFE_FREE ( buf ) ;
buf = SMB_MALLOC_ARRAY ( unsigned char , len ) ;
if ( ! buf ) {
goto out ;
}
buflen = len ;
goto again ;
}
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 ;
}
out :
SAFE_FREE ( valstr ) ;
SAFE_FREE ( buf ) ;
return ret ;
}
2002-09-11 18:07:21 +04:00
/**
* 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
2003-01-04 11:48:15 +03:00
* @ param data void pointer to an arbitrary data that is passed directly to the fn
* function on each call
2002-09-11 18:07:21 +04:00
* @ param keystr_pattern pattern the existing entries ' keys are matched to
*
* */
2003-01-04 11:48:15 +03:00
void gencache_iterate ( void ( * fn ) ( const char * key , const char * value , time_t timeout , void * dptr ) ,
void * data , const char * keystr_pattern )
2002-09-11 18:07:21 +04:00
{
TDB_LIST_NODE * node , * first_node ;
TDB_DATA databuf ;
char * keystr = NULL , * valstr = NULL , * entry = NULL ;
time_t timeout = 0 ;
2005-09-30 21:13:37 +04:00
int status ;
unsigned u ;
2002-09-11 18:07:21 +04:00
/* fail completely if get null pointers passed */
SMB_ASSERT ( fn & & keystr_pattern ) ;
if ( ! gencache_init ( ) ) return ;
2003-01-04 11:48:15 +03:00
DEBUG ( 5 , ( " Searching cache keys with pattern %s \n " , keystr_pattern ) ) ;
2002-09-11 18:07:21 +04:00
node = tdb_search_keys ( cache , keystr_pattern ) ;
first_node = node ;
while ( node ) {
2006-06-16 01:03:40 +04:00
char * fmt ;
2002-09-11 18:07:21 +04:00
/* ensure null termination of the key string */
2007-03-29 13:35:51 +04:00
keystr = SMB_STRNDUP ( ( const char * ) node - > node_key . dptr , node - > node_key . dsize ) ;
2006-06-16 01:03:40 +04:00
if ( ! keystr ) {
2006-06-16 16:30:39 +04:00
break ;
2006-06-16 01:03:40 +04:00
}
2003-07-01 16:03:00 +04:00
2002-09-11 18:07:21 +04:00
/*
* 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 ) ;
2002-11-23 05:52:36 +03:00
if ( ! databuf . dptr | | databuf . dsize < = TIMEOUT_LEN ) {
SAFE_FREE ( databuf . dptr ) ;
2003-07-01 16:03:00 +04:00
SAFE_FREE ( keystr ) ;
node = node - > next ;
2002-11-23 05:52:36 +03:00
continue ;
}
2007-03-29 13:35:51 +04:00
entry = SMB_STRNDUP ( ( const char * ) databuf . dptr , databuf . dsize ) ;
2006-06-16 01:03:40 +04:00
if ( ! entry ) {
SAFE_FREE ( databuf . dptr ) ;
SAFE_FREE ( keystr ) ;
2006-06-16 16:30:39 +04:00
break ;
2006-06-16 01:03:40 +04:00
}
2002-11-23 05:52:36 +03:00
SAFE_FREE ( databuf . dptr ) ;
2006-06-16 01:03:40 +04:00
2006-07-31 08:30:55 +04:00
valstr = ( char * ) SMB_MALLOC ( databuf . dsize + 1 - TIMEOUT_LEN ) ;
2006-06-16 01:03:40 +04:00
if ( ! valstr ) {
SAFE_FREE ( entry ) ;
SAFE_FREE ( keystr ) ;
2006-06-16 16:30:39 +04:00
break ;
2006-06-16 01:03:40 +04:00
}
2008-02-25 17:24:49 +03:00
if ( asprintf ( & fmt , READ_CACHE_DATA_FMT_TEMPLATE ,
( unsigned int ) databuf . dsize - TIMEOUT_LEN )
= = - 1 ) {
2006-06-16 01:03:40 +04:00
SAFE_FREE ( valstr ) ;
SAFE_FREE ( entry ) ;
SAFE_FREE ( keystr ) ;
2006-06-16 16:30:39 +04:00
break ;
2006-06-16 01:03:40 +04:00
}
status = sscanf ( entry , fmt , & u , valstr ) ;
SAFE_FREE ( fmt ) ;
2005-09-30 21:13:37 +04:00
if ( status ! = 2 ) {
2006-06-16 01:03:40 +04:00
DEBUG ( 0 , ( " gencache_iterate: invalid return from sscanf %d \n " , status ) ) ;
2005-09-30 21:13:37 +04:00
}
timeout = u ;
2002-09-11 18:07:21 +04:00
DEBUG ( 10 , ( " Calling function with arguments (key = %s, value = %s, timeout = %s) \n " ,
keystr , valstr , ctime ( & timeout ) ) ) ;
2003-01-04 11:48:15 +03:00
fn ( keystr , valstr , timeout , data ) ;
2002-09-11 18:07:21 +04:00
SAFE_FREE ( valstr ) ;
SAFE_FREE ( entry ) ;
2003-07-01 16:03:00 +04:00
SAFE_FREE ( keystr ) ;
2002-09-11 18:07:21 +04:00
node = node - > next ;
}
tdb_search_list_free ( first_node ) ;
}
2008-07-11 19:44:09 +04:00
/********************************************************************
lock a key
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int gencache_lock_entry ( const char * key )
{
if ( ! gencache_init ( ) )
return - 1 ;
return tdb_lock_bystring ( cache , key ) ;
}
/********************************************************************
unlock a key
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void gencache_unlock_entry ( const char * key )
{
if ( ! gencache_init ( ) )
return ;
tdb_unlock_bystring ( cache , key ) ;
return ;
}