Automated merge with ssh://dev/flexnbd-c
This commit is contained in:
4
Rakefile
4
Rakefile
@@ -130,6 +130,7 @@ file check("client") =>
|
|||||||
build/mbox.o
|
build/mbox.o
|
||||||
build/mirror.o
|
build/mirror.o
|
||||||
build/status.o
|
build/status.o
|
||||||
|
build/sockutil.o
|
||||||
build/util.o} do |t|
|
build/util.o} do |t|
|
||||||
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
||||||
end
|
end
|
||||||
@@ -165,6 +166,7 @@ file check("serve") =>
|
|||||||
build/acl.o
|
build/acl.o
|
||||||
build/mbox.o
|
build/mbox.o
|
||||||
build/ioutil.o
|
build/ioutil.o
|
||||||
|
build/sockutil.o
|
||||||
build/util.o} do |t|
|
build/util.o} do |t|
|
||||||
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
||||||
end
|
end
|
||||||
@@ -185,6 +187,7 @@ file check("readwrite") =>
|
|||||||
build/nbdtypes.o
|
build/nbdtypes.o
|
||||||
build/mbox.o
|
build/mbox.o
|
||||||
build/ioutil.o
|
build/ioutil.o
|
||||||
|
build/sockutil.o
|
||||||
build/util.o} do |t|
|
build/util.o} do |t|
|
||||||
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
||||||
end
|
end
|
||||||
@@ -194,6 +197,7 @@ file check("flexnbd") =>
|
|||||||
%w{build/tests/check_flexnbd.o
|
%w{build/tests/check_flexnbd.o
|
||||||
build/flexnbd.o
|
build/flexnbd.o
|
||||||
build/ioutil.o
|
build/ioutil.o
|
||||||
|
build/sockutil.o
|
||||||
build/util.o
|
build/util.o
|
||||||
build/control.o
|
build/control.o
|
||||||
build/mbox.o
|
build/mbox.o
|
||||||
|
@@ -146,7 +146,10 @@ int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count)
|
|||||||
ssize_t result = sendfile64(out_fd, in_fd, offset, count-sent);
|
ssize_t result = sendfile64(out_fd, in_fd, offset, count-sent);
|
||||||
debug("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld", out_fd, in_fd, offset, count-sent, result);
|
debug("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld", out_fd, in_fd, offset, count-sent, result);
|
||||||
|
|
||||||
if (result == -1) { return -1; }
|
if (result == -1) {
|
||||||
|
debug( "%s (%i) calling sendfile64()", strerror(errno), errno );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
sent += result;
|
sent += result;
|
||||||
debug("sent=%ld, count=%ld", sent, count);
|
debug("sent=%ld, count=%ld", sent, count);
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,8 @@ void mode(char* mode, int argc, char **argv);
|
|||||||
|
|
||||||
#define OPT_HELP "help"
|
#define OPT_HELP "help"
|
||||||
#define OPT_ADDR "addr"
|
#define OPT_ADDR "addr"
|
||||||
#define OPT_REBIND_ADDR "rebind-addr"
|
|
||||||
#define OPT_BIND "bind"
|
#define OPT_BIND "bind"
|
||||||
#define OPT_PORT "port"
|
#define OPT_PORT "port"
|
||||||
#define OPT_REBIND_PORT "rebind-port"
|
|
||||||
#define OPT_FILE "file"
|
#define OPT_FILE "file"
|
||||||
#define OPT_SOCK "sock"
|
#define OPT_SOCK "sock"
|
||||||
#define OPT_FROM "from"
|
#define OPT_FROM "from"
|
||||||
@@ -44,9 +42,7 @@ void mode(char* mode, int argc, char **argv);
|
|||||||
#define GETOPT_DENY GETOPT_FLAG( OPT_DENY, 'd' )
|
#define GETOPT_DENY GETOPT_FLAG( OPT_DENY, 'd' )
|
||||||
|
|
||||||
#define GETOPT_ADDR GETOPT_ARG( OPT_ADDR, 'l' )
|
#define GETOPT_ADDR GETOPT_ARG( OPT_ADDR, 'l' )
|
||||||
#define GETOPT_REBIND_ADDR GETOPT_ARG( OPT_REBIND_ADDR, 'L')
|
|
||||||
#define GETOPT_PORT GETOPT_ARG( OPT_PORT, 'p' )
|
#define GETOPT_PORT GETOPT_ARG( OPT_PORT, 'p' )
|
||||||
#define GETOPT_REBIND_PORT GETOPT_ARG( OPT_REBIND_PORT, 'P')
|
|
||||||
#define GETOPT_FILE GETOPT_ARG( OPT_FILE, 'f' )
|
#define GETOPT_FILE GETOPT_ARG( OPT_FILE, 'f' )
|
||||||
#define GETOPT_SOCK GETOPT_ARG( OPT_SOCK, 's' )
|
#define GETOPT_SOCK GETOPT_ARG( OPT_SOCK, 's' )
|
||||||
#define GETOPT_FROM GETOPT_ARG( OPT_FROM, 'F' )
|
#define GETOPT_FROM GETOPT_ARG( OPT_FROM, 'F' )
|
||||||
@@ -86,3 +82,4 @@ void mode(char* mode, int argc, char **argv);
|
|||||||
char * help_help_text;
|
char * help_help_text;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -55,3 +55,4 @@ void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to )
|
|||||||
to->error = be32toh( from->error );
|
to->error = be32toh( from->error );
|
||||||
memcpy( to->handle, from->handle, 8 );
|
memcpy( to->handle, from->handle, 8 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,13 +3,19 @@
|
|||||||
|
|
||||||
|
|
||||||
/* http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-09/2332.html */
|
/* http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-09/2332.html */
|
||||||
#define INIT_PASSWD "NBDMAGIC"
|
#define INIT_PASSWD "NBDMAGIC"
|
||||||
#define INIT_MAGIC 0x0000420281861253
|
#define INIT_MAGIC 0x0000420281861253
|
||||||
#define REQUEST_MAGIC 0x25609513
|
#define REQUEST_MAGIC 0x25609513
|
||||||
#define REPLY_MAGIC 0x67446698
|
#define REPLY_MAGIC 0x67446698
|
||||||
#define REQUEST_READ 0
|
#define REQUEST_READ 0
|
||||||
#define REQUEST_WRITE 1
|
#define REQUEST_WRITE 1
|
||||||
#define REQUEST_DISCONNECT 2
|
#define REQUEST_DISCONNECT 2
|
||||||
|
|
||||||
|
/* 1MiB is the de-facto standard for maximum size of header + data */
|
||||||
|
#define NBD_MAX_SIZE ( 1024 * 1024 )
|
||||||
|
|
||||||
|
#define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) )
|
||||||
|
#define NBD_REPLY_SIZE ( sizeof( struct nbd_reply_raw ) )
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@@ -51,7 +57,7 @@ struct nbd_init {
|
|||||||
|
|
||||||
struct nbd_request {
|
struct nbd_request {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint32_t type; /* == READ || == WRITE */
|
uint32_t type; /* == READ || == WRITE || == DISCONNECT */
|
||||||
char handle[8];
|
char handle[8];
|
||||||
uint64_t from;
|
uint64_t from;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
@@ -63,7 +69,6 @@ struct nbd_reply {
|
|||||||
char handle[8]; /* handle you got from request */
|
char handle[8]; /* handle you got from request */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to );
|
void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to );
|
||||||
void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to );
|
void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to );
|
||||||
void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to );
|
void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to );
|
||||||
|
@@ -56,6 +56,25 @@ fail:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int socket_nbd_write_hello(int fd, off64_t out_size)
|
||||||
|
{
|
||||||
|
struct nbd_init init;
|
||||||
|
struct nbd_init_raw init_raw;
|
||||||
|
|
||||||
|
memcpy(&init.passwd, INIT_PASSWD, 8);
|
||||||
|
init.magic = INIT_MAGIC;
|
||||||
|
init.size = out_size;
|
||||||
|
|
||||||
|
memset( &init_raw, 0, sizeof( init_raw ) ); // ensure reserved is 0s
|
||||||
|
nbd_h2r_init(&init, &init_raw);
|
||||||
|
|
||||||
|
if ( 0 > writeloop( fd, &init_raw, sizeof( init_raw ) ) ) {
|
||||||
|
warn( SHOW_ERRNO( "failed to write hello to socket" ) );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
void fill_request(struct nbd_request *request, int type, off64_t from, int len)
|
void fill_request(struct nbd_request *request, int type, off64_t from, int len)
|
||||||
{
|
{
|
||||||
request->magic = htobe32(REQUEST_MAGIC);
|
request->magic = htobe32(REQUEST_MAGIC);
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
int socket_connect(struct sockaddr* to, struct sockaddr* from);
|
int socket_connect(struct sockaddr* to, struct sockaddr* from);
|
||||||
int socket_nbd_read_hello(int fd, off64_t * size);
|
int socket_nbd_read_hello(int fd, off64_t * size);
|
||||||
|
int socket_nbd_write_hello(int fd, off64_t size);
|
||||||
void socket_nbd_read(int fd, off64_t from, int len, int out_fd, void* out_buf, int timeout_secs);
|
void socket_nbd_read(int fd, off64_t from, int len, int out_fd, void* out_buf, int timeout_secs);
|
||||||
void socket_nbd_write(int fd, off64_t from, int len, int out_fd, void* out_buf, int timeout_secs);
|
void socket_nbd_write(int fd, off64_t from, int len, int out_fd, void* out_buf, int timeout_secs);
|
||||||
int socket_nbd_disconnect( int fd );
|
int socket_nbd_disconnect( int fd );
|
||||||
|
117
src/serve.c
117
src/serve.c
@@ -2,6 +2,7 @@
|
|||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "nbdtypes.h"
|
#include "nbdtypes.h"
|
||||||
#include "ioutil.h"
|
#include "ioutil.h"
|
||||||
|
#include "sockutil.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "bitset.h"
|
#include "bitset.h"
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
@@ -20,22 +21,6 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
static inline void* sockaddr_address_data(struct sockaddr* sockaddr)
|
|
||||||
{
|
|
||||||
NULLCHECK( sockaddr );
|
|
||||||
|
|
||||||
struct sockaddr_in* in = (struct sockaddr_in*) sockaddr;
|
|
||||||
struct sockaddr_in6* in6 = (struct sockaddr_in6*) sockaddr;
|
|
||||||
|
|
||||||
if (sockaddr->sa_family == AF_INET) {
|
|
||||||
return &in->sin_addr;
|
|
||||||
}
|
|
||||||
if (sockaddr->sa_family == AF_INET6) {
|
|
||||||
return &in6->sin6_addr;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct server * server_create (
|
struct server * server_create (
|
||||||
struct flexnbd * flexnbd,
|
struct flexnbd * flexnbd,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
@@ -231,82 +216,15 @@ int server_port( struct server * server )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Try to bind to our serving socket, retrying until it works or gives a
|
|
||||||
* fatal error. */
|
|
||||||
void serve_bind( struct server * serve )
|
|
||||||
{
|
|
||||||
int bind_result;
|
|
||||||
|
|
||||||
char s_address[64];
|
|
||||||
memset( s_address, 0, 64 );
|
|
||||||
strcpy( s_address, "???" );
|
|
||||||
inet_ntop( serve->bind_to.generic.sa_family,
|
|
||||||
sockaddr_address_data( &serve->bind_to.generic),
|
|
||||||
s_address, 64 );
|
|
||||||
|
|
||||||
do {
|
|
||||||
bind_result = bind(
|
|
||||||
serve->server_fd,
|
|
||||||
&serve->bind_to.generic,
|
|
||||||
sizeof(serve->bind_to));
|
|
||||||
|
|
||||||
if ( 0 == bind_result ) {
|
|
||||||
info( "Bound to %s port %d",
|
|
||||||
s_address,
|
|
||||||
ntohs(serve->bind_to.v4.sin_port));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
warn( "Couldn't bind to %s port %d: %s",
|
|
||||||
s_address,
|
|
||||||
ntohs(serve->bind_to.v4.sin_port),
|
|
||||||
strerror( errno ) );
|
|
||||||
|
|
||||||
switch (errno){
|
|
||||||
/* bind() can give us EACCES,
|
|
||||||
* EADDRINUSE, EADDRNOTAVAIL, EBADF,
|
|
||||||
* EINVAL or ENOTSOCK.
|
|
||||||
*
|
|
||||||
* Any of these other than EACCES,
|
|
||||||
* EADDRINUSE or EADDRNOTAVAIL signify
|
|
||||||
* that there's a logic error somewhere.
|
|
||||||
*
|
|
||||||
* EADDRINUSE is fatal: if there's
|
|
||||||
* something already where we want to be
|
|
||||||
* listening, we have no guarantees that
|
|
||||||
* any clients will cope with it.
|
|
||||||
*/
|
|
||||||
case EACCES:
|
|
||||||
case EADDRNOTAVAIL:
|
|
||||||
debug("retrying");
|
|
||||||
sleep(1);
|
|
||||||
continue;
|
|
||||||
case EADDRINUSE:
|
|
||||||
fatal( "%s port %d in use, giving up.",
|
|
||||||
s_address,
|
|
||||||
ntohs(serve->bind_to.v4.sin_port));
|
|
||||||
default:
|
|
||||||
fatal( "Giving up" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while ( 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Prepares a listening socket for the NBD server, binding etc. */
|
/** Prepares a listening socket for the NBD server, binding etc. */
|
||||||
void serve_open_server_socket(struct server* params)
|
void serve_open_server_socket(struct server* params)
|
||||||
{
|
{
|
||||||
NULLCHECK( params );
|
NULLCHECK( params );
|
||||||
|
|
||||||
int optval=1;
|
params->server_fd = socket(params->bind_to.generic.sa_family == AF_INET ?
|
||||||
|
|
||||||
params->server_fd= socket(params->bind_to.generic.sa_family == AF_INET ?
|
|
||||||
PF_INET : PF_INET6, SOCK_STREAM, 0);
|
PF_INET : PF_INET6, SOCK_STREAM, 0);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(params->server_fd,
|
FATAL_IF_NEGATIVE( params->server_fd, "Couldn't create server socket" );
|
||||||
"Couldn't create server socket");
|
|
||||||
|
|
||||||
/* We need SO_REUSEADDR so that when we switch from listening to
|
/* We need SO_REUSEADDR so that when we switch from listening to
|
||||||
* serving we don't have to change address if we don't want to.
|
* serving we don't have to change address if we don't want to.
|
||||||
@@ -317,8 +235,7 @@ void serve_open_server_socket(struct server* params)
|
|||||||
* we barf.
|
* we barf.
|
||||||
*/
|
*/
|
||||||
FATAL_IF_NEGATIVE(
|
FATAL_IF_NEGATIVE(
|
||||||
setsockopt(params->server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)),
|
sock_set_reuseaddr( params->server_fd, 1 ), "Couldn't set SO_REUSEADDR"
|
||||||
"Couldn't set SO_REUSEADDR"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* TCP_NODELAY makes everything not be slow. If we can't set
|
/* TCP_NODELAY makes everything not be slow. If we can't set
|
||||||
@@ -326,14 +243,16 @@ void serve_open_server_socket(struct server* params)
|
|||||||
* understand.
|
* understand.
|
||||||
*/
|
*/
|
||||||
FATAL_IF_NEGATIVE(
|
FATAL_IF_NEGATIVE(
|
||||||
setsockopt(params->server_fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)),
|
sock_set_tcp_nodelay( params->server_fd, 1 ), "Couldn't set TCP_NODELAY"
|
||||||
"Couldn't set TCP_NODELAY"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* If we can't bind, presumably that's because someone else is
|
/* If we can't bind, presumably that's because someone else is
|
||||||
* squatting on our ip/port combo, or the ip isn't yet
|
* squatting on our ip/port combo, or the ip isn't yet
|
||||||
* configured. Ideally we want to retry this. */
|
* configured. Ideally we want to retry this. */
|
||||||
serve_bind(params);
|
FATAL_UNLESS_ZERO(
|
||||||
|
sock_try_bind( params->server_fd, ¶ms->bind_to.generic ),
|
||||||
|
SHOW_ERRNO( "Failed to bind() socket" )
|
||||||
|
);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(
|
FATAL_IF_NEGATIVE(
|
||||||
listen(params->server_fd, params->tcp_backlog),
|
listen(params->server_fd, params->tcp_backlog),
|
||||||
@@ -354,17 +273,13 @@ int tryjoin_client_thread( struct client_tbl_entry *entry, int (*joinfunc)(pthre
|
|||||||
int join_errno;
|
int join_errno;
|
||||||
|
|
||||||
if (entry->thread != 0) {
|
if (entry->thread != 0) {
|
||||||
char s_client_address[64];
|
char s_client_address[128];
|
||||||
|
|
||||||
memset(s_client_address, 0, 64);
|
sockaddr_address_string( &entry->address.generic, &s_client_address[0], 128 );
|
||||||
strcpy(s_client_address, "???");
|
|
||||||
inet_ntop( entry->address.generic.sa_family,
|
|
||||||
sockaddr_address_data(&entry->address.generic),
|
|
||||||
s_client_address,
|
|
||||||
64 );
|
|
||||||
|
|
||||||
debug( "%s(%p,...)", joinfunc == pthread_join ? "joining" : "tryjoining", entry->thread );
|
debug( "%s(%p,...)", joinfunc == pthread_join ? "joining" : "tryjoining", entry->thread );
|
||||||
join_errno = joinfunc(entry->thread, &status);
|
join_errno = joinfunc(entry->thread, &status);
|
||||||
|
|
||||||
/* join_errno can legitimately be ESRCH if the thread is
|
/* join_errno can legitimately be ESRCH if the thread is
|
||||||
* already dead, but the client still needs tidying up. */
|
* already dead, but the client still needs tidying up. */
|
||||||
if (join_errno != 0 && !entry->client->stopped ) {
|
if (join_errno != 0 && !entry->client->stopped ) {
|
||||||
@@ -483,9 +398,11 @@ int server_should_accept_client(
|
|||||||
NULLCHECK( client_address );
|
NULLCHECK( client_address );
|
||||||
NULLCHECK( s_client_address );
|
NULLCHECK( s_client_address );
|
||||||
|
|
||||||
if (inet_ntop(client_address->generic.sa_family,
|
const char* result = sockaddr_address_string(
|
||||||
sockaddr_address_data(&client_address->generic),
|
&client_address->generic, s_client_address, s_client_address_len
|
||||||
s_client_address, s_client_address_len ) == NULL) {
|
);
|
||||||
|
|
||||||
|
if ( NULL == result ) {
|
||||||
warn( "Rejecting client %s: Bad client_address", s_client_address );
|
warn( "Rejecting client %s: Bad client_address", s_client_address );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
104
src/sockutil.c
Normal file
104
src/sockutil.c
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include "sockutil.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
size_t sockaddr_size(const struct sockaddr* sa)
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
|
||||||
|
switch( sa->sa_family ) {
|
||||||
|
case AF_INET:
|
||||||
|
ret = sizeof( struct sockaddr_in );
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
ret = sizeof( struct sockaddr_in6 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sockaddr_address_string(const struct sockaddr* sa, char* dest, size_t len)
|
||||||
|
{
|
||||||
|
NULLCHECK( sa );
|
||||||
|
NULLCHECK( dest );
|
||||||
|
|
||||||
|
struct sockaddr_in* in = ( struct sockaddr_in* ) sa;
|
||||||
|
struct sockaddr_in6* in6 = ( struct sockaddr_in6* ) sa;
|
||||||
|
|
||||||
|
unsigned short real_port = ntohs( in->sin_port ); // common to in and in6
|
||||||
|
size_t size;
|
||||||
|
const char* ret = NULL;
|
||||||
|
|
||||||
|
memset( dest, 0, len );
|
||||||
|
|
||||||
|
if ( sa->sa_family == AF_INET ) {
|
||||||
|
ret = inet_ntop( AF_INET, &in->sin_addr, dest, len );
|
||||||
|
} else if ( sa->sa_family == AF_INET6 ) {
|
||||||
|
ret = inet_ntop( AF_INET6, &in6->sin6_addr, dest, len );
|
||||||
|
} else {
|
||||||
|
strncpy( dest, "???", len );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( NULL != ret && real_port > 0 ) {
|
||||||
|
size = strlen( dest );
|
||||||
|
snprintf( dest + size, len - size, " port %d", real_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_set_reuseaddr(int fd, int optval)
|
||||||
|
{
|
||||||
|
return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the tcp_nodelay option */
|
||||||
|
int sock_set_tcp_nodelay(int fd, int optval)
|
||||||
|
{
|
||||||
|
return setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_try_bind(int fd, const struct sockaddr* sa)
|
||||||
|
{
|
||||||
|
int bind_result;
|
||||||
|
char s_address[256];
|
||||||
|
|
||||||
|
sockaddr_address_string( sa, &s_address[0], 256 );
|
||||||
|
|
||||||
|
do {
|
||||||
|
bind_result = bind( fd, sa, sockaddr_size( sa ) );
|
||||||
|
if ( 0 == bind_result ) {
|
||||||
|
info( "Bound to %s", s_address );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
warn( SHOW_ERRNO( "Couldn't bind to %s", s_address ) );
|
||||||
|
|
||||||
|
switch ( errno ) {
|
||||||
|
/* bind() can give us EACCES, EADDRINUSE, EADDRNOTAVAIL, EBADF,
|
||||||
|
* EINVAL, ENOTSOCK, EFAULT, ELOOP, ENAMETOOLONG, ENOENT,
|
||||||
|
* ENOMEM, ENOTDIR, EROFS
|
||||||
|
*
|
||||||
|
* Any of these other than EADDRINUSE & EADDRNOTAVAIL signify
|
||||||
|
* that there's a logic error somewhere.
|
||||||
|
*
|
||||||
|
* EADDRINUSE is fatal: if there's something already where we
|
||||||
|
* want to be listening, we have no guarantees that any clients
|
||||||
|
* will cope with it.
|
||||||
|
*/
|
||||||
|
case EADDRNOTAVAIL:
|
||||||
|
debug( "retrying" );
|
||||||
|
sleep( 1 );
|
||||||
|
continue;
|
||||||
|
case EADDRINUSE:
|
||||||
|
warn( "%s in use, giving up.", s_address );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warn( "giving up" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while ( 1 );
|
||||||
|
|
||||||
|
return bind_result;
|
||||||
|
}
|
||||||
|
|
32
src/sockutil.h
Normal file
32
src/sockutil.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef SOCKUTIL_H
|
||||||
|
|
||||||
|
#define SOCKUTIL_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
|
/* Returns the size of the sockaddr, or 0 on error */
|
||||||
|
size_t sockaddr_size(const struct sockaddr* sa);
|
||||||
|
|
||||||
|
/* Convert a sockaddr into an address. Like inet_ntop, it returns dest if
|
||||||
|
* successful, NULL otherwise. In the latter case, dest will contain "???"
|
||||||
|
*/
|
||||||
|
const char* sockaddr_address_string(const struct sockaddr* sa, char* dest, size_t len);
|
||||||
|
|
||||||
|
/* Set the SOL_REUSEADDR otion */
|
||||||
|
int sock_set_reuseaddr(int fd, int optval);
|
||||||
|
|
||||||
|
/* Set the tcp_nodelay option */
|
||||||
|
int sock_set_tcp_nodelay(int fd, int optval);
|
||||||
|
|
||||||
|
/* TODO: Set the tcp_cork option */
|
||||||
|
// int sock_set_cork(int fd, int optval);
|
||||||
|
|
||||||
|
/* Attempt to bind the fd to the sockaddr, retrying common transient failures */
|
||||||
|
int sock_try_bind(int fd, const struct sockaddr* sa);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@@ -148,6 +148,7 @@ void mylog(int line_level, const char* format, ...);
|
|||||||
|
|
||||||
#define NULLCHECK(value) FATAL_IF_NULL(value, "BUG: " #value " is null")
|
#define NULLCHECK(value) FATAL_IF_NULL(value, "BUG: " #value " is null")
|
||||||
|
|
||||||
|
#define SHOW_ERRNO( msg, ... ) msg ": %s (%i)", ##__VA_ARGS__, ( errno == 0 ? "EOF" : strerror(errno) ), errno
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
13
tests/acceptance/custom.supp
Normal file
13
tests/acceptance/custom.supp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
avoid_glibc_bug_do_lookup
|
||||||
|
Memcheck:Addr8
|
||||||
|
fun:do_lookup_x
|
||||||
|
obj:*
|
||||||
|
fun:_dl_lookup_symbol_x
|
||||||
|
}
|
||||||
|
{
|
||||||
|
avoid_glibc_bug_check_match
|
||||||
|
Memcheck:Addr8
|
||||||
|
fun:check_match.12149
|
||||||
|
}
|
||||||
|
|
@@ -5,7 +5,7 @@ require 'file_writer'
|
|||||||
|
|
||||||
class Environment
|
class Environment
|
||||||
attr_reader( :blocksize, :filename1, :filename2, :ip,
|
attr_reader( :blocksize, :filename1, :filename2, :ip,
|
||||||
:port1, :port2, :nbd1, :nbd2, :file1, :file2, :rebind_port1 )
|
:port1, :port2, :nbd1, :nbd2, :file1, :file2 )
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@blocksize = 1024
|
@blocksize = 1024
|
||||||
@@ -14,9 +14,7 @@ class Environment
|
|||||||
@ip = "127.0.0.1"
|
@ip = "127.0.0.1"
|
||||||
@available_ports = [*40000..41000] - listening_ports
|
@available_ports = [*40000..41000] - listening_ports
|
||||||
@port1 = @available_ports.shift
|
@port1 = @available_ports.shift
|
||||||
@rebind_port1 = @available_ports.shift
|
|
||||||
@port2 = @available_ports.shift
|
@port2 = @available_ports.shift
|
||||||
@rebind_port2 = @available_ports.shift
|
|
||||||
@nbd1 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port1)
|
@nbd1 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port1)
|
||||||
@nbd2 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port2)
|
@nbd2 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port2)
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
require 'flexnbd/fake_source'
|
require 'flexnbd/fake_source'
|
||||||
include FlexNBD
|
include FlexNBD
|
||||||
|
|
||||||
addr, port, srv_pid, rebind_addr, rebind_port = *ARGV
|
addr, port, srv_pid = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new( addr, port, "Timed out connecting" )
|
client = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
client.read_hello
|
client.read_hello
|
||||||
@@ -30,3 +30,4 @@ rescue Timeout::Error
|
|||||||
end
|
end
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ class ValgrindExecutor
|
|||||||
attr_reader :pid
|
attr_reader :pid
|
||||||
|
|
||||||
def run( cmd )
|
def run( cmd )
|
||||||
@pid = fork do exec "valgrind --track-origins=yes #{cmd}" end
|
@pid = fork do exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" end
|
||||||
end
|
end
|
||||||
end # class ValgrindExecutor
|
end # class ValgrindExecutor
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ class ValgrindKillingExecutor
|
|||||||
|
|
||||||
def run( cmd )
|
def run( cmd )
|
||||||
@io_r, io_w = IO.pipe
|
@io_r, io_w = IO.pipe
|
||||||
@pid = fork do exec( "valgrind --xml=yes --xml-fd=#{io_w.fileno} " + cmd ) end
|
@pid = fork do exec( "valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd ) end
|
||||||
launch_watch_thread( @pid, @io_r )
|
launch_watch_thread( @pid, @io_r )
|
||||||
@pid
|
@pid
|
||||||
end
|
end
|
||||||
@@ -521,3 +521,4 @@ module FlexNBD
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
109
tests/unit/check_sockutil.c
Normal file
109
tests/unit/check_sockutil.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "sockutil.h"
|
||||||
|
|
||||||
|
#include <check.h>
|
||||||
|
|
||||||
|
START_TEST( test_sockaddr_address_string_af_inet_converts_to_string )
|
||||||
|
{
|
||||||
|
struct sockaddr sa;
|
||||||
|
struct sockaddr_in* v4 = (struct sockaddr_in*) &sa;
|
||||||
|
char testbuf[128];
|
||||||
|
const char* result;
|
||||||
|
|
||||||
|
v4->sin_family = AF_INET;
|
||||||
|
v4->sin_port = htons( 4777 );
|
||||||
|
ck_assert_int_eq( 1, inet_pton( AF_INET, "192.168.0.1", &v4->sin_addr ));
|
||||||
|
|
||||||
|
result = sockaddr_address_string( &sa, &testbuf[0], 128 );
|
||||||
|
ck_assert( result != NULL );
|
||||||
|
|
||||||
|
ck_assert_str_eq( "192.168.0.1 port 4777", testbuf );
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
START_TEST( test_sockaddr_address_string_af_inet6_converts_to_string )
|
||||||
|
{
|
||||||
|
struct sockaddr_in6 v6_raw;
|
||||||
|
struct sockaddr_in6* v6 = &v6_raw;
|
||||||
|
struct sockaddr* sa = (struct sockaddr*) &v6_raw;
|
||||||
|
|
||||||
|
char testbuf[128];
|
||||||
|
const char* result;
|
||||||
|
|
||||||
|
v6->sin6_family = AF_INET6;
|
||||||
|
v6->sin6_port = htons( 4777 );
|
||||||
|
ck_assert_int_eq( 1, inet_pton( AF_INET6, "fe80::1", &v6->sin6_addr ));
|
||||||
|
|
||||||
|
result = sockaddr_address_string( sa, &testbuf[0], 128 );
|
||||||
|
ck_assert( result != NULL );
|
||||||
|
|
||||||
|
ck_assert_str_eq( "fe80::1 port 4777", testbuf );
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
/* We don't know what it is, so we just call it "???" and return NULL */
|
||||||
|
START_TEST( test_sockaddr_address_string_af_unspec_is_failure )
|
||||||
|
{
|
||||||
|
struct sockaddr sa;
|
||||||
|
struct sockaddr_in* v4 = (struct sockaddr_in*) &sa;
|
||||||
|
char testbuf[128];
|
||||||
|
const char* result;
|
||||||
|
|
||||||
|
v4->sin_family = AF_UNSPEC;
|
||||||
|
v4->sin_port = htons( 4777 );
|
||||||
|
ck_assert_int_eq( 1, inet_pton( AF_INET, "192.168.0.1", &v4->sin_addr ));
|
||||||
|
|
||||||
|
result = sockaddr_address_string( &sa, &testbuf[0], 128 );
|
||||||
|
ck_assert( result == NULL );
|
||||||
|
|
||||||
|
ck_assert_str_eq( "???", testbuf );
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
/* This is a complete failure to parse, rather than a partial failure */
|
||||||
|
START_TEST( test_sockaddr_address_string_doesnt_overflow_short_buffer )
|
||||||
|
{
|
||||||
|
struct sockaddr sa;
|
||||||
|
struct sockaddr_in* v4 = (struct sockaddr_in*) &sa;
|
||||||
|
char testbuf[128];
|
||||||
|
const char* result;
|
||||||
|
|
||||||
|
v4->sin_family = AF_INET;
|
||||||
|
v4->sin_port = htons( 4777 );
|
||||||
|
ck_assert_int_eq( 1, inet_pton( AF_INET, "192.168.0.1", &v4->sin_addr ));
|
||||||
|
|
||||||
|
result = sockaddr_address_string( &sa, &testbuf[0], 4 );
|
||||||
|
ck_assert( result == NULL );
|
||||||
|
|
||||||
|
ck_assert_str_eq( "", testbuf );
|
||||||
|
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *sockutil_suite(void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create("sockutil");
|
||||||
|
|
||||||
|
TCase *tc_sockaddr_address_string = tcase_create("sockaddr_address_string");
|
||||||
|
|
||||||
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_af_inet_converts_to_string);
|
||||||
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_af_inet6_converts_to_string);
|
||||||
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_af_unspec_is_failure);
|
||||||
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_doesnt_overflow_short_buffer);
|
||||||
|
suite_add_tcase(s, tc_sockaddr_address_string);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int number_failed;
|
||||||
|
|
||||||
|
Suite *s = sockutil_suite();
|
||||||
|
SRunner *sr = srunner_create(s);
|
||||||
|
srunner_run_all(sr, CK_NORMAL);
|
||||||
|
number_failed = srunner_ntests_failed(sr);
|
||||||
|
srunner_free(sr);
|
||||||
|
return (number_failed == 0) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user