aboutsummaryrefslogtreecommitdiffstats
path: root/daemon/syslogd.c
diff options
context:
space:
mode:
authoryaworsky <yaworsky>2005-09-16 08:33:41 +0000
committeryaworsky <yaworsky>2005-09-16 08:33:41 +0000
commita2c534419863b2c09fd122d8d1ef6769398f7cf0 (patch)
treec58907db78a04c9d4fae9999651e1a7293cf38f1 /daemon/syslogd.c
parent7875bee3ddfd14f2128540609519306742a78dec (diff)
downloadsyslog-win32-a2c534419863b2c09fd122d8d1ef6769398f7cf0.tar.gz
syslog-win32-a2c534419863b2c09fd122d8d1ef6769398f7cf0.tar.xz
syslog-win32-a2c534419863b2c09fd122d8d1ef6769398f7cf0.zip
Added to repository.
Diffstat (limited to '')
-rw-r--r--daemon/syslogd.c600
1 files changed, 600 insertions, 0 deletions
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 <ctype.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <winsock2.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+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" );
+}