From a2c534419863b2c09fd122d8d1ef6769398f7cf0 Mon Sep 17 00:00:00 2001 From: yaworsky Date: Fri, 16 Sep 2005 08:33:41 +0000 Subject: Added to repository. --- daemon/syslogd.c | 600 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 daemon/syslogd.c (limited to 'daemon/syslogd.c') diff --git a/daemon/syslogd.c b/daemon/syslogd.c new file mode 100644 index 0000000..59ede6d --- /dev/null +++ b/daemon/syslogd.c @@ -0,0 +1,600 @@ +/* + * syslogd.c - syslogd implementation for windows + * + * Created by Alexander Yaworsky + * + * 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 + +static struct fifo *raw_message_queue = NULL; +static HANDLE fifo_semaphore = NULL; + +/* cache for source hostnames */ +struct hostname +{ + struct sockaddr_in addr; + gchar *host; + time_t top_age; /* zero prevents aging */ +}; +static GList *hostnames = NULL; + +#define HOSTNAME_LIFETIME 60 /* seconds */ +/* FIXME: is this value correct? maybe we should make it configurable? */ + +static GIConv conversion_descriptor = (GIConv) -1; + +char *str_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +/****************************************************************************** + * refrence_message + * + * increment reference count + */ +void reference_message( struct message* msg ) +{ + TRACE_ENTER( "message=%p\n", msg ); + InterlockedIncrement( &msg->refcount ); +} + +/****************************************************************************** + * release_message + * + * decrement reference count and destroy message structure if it becomes zero + */ +void release_message( struct message* msg ) +{ + TRACE_ENTER( "message=%p\n", msg ); + if( InterlockedDecrement( &msg->refcount ) ) + { + TRACE_LEAVE( "done; still referenced\n" ); + return; + } + g_free( msg->sender ); + g_free( msg->timestamp ); + g_free( msg->hostname ); + g_free( msg->program ); + g_free( msg->message ); + g_free( msg ); + TRACE_LEAVE( "done\n" ); +} + +/****************************************************************************** + * convert_message_encoding + */ +static void convert_message_encoding( struct message* msg ) +{ + gchar *converted_msg; + + TRACE_ENTER( "message=%p\n", msg ); + + if( conversion_descriptor == (GIConv) -1 ) + { + TRACE_LEAVE( "nothing to do\n" ); + return; + } + + converted_msg = g_convert_with_iconv( msg->message, -1, + conversion_descriptor, NULL, NULL, NULL ); + if( !converted_msg ) + { + TRACE_LEAVE( "conversion error\n" ); + return; + } + + g_free( msg->message ); + msg->message = converted_msg; + + TRACE_LEAVE( "done; %s\n", msg->message ); +} + +/****************************************************************************** + * filter_message + * + * return: TRUE - accepted message, FALSE - rejected message + */ +static gboolean filter_message( struct message* msg, struct filter* filter ) +{ + gboolean ret = FALSE; + + TRACE_ENTER( "message=%p, filter=%s\n", msg, filter? filter->name : "NULL" ); + + if( !filter ) + goto passed; + + /* check facility */ + if( !filter->facilities[ msg->facility ] ) + goto done; + + /* check priority */ + if( !filter->priorities[ msg->priority ] ) + goto done; + +passed: + /* message is passed through filter */ + ret = TRUE; + +done: + TRACE_LEAVE( "done; ret=%d\n", ret ); + return ret; +} + +/****************************************************************************** + * mux_message + * + * filter and multiplex message to destinations; + * release message + */ +static void mux_message( struct message* msg ) +{ + GList *item; + + TRACE_ENTER( "message=%p\n", msg ); + + convert_message_encoding( msg ); + + for( item = logpaths; item; item = item->next ) + { + struct logpath *logpath = item->data; + + if( logpath->source != msg->source ) + continue; + + if( !filter_message( msg, logpath->filter ) ) + continue; + + write_message( msg, logpath->destination ); + } + + release_message( msg ); + + TRACE_LEAVE( "done\n" ); +} + +/****************************************************************************** + * get_hostname + * + * convert addr to string and return it + */ +static gchar* get_hostname( struct sockaddr_in* addr ) +{ + gchar *ret; + time_t current_time; + GList *item; + struct hostname *new_hostname; + + TRACE_ENTER( "%d.%d.%d.%d\n", + addr->sin_addr.S_un.S_un_b.s_b1, addr->sin_addr.S_un.S_un_b.s_b2, + addr->sin_addr.S_un.S_un_b.s_b3, addr->sin_addr.S_un.S_un_b.s_b4 ); + + current_time = time(NULL); + + /* at first, try to find a cached entry */ + item = hostnames; + while( item ) + { + struct hostname *h = item->data; + if( h->top_age && h->top_age < current_time ) + { + GList *next_item = item->next; + + TRACE_2( "delete old entry %s\n", h->host ); + g_free( h->host ); + g_free( h ); + hostnames = g_list_delete_link( hostnames, item ); + item = next_item; + continue; + } + if( h->addr.sin_addr.S_un.S_addr == addr->sin_addr.S_un.S_addr ) + { + /* found in cache */ + ret = g_strdup( h->host ); + /* move entry to the beginning of the list */ + item->data = hostnames->data; + hostnames->data = h; + TRACE_LEAVE( "done; found cached entry: %s\n", ret ); + return ret; + } + item = item->next; + } + /* add new entry */ + new_hostname = g_malloc( sizeof(struct hostname) ); + memcpy( &new_hostname->addr, addr, sizeof(struct sockaddr_in) ); + if( use_dns ) + { + struct hostent *he = gethostbyaddr( (char*) &addr->sin_addr.S_un.S_addr, + sizeof(addr->sin_addr.S_un.S_addr), AF_INET ); + new_hostname->top_age = time(NULL) + HOSTNAME_LIFETIME; + if( !he ) + goto use_addr; + new_hostname->host = g_strdup( he->h_name ); + } + else + { + new_hostname->top_age = 0; +use_addr: + new_hostname->host = g_malloc( 16 ); + sprintf( new_hostname->host, "%d.%d.%d.%d", + addr->sin_addr.S_un.S_un_b.s_b1, addr->sin_addr.S_un.S_un_b.s_b2, + addr->sin_addr.S_un.S_un_b.s_b3, addr->sin_addr.S_un.S_un_b.s_b4 ); + } + hostnames = g_list_prepend( hostnames, new_hostname ); + ret = g_strdup( new_hostname->host ); + TRACE_LEAVE( "done; ret=%s\n", ret ); + return ret; +} + +/****************************************************************************** + * free_hostnames + */ +static void free_hostnames() +{ + GList *item; + for( item = hostnames; item; item = item->next ) + { + struct hostname *h = item->data; + g_free( h->host ); + g_free( h ); + } + g_list_free( hostnames ); + hostnames = NULL; +} + +/****************************************************************************** + * parse_PRI + * + * parse PRI part of message; + * set facility and priority; + * return pointer to the next char after PRI + */ +static gchar* parse_PRI( gchar* msg, int* facility, int* priority ) +{ + int i, pri; + + TRACE_ENTER( "\n" ); + + if( *msg != '<' ) + goto no_pri; + + pri = 0; + for( i = 1; i < 5; i++ ) + { + if( msg[ i ] == '>' ) + break; + if( !isdigit( msg[ i ] ) ) + goto no_pri; + pri = (pri * 10) + (msg[ i ] - '0'); + } + if( i == 5 ) + goto no_pri; + if( i == 1 ) + /* FIXME: is this right? or "<>" is an unidentifiable PRI? */ + goto no_pri; + + msg += i + 1; + + if( pri > LOG_NFACILITIES * 8 + 7 ) + { + TRACE_2( "Unidentifiable PRI %d\n", pri ); + *facility = LOG_USER; + *priority = LOG_NOTICE; + } + else + { + TRACE_2( "PRI=%d\n", pri ); + *facility = LOG_FAC( pri ); + *priority = LOG_PRI( pri ); + } + + TRACE_LEAVE( "done; facility=%d, priority=%d\n", *facility, *priority ); + return msg; + +no_pri: + *facility = LOG_USER; + *priority = LOG_NOTICE; + TRACE_LEAVE( "done; message contains no PRI\n" ); + return msg; +} + +/****************************************************************************** + * parse_timestamp + * + * parse TIMESTAMP part of message; + * allocate string; + * return pointer to the next char after TIMESTAMP + */ +static gchar* parse_timestamp( gchar* msg, gchar** timestamp ) +{ + int i; + SYSTEMTIME stm; + + TRACE_ENTER( "\n" ); + + for( i = 0; i < 12; i++ ) + if( strncasecmp( msg, str_month[ i ], 3 ) == 0 ) + break; + if( i == 12 ) + goto no_timestamp; + stm.wMonth = i + 1; + + if( msg[3] != ' ' ) + goto no_timestamp; + + if( msg[4] != ' ' ) + { + if( (!isdigit( msg[4] )) || (!isdigit( msg[5] )) ) + goto no_timestamp; + stm.wDay = (msg[4] - '0') * 10 + (msg[5] - '0'); + } + else + { + if( !isdigit( msg[5] ) ) + goto no_timestamp; + stm.wDay = msg[5] - '0'; + } + + if( msg[6] != ' ' ) + goto no_timestamp; + + if( (!isdigit( msg[7] )) || (!isdigit( msg[8] )) || msg[9] != ':' ) + goto no_timestamp; + stm.wHour = (msg[7] - '0') * 10 + (msg[8] - '0'); + + if( (!isdigit( msg[10] )) || (!isdigit( msg[11] )) || msg[12] != ':' ) + goto no_timestamp; + stm.wMinute = (msg[10] - '0') * 10 + (msg[11] - '0'); + + if( (!isdigit( msg[13] )) || (!isdigit( msg[14] )) || msg[15] != ' ' ) + goto no_timestamp; + stm.wSecond = (msg[13] - '0') * 10 + (msg[14] - '0'); + msg += 16; + goto done; + +no_timestamp: + TRACE_2( "no timestamp\n" ); + GetLocalTime( &stm ); + +done: + *timestamp = g_strdup_printf( "%s %2d %02d:%02d:%02d", + str_month[ stm.wMonth - 1 ], + stm.wDay, stm.wHour, stm.wMinute, stm.wSecond ); + TRACE_LEAVE( "done\n" ); + return msg; +} + +/****************************************************************************** + * parse_raw_message + * + * parse raw message and make a new message; + * destroy raw message + */ +static struct message* parse_raw_message( struct raw_message* raw_msg ) +{ + struct message* msg; + gchar *current_part, *next_part; + + TRACE_ENTER( "raw message=%p\n", raw_msg ); + + /* allocate and initialize message structure */ + msg = g_malloc( sizeof(struct message) ); + msg->refcount = 1; + msg->source = raw_msg->source; + + /* get sender's hostname */ + msg->sender = get_hostname( &raw_msg->sender_addr ); + + current_part = raw_msg->msg; + next_part = parse_PRI( current_part, &msg->facility, &msg->priority ); + + current_part = next_part; + next_part = parse_timestamp( current_part, &msg->timestamp ); + if( next_part == current_part ) + { + /* no valid timestamp */ + TRACE_2( "no valid timestamp: msg=%s\n", current_part ); + msg->hostname = g_strdup( msg->sender ); + msg->message = g_strdup( current_part ); + } + else + { + /* have valid timestamp, go ahead */ + current_part = next_part; + while( isalnum( *next_part ) || *next_part == '-' || *next_part == '.' ) + next_part++; + if( *next_part != ' ' ) + { + /* invalid hostname */ + msg->hostname = g_strdup( msg->sender ); + msg->message = g_strdup( current_part ); + TRACE_2( "invalid hostname; set sender (%s); msg=%s\n", msg->hostname, msg->message ); + } + else + { + msg->hostname = g_strndup( current_part, next_part - current_part ); + while( *next_part == ' ' && *next_part != 0 ) + next_part++; + msg->message = g_strdup( next_part ); + TRACE_2( "hostname=%s; msg=%s\n", msg->hostname, msg->message ); + } + } + + /* try to find program name */ + current_part = msg->message; + next_part = current_part; + while( *next_part != ' ' && *next_part != ':' && *next_part != '[' && *next_part != 0 ) + next_part++; + if( *next_part == ' ' || *next_part == 0 ) + msg->program = g_strdup(""); + else + msg->program = g_strndup( current_part, next_part - current_part ); + + /* destroy raw message */ + g_free( raw_msg->msg ); + g_free( raw_msg ); + + TRACE_LEAVE( "done; message=%p\n", msg ); + return msg; +} + +/****************************************************************************** + * message_processor + * + * main function; extract raw messages from queue and parse them + */ +static unsigned __stdcall message_processor( void* arg ) +{ + HANDLE wait_handles[2] = { fifo_semaphore, service_stop_event }; + + TRACE_ENTER( "\n" ); + for(;;) + { + DWORD w; + struct raw_message *raw_msg; + + w = WaitForMultipleObjects( 2, wait_handles, FALSE, INFINITE ); + if( WAIT_OBJECT_0 + 1 == w ) + /* shutdown */ + break; + if( w != WAIT_OBJECT_0 ) + { + ERR( "WaitForMultipleObjects() error %lu\n", GetLastError() ); + SetEvent( service_stop_event ); + break; + } + /* extract raw message from queue */ + raw_msg = fifo_pop( raw_message_queue ); + + TRACE_2( "got message %p from queue\n", raw_msg ); + + mux_message( parse_raw_message( raw_msg ) ); + } + TRACE_LEAVE( "done\n" ); + return 0; +} + +/****************************************************************************** + * main syslogd function + * + * global initialization; invoke listener + */ +void syslogd_main() +{ + HANDLE message_processor_thread = NULL; + unsigned tid; + + TRACE_ENTER( "\n" ); + + SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ); + + if( !read_configuration() ) + goto done; + + if( !init_purger() ) + goto done; + + if( !init_listener() ) + goto done; + + purge_log_dirs(); + + if( source_encoding && destination_encoding ) + { + conversion_descriptor = g_iconv_open( destination_encoding, source_encoding ); + if( conversion_descriptor == (GIConv) -1 ) + { + ERR( "Cannot convert messages from %s to %s\n", + source_encoding, destination_encoding ); + } + } + + /* create message queue and semaphore; + * avoid using Glib's asynchronous queues and threading support + * because synchronization capabilities are very limited; + */ + raw_message_queue = fifo_create(); + fifo_semaphore = CreateSemaphore( NULL, 0, LONG_MAX, NULL ); + if( !fifo_semaphore ) + { + ERR( "Cannot create semaphore; error %lu\n", GetLastError() ); + goto done; + } + + message_processor_thread = (HANDLE) _beginthreadex( NULL, 0, message_processor, NULL, 0, &tid ); + if( !message_processor_thread ) + { + ERR( "Cannot create thread; error %lu\n", GetLastError() ); + goto done; + } + + /* get messages from the listener */ + for(;;) + { + struct raw_message *raw_msg; + + switch( listener( &raw_msg ) ) + { + case LSNR_ERROR: + case LSNR_SHUTDOWN: + goto done; + + case LSNR_GOT_MESSAGE: + TRACE_2( "got message from %d.%d.%d.%d; source name %s; ptr=%p: %s\n", + raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b1, + raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b2, + raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b3, + raw_msg->sender_addr.sin_addr.S_un.S_un_b.s_b4, + raw_msg->source->name, raw_msg, raw_msg->msg ); + /* add raw message to queue */ + fifo_push( raw_message_queue, raw_msg ); + ReleaseSemaphore( fifo_semaphore, 1, NULL ); + break; + } + } + +done: + /* signal to all possibly running threads */ + SetEvent( service_stop_event ); + + if( message_processor_thread ) + { + /* wait for message processor shutdown */ + WaitForSingleObject( message_processor_thread, INFINITE ); + CloseHandle( message_processor_thread ); + } + + fini_writer(); + fini_purger(); + free_hostnames(); + + if( raw_message_queue ) fifo_destroy( raw_message_queue ); + if( conversion_descriptor != (GIConv) -1 ) + g_iconv_close( conversion_descriptor ); + + fini_listener(); + + if( fifo_semaphore ) CloseHandle( fifo_semaphore ); + + TRACE_LEAVE( "done\n" ); +} -- cgit v1.2.3