#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stumpless/target.h>
#include <stumpless/target/socket.h>
#include "private/config/wrapper/locale.h"
#include "private/config/wrapper/socket.h"
#include "private/error.h"
#include "private/memory.h"
#include "private/target.h"
#include "private/target/socket.h"
#include "private/validate.h"
void
stumpless_close_socket_target( const struct stumpless_target *target ) {
if( !target ) {
raise_argument_empty( L10N_NULL_ARG_ERROR_MESSAGE( "target" ) );
return;
}
if( target->type != STUMPLESS_SOCKET_TARGET ) {
raise_target_incompatible( L10N_INVALID_TARGET_TYPE_ERROR_MESSAGE );
return;
}
destroy_socket_target( target->id );
destroy_target( target );
clear_error( );
}
struct stumpless_target *
stumpless_open_socket_target( const char *name,
const char *local_socket ) {
struct stumpless_target *target;
size_t name_len;
size_t local_socket_len;
char temp_name[18];
size_t temp_name_len;
VALIDATE_ARG_NOT_NULL( name );
target = new_target( STUMPLESS_SOCKET_TARGET, name );
if( !target ) {
goto fail;
}
name_len = strlen( name );
if( !local_socket ) {
temp_name_len = config_get_local_socket_name( temp_name );
target->id = new_socket_target( name, name_len, temp_name, temp_name_len );
} else {
local_socket_len = strlen( local_socket );
target->id =
new_socket_target( name, name_len, local_socket, local_socket_len );
}
if( !target->id ) {
goto fail_id;
}
stumpless_set_current_target( target );
return target;
fail_id:
destroy_target( target );
fail:
return NULL;
}
void
destroy_socket_target( const struct socket_target *trgt ) {
if( !trgt ) {
return;
}
close( trgt->local_socket );
unlink( trgt->local_addr.sun_path );
free_mem( trgt );
}
struct socket_target *
new_socket_target( const char *dest,
size_t dest_len,
const char *source,
size_t source_len ) {
struct socket_target *target;
int bind_result;
target = alloc_mem( sizeof( *target ) );
if( !target ) {
goto fail;
}
target->target_addr.sun_family = AF_UNIX;
memcpy( &target->target_addr.sun_path, dest, dest_len );
target->target_addr.sun_path[dest_len] = '\0';
target->local_addr.sun_family = AF_UNIX;
memcpy( &target->local_addr.sun_path, source, source_len );
target->local_addr.sun_path[source_len] = '\0';
target->local_socket = socket( target->local_addr.sun_family, SOCK_DGRAM, 0 );
if( target->local_socket < 0 ) {
raise_socket_failure( L10N_UNIX_SOCKET_FAILED_ERROR_MESSAGE,
errno,
L10N_ERRNO_ERROR_CODE_TYPE );
goto fail_socket;
}
bind_result= bind( target->local_socket,
( struct sockaddr * ) &target->local_addr,
sizeof( target->local_addr ) );
if( bind_result < 0 ) {
raise_socket_bind_failure( L10N_BIND_UNIX_SOCKET_FAILED_ERROR_MESSAGE,
errno,
L10N_ERRNO_ERROR_CODE_TYPE );
goto fail_bind;
}
target->target_addr_len = sizeof( target->target_addr );
return target;
fail_bind:
close( target->local_socket );
fail_socket:
free_mem( target );
fail:
return NULL;
}
int
sendto_socket_target( const struct socket_target *target,
const char *msg, size_t msg_length ) {
int result;
msg_length--;
result = sendto( target->local_socket,
msg,
msg_length,
0,
( const struct sockaddr * ) &target->target_addr,
target->target_addr_len );
if( result == -1 ) {
raise_socket_send_failure( L10N_SENDTO_UNIX_SOCKET_FAILED_ERROR_MESSAGE,
errno,
L10N_ERRNO_ERROR_CODE_TYPE );
}
return result;
}