#ifndef _DWC_IPC_C
#define _DWC_IPC_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_ipc.c,v 1.78 2008/03/28 13:13:14 akorkov Exp $";
#endif

#if defined(__UNIX_DW)
#include <unistd.h>
#if defined(FreeBSD_DW)
#include <sys/time.h>
#endif /* FreeBSD */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#endif /* __UNIX_DW */
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "dwc_lib.h"
#include "dwc_err.h"
#include "dwc_macro.h"
#include "dwc_ipc.h"
#include "dwc_tcp.h"
#include "dwc_unix.h"
#include "dwc_conf.h"
#include "dwc_strings.h"

typedef union {
    DWC_fdsocket_t          hnd_fd;
    DWC_tcp_socket_t        hnd_tcp;
    DWC_unix_socket_t       hnd_unix;
} DWC_ipc_handle_t;

typedef union {
    DWC_tcp_socket_addr_t   sa_tcp;
    DWC_unix_socket_addr_t  sa_unix;
} DWC_ipc_address_t;

typedef struct _DWC_ipc_addr {
    DWC_char_t              name[DWC_MAX_ADDR_LEN];
    DWC_length_t            name_len;
    DWC_bool_t              cached;
    DWC_bool_t              needed;
    DWC_ipc_address_t       addr;
} DWC_ipc_addr_t;

#define NULL_IPC_ADDR( a_ipc )\
BEGIN_MACRO\
    (a_ipc).name[0] = NULLCHAR;\
    (a_ipc).name_len = 0;\
    (a_ipc).cached = FALSE;\
    memset( &(a_ipc).addr, 0, sizeof(DWC_ipc_address_t) );\
END_MACRO

typedef DWC_error_code_t   (*DWC_SendFunc_t)();
typedef DWC_error_code_t   (*DWC_RecvFunc_t)();

struct _DWC_ipc {
    DWC_ipc_type_t          type;
    DWC_bool_t              allocated;
    DWC_bool_t              connected;
    DWC_bool_t              nonblocking;
    DWC_bool_t              listed;
    DWC_bool_t              binded;
    DWC_bool_t              localscan_allowed;
    DWC_ipc_handle_t        handle;
    DWC_ipc_addr_t          local;
    DWC_ipc_addr_t          remote;
    DWC_timeout_t           timeout;
    DWC_SendFunc_t          send_func;
    DWC_RecvFunc_t          recv_func;
    struct timeval          started;
};

typedef struct {
    unsigned int            inited;
    unsigned int            tcp_sock:1,
                            unix_sock:1;
} DWC_ipc_subsys_state_t;

static const dwc_string_t           default_ipc_address = "inet:3000@localhost";
static DWC_ipc_subsys_state_t       ipcState = { 0 };

static void         set_name_by_addr( DWC_ipc_type_t itype, DWC_ipc_addr_t* paddr );
static dwc_ipc_t    dw_ipc_create( dwc_const_string_t addr, dwc_timeout_t timeout, dwc_bool_t cache, dwc_bool_t is_server, dwc_count_t backlog );

/* ---------------------------------------------------------------------- */
dwc_bool_t   dwc_ipc_inited ( void )
{
  return (ipcState.inited == ENABLE);
}
/* ---------------------------------------------------------------------- */
dwc_bool_t dwc_ipc_set_nodelay( dwc_ipc_t ipc, dwc_bool_t nodelay )
{
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc::set_nodelay null pointer" );
        return FALSE;        
    }
    if( DWC_IPC_TCP_SOCKET != ipc->type )
    {
        printLib( DW_LL_DEBUG, "dwlib: ipc::set_nodelay for non-tcp socket ignored" );
        return TRUE;        
    }
    return set_tcp_nodelay( ipc->handle.hnd_tcp, nodelay );
}

/* ---------------------------------------------------------------------- */
dwc_fdsocket_t dwc_ipc_get_handle( dwc_ipc_t ipc )
{
    if( ipc )
        return ipc->handle.hnd_fd;
    else
        return INVALID_SOCKET;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dwc_ipc_set_timeout( dwc_ipc_t ipc, dwc_timeout_t seconds )
{
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc::set_timeout null pointer" );
        return FALSE;
    }
    ipc->timeout = SEC_TO_TIMEOUT(seconds);
    if( !VALID_TIMEOUT(ipc->timeout) )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc::set_timeout invalid timeout %d specified (should be in 0 (infinite) .. 4 hours in seconds)", seconds );
        return FALSE;
    }
    return TRUE;
}

/* -------------------------------------------------- */
dwc_bool_t dw_sig_ignore( int sig )
{
#if defined(__UNIX_DW)
    return dw_sig_sethandler( sig, SIG_IGN );
#else /* not UNIX */
    return TRUE;
#endif /* UNIX */
}

/* -------------------------------------------------- */
dwc_bool_t dw_sig_sethandler( int sig, void (*handler)(int) )
{
#if defined(__UNIX_DW)
    struct sigaction        sigact;

    sigact.sa_flags = 0;
    sigact.sa_handler = handler;
    sigemptyset(&sigact.sa_mask);
    if( sigaction( sig, &sigact, NULL ) < 0 )
    {
        printLib( DW_LL_ERROR, "dwlib: set signal(%d) handler failed - %s", sig, stdlib_error(errno) );
        return FALSE;
    }
#endif /* __UNIX_DW */
    return TRUE;
}

/* ---------------------------------------------------------------------- */
dwc_ipc_t dwc_ipc_create_server( dwc_const_string_t addr, dwc_timeout_t timeout, dwc_bool_t cache, dwc_count_t backlog )
{
    return dw_ipc_create( addr, timeout, cache, TRUE, backlog );
}

/* ---------------------------------------------------------------------- */
dwc_ipc_t dwc_ipc_create_client( dwc_const_string_t addr, dwc_timeout_t timeout, dwc_bool_t cache )
{
    return dw_ipc_create( addr, timeout, cache, FALSE, 0 );
}

/* ---------------------------------------------------------------------- */
static dwc_ipc_t dw_ipc_create( dwc_const_string_t addr, dwc_timeout_t timeout, dwc_bool_t cache, dwc_bool_t is_server, dwc_count_t backlog )
{
    dwc_ipc_t           ipc = INVALID_IPC;
    dwc_ipc_addr_ptr_t  paddr = NULL;
    dwc_ipc_type_t      itype = DWC_IPC_TYPE_0;

    if( !addr )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc::create null address for control ipc passed" );
        return INVALID_IPC;
    }
    paddr = create_ipc_addr( addr, cache, &itype );
    if( !paddr )
    {
        goto error_exit;
    }
    if( is_server )
    {  
        printLib( DW_LL_DEBUG, "dwlib: ipc::create server ipc on %s", addr );
        if( (create_ipc( itype, paddr, NULL, timeout, FALSE, TRUE, &ipc ) != DWC_NOERROR) 
            ||
            (bind_ipc( ipc ) != DWC_NOERROR)
            ||
            (listen_ipc( ipc, backlog ) != DWC_NOERROR) )
        {
            goto error_exit;
        }
    }
    else
    {
        printLib( DW_LL_DEBUG, "dwlib: ipc::create  client ipc to %s", addr );
        if( (create_ipc( itype, NULL, paddr, timeout, FALSE, TRUE, &ipc ) != DWC_NOERROR)
            ||
            (connect_ipc ( ipc ) != DWC_NOERROR) )
        {
            goto error_exit;
        }
        printLib( DW_LL_DEBUG, "dwlib: ipc::create ipc connected to %s", addr );
    }
    if( paddr )
        delete_ipc_addr( paddr );
    return ipc;

  error_exit:
    if( paddr )
        delete_ipc_addr( paddr );
    if( ipc != INVALID_IPC )
        delete_ipc( &ipc );
    return INVALID_IPC;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dwc_ipc_delete( dwc_ipc_t ipc )
{
    if( !ipc )
    {
        printLib( DW_LL_DEBUG, "dwlib: ipc::delete null control socket passed" );
        return FALSE;
    }
    delete_ipc( &ipc );
    return TRUE;    
}

/* -------------------------------------------------- */
dwc_string_t dwc_ipc_unix_filename( DWC_ipc_addr_ptr_t addr )
{
    EXIT_IF_1PARAM( !addr, "ipc::unix::filename", return NULL );
    return addr->addr.sa_unix.sun_path;    
}

/* -------------------------------------------------- */
dwc_string_t dwc_ipc_get_local_address( dwc_ipc_t ipc )
{
    EXIT_IF_1PARAM( INVALID_IPC == ipc, "ipc::get_local_addr", return NULL );
    return ipc->local.name;
}

/* -------------------------------------------------- */
dwc_string_t dwc_ipc_get_remote_address( dwc_ipc_t ipc )
{
    EXIT_IF_1PARAM( INVALID_IPC == ipc, "ipc::get_remote_addr", return NULL );
    return ipc->remote.name;
}

/* -------------------------------------------------- */
static void set_name_by_addr( DWC_ipc_type_t itype, DWC_ipc_addr_t* paddr )
{
    switch( itype )
    {
        case DWC_IPC_TCP_SOCKET:
            paddr->name_len = snprintf( paddr->name, sizeof(paddr->name) - 1, "inet:%d@%s", ntohs(paddr->addr.sa_tcp.sin_port), inet_ntoa( paddr->addr.sa_tcp.sin_addr ) );
            if( paddr->name_len < 0 )
            {
                strncpy( paddr->name, "inet:unknown", sizeof(paddr->name) );
                paddr->name_len = strlen(paddr->name);
            }
            paddr->name[sizeof(paddr->name) - 1] = NULLCHAR;
            break;
        case DWC_IPC_UNIX_SOCKET:
            paddr->name_len = snprintf( paddr->name, sizeof(paddr->name) - 1, "local:%s", paddr->addr.sa_unix.sun_path );
            if( paddr->name_len < 0 )
            {
                strncpy( paddr->name, "local:unknown", sizeof(paddr->name) );
                paddr->name_len = strlen(paddr->name);
            }
            paddr->name[sizeof(paddr->name) - 1] = NULLCHAR;
            break;
        default:
            paddr->name[0] = NULLCHAR;
            paddr->name_len = 0;
            break;
    }
    
}

/* -------------------------------------------------- */
DWC_bool_t is_localscan_allowed_by_ipc( DWC_ipc_t ipc )
{
    if( ipc )    
        return ipc->localscan_allowed;
    else
        return FALSE;
}

/* //////////////////////////////////////////////////////////////////////// */
/* OLD INTERFACE */
/* //////////////////////////////////////////////////////////////////////// */
/* -------------------------------------------------- */
DWC_ipc_addr_ptr_t create_ipc_addr ( dwc_const_string_t spec, dwc_bool_t cache, DWC_ipc_type_t* ipc_type )
{
    DWC_ipc_addr_ptr_t  addr = NULL;    
    dwc_const_string_t  addr_spec = NULL;

    EXIT_IF_2PARAM( !spec, !ipc_type, "dwlib: ipc:", return NULL );
    *ipc_type = DWC_IPC_TYPE_0;
    if( 0 == DWC_strncasecmp( "default", spec, strlen("default") ) )
    {
        addr_spec = default_ipc_address;
    }
    else 
    {    
        if( NULL == strchr( spec, DWC_IPC_SEPARATOR_CHR ) )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: format separator %c not found in %s", DWC_IPC_SEPARATOR_CHR, spec );
            return NULL;
        }
        if( 0 == DWC_strncasecmp( spec, DWC_IPC_PID_PREFIX, strlen(DWC_IPC_PID_PREFIX) ) )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: using recursive pid: format is invalid" );
            return NULL;
        }
        addr_spec = spec;
    }
    addr = DWC_Malloc( DWC_ipc_addr_ptr_t, sizeof(*addr) );
    if( !addr )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: no memory for address" );
        goto error_exit;   
    }
    memset( addr, 0, sizeof(*addr) );
    if( !parse_addr( addr_spec, addr, ipc_type, cache ) )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: cannot parse address from specification <%s>", spec );
        goto error_exit;
    }
    addr->cached = cache;
    printLib( DW_LL_VERBOSE, "dwlib: ipc: address %s added", spec );
    return addr;
    
  error_exit:
    if( addr )
    {
        DWC_Free( addr );
    }
    *ipc_type = DWC_IPC_TYPE_0;
    return NULL;
}

/* -------------------------------------------------- */
DWC_error_code_t delete_ipc_addr ( DWC_ipc_addr_ptr_t addr )
{
    if( addr )
    {
        DWC_Free( addr );
        return DWC_NOERROR;
    }
    return DWC_BADPARAMS;
}

/* -------------------------------------------------- */
DWC_error_code_t init_ipc_subsystem ( void )
{
    int ret;

    if( ipcState.inited == ENABLE )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: system has already been initialized" );
        return DWC_ALREADYPERFORM;
    }
    if( !dw_sig_ignore( SIGPIPE ) )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: cannot ignore SIGPIPE" );
        return DWC_COMMONERROR;
    }
    ret = init_tcp_sockets();
    if( ret == DWC_NOERROR )
    {
        ipcState.tcp_sock = ENABLE;
    }
    else
    {
        printLib( DW_LL_VERBOSE, "dwlib: ipc: tcp sockets subsystem disabled" );
        ipcState.tcp_sock = DISABLE;
    }
    ret = init_unix_sockets();
    if( ret == DWC_NOERROR )
    {
        ipcState.unix_sock = ENABLE;
    }
    else
    {
        printLib( DW_LL_VERBOSE, "dwlib: ipc: unix sockets subsystem disabled" );
        ipcState.unix_sock = DISABLE;
    }
    ipcState.inited = ENABLE;
    return ( ipcState.unix_sock == DISABLE && ipcState.tcp_sock == DISABLE ? DWC_IPCSYSTEM : DWC_NOERROR );
}

/* -------------------------------------------------- */
DWC_error_code_t dinit_ipc_subsystem ( void )
{
    int tret = DWC_NOERROR;
    int uret = DWC_NOERROR;

    if( ipcState.inited != ENABLE )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: subsystem is not initialized" );
        return DWC_NOTREADY;
    }
    if( ipcState.tcp_sock == ENABLE )
    {
        tret = dinit_tcp_sockets();
    }
    else
    {
        tret = DWC_NOERROR;
    }
    if( ipcState.unix_sock == ENABLE )
    {
        uret = dinit_unix_sockets();
    }
    else
    {
        uret = DWC_NOERROR;
    }
    ipcState.inited = DISABLE; 
    return ( tret == DWC_NOERROR ? uret : tret );
}

/* -------------------------------------------------- */
DWC_error_code_t create_ipc ( DWC_ipc_type_t type, DWC_ipc_addr_ptr_t local, DWC_ipc_addr_ptr_t remote, DWC_timeout_t timeout, DWC_bool_t localscan, DWC_bool_t phys, DWC_ipc_t* ipc )
{
    DWC_error_code_t error = DWC_NOERROR;

    EXIT_IF_3PARAM( !ipc, !VALID_TIMEOUT(timeout), ((type <= DWC_IPC_TYPE_0) || (DWC_IPC_TYPE_MAX <= type)), "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( !(*ipc) )
    {
        (*ipc) = DWC_Malloc( DWC_ipc_t, sizeof(struct _DWC_ipc) );
        if( !ipc )
        {
            return DWC_NOMEMORY;
        }
        (*ipc)->allocated = TRUE;
    }
    else
    {
        (*ipc)->allocated = FALSE;
    }
    if( !dwc_ipc_set_timeout( *ipc, timeout ) )
    {
        error = DWC_BADPARAMS;
        goto error_exit;
    }
    (*ipc)->type = type;
    (*ipc)->localscan_allowed = localscan;
    (*ipc)->connected = FALSE;
    (*ipc)->binded = FALSE;
#if defined(__WIN32_DW)
    (*ipc)->nonblocking = TRUE;
#else /* __WIN32_DW */
    (*ipc)->nonblocking = FALSE;
#endif /* __WIN32_DW */
    (*ipc)->listed = FALSE;
    if( local )
    {
        memcpy( &(*ipc)->local, local, sizeof((*ipc)->local) );
        if( ((*ipc)->type == DWC_IPC_TCP_SOCKET)
            &&
            !(*ipc)->local.cached 
            && 
            (resolve_tcp_addr( &(*ipc)->local.addr.sa_tcp, (*ipc)->local.name, TRUE ) != DWC_NOERROR) )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: cannot resolve local hostname %s", (*ipc)->local.name );
            error = DWC_IPCSYSTEM;
            goto error_exit;
        }
    }
    else
    {
        NULL_IPC_ADDR( (*ipc)->local );
    }
    if( remote )
    {
        memcpy( &(*ipc)->remote, remote, sizeof((*ipc)->remote) );
        if( ((*ipc)->type == DWC_IPC_TCP_SOCKET)
            &&
            !(*ipc)->remote.cached 
            && 
            (resolve_tcp_addr( &(*ipc)->remote.addr.sa_tcp, (*ipc)->remote.name, TRUE ) != DWC_NOERROR) )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: cannot resolve remote hostname %s", (*ipc)->remote.name );
            error = DWC_IPCSYSTEM;
            goto error_exit;
        }
    }
    else
    {
        NULL_IPC_ADDR( (*ipc)->remote );
    }
    if( !phys )
    {
        (*ipc)->handle.hnd_fd = INVALID_SOCKET;
    }
    switch( type )
    {
        case DWC_IPC_TCP_SOCKET:           
            if( ipcState.tcp_sock != ENABLE )
            {
                printLib( DW_LL_ERROR, "dwlib: ipc: tcp sockets not allowed" );
                error = DWC_BADIPC;
                goto error_exit;
            }
            if( phys )
                error = create_tcp_socket( &(*ipc)->handle.hnd_tcp, (*ipc)->nonblocking );
            if( error != DWC_NOERROR )
            {
                printLib( DW_LL_VERBOSE, "dwlib: ipc: cannot create tcp socket" );
                goto error_exit;
            }
            (*ipc)->send_func = send_tcp_socket;
            (*ipc)->recv_func = recv_tcp_socket;
            (*ipc)->local.addr.sa_tcp.sin_family = PF_INET;
            (*ipc)->remote.addr.sa_tcp.sin_family = PF_INET;
            break;
        case DWC_IPC_UNIX_SOCKET:
            if( ipcState.unix_sock != ENABLE )
            {
                printLib( DW_LL_ERROR, "dwlib: ipc: unix sockets not allowed" );
                error = DWC_BADIPC;
                goto error_exit;
            }
            if( phys )
                error = create_unix_socket( &(*ipc)->handle.hnd_unix, (*ipc)->nonblocking );
            if( error != DWC_NOERROR )
            {
                printLib( DW_LL_VERBOSE, "dwlib: ipc: cannot create unix socket" );
                goto error_exit;
            }
            (*ipc)->send_func = send_unix_socket;
            (*ipc)->recv_func = recv_unix_socket;
            (*ipc)->local.addr.sa_unix.sun_family = PF_UNIX;
            (*ipc)->remote.addr.sa_unix.sun_family = PF_UNIX;
            break;
        default:
            return DWC_BADIPC;
    }
    gettimeofday( &(*ipc)->started, NULL );
    return DWC_NOERROR;

  error_exit:
    delete_ipc( ipc );
    return error;
}

/* -------------------------------------------------- */
#if defined(Solaris_DW)
#define timersub(a, b, r)                                               \
BEGIN_MACRO                                                              \
    (r)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (r)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if( (r)->tv_usec < 0 )                                               \
    {                                                                    \
      (r)->tv_sec -= 1;                                                  \
      (r)->tv_usec += 1000000;                                           \
    }                                                                    \
END_MACRO
#endif /* Solaris */

/* -------------------------------------------------- */
DWC_error_code_t delete_ipc ( DWC_ipc_t* ipc )
{
    DWC_error_code_t error;
    struct timeval   end, duration;
    double           etime;

    EXIT_IF_1PARAM( !ipc, "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( !(*ipc) )
        return DWC_BADIPC;
    if( INVALID_SOCKET != (*ipc)->handle.hnd_fd )
    {
        switch( (*ipc)->type )
        {
            case DWC_IPC_TCP_SOCKET:
                error = close_tcp_socket( &(*ipc)->handle.hnd_tcp );
                break;
            case DWC_IPC_UNIX_SOCKET:
                error = close_unix_socket( &(*ipc)->handle.hnd_unix, (*ipc)->local.addr.sa_unix, (*ipc)->binded );
                break;
            default:
                error = DWC_BADIPC;
                break;
        }        
        gettimeofday( &end, NULL );
        timersub( &end, &(*ipc)->started, &duration );
        etime = duration.tv_sec * 1000 + (double) duration.tv_usec / 1000.0;
        printLib( DW_LL_DEBUG, "dwlib: ipc: lifetime = %6.1lf milliseconds", etime );    
    }
    else
    {
        error = DWC_NOERROR;
    }
    if( (*ipc)->allocated )
        DWC_Free( *ipc );
    *ipc = INVALID_IPC;
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t bind_ipc ( DWC_ipc_t ipc )
{
    DWC_error_code_t  error;

    EXIT_IF_1PARAM( !ipc, "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( ipc->binded )
    {
        return DWC_ALREADYPERFORM;
    }
    switch( ipc->type )
    {
        case DWC_IPC_TCP_SOCKET:
            error = bind_tcp_socket( ipc->handle.hnd_tcp, &ipc->local.addr.sa_tcp );
            break;
        case DWC_IPC_UNIX_SOCKET:
            error = bind_unix_socket( ipc->handle.hnd_unix, &ipc->local.addr.sa_unix );
            break;
        default:
            return DWC_BADIPC;
    }
    if( error == DWC_NOERROR )
    {
        ipc->binded = TRUE;
    }
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t listen_ipc ( DWC_ipc_t ipc, int backlog )
{
    DWC_error_code_t  error;

    EXIT_IF_2PARAM( !ipc, (backlog < 0), "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( ipc->listed || ipc->connected )
    {
        return DWC_ALREADYPERFORM;
    }
    switch( ipc->type )
    {
        case DWC_IPC_TCP_SOCKET:
            error = listen_tcp_socket( ipc->handle.hnd_tcp, backlog );
            break;
        case DWC_IPC_UNIX_SOCKET:
            error = listen_unix_socket( ipc->handle.hnd_unix, backlog );
            break;
        default:
            return DWC_BADIPC;
    }
    if( error == DWC_NOERROR )
    {
        ipc->listed = TRUE;
    }
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t accept_ipc ( DWC_ipc_t ipc, DWC_ipc_t* ipc_ptr, DWC_timeout_t timeout )
{
    DWC_error_code_t  error;

    EXIT_IF_3PARAM( !ipc, !ipc_ptr, !VALID_TIMEOUT(timeout), "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( !ipc->listed )
    {
        return DWC_NOTREADY;
    }
    error = create_ipc( ipc->type, NULL, NULL, timeout, is_localscan_allowed_by_ipc( ipc ), FALSE, ipc_ptr );
    if( error != DWC_NOERROR )
    {
        return error;
    }
    gettimeofday( &(*ipc_ptr)->started, NULL );
    switch( ipc->type )
    {
        case DWC_IPC_TCP_SOCKET:
            error = accept_tcp_socket( ipc->handle.hnd_tcp, &(*ipc_ptr)->remote.addr.sa_tcp, &(*ipc_ptr)->handle.hnd_tcp );
            if( error != DWC_NOERROR )
            {
                goto error_exit;
            }
            break;
        case DWC_IPC_UNIX_SOCKET:
            error = accept_unix_socket( ipc->handle.hnd_unix, &(*ipc_ptr)->remote.addr.sa_unix, &(*ipc_ptr)->handle.hnd_unix );
            if( error != DWC_NOERROR )
            {
                goto error_exit;
            }
            break;
        default:
            error = DWC_BADIPC;
            goto error_exit;
    }
    memcpy( &(*ipc_ptr)->local, &ipc->local, sizeof(DWC_ipc_addr_t) );
    set_name_by_addr( ipc->type, &(*ipc_ptr)->remote );
    (*ipc_ptr)->connected = TRUE;
    return DWC_NOERROR;

  error_exit:
    delete_ipc( ipc_ptr );
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t connect_ipc ( DWC_ipc_t ipc )
{
    DWC_error_code_t  error = DWC_COMMONERROR;
    struct sockaddr  *psa = NULL;
    dwc_socklen_t     len;

    EXIT_IF_1PARAM( !ipc, "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( ipc->listed )
    {
        return DWC_NOTREADY;
    }
    gettimeofday( &ipc->started, NULL );
    switch( ipc->type )
    {
        case DWC_IPC_TCP_SOCKET:
            error = connect_tcp_socket( ipc->handle.hnd_tcp, &ipc->remote.addr.sa_tcp, ipc->timeout );
            if( error == DWC_TIMED )
            {
                printLib( DW_LL_ERROR, "dwlib: ipc: timeout expired");
            }
            break;
        case DWC_IPC_UNIX_SOCKET:
            error = connect_unix_socket( ipc->handle.hnd_unix, &ipc->remote.addr.sa_unix, ipc->timeout );
            if( error == DWC_TIMED )
            {
                printLib( DW_LL_ERROR, "dwlib: ipc: timeout expired");
            }
            break;
        default:
            error = DWC_BADIPC;
            break;
    }
    if( error == DWC_NOERROR )
    {
        if( 0 == ipc->local.name_len )
        {
            if( ipc->type == DWC_IPC_TCP_SOCKET )
            {
                psa = (struct sockaddr*) &ipc->local.addr.sa_tcp;
                len = sizeof(ipc->local.addr.sa_tcp);
            }
            else 
            {
                psa = (struct sockaddr*) &ipc->local.addr.sa_unix;
                len = sizeof(ipc->local.addr.sa_unix);
            }
            if( 0 == getpeername( ipc->handle.hnd_fd, psa, &len )  )
            {                
                set_name_by_addr( ipc->type, &ipc->local );
            }
            else
            {
                printLib( DW_LL_ALERT, "dwlib: ipc: getpeername failed - %s", stdlib_error(errno) );
            }
        }
        ipc->connected = TRUE;
    }
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t send_ipc ( DWC_ipc_t ipc, DWC_const_pointer_t buf, DWC_length_t* len_ptr )
{
    DWC_error_code_t    error = DWC_NOERROR;
    DWC_length_t        needbytes = (*len_ptr);
    DWC_length_t        sendbytes = 0;
    DWC_systimeout_t    stimo;

    EXIT_IF_4PARAM( !ipc, !buf, !len_ptr, (*len_ptr < 0), "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( (*len_ptr) == 0 )
    {
        return DWC_NOERROR;
    }        
    if( !VALID_TIMEOUT(ipc->timeout) )
    {
        return DWC_TIMED;
    }
    while( sendbytes < needbytes  )
    {
        stimo = start_timeout( ipc->timeout );
        error = ipc->send_func( DWC_pointer(&ipc->handle), DWC_pointer(((char*)buf + sendbytes)), len_ptr, ipc->timeout );
        if( error != DWC_NOERROR )
        {
            break;
        }
        ipc->timeout = stop_timeout( ipc->timeout, stimo );
        sendbytes += (*len_ptr);
        if( sendbytes >= needbytes )
        {
            break;
        }
        if( ipc->timeout == EXPIRED_TIMEOUT )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: send %d bytes failed (only %d send) - timeout expired", needbytes, sendbytes );
            error = DWC_TIMED;
            break;
        }
        *len_ptr = needbytes - sendbytes;
    }
    printLib( DW_LL_EXT_DEBUG, "dwlib: ipc: sent %d bytes (need %d bytes) - errorcode[%d]", sendbytes, needbytes, error );
    *len_ptr = sendbytes;
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t recv_ipc ( DWC_ipc_t ipc, DWC_pointer_t buf, DWC_length_t* len_ptr )
{
    DWC_error_code_t    error = DWC_NOERROR;
    DWC_length_t        needbytes = (*len_ptr);
    DWC_length_t        recvbytes = 0;
    DWC_systimeout_t    stimo;

    EXIT_IF_4PARAM( !ipc, !buf, !len_ptr, (*len_ptr < 0), "dwlib: ipc:", return DWC_IPCSYSTEM );
    if( (*len_ptr) == 0 )
    {
        return DWC_NOERROR;
    }
    if( !VALID_TIMEOUT( ipc->timeout ) )
    {
        return DWC_TIMED;
    }
    while( recvbytes < needbytes  )
    {
        stimo = start_timeout( ipc->timeout );
        error = ipc->recv_func( DWC_pointer(&ipc->handle), DWC_pointer((char*)buf + recvbytes), len_ptr, ipc->timeout );
        if( error != DWC_NOERROR )
        {
            break;
        }
        ipc->timeout = stop_timeout( ipc->timeout, stimo );
        recvbytes += (*len_ptr);
        if( recvbytes >= needbytes )
        {
            break;
        }
        if( ipc->timeout == EXPIRED_TIMEOUT )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: recv %d bytes failed (only %d recv) - timeout expired", needbytes, recvbytes );
            error = DWC_TIMED;
            break;
        }
        *len_ptr = needbytes - recvbytes;
    }
    printLib( DW_LL_EXT_DEBUG, "dwlib: ipc: recv %d bytes (need %d bytes) - errorcode[%d]", recvbytes, needbytes, error );
    *len_ptr = recvbytes;
    return error;
}

/* -------------------------------------------------- */
DWC_error_code_t recv_ipc_all ( DWC_ipc_t ipc, DWC_pointer_t *buf_ptr, DWC_length_t* len_ptr, DWC_bool_t is_string )
{
    DWC_error_code_t    error = DWC_NOERROR;
    dwc_mbuf_list_t     buf_list = NULL;
    dwc_membuf_t        rbuf = NULL;
    dwc_length_t        rlen = 0;
    DWC_systimeout_t    stimo;

    EXIT_IF_3PARAM( !ipc, !buf_ptr, !len_ptr, "dwlib: ipc:", return DWC_IPCSYSTEM );
    (*buf_ptr) = NULL;
    (*len_ptr) = 0;
    if( !VALID_TIMEOUT( ipc->timeout ) )
    {
        return DWC_TIMED;
    }
    buf_list = dw_mbuf_create( 0 );
    if( !buf_list )
    {
        error = DWC_NOMEMORY;
        goto error_exit;
    }
    rbuf = DWC_Malloc( dwc_membuf_t, IO_BUF_LEN);
    if( !rbuf )
    {
        error = DWC_NOMEMORY;
        goto error_exit;
    }
    while( rbuf )
    {        
        rlen = IO_BUF_LEN;
        memset( rbuf, 0, rlen );
        stimo = start_timeout( ipc->timeout );
        error = ipc->recv_func( DWC_pointer(&ipc->handle), DWC_pointer(rbuf), &rlen, ipc->timeout );        
        if( (error != DWC_NOERROR) && (error != DWC_NODATA) )
        {        
            break;
        }
        if( (rlen > 0) && !dw_mbuf_add_chunk( buf_list, rbuf, rlen ) )
        {
            error = DWC_COMMONERROR;
            break;
        }
        ipc->timeout = stop_timeout( ipc->timeout, stimo );
        if( ipc->timeout == EXPIRED_TIMEOUT )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: recv_all: timeout expired" );
            error = DWC_TIMED;
            break;
        }
        if( error == DWC_NODATA )
        {
            /* unique normal exit from recv loop */
            printLib( DW_LL_VERBOSE, "dwlib: ipc: recv_all: connection closed" );
            error = DWC_NOERROR;
            break;
        }
    }
 error_exit:
    DWC_Free( rbuf );
    if( buf_list )
    {        
        (*len_ptr) = dw_mbuf_size( buf_list );
        if( *len_ptr > 0 )
        {
            (*buf_ptr) = dw_mbuf_to_linear( buf_list, DW_SD_FORWARD, is_string );
            if( !(*buf_ptr) )
            {
                error = DWC_NOMEMORY;
            }
        }
        dw_mbuf_delete( buf_list );
    }
    printLib( DW_LL_EXT_DEBUG, "dwlib: ipc: recv_all: got %d bytes - retcode[%d]", (*len_ptr), error );
    return error;
}

/* -------------------------------------------------------------------------- */
dwc_bool_t parse_addr( dwc_const_string_t spec, DWC_ipc_addr_ptr_t paddr, DWC_ipc_type_t* ptype, dwc_bool_t need_resolve )
{
    const char*     sub;
    const char*     host;
    char            port[256];
    
    EXIT_IF_3PARAM( !spec, !paddr, !ptype, "dwlib: ipc:", return FALSE );
    *ptype = DWC_IPC_TYPE_0;        
    printLib( DW_LL_DEBUG, "dwlib: ipc: parse address string <%s>", spec );
    sub = strchr( spec, ':' );
    if( !sub )
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: wrong format of IPC address <%s>", spec );
        return FALSE;
    }
    ++sub;
    if( 0 == DWC_strncasecmp( spec, DWC_IPC_INET_PREFIX, strlen(DWC_IPC_INET_PREFIX) ) )
    {        
        host = strchr( sub, '@' );        
        if( !host )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: not a tcp-socket address string <%s>", sub );
            return FALSE;
        }
        /* split address string into port string & host string */
        ++host;
        strncpy( paddr->name, host, sizeof(paddr->name) );
        paddr->name[ sizeof(paddr->name) - 1 ] = NULLCHAR;
        paddr->name_len = DWC_strlen(paddr->name);
        strncpy( port, sub, sizeof(port) );
        port[ sizeof(port) - 1 ] = NULLCHAR;        
        if( !fill_tcp_addr( host, port, &paddr->addr.sa_tcp, need_resolve ) )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: wrong tcp-socket address <%s>", host );
            return FALSE;
        }
        *ptype = DWC_IPC_TCP_SOCKET;                
    }
    else if( 0 == DWC_strncasecmp( spec, DWC_IPC_UNIX_PREFIX, strlen(DWC_IPC_UNIX_PREFIX) ) )
    {
        if( (sub[0] != PATH_SEPARATOR)
#if defined(DW_HAVE_SEPARATOR_2)
            &&
            (sub[0] != PATH_SEPARATOR_2)
#endif /* HAVE_SEPARATOR_2 */        
            )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: not a unix-socket path <%s>", sub );
            return FALSE;
        }
        strncpy( paddr->name, sub, sizeof(paddr->name) );
        paddr->name[ sizeof(paddr->name) - 1 ] = NULLCHAR;
        paddr->name_len = DWC_strlen(paddr->name);
        if( !fill_unix_addr( paddr->name, &paddr->addr.sa_unix ) )
        {
            printLib( DW_LL_ERROR, "dwlib: ipc: wrong unix-socket address <%s>", sub );
            return FALSE;
        }
        *ptype = DWC_IPC_UNIX_SOCKET;        
    }
    else
    {
        printLib( DW_LL_ERROR, "dwlib: ipc: invalid family specified %s", spec );
        return FALSE;
    }    
    return TRUE;    
}

#endif /* _DWC_IPC_C */

