aboutsummaryrefslogblamecommitdiffstats
path: root/daemon/conf.c
blob: a80a689c140aee8a97801063e27cb614c9436d36 (plain) (tree)





































                                                                      


























                                                                               
                
  

                                                                     
   


                                                           
 

                                                              
 




                                                   
 
























                                                                                          

                                                                                
     
 












                                                                               
                 
  
                                                                  
   

                                                         



                                                                
 


                                


                                                                                      

                                                                   


                                                 
                                               
                                                    
                                                
                                                     
                                                 


                                                                           
                                                 




                                               
                                                            
                                
                                          
                                     
                                                 

                                                   
                                                             


                                                  
                                            
                                                
                                             

                
                                            




                                                                                              
                                                             
                                                      
                                                                                        
                                                          
                                                                                            
     
                                    
                                                                      
                                                                           
     













                                                                                  







































                                                                                             









                                                                          

                              




                                                                                  

                                                                                   
                              


                                                                       
     
                                         


                                                                          

                                                                              
                                               
                                 
                                                    
                                  



                                                                      


                       
 





                                                                                   



                                                                                    











                                                                      


















                                                                                      
     









                                                                                                 
 











                                                                               
 

                                                 


                                                                                          
                                               
             






                                                                                 
             
                                                   
             







                                                        
                 

                                                                                 

                           
             
         
                       
         
                                                                  

                   


                                                 
     
 

                                                 


                                                                                          
                                               
             

                                              
                 


                                                                                 

                 
                                                   
             







                                                        
                 
                                                                                 
                                                    
                           

                 
         
                       
         
                                                                  

                   
 



                                                 
 























                                                                                      
     




                                                                          
     







                                                                  
 














                                                                                      
         





                                                                                                         
         




                                                                            
         


                                            
         

                                                 
     



























                                                                                      
     





                                                        
 

































                                                                               


                                                     
                                                                       


                                                   
                                                                            



                                    
                                                                                    














































































                                                                               


                                        


                                                                    








                                                                             

                                                                                         










                                                                             














                                                                                           

     







                                                                                

                                                                                                     
             
                                                                   



                                                                       
                                                                                 







                                                                               

















                                                                       


                                             
 











































                                                                                    
 
                                                 
 
































                                                                               
                                                                                   

                                                                                                 
                                                  







                                                                                  


                         
                                                      

                                                                   


                                                      








                                                                                       
                                                           
                                            


                                                      

                                                            

                  
         






























                                                                                            




























                                                                               


                                                                                                   












































































                                                                                               
/*
 * 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;
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;

/******************************************************************************
 * create_source
 *
 * read attributes of element, initialize source structure and append
 * it to the list of sources
 */
static void create_source( int line_number,
                           const gchar** attribute_names,
                           const gchar** attribute_values )
{
    const gchar *aname;
    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++ )
    {
        const gchar *aval = *attribute_values;
        if( strcmp( aname, "name" ) == 0 )
            source->name = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        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 ) );
        else if( strcmp( aname, "encoding" ) == 0 )
            source->encoding = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
    }

    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 );
}

/******************************************************************************
 * init_file_dest
 *
 * read attributes of element and initialize destination structure
 */
static gboolean init_file_dest( struct destination *dest,
                                int line_number,
                                const gchar** attribute_names,
                                const gchar** attribute_values )
{
    const gchar *aname;

    /* set nonzero defaults */
    dest->u.file.ifempty = TRUE;

    for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
    {
        const gchar *aval = *attribute_values;
        if( strcmp( aname, "file" ) == 0 )
            dest->u.file.name_pattern = normalize_pathname( aval );
        else if( strcmp( aname, "rotate" ) == 0 )
        {
            if( strcmp( aval, "daily" ) == 0 )
                dest->u.file.rotate = RP_DAILY;
            else if( strcmp( aval, "weekly" ) == 0 )
                dest->u.file.rotate = RP_WEEKLY;
            else if( strcmp( aval, "monthly" ) == 0 )
                dest->u.file.rotate = RP_MONTHLY;
            else
            {
                ERR( "Invalid rotation period at line %d\n", line_number );
                dest->u.file.rotate = RP_INVALID;
            }
        }
        else if( strcmp( aname, "size" ) == 0 )
        {
            char *endptr;
            dest->u.file.size = strtoul( aval, &endptr, 0 );
            if( 'k' == *endptr )
                dest->u.file.size *= 1024;
            else if( 'M' == *endptr )
                dest->u.file.size *= 1024 * 1024;
        }
        else if( strcmp( aname, "backlogs" ) == 0 )
            dest->u.file.backlogs = strtoul( aval, NULL, 0 );
        else if( strcmp( aname, "ifempty" ) == 0 )
        {
            if( strcmp( aval, "yes" ) == 0 )
                dest->u.file.ifempty = TRUE;
            else if( strcmp( aval, "no" ) == 0 )
                dest->u.file.ifempty = FALSE;
            else
            {
                dest->u.file.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->u.file.olddir = normalize_pathname( aval );
        else if( strcmp( aname, "compresscmd" ) == 0 )
            dest->u.file.compresscmd = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        else if( strcmp( aname, "compressoptions" ) == 0 )
            dest->u.file.compressoptions = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
    }
    if( !dest->u.file.name_pattern )
        ERR( "Undefined destination file at line %d\n", line_number );
    if( (!dest->u.file.name_pattern) || RP_INVALID == dest->u.file.rotate )
    {
        if( dest->u.file.name_pattern ) g_free( dest->u.file.name_pattern );
        if( dest->u.file.olddir ) g_free( dest->u.file.olddir );
        if( dest->u.file.compresscmd ) g_free( dest->u.file.compresscmd );
        if( dest->u.file.compressoptions ) g_free( dest->u.file.compressoptions );
        return FALSE;
    }

    if( dest->u.file.compresscmd && !dest->u.file.compressoptions )
        dest->u.file.compressoptions = g_strdup( "$PATHNAME" );

    return init_destination_file( dest );
}

/******************************************************************************
 * init_relay_dest
 *
 * read attributes of element and initialize destination structure
 */
static gboolean init_relay_dest( struct destination *dest,
                                 int line_number,
                                 const gchar** attribute_names,
                                 const gchar** attribute_values )
{
    const gchar *aname;

    for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
    {
        const gchar *aval = *attribute_values;
        if( strcmp( aname, "collector" ) == 0 )
            dest->u.relay.collector = g_strdup( aval );
        else if( strcmp( aname, "omit_hostname" ) == 0 )
        {
            if( strcmp( aval, "yes" ) == 0 )
                dest->u.relay.omit_hostname = TRUE;
            else if( strcmp( aval, "no" ) == 0 )
                dest->u.relay.omit_hostname = FALSE;
            else
            {
                dest->u.relay.omit_hostname = FALSE;
                ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d; assumed \"no\"\n",
                     aval, aname, line_number );
            }
        }
    }
    if( !dest->u.relay.collector )
    {
        ERR( "Undefined destination relay at line %d\n", line_number );
        return FALSE;
    }

    return init_destination_relay( dest );
}

/******************************************************************************
 * create_destination
 *
 * read attributes of element, initialize destination structure and append
 * it to the list of destinations
 */
static void create_destination( int line_number,
                                const gchar** attribute_names,
                                const gchar** attribute_values )
{
    gboolean r = FALSE;
    const gchar **attr_names;
    const gchar **attr_values;
    const gchar *aname;
    struct destination *dest = g_malloc0( sizeof(struct destination) );

    /* at first, we must determine destination type for selection of type-specific
       structure that we'll fill later;
       also, look for 'name' and 'encoding' attributes and set corresponding fields
       in destination structure */
    dest->type = DT_UNDEFINED;
    attr_names = attribute_names;
    attr_values = attribute_values;
    for( ; (aname = *attr_names) != NULL; attr_names++, attr_values++ )
    {
        const gchar *aval = *attr_values;

        if( strcmp( aname, "name" ) == 0 )
            dest->name = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        else if( strcmp( aname, "encoding" ) == 0 )
            dest->encoding = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        else if( strcmp( aname, "file" ) == 0 )
            dest->type = DT_FILE;
        else if( strcmp( aname, "collector" ) == 0 )
            dest->type = DT_RELAY;
    }
    if( !dest->name )
    {
        ERR( "Undefined destination name at line %d\n", line_number );
        g_free( dest );
        return;
    }

    switch( dest->type )
    {
    case DT_FILE:
        r = init_file_dest( dest, line_number, attribute_names, attribute_values );
        break;

    case DT_RELAY:
        r = init_relay_dest( dest, line_number, attribute_names, attribute_values );
        break;

    default:
        ERR( "Undefined destination type at line %d\n", line_number );
        break;
    }

    if( !r )
    {
        /* FIXME: call fini method? */
        g_free( dest->name );
        g_free( dest );
        return;
    }

    destinations = g_list_append( destinations, dest );
}

/******************************************************************************
 * create_filter
 *
 * read attributes of element and initialize filter structure;
 * save pointer to the structure in current_filter
 */
static void create_filter( int line_number,
                           const gchar** attribute_names,
                           const gchar** attribute_values )
{
    const gchar *aname;

    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_locale_from_utf8( *attribute_values, -1, NULL, NULL, NULL );
    }
    if( !current_filter->name )
    {
        ERR( "Undefined filter name at line %d\n", line_number );
        g_free( current_filter );
        current_filter = NULL;
    }
}

/******************************************************************************
 * init_filter
 *
 * parse sub-element of 'filter' and fill the structure current_filter
 */
static void init_filter( int line_number,
                         const gchar* element_name,
                         const gchar** attribute_names,
                         const gchar** attribute_values )
{
    const gchar *aname, *aval;
    int val = -1;

    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 )
                {
                    ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n",
                         aval, aname, line_number );
                    return;
                }
            }
            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 )
                {
                    ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n",
                         aval, aname, line_number );
                    return;
                }
            }
        }
        if( -1 == val )
        {
            ERR( "Undefined facility at line %d\n", line_number );
            return;
        }

        current_filter->facilities[ val ] = TRUE;
        return;
    }

    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 )
                {
                    ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n",
                         aval, aname, line_number );
                    return;
                }
            }
            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 )
                {
                    ERR( "Invalid value \"%s\" of attribute \"%s\" at line %d\n",
                         aval, aname, line_number );
                    return;
                }
            }
        }
        if( -1 == val )
        {
            ERR( "Undefined priority at line %d\n", line_number );
            return;
        }

        current_filter->priorities[ val ] = TRUE;
        return;
    }
}

/******************************************************************************
 * create_logpath
 *
 * read attributes of element, initialize logpath_names structure and append
 * if to the list of filters;
 * logpath_names will be replaced with logpath structures after all the
 * configuration has been read
 */
static void create_logpath( int line_number,
                            const gchar** attribute_names,
                            const gchar** attribute_values )
{
    const gchar *aname;
    struct logpath_names *logpath = g_malloc0( sizeof(struct logpath_names) );

    for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
    {
        const gchar *aval = *attribute_values;
        if( strcmp( aname, "source" ) == 0 )
            logpath->source = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        else if( strcmp( aname, "filter" ) == 0 )
            logpath->filter = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        else if( strcmp( aname, "destination" ) == 0 )
            logpath->destination = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
    }
    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 );
}

/******************************************************************************
 * read_options
 *
 * read attributes of element and set corresponding options
 */
static void read_options( int line_number,
                          const gchar** attribute_names,
                          const gchar** attribute_values )
{
    const gchar *aname;

    for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
    {
        const gchar *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, "mark_interval" ) == 0 )
            mark_interval = strtoul( aval, NULL, 0 );
        else if( strcmp( aname, "mark_message" ) == 0 )
            mark_message = g_locale_from_utf8( aval, -1, NULL, NULL, NULL );
        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 );
    }
}

/******************************************************************************
 * create_purger_dir
 *
 * read attributes of element, initialize purger_dir structure and append
 * it to the list of directories
 */
static void create_purger_dir( int line_number,
                               const gchar** attribute_names,
                               const gchar** attribute_values )
{
    const gchar *aname;
    struct purger_dir *pdir = g_malloc0( sizeof(struct purger_dir) );

    for( ; (aname = *attribute_names) != NULL; attribute_names++, attribute_values++ )
    {
        const gchar *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 );
}

/******************************************************************************
 * 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)
{
    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 )
    {
        create_source( line_number, attribute_names, attribute_values );
    }
    else if( strcmp( element_name, "destination" ) == 0 )
    {
        create_destination( line_number, attribute_names, attribute_values );
    }
    else if( strcmp( element_name, "filter" ) == 0 )
    {
        create_filter( line_number, attribute_names, attribute_values );
    }
    else if( strcmp( element_name, "logpath" ) == 0 )
    {
        create_logpath( line_number, attribute_names, attribute_values );
    }
    else if( strcmp( element_name, "options" ) == 0 )
    {
        read_options( line_number, attribute_names, attribute_values );
    }
    else if( strcmp( element_name, "purge" ) == 0 )
    {
        create_purger_dir( line_number, attribute_names, attribute_values );
    }
    else if( current_filter )
    {
        /* sub-elements of filter */
        init_filter( line_number, element_name, attribute_names, attribute_values );
    }
}

/******************************************************************************
 * 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;
}

/******************************************************************************
 * create_conversion_descriptors
 *
 * helper function for resolve_logpaths;
 */
static void create_conversion_descriptors( struct logpath* logpath )
{
    if( logpath->source->encoding || logpath->destination->encoding )
    {
        char *source_encoding = logpath->source->encoding?
                                    logpath->source->encoding : "UTF-8";
        char *destination_encoding = logpath->destination->encoding?
                                    logpath->destination->encoding : "UTF-8";

        if( strcasecmp( source_encoding, destination_encoding ) != 0 )
        {
            logpath->src2dest_cd = g_iconv_open( destination_encoding, source_encoding );
            if( logpath->src2dest_cd == (GIConv) -1 )
            {
                ERR( "Cannot convert messages from %s to %s\n",
                     source_encoding, destination_encoding );
            }
            else
            {
                TRACE( "Log path %s-%s: converting messages from %s to %s\n",
                       logpath->source->name, logpath->destination->name,
                       source_encoding, destination_encoding );
            }
        }

        if( strcasecmp( "ASCII", destination_encoding ) != 0 )
        {
            logpath->ascii2dest_cd = g_iconv_open( destination_encoding, source_encoding );
            if( logpath->ascii2dest_cd == (GIConv) -1 )
            {
                ERR( "Cannot convert messages from ASCII to %s\n", destination_encoding );
            }
            else
            {
                TRACE( "Log path %s-%s: converting messages from ASCII to %s\n",
                       logpath->source->name, logpath->destination->name,
                       destination_encoding );
            }
        }
    }

    if( logpath->source->encoding )
    {
        const gchar *destination_encoding;

        g_get_charset( &destination_encoding );

        if( strcasecmp( logpath->source->encoding, destination_encoding ) != 0 )
        {
            logpath->src2locale_cd = g_iconv_open( destination_encoding, logpath->source->encoding );
            if( logpath->src2locale_cd == (GIConv) -1 )
            {
                ERR( "Cannot convert locale parts from %s to %s\n",
                     logpath->source->encoding, destination_encoding );
            }
            else
            {
                TRACE( "Log path %s-%s: converting locale parts from %s to %s\n",
                       logpath->source->name, logpath->destination->name,
                       logpath->source->encoding, destination_encoding );
            }
        }
    }
}

/******************************************************************************
 * 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) );

        logpath->src2dest_cd = (GIConv) -1;
        logpath->ascii2dest_cd = (GIConv) -1;
        logpath->src2locale_cd = (GIConv) -1;

        /* 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;
        }

        create_conversion_descriptors( logpath );

        /* 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\tencoding=%s\tinterface=%d:%d:%d:%d\tport=%d\n",
               s->name,
               (s->type == ST_INTERNAL)? "internal" : ((s->type == ST_UDP)? "udp" : "undefined"),
               s->encoding? s->encoding : "UTF-8",
               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;
        switch( d->type )
        {
        case DT_FILE:
            TRACE( "\tname=%s\tencoding=%s\tfile=%s\n"
                   "\t\trotate=%s size=%d backlogs=%d ifempty=%s\n"
                   "\t\tolddir=%s compresscmd=%s\n",
                   d->name,
                   d->encoding? d->encoding : "UTF-8",
                   d->u.file.name_pattern,
                   (d->u.file.rotate == RP_DAILY)? "daily"
                       : (d->u.file.rotate == RP_WEEKLY)? "weekly"
                       : (d->u.file.rotate == RP_MONTHLY)? "monthly"
                       : "undefined",
                   d->u.file.size, d->u.file.backlogs, d->u.file.ifempty? "yes" : "no",
                   d->u.file.olddir? d->u.file.olddir : "NULL",
                   d->u.file.compresscmd? d->u.file.compresscmd : "NULL" );
            break;
        case DT_RELAY:
            TRACE( "\tname=%s\tencoding=%s\tcollector=%s\n"
                   "\t\tomit_hostname=%s\n",
                   d->name,
                   d->encoding? d->encoding : "UTF-8",
                   d->u.relay.collector,
                   d->u.relay.omit_hostname? "yes" : "no" );
            break;
        default:
            break;
        }
    }
    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( "\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( conf_file_name )
        pathname = g_strdup( conf_file_name );
    else 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;
}