-
/*
* Unix SMB/CIFS implementation.
* Eventlog utility routines
* Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Brian Moran 2005.
* Copyright (C) Gerald (Jerry) Carter 2005.
*
* 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 2 of the License, or
+ * 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,
* 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
+/* maintain a list of open eventlog tdbs with reference counts */
+
+static ELOG_TDB *open_elog_list;
+
+/********************************************************************
+ Init an Eventlog TDB, and return it. If null, something bad
+ happened.
+********************************************************************/
-/****************************************************************
-Init an Eventlog TDB, and return it. If null, something bad happened.
-****************************************************************/
-TDB_CONTEXT *init_eventlog_tdb( char *tdbfilename )
+TDB_CONTEXT *elog_init_tdb( char *tdbfilename )
{
- TDB_CONTEXT *the_tdb;
+ TDB_CONTEXT *tdb;
- unlink( tdbfilename );
+ DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n",
+ tdbfilename));
- the_tdb =
- tdb_open_log( tdbfilename, 0, TDB_DEFAULT, O_RDWR | O_CREAT,
- 0664 );
- if ( the_tdb == NULL ) {
- DEBUG( 1, ( "Can't open tdb for [%s]\n", tdbfilename ) );
+ tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT|O_TRUNC, 0660 );
+
+ if ( !tdb ) {
+ DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) );
return NULL;
}
- tdb_store_int32( the_tdb, VN_oldest_entry, 1 );
- tdb_store_int32( the_tdb, VN_next_record, 1 );
/* initialize with defaults, copy real values in here from registry */
- tdb_store_int32( the_tdb, VN_maxsize, 0x80000 );
- tdb_store_int32( the_tdb, VN_retention, 0x93A80 );
-
- tdb_store_int32( the_tdb, VN_version, EVENTLOG_DATABASE_VERSION_V1 );
- return the_tdb;
-}
-
-/* make the tdb file name for an event log, given destination buffer and size */
-char *mk_tdbfilename( char *dest_buffer, char *eventlog_name, int size_dest )
-{
- pstring ondisk_name;
-
- if ( !dest_buffer )
- return NULL;
-
- pstrcpy( ondisk_name, "EV" );
- pstrcat( ondisk_name, eventlog_name );
- pstrcat( ondisk_name, ".tdb" );
+ tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 );
+ tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 );
+ tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 );
+ tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 );
- memset( dest_buffer, 0, size_dest );
+ tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 );
- /* BAD things could happen if the dest_buffer is not large enough... */
- if ( strlen( ondisk_name ) > size_dest ) {
- DEBUG( 3, ( "Buffer not big enough for filename\n" ) );
- return NULL;
- }
+ return tdb;
+}
- strncpy( dest_buffer, ondisk_name, size_dest );
+/********************************************************************
+ make the tdb file name for an event log, given destination buffer
+ and size. Caller must free memory.
+********************************************************************/
- return dest_buffer;
+char *elog_tdbname( const char *name )
+{
+ fstring path;
+ char *tdb_fullpath;
+ char *eventlogdir = lock_path( "eventlog" );
+
+ pstr_sprintf( path, "%s/%s.tdb", eventlogdir, name );
+ strlower_m( path );
+ tdb_fullpath = SMB_STRDUP( path );
+
+ return tdb_fullpath;
}
-/* count the number of bytes in the TDB */
-
-/* Arg! Static Globals! */
+/********************************************************************
+ this function is used to count up the number of bytes in a
+ particular TDB
+********************************************************************/
-static int eventlog_tdbcount;
-static int eventlog_tdbsize;
+struct trav_size_struct {
+ int size;
+ int rec_count;
+};
-/* this function is used to count up the number of bytes in a particular TDB */
-int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
+static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
void *state )
{
- eventlog_tdbsize += data.dsize;
- eventlog_tdbcount++;
+ struct trav_size_struct *tsize = (struct trav_size_struct *)state;
+
+ tsize->size += data.dsize;
+ tsize->rec_count++;
+
return 0;
}
-/* returns the size of the eventlog, and if MaxSize is a non-null ptr, puts
- the MaxSize there. This is purely a way not to have yet another function that solely
- reads the maxsize of the eventlog. Yeah, that's it. */
+/********************************************************************
+ returns the size of the eventlog, and if MaxSize is a non-null
+ ptr, puts the MaxSize there. This is purely a way not to have yet
+ another function that solely reads the maxsize of the eventlog.
+ Yeah, that's it.
+********************************************************************/
-int eventlog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
+int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
{
+ struct trav_size_struct tsize;
+
if ( !tdb )
return 0;
- eventlog_tdbcount = 0;
- eventlog_tdbsize = 0;
+
+ ZERO_STRUCT( tsize );
- tdb_traverse( tdb, eventlog_tdb_size_fn, NULL );
+ tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize );
if ( MaxSize != NULL ) {
- *MaxSize = tdb_fetch_int32( tdb, VN_maxsize );
+ *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE );
}
if ( Retention != NULL ) {
- *Retention = tdb_fetch_int32( tdb, VN_retention );
+ *Retention = tdb_fetch_int32( tdb, EVT_RETENTION );
}
DEBUG( 1,
- ( "eventlog size: [%d] for [%d] records\n", eventlog_tdbsize,
- eventlog_tdbcount ) );
- return eventlog_tdbsize;
+ ( "eventlog size: [%d] for [%d] records\n", tsize.size,
+ tsize.rec_count ) );
+ return tsize.size;
}
+/********************************************************************
+ Discard early event logs until we have enough for 'needed' bytes...
+ NO checking done beforehand to see that we actually need to do
+ this, and it's going to pluck records one-by-one. So, it's best
+ to determine that this needs to be done before doing it.
+
+ Setting whack_by_date to True indicates that eventlogs falling
+ outside of the retention range need to go...
+
+ return True if we made enough room to accommodate needed bytes
+********************************************************************/
-/*
- Discard early event logs until we have enough for 'needed' bytes...
- NO checking done beforehand to see that we actually need to do this, and
- it's going to pluck records one-by-one. So, it's best to determine that this
- needs to be done before doing it.
-
- Setting whack_by_date to True indicates that eventlogs falling outside of the
- retention range need to go...
-
-*/
-
-/* return True if we made enough room to accommodate needed bytes */
-
-BOOL make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
- BOOL whack_by_date )
+bool make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
+ bool whack_by_date )
{
int start_record, i, new_start;
int end_record;
int nbytes, reclen, len, Retention, MaxSize;
-
int tresv1, trecnum, timegen, timewr;
-
TDB_DATA key, ret;
TALLOC_CTX *mem_ctx = NULL;
-
time_t current_time, exp_time;
/* discard some eventlogs */
if ( mem_ctx == NULL )
return False; /* can't allocate memory indicates bigger problems */
/* lock */
- tdb_lock_bystring( the_tdb, VN_next_record, 1 );
+ tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
/* read */
- end_record = tdb_fetch_int32( the_tdb, VN_next_record );
- start_record = tdb_fetch_int32( the_tdb, VN_oldest_entry );
- Retention = tdb_fetch_int32( the_tdb, VN_retention );
- MaxSize = tdb_fetch_int32( the_tdb, VN_maxsize );
+ end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
+ start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
+ Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
+ MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );
time( ¤t_time );
for ( i = start_record; i < end_record; i++ ) {
/* read a record, add the amt to nbytes */
key.dsize = sizeof( int32 );
- key.dptr = ( char * ) ( int32 * ) & i;
+ key.dptr = ( uint8 * ) ( int32 * ) & i;
ret = tdb_fetch( the_tdb, key );
if ( ret.dsize == 0 ) {
DEBUG( 8,
( "Can't find a record for the key, record [%d]\n",
i ) );
- tdb_unlock_bystring( the_tdb, VN_next_record );
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
return False;
}
nbytes += ret.dsize; /* note this includes overhead */
len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
&tresv1, &trecnum, &timegen, &timewr );
+ if (len == -1) {
+ DEBUG( 10,("make_way_for_eventlogs: tdb_unpack failed.\n"));
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
+ return False;
+ }
+
DEBUG( 8,
( "read record %d, record size is [%d], total so far [%d]\n",
i, reclen, nbytes ) );
if ( start_record != new_start ) {
for ( i = start_record; i < new_start; i++ ) {
key.dsize = sizeof( int32 );
- key.dptr = ( char * ) ( int32 * ) & i;
+ key.dptr = ( uint8 * ) ( int32 * ) & i;
tdb_delete( the_tdb, key );
}
- tdb_store_int32( the_tdb, VN_oldest_entry, new_start );
+ tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
}
- tdb_unlock_bystring( the_tdb, VN_next_record );
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
return True;
}
-/*
+/********************************************************************
some hygiene for an eventlog - see how big it is, and then
calculate how many bytes we need to remove
-*/
+********************************************************************/
-BOOL prune_eventlog( TDB_CONTEXT * tdb )
+bool prune_eventlog( TDB_CONTEXT * tdb )
{
int MaxSize, Retention, CalcdSize;
return False;
}
- CalcdSize = eventlog_tdb_size( tdb, &MaxSize, &Retention );
+ CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention );
DEBUG( 3,
( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
MaxSize ) );
return make_way_for_eventlogs( tdb, 0, True );
}
-BOOL can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
+/********************************************************************
+********************************************************************/
+
+bool can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
{
int calcd_size;
int MaxSize, Retention;
MaxSize = 0;
Retention = 0;
- calcd_size = eventlog_tdb_size( tdb, &MaxSize, &Retention );
+ calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention );
if ( calcd_size <= MaxSize )
return True; /* you betcha */
return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
}
-TDB_CONTEXT *open_eventlog_tdb( char *tdbfilename )
+/*******************************************************************
+*******************************************************************/
+
+ELOG_TDB *elog_open_tdb( char *logname, bool force_clear )
+{
+ TDB_CONTEXT *tdb = NULL;
+ uint32 vers_id;
+ ELOG_TDB *ptr;
+ char *tdbfilename;
+ pstring tdbpath;
+ ELOG_TDB *tdb_node = NULL;
+ char *eventlogdir;
+
+ /* first see if we have an open context */
+
+ for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
+ if ( strequal( ptr->name, logname ) ) {
+ ptr->ref_count++;
+
+ /* trick to alow clearing of the eventlog tdb.
+ The force_clear flag should imply that someone
+ has done a force close. So make sure the tdb
+ is NULL. If this is a normal open, then just
+ return the existing reference */
+
+ if ( force_clear ) {
+ SMB_ASSERT( ptr->tdb == NULL );
+ break;
+ }
+ else
+ return ptr;
+ }
+ }
+
+ /* make sure that the eventlog dir exists */
+
+ eventlogdir = lock_path( "eventlog" );
+ if ( !directory_exist( eventlogdir, NULL ) )
+ mkdir( eventlogdir, 0755 );
+
+ /* get the path on disk */
+
+ tdbfilename = elog_tdbname( logname );
+ pstrcpy( tdbpath, tdbfilename );
+ SAFE_FREE( tdbfilename );
+
+ DEBUG(7,("elog_open_tdb: Opening %s...(force_clear == %s)\n",
+ tdbpath, force_clear?"True":"False" ));
+
+ /* the tdb wasn't already open or this is a forced clear open */
+
+ if ( !force_clear ) {
+
+ tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, O_RDWR , 0 );
+ if ( tdb ) {
+ vers_id = tdb_fetch_int32( tdb, EVT_VERSION );
+
+ if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) {
+ DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n",
+ vers_id, tdbpath));
+ tdb_close( tdb );
+ tdb = elog_init_tdb( tdbpath );
+ }
+ }
+ }
+
+ if ( !tdb )
+ tdb = elog_init_tdb( tdbpath );
+
+ /* if we got a valid context, then add it to the list */
+
+ if ( tdb ) {
+ /* on a forced clear, just reset the tdb context if we already
+ have an open entry in the list */
+
+ if ( ptr ) {
+ ptr->tdb = tdb;
+ return ptr;
+ }
+
+ if ( !(tdb_node = TALLOC_ZERO_P( NULL, ELOG_TDB)) ) {
+ DEBUG(0,("elog_open_tdb: talloc() failure!\n"));
+ tdb_close( tdb );
+ return NULL;
+ }
+
+ tdb_node->name = talloc_strdup( tdb_node, logname );
+ tdb_node->tdb = tdb;
+ tdb_node->ref_count = 1;
+
+ DLIST_ADD( open_elog_list, tdb_node );
+ }
+
+ return tdb_node;
+}
+
+/*******************************************************************
+ Wrapper to handle reference counts to the tdb
+*******************************************************************/
+
+int elog_close_tdb( ELOG_TDB *etdb, bool force_close )
{
- TDB_CONTEXT *the_tdb;
+ TDB_CONTEXT *tdb;
- the_tdb =
- tdb_open_log( tdbfilename, 0, TDB_DEFAULT, O_RDWR | O_CREAT,
- 0664 );
- if ( the_tdb == NULL ) {
- return init_eventlog_tdb( tdbfilename );
+ if ( !etdb )
+ return 0;
+
+ etdb->ref_count--;
+
+ SMB_ASSERT( etdb->ref_count >= 0 );
+
+ if ( etdb->ref_count == 0 ) {
+ tdb = etdb->tdb;
+ DLIST_REMOVE( open_elog_list, etdb );
+ TALLOC_FREE( etdb );
+ return tdb_close( tdb );
}
- if ( EVENTLOG_DATABASE_VERSION_V1 !=
- tdb_fetch_int32( the_tdb, VN_version ) ) {
- tdb_close( the_tdb );
- return init_eventlog_tdb( tdbfilename );
+
+ if ( force_close ) {
+ tdb = etdb->tdb;
+ etdb->tdb = NULL;
+ return tdb_close( tdb );
}
- return the_tdb;
+
+ return 0;
}
-/* write an eventlog entry. Note that we have to lock, read next eventlog, increment, write, write the record, unlock */
-/* coming into this, ee has the eventlog record, and the auxilliary date (computer name, etc.)
- filled into the other structure. Before packing into a record, this routine will calc the
- appropriate padding, etc., and then blast out the record in a form that can be read back in */
+/*******************************************************************
+ write an eventlog entry. Note that we have to lock, read next
+ eventlog, increment, write, write the record, unlock
+
+ coming into this, ee has the eventlog record, and the auxilliary date
+ (computer name, etc.) filled into the other structure. Before packing
+ into a record, this routine will calc the appropriate padding, etc.,
+ and then blast out the record in a form that can be read back in
+*******************************************************************/
+
+#define MARGIN 512
+
int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
{
int32 next_record;
if ( ee->record.time_generated == 0 )
return 0;
-#define MARGIN 512
-
/* todo - check for sanity in next_record */
fixup_eventlog_entry( ee );
}
/* alloc mem for the packed version */
- packed_ee = TALLOC( mem_ctx, ee->record.length + MARGIN );
+ packed_ee = (uint8 *)TALLOC( mem_ctx, ee->record.length + MARGIN );
if ( !packed_ee ) {
talloc_destroy( mem_ctx );
return 0;
/* need to read the record number and insert it into the entry here */
/* lock */
- tdb_lock_bystring( the_tdb, VN_next_record, 1 );
+ tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
/* read */
- next_record = tdb_fetch_int32( the_tdb, VN_next_record );
+ next_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
n_packed =
- tdb_pack( packed_ee, ee->record.length + MARGIN,
+ tdb_pack( (uint8 *)packed_ee, ee->record.length + MARGIN,
"ddddddwwwwddddddBBdBBBd", ee->record.length,
ee->record.reserved1, next_record,
ee->record.time_generated, ee->record.time_written,
/* increment the record count */
kbuf.dsize = sizeof( int32 );
- kbuf.dptr = ( uint8 * ) & next_record;
+ kbuf.dptr = (uint8 * ) & next_record;
ebuf.dsize = n_packed;
- ebuf.dptr = packed_ee;
+ ebuf.dptr = (uint8 *)packed_ee;
if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
/* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
- tdb_unlock_bystring( the_tdb, VN_next_record );
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
talloc_destroy( mem_ctx );
return 0;
}
next_record++;
- tdb_store_int32( the_tdb, VN_next_record, next_record );
- tdb_unlock_bystring( the_tdb, VN_next_record );
+ tdb_store_int32( the_tdb, EVT_NEXT_RECORD, next_record );
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
talloc_destroy( mem_ctx );
return ( next_record - 1 );
}
-/* calculate the correct fields etc for an eventlog entry */
+/*******************************************************************
+ calculate the correct fields etc for an eventlog entry
+*******************************************************************/
void fixup_eventlog_entry( Eventlog_entry * ee )
{
}
/********************************************************************
-Note that it's a pretty good idea to initialize the Eventlog_entry structure to zero's before
-calling parse_logentry on an batch of lines that may resolve to a record.
-ALSO, it's a good idea to remove any linefeeds (that's EOL to you and me) on the lines going in.
-
+ Note that it's a pretty good idea to initialize the Eventlog_entry
+ structure to zero's before calling parse_logentry on an batch of
+ lines that may resolve to a record. ALSO, it's a good idea to
+ remove any linefeeds (that's EOL to you and me) on the lines
+ going in.
********************************************************************/
-BOOL parse_logentry( char *line, Eventlog_entry * entry, BOOL * eor )
+bool parse_logentry( char *line, Eventlog_entry * entry, bool * eor )
{
char *start = NULL, *stop = NULL;
pstring temp;
memset( temp, 0, sizeof( temp ) );
strncpy( temp, stop, temp_len );
rpcstr_push( ( void * ) ( entry->data_record.strings +
- entry->data_record.strings_len ),
+ ( entry->data_record.strings_len / 2 ) ),
temp,
sizeof( entry->data_record.strings ) -
- entry->data_record.strings_len, STR_TERMINATE );
- entry->data_record.strings_len += temp_len + 1;
+ ( entry->data_record.strings_len / 2 ), STR_TERMINATE );
+ entry->data_record.strings_len += ( temp_len * 2 ) + 2;
entry->record.num_strings++;
} else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
- /* Now that we're done processing the STR data, adjust the length to account for
- unicode, then proceed with the DAT data. */
- entry->data_record.strings_len *= 2;
/* skip past initial ":" */
stop++;
/* now skip any other leading whitespace */