#ifndef _DWC_ADDRLIST_C
#define _DWC_ADDRLIST_C

/*--------------------------------------------------------------------------
 *  Copyright (c) 2001-2004 Igor Daniloff. 
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at this directory.
 *------------------------------------------------------------------------*/
 
#ifndef lint
static const char rcsid[] = "$Id: dwc_addrlist.c,v 1.20 2005/09/21 09:20:43 asv Exp $";
#endif

#include <errno.h>
#include "dwc_lib.h"
#include "dwc_log.h"
#include "dwc_mutex.h"
#include "dwc_macro.h"
#include "dwc_conf.h"
#include "dwc_addrlist.h"

typedef struct {
    dwc_timeout_t          timeout; /* in millisec */
    dwc_ipc_type_t         ipc_type;
    dwc_ipc_addr_ptr_t     ipc_addr;
} dwc_addr_entry_data_t;

struct dwc_addrentry_st {
    define_queue_stuff( struct dwc_addrentry_st );
    dwc_addr_entry_data_t      addr;
};

define_queue_of( struct dwc_addrentry_st )  dwc_addr_list_header_t;

struct dwc_addrlist_st { 
    dwc_addr_list_header_t      list_header;
    dwc_mutex_t                 op_lock;
};

/* -------------------------------------------------------------------- */
dwc_addrlist_t dwc_addrlist_create( void )
{
    dwc_addrlist_t   list;
    
    list = DWC_Malloc( dwc_addrlist_t, sizeof(struct dwc_addrlist_st) );
    if( !list  )
    {
        printLib( DW_LL_ERROR, "%s(%d): dwlib: no memory for address list", __FILE__, __LINE__ );
        return NULL;
    }
    DWC_QUEUE_INIT( &list->list_header );
    list->op_lock = dwc_mutex_create( "DwLib::AddressListLocker" );
    if( !list->op_lock )
    {        
        DWC_Free( list );
        return NULL;
    }
    return list;
}

/* -------------------------------------------------------------------- */
dwc_bool_t dwc_addrlist_add( dwc_addrlist_t list, const char* address, dwc_timeout_t timeout, dwc_bool_t cache )
{
    dwc_bool_t          have_addr = FALSE;
    dwc_addrentry_t     new_entry = NULL;
    dwc_string_t        spec = NULL;
    dwc_multistring_t   spec_list = NULL;
#if defined(__UNIX_DW)
    dwc_path_t          pidfile = NULL;
    dwc_pid_t           pid;
    FILE               *pidstream = NULL;
    char                buffer[DWC_MAX_ADDR_LEN];
    char               *pbuffer = NULL;
    char               *ptok = NULL;
    int                 line = 1;
    dwc_length_t        len;
#endif /* __UNIX_DW */
    
    if( !list || !address )
    {
        printLib( DW_LL_ERROR, "dwlib: null args passed to addrlist_add routine" );
        goto error_exit;
    }
    spec_list = dw_multistring_create( 0 );
    if( !spec_list )
    {
        printLib( DW_LL_ERROR, "%s(%d): dwlib: no memory for address specs", __FILE__, __LINE__ );
        goto error_exit;
    }    
    printLib( DW_LL_VERBOSE, "dwlib: set timeout for each scanning session with %s to %d seconds", address, timeout );
    if( 0 == DWC_strncasecmp( address, DWC_IPC_PID_PREFIX, strlen(DWC_IPC_PID_PREFIX) ) )
    {
#if !defined(__UNIX_DW)
        printLib( DW_LL_ERROR, "dwlib: "DWC_IPC_PID_PREFIX" address format not supported for this platform" );    
        goto error_exit;
#else /* UNIX */
        spec = strchr( address, DWC_IPC_SEPARATOR_CHR );
        if( !spec )
        {
            printLib( DW_LL_ERROR, "dwlib: "DWC_IPC_PID_PREFIX" address format separator %c not found in %s", address, DWC_IPC_SEPARATOR_CHR );
            goto error_exit;
        }
        pidfile = spec + strlen(DWC_IPC_SEPARATOR_STR);
#if defined( __UNIX_DW) && !defined(EMX_DW)
        pidstream = fopen( pidfile, "r" );
#else /* not Unix or EMX */
        pidstream = fopen( pidfile, "rb" );
#endif /* EMX */
        if( pidstream )
        {
            pbuffer = fgets( buffer, sizeof(buffer)-1, pidstream );
            if( !pbuffer )
            {
                printLib( DW_LL_ERROR, "dwlib: cannot read line %d from daemon pidfile %s", line, pidfile );
                goto error_exit;
            }
            if( sscanf( pbuffer, "%d", &pid ) != 1 )
            {
                printLib( DW_LL_ERROR, "dwlib: cannot read PID from daemon pidfile %s", pidfile );
                goto error_exit;
            }    
            printLib( DW_LL_VERBOSE, "dwlib: daemon pid = %d", pid );        
            for( line = 2; NULL != (pbuffer = fgets( buffer, sizeof(buffer)-1, pidstream )); ++line )
            {
                if( (buffer[0] == PATH_SEPARATOR)
#if defined(DW_HAVE_SEPARATOR_2)
                    ||
                    (buffer[0] == PATH_SEPARATOR_2)
#endif /* HAVE_SEPARATOR_2 */        
                    )
                {
                    delete_spaces( pbuffer );
                    len = strlen(DWC_IPC_UNIX_PREFIX) + strlen(pbuffer) + sizeof(char);
                    spec = DWC_Malloc( dwc_string_t, len );
                    if( !spec )
                    {
                        printLib( DW_LL_ERROR, "dwlib: no memory for address spec %s", pbuffer );
                        goto error_exit;
                    }
                    snprintf( spec, len, DWC_IPC_UNIX_PREFIX"%s", pbuffer ); 
                }
                else 
                {
                    ptok = strrchr( pbuffer, ':' );
                    if( !ptok )
                    {
                        printLib( DW_LL_ERROR, "dwlib: read pidfile: cannot parse inet-address in %s from pidfile", pbuffer );
                        goto error_exit;
                    }
                    *ptok = NULLCHAR;
                    ++ptok;
                    delete_spaces( pbuffer );
                    delete_spaces( ptok );
                    len = strlen(DWC_IPC_INET_PREFIX) + strlen(pbuffer) + strlen(ptok) + 2 * sizeof(char);
                    spec = DWC_Malloc( dwc_string_t, len ); 
                    if( !spec )
                    {
                        printLib( DW_LL_ERROR, "dwlib: no memory for address spec %s", pbuffer );
                        goto error_exit;
                    }
                    if( snprintf( spec, len, DWC_IPC_INET_PREFIX"%s@%s", ptok, pbuffer ) < 0 )
                    {
                        printLib( DW_LL_ERROR, "dwlib: read pidfile: cannot format inet-address string" );
                        DWC_Free( spec );
                        goto error_exit;
                    }                
                }
                if( !dw_multistring_add( spec_list, spec ) )
                {
                    printLib( DW_LL_ERROR, "dwlib: cannot add %s address from pidfile to list", spec );
                    goto error_exit;
                }
            }
            if( line < 3 )
            {
                printLib( DW_LL_ERROR, "dwlib: cannot read line %d from daemon pidfile %s", line, pidfile );
                goto error_exit;
            }
            fclose( pidstream );
            pidstream = NULL;
        }
        else if( !cache && (ENOENT == errno) ) 
        {
            printLib( DW_LL_ERROR, "dwlib: cannot open daemon pidfile %s - no such file, set ipc address to /nonexisten", pidfile );
            if( !dw_multistring_add( spec_list, "local:/nonexisten" ) )
            {
                printLib( DW_LL_ERROR, "dwlib: cannot add local:/nonexisten address to list" );
                goto error_exit;
            }
        }
        else
        {
            printLib( DW_LL_ERROR, "dwlib: cannot open daemon pidfile %s - %s", pidfile, stdlib_error(errno) );
            goto error_exit;
        }
#endif /* UNIX */        
    }
    else
    {
        printLib( DW_LL_DEBUG, "dwlib: dont match to "DWC_IPC_PID_PREFIX" prefix for pidfile addressing" );
        if( !dw_multistring_add( spec_list, address ) )
        {
            printLib( DW_LL_ERROR, "dwlib: cannot add %s address to list", address );
            goto error_exit;
        }
    }
    dw_multistring_rewind( spec_list, DW_SD_FORWARD );
    while( NULL != (spec = dw_multistring_getnext( spec_list )) )
    {
        new_entry = DWC_Malloc( dwc_addrentry_t, sizeof(struct dwc_addrentry_st) );
        if( !new_entry )
        {
            printLib( DW_LL_ERROR, "%s(%d): dwlib: no memory for address entry", __FILE__, __LINE__ );
            goto error_exit;
        }
        new_entry->addr.ipc_addr = create_ipc_addr( spec, cache, &new_entry->addr.ipc_type );        
        if( new_entry->addr.ipc_type == DWC_IPC_TYPE_0 )
        {
            goto error_exit;
        }
        new_entry->addr.timeout = timeout;
        if( !dwc_mutex_lock( list->op_lock ) )
        {
            printLib( DW_LL_ERROR, "dwlib: FATAL ERROR: cannot lock address list" );
            goto error_exit;
        }
        DWC_QUEUE_INSERT_TAIL( &list->list_header, new_entry );
        if( !dwc_mutex_unlock( list->op_lock ) )
        {   
            printLib( DW_LL_ERROR, "dwlib: FATAL ERROR: cannot unlock address list" );
            goto error_exit;
        }
        have_addr = TRUE;
        printLib( DW_LL_DEBUG, "dwlib: address %s added to list", spec );
    }
    dw_multistring_delete( spec_list );
    return have_addr;
    
  error_exit:
    if( new_entry )
    {
        if( new_entry->addr.ipc_addr )
        {
            delete_ipc_addr( new_entry->addr.ipc_addr );
        }
        DWC_Free( new_entry );
    }
#if defined(__UNIX_DW)    
    if( pidstream )
    {
        fclose( pidstream );
    }
#endif /* UNIX */    
    dw_multistring_delete( spec_list );
    return FALSE;    
}

/* -------------------------------------------------------------------- */
dwc_addrentry_t dwc_addrlist_get( dwc_addrlist_t list, dwc_addrentry_t entry )
{
    dwc_addrentry_t   nentry;
    
    if( !list )
        return NULL;
    if( !dwc_mutex_lock( list->op_lock ) )
    {
        printLib( DW_LL_ERROR, "dwlib: FATAL ERROR: cannot lock address list" );
        return NULL;    
    }    
    if( entry )
    {
        nentry = DWC_QUEUE_NEXT( entry );
    }
    else
    {
        nentry = DWC_QUEUE_FIRST( &list->list_header );
    }
    if( !dwc_mutex_unlock( list->op_lock ) )
    {
        printLib( DW_LL_ERROR, "dwlib: FATAL ERROR: cannot lock address list" );
        return NULL;    
    }        
    return nentry;
}

/* -------------------------------------------------------------------- */
dwc_void_t dwc_addrlist_delete( dwc_addrlist_t list )
{
    dwc_addrentry_t   entry;
    
    if( !list )
        return;
    if( !dwc_mutex_lock( list->op_lock ) )
    {
        printLib( DW_LL_ERROR, "dwlib: FATAL ERROR: cannot lock address list" );
        return;    
    }    
    while( NULL != (entry = DWC_QUEUE_FIRST( &list->list_header )) )
    {   
        DWC_QUEUE_REMOVE( &list->list_header, entry );
        delete_ipc_addr( entry->addr.ipc_addr );
        DWC_Free( entry );
    }
    if( !dwc_mutex_unlock( list->op_lock ) )
    {
        printLib( DW_LL_ERROR, "dwlib: FATAL ERROR: cannot lock address list" );
        return;    
    }        
    dwc_mutex_destroy( list->op_lock );
    DWC_Free( list );
}

/* -------------------------------------------------------------------- */
DWC_ipc_type_t dwc_addrentry_ipc_type( dwc_addrentry_t entry )
{
    if( entry )
        return entry->addr.ipc_type;
    else
        return DWC_IPC_TYPE_0;        
}

/* -------------------------------------------------------------------- */
DWC_ipc_addr_ptr_t dwc_addrentry_ipc_addr( dwc_addrentry_t entry )
{
    if( entry )
        return entry->addr.ipc_addr;
    else
        return NULL;
}   

/* -------------------------------------------------------------------- */
DWC_timeout_t dwc_addrentry_timeout( dwc_addrentry_t entry )
{
    if( entry )
        return entry->addr.timeout;
    else
        return 0;
}

#endif /* _DWC_ADDRLIST_C */
