/*
* listener.c - syslogd implementation for windows, listener for UDP
* and "internal" sources
*
* 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 <stdio.h>
#include <winsock2.h>
#include <glib.h>
#include <syslog.h>
#include <syslogd.h>
static SOCKET *socket_array = NULL;
static int socket_count = 0;
static struct source **source_references = NULL;
static HANDLE *event_array = NULL;
static int event_count = 0;
/* message data */
static unsigned max_datagram_size = 1024;
static gchar *datagram_buffer = NULL;
static struct raw_message message;
static CRITICAL_SECTION cs_internal_message;
static HANDLE internal_message_accepted = NULL;
static gchar internal_message_buffer[ 1024 ];
/******************************************************************************
* init_listener
*
* create sockets and synchronization objects including ones for "internal"
* source
*/
gboolean init_listener()
{
gboolean ret = FALSE;
unsigned n;
GList *item;
int i;
struct source *internal_src;
TRACE_ENTER( "\n" );
/* create critical section and event for the access serialization to internal message buffer
*/
InitializeCriticalSection( &cs_internal_message );
internal_message_accepted = CreateEvent( NULL, FALSE, FALSE, NULL );
if( !internal_message_accepted )
{
ERR( "Cannot create event; error %lu\n", GetLastError() );
goto done;
}
/* allocate memory for sockets and events;
* the number of sockets is not greater than number of sources
*/
n = g_list_length( sources );
socket_array = g_malloc( n * sizeof(SOCKET) );
/* number of source references is greater by one because of inclusion the event
* for "internal" source
* FIXME: how about multiple internal sources?
*/
source_references = g_malloc( (n + 1) * sizeof(struct source*) );
/* number of events is greater by two because of inclusion the event
* for "internal" source and the service_stop_event
*/
event_array = g_malloc( (n + 2) * sizeof(HANDLE) );
/* create sockets */
for( item = sources; item; item = item->next )
{
struct source *src = item->data;
SOCKET sock;
unsigned dgram_size;
int size;
if( src->type == ST_INTERNAL )
{
internal_src = src;
continue;
}
if( src->type != ST_UDP )
continue;
sock = socket( AF_INET, SOCK_DGRAM, 0 );
if( INVALID_SOCKET == sock )
{
ERR( "socket() error %lu\n", WSAGetLastError() );
goto done;
}
if( bind( sock, (struct sockaddr*) &src->udp, sizeof(src->udp) ) )
{
ERR( "bind() error %lu\n", WSAGetLastError() );
closesocket( sock );
goto done;
}
size = sizeof(dgram_size);
if( getsockopt( sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &dgram_size, &size ) )
{
ERR( "getsockopt( SO_MAX_MSG_SIZE ) error %lu\n", WSAGetLastError() );
closesocket( sock );
goto done;
}
TRACE( "datagram size for %d.%d.%d.%d:%d is %u\n",
src->udp.sin_addr.S_un.S_un_b.s_b1, src->udp.sin_addr.S_un.S_un_b.s_b2,
src->udp.sin_addr.S_un.S_un_b.s_b3, src->udp.sin_addr.S_un.S_un_b.s_b4,
ntohs( src->udp.sin_port ), dgram_size );
if( dgram_size > max_datagram_size )
max_datagram_size = dgram_size;
source_references[ socket_count ] = src;
socket_array[ socket_count++ ] = sock;
}
source_references[ socket_count ] = internal_src;
/* create events;
* service_stop_event is added to the array
*/
while( event_count <= socket_count )
{
HANDLE evt = CreateEvent( NULL, FALSE, FALSE, NULL );
if( !evt )
{
ERR( "Cannot create event; error %lu\n", GetLastError() );
goto done;
}
event_array[ event_count++ ] = evt;
}
event_array[ event_count++ ] = service_stop_event;
/* bind events to sockets */
for( i = 0; i < socket_count; i++ )
{
if( WSAEventSelect( socket_array[ i ], event_array[ i ], FD_READ ) )
{
ERR( "WSAEventSelect() error %lu\n", WSAGetLastError() );
goto done;
}
}
/* allocate datagram buffer */
datagram_buffer = g_malloc( max_datagram_size );
ret = TRUE;
done:
if( !ret )
fini_listener();
TRACE_LEAVE( "done; socket_count=%d, event_count=%d, max_datagram_size=%d, ret=%d\n",
socket_count, event_count, max_datagram_size, (int) ret );
return ret;
}
/******************************************************************************
* fini_listener
*/
void fini_listener()
{
int i;
TRACE_ENTER( "\n" );
for( i = 0; i < socket_count; i++ )
closesocket( socket_array[ i ] );
g_free( socket_array );
socket_array = NULL;
socket_count = 0;
g_free( source_references );
source_references = NULL;
/* note that the last event is the service_stop_event
* and should not be destroyed
*/
for( i = 0; i < event_count - 1; i++ )
CloseHandle( event_array[ i ] );
g_free( event_array );
event_array = NULL;
event_count = 0;
g_free( datagram_buffer );
datagram_buffer = NULL;
if( internal_message_accepted )
{
CloseHandle( internal_message_accepted );
internal_message_accepted = NULL;
}
DeleteCriticalSection( &cs_internal_message );
TRACE_LEAVE( "done\n" );
}
/******************************************************************************
* listener
*
* wait for a message; generate mark message;
* allocates a new raw_message structure and assigns its pointer to *msg
*/
enum listener_status listener( struct raw_message** msg )
{
enum listener_status ret = LSNR_ERROR;
DWORD t, w;
int r;
int addrlen;
TRACE_ENTER( "\n" );
for(;;)
{
if( !mark_interval )
t = INFINITE;
else
t = mark_interval * 1000;
w = WaitForMultipleObjects( event_count, event_array, FALSE, t );
if( WAIT_TIMEOUT == w )
{
/* issue mark message */
log_internal( LOG_NOTICE, "%s", mark_message );
continue;
}
if( WAIT_FAILED == w )
{
ERR( "Wait error %lu\n", GetLastError() );
goto done;
}
if( w >= event_count )
{
ERR( "Unknown wait error\n" );
goto done;
}
if( w == event_count - 1 )
{
/* shut down */
ret = LSNR_SHUTDOWN;
goto done;
}
if( w == event_count - 2 )
{
/* got "internal" message */
message.source = source_references[ socket_count ];
if( !message.source )
{
/* internal source is not defined, cannot handle message */
SetEvent( internal_message_accepted );
continue;
}
message.msg = g_strdup( internal_message_buffer );
SetEvent( internal_message_accepted );
memset( &message.sender_addr, 0, sizeof(message.sender_addr) );
goto alloc_msg;
}
/* got UDP message, read it */
addrlen = sizeof(message.sender_addr);
r = recvfrom( socket_array[ w ], datagram_buffer, max_datagram_size,
0, (struct sockaddr*) &message.sender_addr, &addrlen );
if( r < 0 )
{
ERR( "recvfrom() error %lu\n", WSAGetLastError() );
goto done;
}
if( !r )
continue;
message.msg = g_strndup( datagram_buffer, r );
message.source = source_references[ w ];
alloc_msg:
*msg = g_malloc( sizeof(struct raw_message) );
memcpy( *msg, &message, sizeof(struct raw_message) );
ret = LSNR_GOT_MESSAGE;
goto done;
}
done:
TRACE_LEAVE( "done; ret=%d\n", (int) ret );
return ret;
}
/******************************************************************************
* log_internal
*
* generate internal log message
*/
void log_internal( int pri, char* fmt, ... )
{
va_list args;
SYSTEMTIME stm;
int len;
char *p;
TRACE_ENTER( "\n" );
EnterCriticalSection( &cs_internal_message );
GetLocalTime( &stm );
len = sprintf( internal_message_buffer, "<%d>%s %2d %02d:%02d:%02d %s syslog: ",
LOG_SYSLOG | pri,
str_month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
local_hostname );
va_start( args, fmt );
vsnprintf( internal_message_buffer + len, sizeof(internal_message_buffer) - len, fmt, args );
va_end( args );
p = strchr( internal_message_buffer, '\n' );
if( p )
*p = 0;
p = strchr( internal_message_buffer, '\r' );
if( p )
*p = 0;
SetEvent( event_array[ event_count - 2 ] );
LeaveCriticalSection( &cs_internal_message );
TRACE_LEAVE( "done\n" );
}