#ifndef __DWC_UNIX_C
#define __DWC_UNIX_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.
 *------------------------------------------------------------------------*/
 
#if defined(__UNIX_DW)

#ifndef lint
static const char rcsid[] = "$Id: dwc_unix.c,v 1.34 2005/09/21 09:20:44 asv Exp $";
#endif

#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "dwc_lib.h"
#include "dwc_unix.h"
#include "dwc_err.h"
#include "dwc_macro.h"
#include "dwc_timo.h"
#include "dwc_fd.h"

/* -------------------------------------------------------------------- */
DWC_error_code_t init_unix_sockets ( void )
{
    return DWC_NOERROR;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t dinit_unix_sockets ( void )
{
    return DWC_NOERROR;
}

/* -------------------------------------------------------------------- */
dwc_bool_t fill_unix_addr( const char *path, DWC_unix_socket_addr_t *naddr )
{
    naddr->sun_family = AF_UNIX;
    strncpy( naddr->sun_path, path, sizeof(naddr->sun_path) );
    naddr->sun_path[ sizeof(naddr->sun_path) - 1 ] = NULLCHAR;
    return TRUE;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t create_unix_socket( DWC_unix_socket_t *sock, DWC_bool_t nonblocking )
{
    EXIT_IF_1PARAM( !sock, "dwlib: unix:", return DWC_IPCSYSTEM );
    *sock = socket( PF_UNIX, SOCK_STREAM, 0 );
    if( (*sock) == INVALID_UNIX_SOCKET )
    {
        printLib( DW_LL_ERROR, "dwlib: unix: socket() failed - %s", stdlib_error(errno) );
        return DWC_IPCSYSTEM;
    }
    if( nonblocking && !set_nonblocking_fd(*sock) )
    {
        printLib( DW_LL_ERROR, "dwlib: unix: cannot set nonblocking mode");
        close( (*sock) );
        *sock = INVALID_UNIX_SOCKET;
        return DWC_IPCSYSTEM;
    }
    return DWC_NOERROR;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t bind_unix_socket ( DWC_unix_socket_t sock, DWC_unix_socket_addr_t* addr )
{
    DWC_socklen_t len = sizeof(DWC_unix_socket_addr_t);
    DWC_unix_socket_addr_t taddr;
    DWC_unix_socket_t probe;
    DWC_error_code_t  err;

    err = bind_socket( sock, (struct sockaddr*) addr, len );
    if( DWC_NOERROR != err )
    {
        if( DWC_NOERROR == create_unix_socket( &probe, FALSE ) )
        {
            memcpy( &taddr, addr, len );
            if( DWC_NOERROR != connect_unix_socket( probe, &taddr, 0 ) )
            {
                if( 0 == unlink( taddr.sun_path ) )
                {
                    printLib( DW_LL_ALERT, "dwlib: unix: stalled socket %s has been detected and was removed", addr->sun_path );
                    return bind_socket( sock, (struct sockaddr*) addr, len );
                }
                printLib( DW_LL_ERROR, "dwlib: unix: stalled socket %s has been detected and was not removed - %s", addr->sun_path, stdlib_error(errno) );
            }
            else
            {
                close_unix_socket( &probe, *addr, FALSE );
                printLib( DW_LL_ALERT, "dwlib: unix: cannot bind %s - socket is used", addr->sun_path );
            }
        }
    }
    return err;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t listen_unix_socket ( DWC_unix_socket_t sock, int backlog )
{
    return listen_socket( sock, backlog );
}

/* -------------------------------------------------------------------- */
DWC_error_code_t accept_unix_socket ( DWC_unix_socket_t sock, DWC_unix_socket_addr_t *addr, DWC_unix_socket_t *newsock )
{
    DWC_error_code_t error;
    DWC_socklen_t len = sizeof(DWC_unix_socket_addr_t);

    error = accept_socket( sock, (struct sockaddr*) addr, &len, newsock );
    if( error != DWC_NOERROR )
    {
        (*newsock) = INVALID_UNIX_SOCKET;
    }
    return error;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t connect_unix_socket ( DWC_unix_socket_t sock, DWC_unix_socket_addr_t *addr, DWC_timeout_t timeout )
{
    DWC_error_code_t ret;
    
    ret = connect_socket( sock, (struct sockaddr*) addr, sizeof(DWC_unix_socket_addr_t), timeout );
    if( ret != DWC_NOERROR )
    {
        printLib(DW_LL_ERROR,"dwlib: unix: connect() to %s failed", addr->sun_path);
    }
    return ret;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t send_unix_socket ( DWC_unix_socket_t *sock, DWC_pointer_t buf, DWC_length_t *len, DWC_timeout_t timo )
{
    return send_socket( *sock, buf, len, timo );
}

/* -------------------------------------------------------------------- */
DWC_error_code_t recv_unix_socket ( DWC_unix_socket_t *sock, DWC_pointer_t buf, DWC_length_t *len, DWC_timeout_t timo )
{
    return recv_socket( *sock, buf, len, timo );
}

/* -------------------------------------------------------------------- */
DWC_error_code_t shutdown_unix_socket ( DWC_unix_socket_t *sock, DWC_unix_socket_addr_t addr, DWC_bool_t linked )
{
    int serror, uerror;

    serror = shutdown( *sock, SHUT_RDWR );
#if !defined(EMX_DW)
    if( linked )
    {
        uerror = unlink( addr.sun_path );
    }
    else
#endif /* EMX */
    {
        uerror = DWC_NOERROR;
    }
    (*sock) = INVALID_UNIX_SOCKET;
    if( serror || uerror )
    {
        printLib( DW_LL_ERROR, "dwlib: unix: shutdown() failed - %s", stdlib_error(errno) );
        return DWC_IPCSYSTEM;
    }
    return DWC_NOERROR;
}

/* -------------------------------------------------------------------- */
DWC_error_code_t close_unix_socket ( DWC_unix_socket_t *sock, DWC_unix_socket_addr_t addr, DWC_bool_t linked )
{
    int cerror, uerror;

    if( *sock == INVALID_UNIX_SOCKET )
    {
        printLib( DW_LL_DEBUG, "dwlib: unix: close() failed - closed socket" );
        return DWC_NOERROR;
    }
    cerror = close( *sock );
    if( linked )
    {
        uerror = unlink( addr.sun_path );
    }
    else
    {
        uerror = DWC_NOERROR;
    }
    *sock = INVALID_UNIX_SOCKET;
    if( cerror || uerror )
    {
        printLib( DW_LL_ERROR, "dwlib: unix: close() failed - %s", stdlib_error(errno) );
        return DWC_IPCSYSTEM;
    }
    return DWC_NOERROR;
}

#else /* !UNIX */

#include "dwc_unix.h"
#include "dwc_err.h"

/* -------------------------------------------------- */
DWC_error_code_t init_unix_sockets ( void )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t dinit_unix_sockets ( void )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t create_unix_socket( DWC_unix_socket_t *sock, DWC_bool_t nonblocking )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t bind_unix_socket ( DWC_unix_socket_t sock, DWC_unix_socket_addr_t* addr )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t listen_unix_socket ( DWC_unix_socket_t sock, int backlog )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t accept_unix_socket ( DWC_unix_socket_t sock, DWC_unix_socket_addr_t *addr, DWC_unix_socket_t *newsock )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t connect_unix_socket ( DWC_unix_socket_t sock, DWC_unix_socket_addr_t *addr, DWC_timeout_t timeout )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t send_unix_socket ( DWC_unix_socket_t *sock, DWC_pointer_t buf, DWC_length_t *len, DWC_timeout_t timo )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t recv_unix_socket ( DWC_unix_socket_t *sock, DWC_pointer_t buf, DWC_length_t *len, DWC_timeout_t timo )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t shutdown_unix_socket ( DWC_unix_socket_t *sock, DWC_unix_socket_addr_t addr, DWC_bool_t linked )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------- */
DWC_error_code_t close_unix_socket ( DWC_unix_socket_t *sock, DWC_unix_socket_addr_t addr, DWC_bool_t linked )
{
    return DWC_NOTSUPPORTED;  
}

/* -------------------------------------------------------------------- */
dwc_bool_t fill_unix_addr( const char *path, DWC_unix_socket_addr_t *naddr )
{
    return FALSE;  
}

#endif /* __UNIX_DW && !EMX */

#endif /* __DWC_UNIX_H */
