From c16b1e3d576840684f63a64e87504de40a207a5e Mon Sep 17 00:00:00 2001 From: yaworsky Date: Mon, 24 Oct 2005 13:00:40 +0000 Subject: Renamed writer.c to dest_file.c. --- daemon/Makefile.am | 4 +- daemon/dest_file.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++++++++ daemon/writer.c | 522 ----------------------------------------------------- 3 files changed, 524 insertions(+), 524 deletions(-) create mode 100644 daemon/dest_file.c delete mode 100644 daemon/writer.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index aa2599a..81fddfa 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -13,8 +13,8 @@ endif AM_CPPFLAGS += -I. -I../include $(GLIB_CFLAGS) bin_PROGRAMS = syslogd -syslogd_SOURCES = conf.c fifo.c listener.c logrotate.c main.c names.c \ - pathnames.c purger.c syslogd.c writer.c syslogd.h +syslogd_SOURCES = conf.c dest_file.c fifo.c listener.c logrotate.c \ + main.c names.c pathnames.c purger.c syslogd.c syslogd.h syslogd_LDADD = -lws2_32 $(GLIB_LIBS) endif diff --git a/daemon/dest_file.c b/daemon/dest_file.c new file mode 100644 index 0000000..7761bbc --- /dev/null +++ b/daemon/dest_file.c @@ -0,0 +1,522 @@ +/* + * writer.c - syslogd implementation for windows, message writer + * + * Created by Alexander Yaworsky + * + * Asynchronous i/o is not supported under win9x so we have to use + * a separate thread for each file. + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define WRITER_KEEPALIVE_TIME 10000 + +struct file_writer +{ + struct destination *destination; + gchar *file_name; + struct fifo *message_queue; + HANDLE fifo_semaphore; + HANDLE shutdown_event; /* manual-reset event */ + HANDLE fd; + struct message *first_msg, *second_msg, *current_msg; + int coalesce_count; + time_t max_hold; +}; + +struct dest_extra +{ + GList *file_writers; + CRITICAL_SECTION cs_file_writers; +}; + +/* forward declarations */ +static unsigned __stdcall writer_thread_proc( void* arg ); + +/****************************************************************************** + * compare_current_and_first_messages + * + * Auxiliary function for coalescer. + * If messages aren't identical, reset coalescer. + */ +static gboolean compare_current_and_first_messages( struct file_writer* writer ) +{ + TRACE_ENTER( "%p\n", writer ); + + if( strcmp( writer->current_msg->hostname, writer->first_msg->hostname ) + || strcmp( writer->current_msg->message, writer->first_msg->message ) ) + { + release_message( writer->first_msg ); + writer->first_msg = NULL; + TRACE_LEAVE( "%p done; messages aren't identical\n", writer ); + return FALSE; + } + + TRACE_LEAVE( "%p ok\n" ); + return TRUE; +} + +/****************************************************************************** + * init_coalescer + * + * Should be called when we've got the first unique message. + * Save message for subsequent comparsions and set max hold time. + */ +static void init_coalescer( struct file_writer* writer ) +{ + writer->first_msg = writer->current_msg; + reference_message( writer->first_msg ); + writer->max_hold = time(NULL) + hold; + writer->coalesce_count = 1; + TRACE_2( "%p max_hold=%ld\n", writer, writer->max_hold ); +} + +/****************************************************************************** + * coalesce + * + * coalesce messages; + * If there are more than two sequential identical messages then we + * should write first of them followed by message with the number of + * subsequent messages. + * + * The caller always must process current_msg unless it is set NULL + * when coalesced; + * return: TRUE if messages are coalesced; FALSE if flushing is required + */ +static gboolean coalesce( struct file_writer* writer ) +{ + time_t current_time; + + TRACE_ENTER( "%p\n", writer ); + + switch( writer->coalesce_count ) + { + case 0: + /* the first message */ + init_coalescer( writer ); + return TRUE; + + case 1: + /* the second message */ + TRACE_2( "%p second message\n", writer ); + if( !compare_current_and_first_messages( writer ) ) + return FALSE; + writer->second_msg = writer->current_msg; + writer->current_msg = NULL; + writer->coalesce_count = 2; + goto check_hold_time; + + case 2: + /* the third message */ + TRACE_2( "%p third message\n", writer ); + if( !compare_current_and_first_messages( writer ) ) + /* leave the second message; it will be written by flush_coalescer */ + return FALSE; + release_message( writer->second_msg ); + writer->second_msg = NULL; + release_message( writer->current_msg ); + writer->current_msg = NULL; + writer->coalesce_count = 3; + goto check_hold_time; + + default: + /* the fourth and subsequent messages */ + TRACE_2( "%p fourth+ message\n", writer ); + if( !compare_current_and_first_messages( writer ) ) + return FALSE; + release_message( writer->current_msg ); + writer->current_msg = NULL; + writer->coalesce_count++; + TRACE_2( "%p coalesce_count=%d\n", writer, writer->coalesce_count ); + goto check_hold_time; + } + +check_hold_time: + current_time = time(NULL); + TRACE_2( "%p current_time=%ld\n", writer, current_time ); + if( writer->max_hold < current_time ) + { + TRACE_LEAVE( "%p done; elapsed hold time\n", writer ); + return FALSE; + } + TRACE_LEAVE( "%p done\n", writer ); + return TRUE; +} + +/****************************************************************************** + * write_message_to_logfile + */ +static void write_message_to_logfile( struct file_writer* writer, struct message** msg ) +{ + gchar *buffer; + DWORD written; + + if( INVALID_HANDLE_VALUE == writer->fd ) + return; + TRACE_2( "%p: %s\n", writer, (*msg)->message ); + buffer = g_strconcat( (*msg)->timestamp, " ", (*msg)->hostname, " ", (*msg)->message, "\n", NULL ); + WriteFile( writer->fd, buffer, strlen( buffer ), &written, NULL ); + g_free( buffer ); + release_message( *msg ); + *msg = NULL; +} + +/****************************************************************************** + * flush_coalescer + */ +static void flush_coalescer( struct file_writer* writer ) +{ + if( writer->second_msg ) + { + write_message_to_logfile( writer, &writer->second_msg ); + TRACE_2( "%p written second message\n", writer ); + } + + if( writer->coalesce_count > 2 ) + { + SYSTEMTIME stm; + char buffer[ 1024 ]; + int size; + DWORD written; + + GetLocalTime( &stm ); + + /* make informational message */ + size = snprintf( buffer, sizeof(buffer), + "%s %2d %02d:%02d:%02d %s syslog: last message repeated %d times\n", + str_month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond, + local_hostname, + writer->coalesce_count - 1 ); + WriteFile( writer->fd, buffer, size, &written, NULL ); + TRACE_2( "%p made informational message\n", writer ); + } + + writer->coalesce_count = 0; + if( writer->first_msg ) + { + release_message( writer->first_msg ); + writer->first_msg = NULL; + } + if( writer->current_msg ) + { + /* we just got the first message and should initialize coalescer */ + init_coalescer( writer ); + write_message_to_logfile( writer, &writer->current_msg ); + TRACE_2( "%p written current message\n", writer ); + } +} + +/****************************************************************************** + * destroy_file_writer + */ +static void destroy_file_writer( struct file_writer* writer ) +{ + TRACE_ENTER( "%p\n", writer ); + if( writer->fifo_semaphore ) CloseHandle( writer->fifo_semaphore ); + if( writer->shutdown_event ) CloseHandle( writer->shutdown_event ); + fifo_destroy( writer->message_queue ); + g_free( writer->file_name ); + g_free( writer ); + TRACE_LEAVE( "done\n" ); +} + +/****************************************************************************** + * create_file_writer + */ +static struct file_writer* create_file_writer( gchar* file_name ) +{ + struct file_writer *ret; + unsigned writer_thread_id; + HANDLE *writer_thread; + + TRACE_ENTER( "file_name=%s\n", file_name ); + ret = g_malloc0( sizeof(struct file_writer) ); + ret->file_name = g_strdup( file_name ); + ret->message_queue = fifo_create(); + ret->fifo_semaphore = CreateSemaphore( NULL, 0, LONG_MAX, NULL ); + if( !ret->fifo_semaphore ) + { + ERR( "Cannot create semaphore; error %lu\n", GetLastError() ); + goto error; + } + ret->shutdown_event = CreateEvent( NULL, TRUE, FALSE, NULL ); + if( !ret->shutdown_event ) + { + ERR( "Cannot create event; error %lu\n", GetLastError() ); + goto error; + } + writer_thread = (HANDLE) _beginthreadex( NULL, 0, writer_thread_proc, ret, + 0, &writer_thread_id ); + if( !writer_thread ) + { + ERR( "Cannot create thread; error %lu\n", GetLastError() ); + goto error; + } + CloseHandle( writer_thread ); + TRACE_LEAVE( "done; ret=%p\n", ret ); + return ret; + +error: + destroy_file_writer( ret ); + TRACE_LEAVE( "error\n" ); + return NULL; +} + +/****************************************************************************** + * detach_writer_from_destination + */ +static void detach_writer_from_destination( struct file_writer* writer ) +{ + struct dest_extra *extra; + + TRACE_ENTER( "%p\n", writer ); + if( !writer->destination ) + { + TRACE_LEAVE( "done; already detached\n" ); + return; + } + extra = writer->destination->extra; + EnterCriticalSection( &extra->cs_file_writers ); + extra->file_writers = g_list_remove( extra->file_writers, writer ); + LeaveCriticalSection( &extra->cs_file_writers ); + writer->destination = NULL; + TRACE_LEAVE( "done\n" ); +} + +/****************************************************************************** + * writer_thread_proc + * + * Open file, extract messages from queue and write them to file. + * If queue is empty long enough, close file and destroy itself. + */ +static unsigned __stdcall writer_thread_proc( void* arg ) +{ + struct file_writer *writer = arg; + HANDLE wait_objects[2] = { writer->fifo_semaphore, writer->shutdown_event }; + gchar *pathname; + + TRACE_ENTER( "writer=%p\n", writer ); + + pathname = make_absolute_log_pathname( writer->file_name ); + create_directories( pathname ); + + rotate_logfile( pathname, writer->destination ); + + writer->fd = CreateFile( pathname, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if( INVALID_HANDLE_VALUE == writer->fd ) + { + ERR( "CreateFile(%s) error %lu\n", pathname, GetLastError() ); + g_free( pathname ); + goto done; + } + + /* there's a strange bug or feature in windows: if there was a file with the same + name in the directory, a new file will inherit its creation time; + because of this logs will be rotated each time; + here is a workaround: + */ + if( GetLastError() != ERROR_ALREADY_EXISTS ) + { + FILETIME systime; + GetSystemTimeAsFileTime( &systime ); + if( !SetFileTime( writer->fd, &systime, &systime, &systime ) ) + ERR( "SetFileTime error %lu\n", GetLastError() ); + } + + g_free( pathname ); + SetFilePointer( writer->fd, 0, NULL, FILE_END ); + + for(;;) + { + DWORD w = WaitForMultipleObjects( 2, wait_objects, FALSE, WRITER_KEEPALIVE_TIME ); + if( WAIT_TIMEOUT == w ) + { + /* prepare to suicide; at this point a new message may be put into queue; + detach writer from destination and continue to write any pending messages */ + detach_writer_from_destination( writer ); + /* from now no new messages will be put into queue */ + SetEvent( writer->shutdown_event ); + } + + writer->current_msg = fifo_pop( writer->message_queue ); + if( !writer->current_msg ) + { + /* shutdown */ + goto done; + } + + if( coalesce( writer ) ) + { + if( writer->current_msg ) + { + write_message_to_logfile( writer, &writer->current_msg ); + TRACE_2( "%p written current message\n", writer ); + } + } + else + { + flush_coalescer( writer ); + } + } +done: + detach_writer_from_destination( writer ); + flush_coalescer( writer ); + if( writer->fd != INVALID_HANDLE_VALUE ) CloseHandle( writer->fd ); + destroy_file_writer( writer ); + + purge_log_dirs(); + + TRACE_LEAVE( "done\n" ); + return 0; +} + +/****************************************************************************** + * make_file_name + * + * expand filename pattern (message->file) + */ +static void make_file_name( char* pattern, struct message* message, char* buffer ) +{ + char *dest = buffer; + SYSTEMTIME stm; + + GetLocalTime( &stm ); + for(;;) + { + char c = *pattern++; + if( c != '%' ) + { + *dest++ = c; + if( '\0' == c ) + break; + continue; + } + c = *pattern++; + switch( c ) + { + case 'Y': dest += sprintf( dest, "%u", stm.wYear ); break; + case 'M': dest += sprintf( dest, "%02u", stm.wMonth ); break; + case 'm': dest += sprintf( dest, "%u", stm.wMonth ); break; + case 'D': dest += sprintf( dest, "%02u", stm.wDay ); break; + case 'd': dest += sprintf( dest, "%u", stm.wDay ); break; + case 'W': dest += sprintf( dest, "%u", stm.wDayOfWeek + 1 ); break; + case 'F': dest += sprintf( dest, "%s", get_facility_name( message->facility ) ); break; + case 'f': dest += sprintf( dest, "%d", message->facility ); break; + case 'L': dest += sprintf( dest, "%s", get_priority_name( message->priority ) ); break; + case 'l': dest += sprintf( dest, "%d", message->priority ); break; + case 'H': dest += sprintf( dest, "%s", message->hostname ); break; + case 'h': dest += sprintf( dest, "%s", message->sender ); break; + case 'P': dest += sprintf( dest, "%s", message->program ); break; + default: *dest++ = c; break; + } + } + strlwr( buffer ); +} + +/****************************************************************************** + * put_message_to_file_dest + */ +static void put_message_to_file_dest( struct destination* destination, struct message* msg ) +{ + struct dest_extra *extra = destination->extra; + char file_name[ MAX_PATH ]; + GList *item; + struct file_writer *writer; + + TRACE_ENTER( "msg=%p, destination=%s\n", msg, destination->name ); + make_file_name( destination->u.file.name_pattern, msg, file_name ); + EnterCriticalSection( &extra->cs_file_writers ); + /* find existing writer */ + for( writer = NULL, item = extra->file_writers; item; item = item->next ) + if( strcmp( ((struct file_writer*) (item->data))->file_name, file_name ) == 0 ) + { + writer = item->data; + break; + } + if( !writer ) + { + /* create new writer */ + writer = create_file_writer( file_name ); + if( !writer ) + goto done; + /* add writer to destination */ + extra->file_writers = g_list_append( extra->file_writers, writer ); + writer->destination = destination; + } + /* put message into queue */ + reference_message( msg ); + fifo_push( writer->message_queue, msg ); + ReleaseSemaphore( writer->fifo_semaphore, 1, NULL ); + +done: + LeaveCriticalSection( &extra->cs_file_writers ); + TRACE_LEAVE( "done\n" ); +} + +/****************************************************************************** + * finalize_file_dest + * + * stop all writers + */ +static void finalize_file_dest( struct destination* destination ) +{ + struct dest_extra *extra = destination->extra; + GList *wr_item; + + TRACE_ENTER( "destination=%s\n", destination->name ); + /* setting shutdown event */ + EnterCriticalSection( &extra->cs_file_writers ); + for( wr_item = extra->file_writers; wr_item; wr_item = wr_item->next ) + { + struct file_writer *writer = wr_item->data; + SetEvent( writer->shutdown_event ); + } + LeaveCriticalSection( &extra->cs_file_writers ); + TRACE_2( "waiting for shutdown\n" ); + while( extra->file_writers ) + { + Sleep( 60 ); + } + TRACE_LEAVE( "done\n" ); +} + +/****************************************************************************** + * init_destination_file + * + * initialize file destination + */ +gboolean init_destination_file( struct destination* destination ) +{ + struct dest_extra *extra = g_malloc( sizeof(struct dest_extra) ); + + destination->extra = extra; + destination->put = put_message_to_file_dest; + destination->fini = finalize_file_dest; + extra->file_writers = NULL; + InitializeCriticalSection( &extra->cs_file_writers ); + return TRUE; +} diff --git a/daemon/writer.c b/daemon/writer.c deleted file mode 100644 index 7761bbc..0000000 --- a/daemon/writer.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - * writer.c - syslogd implementation for windows, message writer - * - * Created by Alexander Yaworsky - * - * Asynchronous i/o is not supported under win9x so we have to use - * a separate thread for each file. - * - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#define WRITER_KEEPALIVE_TIME 10000 - -struct file_writer -{ - struct destination *destination; - gchar *file_name; - struct fifo *message_queue; - HANDLE fifo_semaphore; - HANDLE shutdown_event; /* manual-reset event */ - HANDLE fd; - struct message *first_msg, *second_msg, *current_msg; - int coalesce_count; - time_t max_hold; -}; - -struct dest_extra -{ - GList *file_writers; - CRITICAL_SECTION cs_file_writers; -}; - -/* forward declarations */ -static unsigned __stdcall writer_thread_proc( void* arg ); - -/****************************************************************************** - * compare_current_and_first_messages - * - * Auxiliary function for coalescer. - * If messages aren't identical, reset coalescer. - */ -static gboolean compare_current_and_first_messages( struct file_writer* writer ) -{ - TRACE_ENTER( "%p\n", writer ); - - if( strcmp( writer->current_msg->hostname, writer->first_msg->hostname ) - || strcmp( writer->current_msg->message, writer->first_msg->message ) ) - { - release_message( writer->first_msg ); - writer->first_msg = NULL; - TRACE_LEAVE( "%p done; messages aren't identical\n", writer ); - return FALSE; - } - - TRACE_LEAVE( "%p ok\n" ); - return TRUE; -} - -/****************************************************************************** - * init_coalescer - * - * Should be called when we've got the first unique message. - * Save message for subsequent comparsions and set max hold time. - */ -static void init_coalescer( struct file_writer* writer ) -{ - writer->first_msg = writer->current_msg; - reference_message( writer->first_msg ); - writer->max_hold = time(NULL) + hold; - writer->coalesce_count = 1; - TRACE_2( "%p max_hold=%ld\n", writer, writer->max_hold ); -} - -/****************************************************************************** - * coalesce - * - * coalesce messages; - * If there are more than two sequential identical messages then we - * should write first of them followed by message with the number of - * subsequent messages. - * - * The caller always must process current_msg unless it is set NULL - * when coalesced; - * return: TRUE if messages are coalesced; FALSE if flushing is required - */ -static gboolean coalesce( struct file_writer* writer ) -{ - time_t current_time; - - TRACE_ENTER( "%p\n", writer ); - - switch( writer->coalesce_count ) - { - case 0: - /* the first message */ - init_coalescer( writer ); - return TRUE; - - case 1: - /* the second message */ - TRACE_2( "%p second message\n", writer ); - if( !compare_current_and_first_messages( writer ) ) - return FALSE; - writer->second_msg = writer->current_msg; - writer->current_msg = NULL; - writer->coalesce_count = 2; - goto check_hold_time; - - case 2: - /* the third message */ - TRACE_2( "%p third message\n", writer ); - if( !compare_current_and_first_messages( writer ) ) - /* leave the second message; it will be written by flush_coalescer */ - return FALSE; - release_message( writer->second_msg ); - writer->second_msg = NULL; - release_message( writer->current_msg ); - writer->current_msg = NULL; - writer->coalesce_count = 3; - goto check_hold_time; - - default: - /* the fourth and subsequent messages */ - TRACE_2( "%p fourth+ message\n", writer ); - if( !compare_current_and_first_messages( writer ) ) - return FALSE; - release_message( writer->current_msg ); - writer->current_msg = NULL; - writer->coalesce_count++; - TRACE_2( "%p coalesce_count=%d\n", writer, writer->coalesce_count ); - goto check_hold_time; - } - -check_hold_time: - current_time = time(NULL); - TRACE_2( "%p current_time=%ld\n", writer, current_time ); - if( writer->max_hold < current_time ) - { - TRACE_LEAVE( "%p done; elapsed hold time\n", writer ); - return FALSE; - } - TRACE_LEAVE( "%p done\n", writer ); - return TRUE; -} - -/****************************************************************************** - * write_message_to_logfile - */ -static void write_message_to_logfile( struct file_writer* writer, struct message** msg ) -{ - gchar *buffer; - DWORD written; - - if( INVALID_HANDLE_VALUE == writer->fd ) - return; - TRACE_2( "%p: %s\n", writer, (*msg)->message ); - buffer = g_strconcat( (*msg)->timestamp, " ", (*msg)->hostname, " ", (*msg)->message, "\n", NULL ); - WriteFile( writer->fd, buffer, strlen( buffer ), &written, NULL ); - g_free( buffer ); - release_message( *msg ); - *msg = NULL; -} - -/****************************************************************************** - * flush_coalescer - */ -static void flush_coalescer( struct file_writer* writer ) -{ - if( writer->second_msg ) - { - write_message_to_logfile( writer, &writer->second_msg ); - TRACE_2( "%p written second message\n", writer ); - } - - if( writer->coalesce_count > 2 ) - { - SYSTEMTIME stm; - char buffer[ 1024 ]; - int size; - DWORD written; - - GetLocalTime( &stm ); - - /* make informational message */ - size = snprintf( buffer, sizeof(buffer), - "%s %2d %02d:%02d:%02d %s syslog: last message repeated %d times\n", - str_month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond, - local_hostname, - writer->coalesce_count - 1 ); - WriteFile( writer->fd, buffer, size, &written, NULL ); - TRACE_2( "%p made informational message\n", writer ); - } - - writer->coalesce_count = 0; - if( writer->first_msg ) - { - release_message( writer->first_msg ); - writer->first_msg = NULL; - } - if( writer->current_msg ) - { - /* we just got the first message and should initialize coalescer */ - init_coalescer( writer ); - write_message_to_logfile( writer, &writer->current_msg ); - TRACE_2( "%p written current message\n", writer ); - } -} - -/****************************************************************************** - * destroy_file_writer - */ -static void destroy_file_writer( struct file_writer* writer ) -{ - TRACE_ENTER( "%p\n", writer ); - if( writer->fifo_semaphore ) CloseHandle( writer->fifo_semaphore ); - if( writer->shutdown_event ) CloseHandle( writer->shutdown_event ); - fifo_destroy( writer->message_queue ); - g_free( writer->file_name ); - g_free( writer ); - TRACE_LEAVE( "done\n" ); -} - -/****************************************************************************** - * create_file_writer - */ -static struct file_writer* create_file_writer( gchar* file_name ) -{ - struct file_writer *ret; - unsigned writer_thread_id; - HANDLE *writer_thread; - - TRACE_ENTER( "file_name=%s\n", file_name ); - ret = g_malloc0( sizeof(struct file_writer) ); - ret->file_name = g_strdup( file_name ); - ret->message_queue = fifo_create(); - ret->fifo_semaphore = CreateSemaphore( NULL, 0, LONG_MAX, NULL ); - if( !ret->fifo_semaphore ) - { - ERR( "Cannot create semaphore; error %lu\n", GetLastError() ); - goto error; - } - ret->shutdown_event = CreateEvent( NULL, TRUE, FALSE, NULL ); - if( !ret->shutdown_event ) - { - ERR( "Cannot create event; error %lu\n", GetLastError() ); - goto error; - } - writer_thread = (HANDLE) _beginthreadex( NULL, 0, writer_thread_proc, ret, - 0, &writer_thread_id ); - if( !writer_thread ) - { - ERR( "Cannot create thread; error %lu\n", GetLastError() ); - goto error; - } - CloseHandle( writer_thread ); - TRACE_LEAVE( "done; ret=%p\n", ret ); - return ret; - -error: - destroy_file_writer( ret ); - TRACE_LEAVE( "error\n" ); - return NULL; -} - -/****************************************************************************** - * detach_writer_from_destination - */ -static void detach_writer_from_destination( struct file_writer* writer ) -{ - struct dest_extra *extra; - - TRACE_ENTER( "%p\n", writer ); - if( !writer->destination ) - { - TRACE_LEAVE( "done; already detached\n" ); - return; - } - extra = writer->destination->extra; - EnterCriticalSection( &extra->cs_file_writers ); - extra->file_writers = g_list_remove( extra->file_writers, writer ); - LeaveCriticalSection( &extra->cs_file_writers ); - writer->destination = NULL; - TRACE_LEAVE( "done\n" ); -} - -/****************************************************************************** - * writer_thread_proc - * - * Open file, extract messages from queue and write them to file. - * If queue is empty long enough, close file and destroy itself. - */ -static unsigned __stdcall writer_thread_proc( void* arg ) -{ - struct file_writer *writer = arg; - HANDLE wait_objects[2] = { writer->fifo_semaphore, writer->shutdown_event }; - gchar *pathname; - - TRACE_ENTER( "writer=%p\n", writer ); - - pathname = make_absolute_log_pathname( writer->file_name ); - create_directories( pathname ); - - rotate_logfile( pathname, writer->destination ); - - writer->fd = CreateFile( pathname, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); - if( INVALID_HANDLE_VALUE == writer->fd ) - { - ERR( "CreateFile(%s) error %lu\n", pathname, GetLastError() ); - g_free( pathname ); - goto done; - } - - /* there's a strange bug or feature in windows: if there was a file with the same - name in the directory, a new file will inherit its creation time; - because of this logs will be rotated each time; - here is a workaround: - */ - if( GetLastError() != ERROR_ALREADY_EXISTS ) - { - FILETIME systime; - GetSystemTimeAsFileTime( &systime ); - if( !SetFileTime( writer->fd, &systime, &systime, &systime ) ) - ERR( "SetFileTime error %lu\n", GetLastError() ); - } - - g_free( pathname ); - SetFilePointer( writer->fd, 0, NULL, FILE_END ); - - for(;;) - { - DWORD w = WaitForMultipleObjects( 2, wait_objects, FALSE, WRITER_KEEPALIVE_TIME ); - if( WAIT_TIMEOUT == w ) - { - /* prepare to suicide; at this point a new message may be put into queue; - detach writer from destination and continue to write any pending messages */ - detach_writer_from_destination( writer ); - /* from now no new messages will be put into queue */ - SetEvent( writer->shutdown_event ); - } - - writer->current_msg = fifo_pop( writer->message_queue ); - if( !writer->current_msg ) - { - /* shutdown */ - goto done; - } - - if( coalesce( writer ) ) - { - if( writer->current_msg ) - { - write_message_to_logfile( writer, &writer->current_msg ); - TRACE_2( "%p written current message\n", writer ); - } - } - else - { - flush_coalescer( writer ); - } - } -done: - detach_writer_from_destination( writer ); - flush_coalescer( writer ); - if( writer->fd != INVALID_HANDLE_VALUE ) CloseHandle( writer->fd ); - destroy_file_writer( writer ); - - purge_log_dirs(); - - TRACE_LEAVE( "done\n" ); - return 0; -} - -/****************************************************************************** - * make_file_name - * - * expand filename pattern (message->file) - */ -static void make_file_name( char* pattern, struct message* message, char* buffer ) -{ - char *dest = buffer; - SYSTEMTIME stm; - - GetLocalTime( &stm ); - for(;;) - { - char c = *pattern++; - if( c != '%' ) - { - *dest++ = c; - if( '\0' == c ) - break; - continue; - } - c = *pattern++; - switch( c ) - { - case 'Y': dest += sprintf( dest, "%u", stm.wYear ); break; - case 'M': dest += sprintf( dest, "%02u", stm.wMonth ); break; - case 'm': dest += sprintf( dest, "%u", stm.wMonth ); break; - case 'D': dest += sprintf( dest, "%02u", stm.wDay ); break; - case 'd': dest += sprintf( dest, "%u", stm.wDay ); break; - case 'W': dest += sprintf( dest, "%u", stm.wDayOfWeek + 1 ); break; - case 'F': dest += sprintf( dest, "%s", get_facility_name( message->facility ) ); break; - case 'f': dest += sprintf( dest, "%d", message->facility ); break; - case 'L': dest += sprintf( dest, "%s", get_priority_name( message->priority ) ); break; - case 'l': dest += sprintf( dest, "%d", message->priority ); break; - case 'H': dest += sprintf( dest, "%s", message->hostname ); break; - case 'h': dest += sprintf( dest, "%s", message->sender ); break; - case 'P': dest += sprintf( dest, "%s", message->program ); break; - default: *dest++ = c; break; - } - } - strlwr( buffer ); -} - -/****************************************************************************** - * put_message_to_file_dest - */ -static void put_message_to_file_dest( struct destination* destination, struct message* msg ) -{ - struct dest_extra *extra = destination->extra; - char file_name[ MAX_PATH ]; - GList *item; - struct file_writer *writer; - - TRACE_ENTER( "msg=%p, destination=%s\n", msg, destination->name ); - make_file_name( destination->u.file.name_pattern, msg, file_name ); - EnterCriticalSection( &extra->cs_file_writers ); - /* find existing writer */ - for( writer = NULL, item = extra->file_writers; item; item = item->next ) - if( strcmp( ((struct file_writer*) (item->data))->file_name, file_name ) == 0 ) - { - writer = item->data; - break; - } - if( !writer ) - { - /* create new writer */ - writer = create_file_writer( file_name ); - if( !writer ) - goto done; - /* add writer to destination */ - extra->file_writers = g_list_append( extra->file_writers, writer ); - writer->destination = destination; - } - /* put message into queue */ - reference_message( msg ); - fifo_push( writer->message_queue, msg ); - ReleaseSemaphore( writer->fifo_semaphore, 1, NULL ); - -done: - LeaveCriticalSection( &extra->cs_file_writers ); - TRACE_LEAVE( "done\n" ); -} - -/****************************************************************************** - * finalize_file_dest - * - * stop all writers - */ -static void finalize_file_dest( struct destination* destination ) -{ - struct dest_extra *extra = destination->extra; - GList *wr_item; - - TRACE_ENTER( "destination=%s\n", destination->name ); - /* setting shutdown event */ - EnterCriticalSection( &extra->cs_file_writers ); - for( wr_item = extra->file_writers; wr_item; wr_item = wr_item->next ) - { - struct file_writer *writer = wr_item->data; - SetEvent( writer->shutdown_event ); - } - LeaveCriticalSection( &extra->cs_file_writers ); - TRACE_2( "waiting for shutdown\n" ); - while( extra->file_writers ) - { - Sleep( 60 ); - } - TRACE_LEAVE( "done\n" ); -} - -/****************************************************************************** - * init_destination_file - * - * initialize file destination - */ -gboolean init_destination_file( struct destination* destination ) -{ - struct dest_extra *extra = g_malloc( sizeof(struct dest_extra) ); - - destination->extra = extra; - destination->put = put_message_to_file_dest; - destination->fini = finalize_file_dest; - extra->file_writers = NULL; - InitializeCriticalSection( &extra->cs_file_writers ); - return TRUE; -} -- cgit v1.2.3