aboutsummaryrefslogtreecommitdiffstats
path: root/daemon/conf.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--daemon/conf.c730
1 files changed, 730 insertions, 0 deletions
diff --git a/daemon/conf.c b/daemon/conf.c
new file mode 100644
index 0000000..654e46b
--- /dev/null
+++ b/daemon/conf.c
@@ -0,0 +1,730 @@
+/*
+ * conf.c - syslogd implementation for windows, configuration reader
+ *
+ * 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.
+ *
+ */
+
+/* define SYSLOG_CONF_DIR where syslog.host should be
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <winsock2.h>
+
+#include <glib.h>
+
+#include <syslog.h>
+#include <syslogd.h>
+
+#ifndef SYSLOG_CONF_DIR
+static char syslog_conf_dir[] = ".";
+#else
+static char syslog_conf_dir[] = SYSLOG_CONF_DIR;
+#endif
+
+/* options and their default values */
+gboolean use_dns = TRUE;
+gchar *source_encoding = NULL;
+gchar *destination_encoding = NULL;
+int mark_interval = 0;
+gchar *mark_message = "-- MARK --";
+int hold = 3;
+gchar *logdir = NULL;
+
+/* sources, destinations, filters and logpaths */
+struct logpath_names
+{
+ gchar *source;
+ gchar *filter;
+ gchar *destination;
+};
+
+GList *sources = NULL;
+GList *destinations = NULL;
+GList *filters = NULL;
+GList *logpaths = NULL;
+
+GList *purger_dirs = NULL;
+
+/* Glib markup wrapper data */
+static gchar *encoding = NULL;
+static gboolean prolog_expected = TRUE;
+/* parser data */
+static struct filter *current_filter = NULL;
+
+/******************************************************************************
+ * xml_start_element
+ *
+ * parse configuration elements
+ */
+static void xml_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ const gchar *aname, *aval;
+ int line_number;
+
+ prolog_expected = FALSE;
+
+ g_markup_parse_context_get_position( context, &line_number, NULL );
+
+ /* top-level elements */
+ if( strcmp( element_name, "source" ) == 0 )
+ {
+ struct source *source = g_malloc( sizeof(struct source) );
+
+ source->name = NULL;
+ source->type = ST_UNDEFINED;
+ memset( &source->udp, 0, sizeof(source->udp) );
+ source->udp.sin_family = AF_INET;
+ source->udp.sin_port = htons( SYSLOG_PORT );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "name" ) == 0 )
+ source->name = g_strdup( aval );
+ else if( strcmp( aname, "type" ) == 0 )
+ {
+ if( strcmp( aval, "internal" ) == 0 )
+ source->type = ST_INTERNAL;
+ else if( strcmp( aval, "udp" ) == 0 )
+ source->type = ST_UDP;
+ }
+ else if( strcmp( aname, "interface" ) == 0 )
+ {
+ struct hostent *he = gethostbyname( aval );
+ if( !he )
+ {
+ ERR( "Cannot resolve hostname %s; error %lu\n", aval, WSAGetLastError() );
+ g_free( source );
+ return;
+ }
+ memcpy( &source->udp.sin_addr.s_addr, he->h_addr, he->h_length );
+ }
+ else if( strcmp( aname, "port" ) == 0 )
+ source->udp.sin_port = htons( strtoul( aval, NULL, 0 ) );
+ }
+
+ if( !source->name )
+ ERR( "Undefined source name at line %d\n", line_number );
+ if( ST_UNDEFINED == source->type )
+ ERR( "Undefined source type at line %d\n", line_number );
+ if( (!source->name) || ST_UNDEFINED == source->type )
+ {
+ g_free( source );
+ return;
+ }
+ sources = g_list_append( sources, source );
+ }
+ else if( strcmp( element_name, "destination" ) == 0 )
+ {
+ struct destination *dest = g_malloc0( sizeof(struct destination) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "name" ) == 0 )
+ dest->name = g_strdup( aval );
+ else if( strcmp( aname, "file" ) == 0 )
+ dest->file = normalize_pathname( aval );
+ else if( strcmp( aname, "rotate" ) == 0 )
+ {
+ if( strcmp( aval, "daily" ) == 0 )
+ dest->rotate = RP_DAILY;
+ else if( strcmp( aval, "weekly" ) == 0 )
+ dest->rotate = RP_WEEKLY;
+ else if( strcmp( aval, "monthly" ) == 0 )
+ dest->rotate = RP_MONTHLY;
+ else
+ {
+ ERR( "Invalid rotation period at line %d\n", line_number );
+ dest->rotate = RP_INVALID;
+ }
+ }
+ else if( strcmp( aname, "size" ) == 0 )
+ {
+ char *endptr;
+ dest->size = strtoul( aval, &endptr, 0 );
+ if( 'k' == *endptr )
+ dest->size *= 1024;
+ else if( 'M' == *endptr )
+ dest->size *= 1024 * 1024;
+ }
+ else if( strcmp( aname, "backlogs" ) == 0 )
+ dest->backlogs = strtoul( aval, NULL, 0 );
+ else if( strcmp( aname, "ifempty" ) == 0 )
+ {
+ if( strcmp( aval, "yes" ) == 0 )
+ dest->ifempty = TRUE;
+ else if( strcmp( aval, "no" ) == 0 )
+ dest->ifempty = FALSE;
+ else
+ {
+ dest->ifempty = TRUE;
+ ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d; assumed \"yes\"\n",
+ aval, aname, line_number );
+ }
+ }
+ else if( strcmp( aname, "olddir" ) == 0 )
+ dest->olddir = normalize_pathname( aval );
+ else if( strcmp( aname, "compresscmd" ) == 0 )
+ dest->compresscmd = g_strdup( aval );
+ else if( strcmp( aname, "compressoptions" ) == 0 )
+ dest->compressoptions = g_strdup( aval );
+ }
+ if( !dest->name )
+ ERR( "Undefined destination name at line %d\n", line_number );
+ if( !dest->file )
+ ERR( "Undefined destination file at line %d\n", line_number );
+ if( (!dest->name) || (!dest->file) || RP_INVALID == dest->rotate )
+ {
+ if( dest->name ) g_free( dest->name );
+ if( dest->file ) g_free( dest->file );
+ if( dest->olddir ) g_free( dest->olddir );
+ if( dest->compresscmd ) g_free( dest->compresscmd );
+ if( dest->compressoptions ) g_free( dest->compressoptions );
+ g_free( dest );
+ return;
+ }
+ if( dest->compresscmd && !dest->compressoptions )
+ dest->compressoptions = g_strdup( "$PATHNAME" );
+
+ dest->file_writers = NULL;
+ InitializeCriticalSection( &dest->cs_file_writers );
+
+ destinations = g_list_append( destinations, dest );
+ }
+ else if( strcmp( element_name, "filter" ) == 0 )
+ {
+ current_filter = g_malloc0( sizeof(struct filter) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ if( strcmp( aname, "name" ) == 0 )
+ current_filter->name = g_strdup( *attribute_values );
+ }
+ if( !current_filter->name )
+ {
+ ERR( "Undefined filter name at line %d\n", line_number );
+ g_free( current_filter );
+ current_filter = NULL;
+ return;
+ }
+ }
+ else if( strcmp( element_name, "logpath" ) == 0 )
+ {
+ /* at first, fill logpaths list with logpath_names structures
+ and replace them later with logpath structures after configuration has been read
+ */
+ struct logpath_names *logpath = g_malloc0( sizeof(struct logpath_names) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "source" ) == 0 )
+ logpath->source = g_strdup( aval );
+ else if( strcmp( aname, "filter" ) == 0 )
+ logpath->filter = g_strdup( aval );
+ else if( strcmp( aname, "destination" ) == 0 )
+ logpath->destination = g_strdup( aval );
+ }
+ if( !logpath->source )
+ ERR( "Undefined log path source at line %d\n", line_number );
+ if( !logpath->destination )
+ ERR( "Undefined log path destination at line %d\n", line_number );
+ if( (!logpath->source) || (!logpath->destination) )
+ {
+ if( logpath->source ) g_free( logpath->source );
+ if( logpath->filter ) g_free( logpath->filter );
+ if( logpath->destination ) g_free( logpath->destination );
+ g_free( logpath );
+ return;
+ }
+ logpaths = g_list_append( logpaths, logpath );
+ }
+ else if( strcmp( element_name, "options" ) == 0 )
+ {
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "dns" ) == 0 )
+ {
+ if( strcmp( aval, "yes" ) == 0 )
+ use_dns = TRUE;
+ else if( strcmp( aval, "no" ) == 0 )
+ use_dns = FALSE;
+ else
+ ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n", aval, aname, line_number );
+ }
+ else if( strcmp( aname, "source_encoding" ) == 0 )
+ source_encoding = g_strdup( aval );
+ else if( strcmp( aname, "destination_encoding" ) == 0 )
+ destination_encoding = g_strdup( aval );
+ else if( strcmp( aname, "mark_interval" ) == 0 )
+ mark_interval = strtoul( aval, NULL, 0 );
+ else if( strcmp( aname, "mark_message" ) == 0 )
+ mark_message = g_strdup( aval );
+ else if( strcmp( aname, "hold" ) == 0 )
+ {
+ hold = strtoul( aval, NULL, 0 );
+ if( hold < 1 )
+ hold = 1;
+ }
+ else if( strcmp( aname, "logdir" ) == 0 )
+ logdir = normalize_pathname( aval );
+ }
+ }
+ else if( strcmp( element_name, "purge" ) == 0 )
+ {
+ struct purger_dir *pdir = g_malloc0( sizeof(struct purger_dir) );
+
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "directory" ) == 0 )
+ pdir->directory = normalize_pathname( aval );
+ else if( strcmp( aname, "keep_days" ) == 0 )
+ pdir->keep_days = strtoul( aval, NULL, 0 );
+ }
+ if( !pdir->directory )
+ ERR( "Undefined purge directory at line %d\n", line_number );
+ if( !pdir->keep_days )
+ ERR( "Undefined keep_days parameter at line %d\n", line_number );
+ if( (!pdir->directory) || (!pdir->keep_days) )
+ {
+ if( pdir->directory ) g_free( pdir->directory );
+ g_free( pdir );
+ return;
+ }
+ purger_dirs = g_list_append( purger_dirs, pdir );
+ }
+ else if( current_filter )
+ {
+ /* sub-elements of filter */
+ int val = -2;
+
+ if( strcmp( element_name, "facility" ) == 0 )
+ {
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "value" ) == 0 )
+ {
+ val = strtol( aval, NULL, 0 );
+ if( val < 0 || val >= LOG_NFACILITIES )
+ {
+ val = -1;
+ break;
+ }
+ }
+ else if( strcmp( aname, "name" ) == 0 )
+ {
+ CODE *c;
+ for( c = facilitynames; c->c_name; c++ )
+ if( strcmp( aval, c->c_name ) == 0 )
+ {
+ val = LOG_FAC( c->c_val );
+ break;
+ }
+ if( !c->c_name )
+ {
+ val = -1;
+ break;
+ }
+ }
+ }
+ if( -2 == val )
+ ERR( "Undefined facility at line %d\n", line_number );
+ else if( val != -1 )
+ current_filter->facilities[ val ] = TRUE;
+ }
+ else if( strcmp( element_name, "priority" ) == 0 )
+ {
+ for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
+ {
+ aval = *attribute_values;
+ if( strcmp( aname, "value" ) == 0 )
+ {
+ val = strtol( aval, NULL, 0 );
+ if( val < 0 || val >= 8 )
+ {
+ val = -1;
+ break;
+ }
+ }
+ else if( strcmp( aname, "name" ) == 0 )
+ {
+ CODE *c;
+ for( c = prioritynames; c->c_name; c++ )
+ if( strcmp( aval, c->c_name ) == 0 )
+ {
+ val = LOG_PRI( c->c_val );
+ break;
+ }
+ if( !c->c_name )
+ {
+ val = -1;
+ break;
+ }
+ }
+ }
+ if( -2 == val )
+ ERR( "Undefined priority at line %d\n", line_number );
+ else if( val != -1 )
+ current_filter->priorities[ val ] = TRUE;
+ }
+
+ if( -1 == val )
+ ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n", aval, aname, line_number );
+ }
+}
+
+/******************************************************************************
+ * xml_end_element
+ */
+static void xml_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ if( strcmp( element_name, "filter" ) == 0 )
+ {
+ if( current_filter )
+ {
+ /* append filter to the list */
+ filters = g_list_append( filters, current_filter );
+ current_filter = NULL;
+ }
+ }
+}
+
+/******************************************************************************
+ * xml_passthrough
+ *
+ * look for encoding name
+ */
+static void xml_passthrough (GMarkupParseContext *context,
+ const gchar *passthrough_text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ const gchar *startptr, *endptr;
+
+ if( !prolog_expected )
+ return;
+
+ startptr = g_strstr_len( passthrough_text, text_len, "<?xml " );
+ if( !startptr )
+ return;
+
+ startptr += 6;
+ text_len -= startptr - passthrough_text;
+
+ endptr = g_strstr_len( startptr, text_len, "?>" );
+ if( !endptr )
+ goto parsed;
+
+ text_len = endptr - startptr;
+ startptr = g_strstr_len( startptr, text_len, "encoding=\"" );
+ if( !startptr )
+ goto parsed;
+
+ startptr += 10;
+
+ endptr = strchr( startptr, '"' );
+ if( !endptr )
+ goto parsed;
+
+ if( strncmp( startptr, "windows-", 8 ) == 0 )
+ {
+ gchar *p;
+
+ startptr += 8;
+ p = g_strndup( startptr, endptr - startptr );
+ if( !p )
+ goto parsed;
+ encoding = g_strdup_printf( "CP%s", p );
+ g_free( p );
+ }
+ else
+ encoding = g_strndup( startptr, endptr - startptr );
+
+parsed:
+ prolog_expected = FALSE;
+}
+
+/******************************************************************************
+ * resolve_logpaths
+ *
+ * replace logpath_names structures
+ */
+static void resolve_logpaths()
+{
+ GList *paths = NULL;
+ GList *path_item;
+ struct logpath *logpath = NULL;
+
+ for( path_item = logpaths; path_item; path_item = path_item->next )
+ {
+ struct logpath_names *names = path_item->data;
+ GList *item;
+
+ g_free( logpath );
+ logpath = g_malloc( sizeof(struct logpath) );
+
+ /* find source */
+ for( item = sources; item; item = item->next )
+ {
+ struct source *s = item->data;
+ if( strcmp( s->name, names->source ) == 0 )
+ break;
+ }
+ if( !item )
+ {
+ ERR( "Undefined source \"%s\" in log path\n", names->source );
+ continue;
+ }
+ logpath->source = item->data;
+
+ /* find destination */
+ for( item = destinations; item; item = item->next )
+ {
+ struct destination *d = item->data;
+ if( strcmp( d->name, names->destination ) == 0 )
+ break;
+ }
+ if( !item )
+ {
+ ERR( "Undefined destination \"%s\" in log path\n", names->destination );
+ continue;
+ }
+ logpath->destination = item->data;
+
+ /* find filter */
+ if( !names->filter )
+ logpath->filter = NULL;
+ else
+ {
+ for( item = filters; item; item = item->next )
+ {
+ struct filter *f = item->data;
+ if( strcmp( f->name, names->filter ) == 0 )
+ break;
+ }
+ if( item )
+ logpath->filter = item->data;
+ else
+ logpath->filter = NULL;
+ }
+ /* add item to paths */
+ paths = g_list_append( paths, logpath );
+ logpath = NULL;
+ }
+
+ /* free list */
+ for( path_item = logpaths; path_item; path_item = path_item->next )
+ {
+ struct logpath_names *names = path_item->data;
+ g_free( names->source );
+ g_free( names->destination );
+ if( names->filter ) g_free( names->filter );
+ g_free( names );
+ }
+ g_list_free( logpaths );
+
+ /* set new list */
+ logpaths = paths;
+}
+
+/******************************************************************************
+ * dump_configuration
+ */
+static void dump_configuration()
+{
+# ifdef HAVE_DEBUG
+
+ GList *item;
+
+ TRACE( "Sources:\n" );
+ for( item = sources; item; item = item->next )
+ {
+ struct source *s = item->data;
+ TRACE( "\tname=%s\ttype=%s\tinterface=%d:%d:%d:%d\tport=%d\n",
+ s->name,
+ (s->type == ST_INTERNAL)? "internal" : ((s->type == ST_UDP)? "udp" : "undefined"),
+ s->udp.sin_addr.S_un.S_un_b.s_b1, s->udp.sin_addr.S_un.S_un_b.s_b2,
+ s->udp.sin_addr.S_un.S_un_b.s_b3, s->udp.sin_addr.S_un.S_un_b.s_b4,
+ ntohs( s->udp.sin_port ) );
+ }
+ TRACE( "Destinations:\n" );
+ for( item = destinations; item; item = item->next )
+ {
+ struct destination *d = item->data;
+ TRACE( "\tname=%s\tfile=%s\n"
+ "\t\trotate=%s size=%d backlogs=%d ifempty=%s\n"
+ "\t\tolddir=%s compresscmd=%s\n",
+ d->name, d->file,
+ (d->rotate == RP_DAILY)? "daily"
+ : (d->rotate == RP_WEEKLY)? "weekly"
+ : (d->rotate == RP_MONTHLY)? "monthly"
+ : "undefined",
+ d->size, d->backlogs, d->ifempty? "yes" : "no",
+ d->olddir? d->olddir : "NULL",
+ d->compresscmd? d->compresscmd : "NULL" );
+ }
+ TRACE( "Filters:\n" );
+ for( item = filters; item; item = item->next )
+ {
+ struct filter *f = item->data;
+ int i;
+ TRACE( "\tname=%s\n", f->name );
+ TRACE( "\tfacilities:\n" );
+ for( i = 0; i < LOG_NFACILITIES; i++ )
+ if( f->facilities[i] )
+ TRACE( "\t\t%s\n", get_facility_name( i ) );
+ TRACE( "\tpriorities:\n" );
+ for( i = 0; i < 8; i++ )
+ if( f->priorities[i] )
+ TRACE( "\t\t%s\n", get_priority_name( i ) );
+ }
+ TRACE( "Log paths:\n" );
+ for( item = logpaths; item; item = item->next )
+ {
+ struct logpath *p = item->data;
+ TRACE( "\tsource=%s\tfilter=%s\tdestination=%s\n",
+ p->source->name, p->filter? p->filter->name : "NULL", p->destination->name );
+ }
+ TRACE( "Purge directories:\n" );
+ for( item = purger_dirs; item; item = item->next )
+ {
+ struct purger_dir *p = item->data;
+ TRACE( "\tdirectory=%s\tkeep_days=%d\n", p->directory, p->keep_days );
+ }
+ TRACE( "Options:\n" );
+ TRACE( "\tuse_dns=%d\n", (int) use_dns );
+ TRACE( "\tsource_encoding=%s\n", source_encoding? source_encoding : "NULL" );
+ TRACE( "\tdestination_encoding=%s\n", destination_encoding? destination_encoding : "NULL" );
+ TRACE( "\tmark_interval=%d\n", mark_interval );
+ TRACE( "\tmark_message=%s\n", mark_message );
+ TRACE( "\thold=%d\n", hold );
+ TRACE( "\tlogdir=%s\n", logdir? logdir : "NULL" );
+
+# endif /* HAVE_DEBUG */
+}
+
+/******************************************************************************
+ * read_configuration
+ */
+gboolean read_configuration()
+{
+ gboolean ret = FALSE;
+ GMarkupParser parser = {
+ xml_start_element,
+ xml_end_element,
+ NULL,
+ xml_passthrough,
+ NULL
+ };
+ gchar *pathname;
+ FILE *fd = NULL;
+ GMarkupParseContext *ctx = NULL;
+ char buffer[256];
+ GError *error = NULL;
+
+ TRACE_ENTER( "\n" );
+
+ if( '\\' == syslog_conf_dir[0] || '/' == syslog_conf_dir[0] || ':' == syslog_conf_dir[1] )
+ /* absolute path */
+ pathname = g_build_filename( syslog_conf_dir, "syslog.conf", NULL );
+ else
+ /* relative path */
+ pathname = g_build_filename( g_path_get_dirname( __argv[0] ),
+ syslog_conf_dir, "syslog.conf", NULL );
+ fd = fopen( pathname, "r" );
+ if( !fd )
+ {
+ ERR( "Cannot open configuration file %s: %s\n", pathname, strerror(errno) );
+ goto done;
+ }
+
+ ctx = g_markup_parse_context_new( &parser, 0, NULL, NULL );
+ if( !ctx )
+ {
+ ERR( "Failed g_markup_parse_context_new\n" );
+ goto done;
+ }
+
+ while( fgets( buffer, sizeof(buffer), fd ) )
+ {
+ gchar *encoded = NULL;
+ gchar *parser_input;
+ gboolean r;
+
+ /* wrapper for Glib's XML parser:
+ determine encoding in xml_passthrough and convert data fed to the parser to UTF-8 */
+ if( encoding )
+ {
+ encoded = g_convert( buffer, -1, "UTF-8", encoding, NULL, NULL, &error );
+ if( !encoded )
+ goto done;
+
+ parser_input = encoded;
+ }
+ else
+ parser_input = buffer;
+
+ r = g_markup_parse_context_parse( ctx, parser_input, strlen( parser_input ), &error );
+ if( encoded ) g_free( encoded );
+ if( !r )
+ goto done;
+ }
+ if( !feof( fd ) )
+ {
+ ERR( "Cannot read configuration file %s: %s\n", pathname, strerror(errno) );
+ goto done;
+ }
+
+ resolve_logpaths();
+ dump_configuration();
+ if( !logdir )
+ {
+ ERR( "logdir is not defined\n" );
+ goto done;
+ }
+ ret = TRUE;
+
+done:
+ if( error )
+ {
+ gchar *locale_msg = g_locale_from_utf8( error->message, -1, NULL, NULL, NULL );
+ if( locale_msg )
+ {
+ ERR( "%s\n", locale_msg );
+ g_free( locale_msg );
+ }
+ g_error_free( error );
+ }
+ if( ctx ) g_markup_parse_context_free( ctx );
+ if( fd ) fclose( fd );
+ g_free( pathname );
+
+ TRACE_LEAVE( "done; ret=%d\n", (int) ret );
+ return ret;
+}