Compare commits
2 Commits
release-to
...
take-reque
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cc69752394 | ||
![]() |
af2bee79fc |
@@ -1,24 +0,0 @@
|
|||||||
# Contribution guide
|
|
||||||
|
|
||||||
The code is formatted using the K&R style of "indent".
|
|
||||||
|
|
||||||
```
|
|
||||||
indent -kr <files go here>
|
|
||||||
```
|
|
||||||
|
|
||||||
The C unit tests have also been indented in the same way, but manually adjsted
|
|
||||||
such that the functions follow the normal libcheck layout.
|
|
||||||
|
|
||||||
```c
|
|
||||||
START_TEST( ... ) {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
END TEST
|
|
||||||
```
|
|
||||||
|
|
||||||
Indent tends to mangle the `END_TEST` macro, so that will need adjusting if
|
|
||||||
`indent` is run over the test files again.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
1
Makefile
1
Makefile
@@ -109,6 +109,7 @@ install:
|
|||||||
clean:
|
clean:
|
||||||
rm -rf build/*
|
rm -rf build/*
|
||||||
|
|
||||||
|
|
||||||
.PHONY: clean objs check_objs all server proxy check_bins check doc build test acceptance
|
.PHONY: clean objs check_objs all server proxy check_bins check doc build test acceptance
|
||||||
|
|
||||||
# Include extra dependencies at the end, NOT before 'all'
|
# Include extra dependencies at the end, NOT before 'all'
|
||||||
|
33
debian/changelog
vendored
33
debian/changelog
vendored
@@ -1,36 +1,9 @@
|
|||||||
flexnbd (0.3.0) stable; urgency=medium
|
flexnbd (0.1.8) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* Force a msync after every write, ignoring FUA flag, or lack thereof (!51).
|
* Set TCP keepalive on sockets so broken connections are reaped (#33, !33)
|
||||||
|
|
||||||
-- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 24 Apr 2018 12:05:43 +0100
|
|
||||||
|
|
||||||
flexnbd (0.2.0) stable; urgency=medium
|
|
||||||
|
|
||||||
[ James Carter ]
|
|
||||||
* Set TCP keepalive on sockets so broken connections are reaped (#33, !33,
|
|
||||||
!36)
|
|
||||||
* Add a context to logs to make debugging problems easier (#34, !34)
|
* Add a context to logs to make debugging problems easier (#34, !34)
|
||||||
|
|
||||||
[ Chris Cottam ]
|
-- James Carter <james.carter@bytemark.co.uk> Thu, 11 Jan 2018 10:05:35 +0000
|
||||||
* Increased NBD_MAX_SIZE from 1MB to 32MB for qemu 2.11 (!35)
|
|
||||||
|
|
||||||
[ Patrick J Cherry ]
|
|
||||||
* Added FLUSH and FUA support (!38)
|
|
||||||
* Server returns ENOSPC in response to writes beyond the end of the
|
|
||||||
filesystem, and EINVAL to unknown commands. (#36, !40)
|
|
||||||
* Proxy passes all NBD protocol errors through to the client instead of
|
|
||||||
disconnecting and retrying (#36, !40)
|
|
||||||
* Fix struct types in readwrite.c (#35, !41)
|
|
||||||
* Ignore ends of discs that stray outside of 512-byte sector sizes (!42).
|
|
||||||
* Tweak logging for readloop failures (!44)
|
|
||||||
* Alter semantics of NBD_MAX_BLOCK_SIZE to remove struct size overheads when
|
|
||||||
calculating if a request exceeds the max block size (!45)
|
|
||||||
* Added tests for setting TCP_NODELAY on upstream-reconnections in the
|
|
||||||
proxy, and refactored the other LD_PRELOAD tests (!43)
|
|
||||||
* Clean up dead threads before calculating the number of connected clients
|
|
||||||
on the status command (!46)
|
|
||||||
|
|
||||||
-- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 20 Feb 2018 11:43:22 +0000
|
|
||||||
|
|
||||||
flexnbd (0.1.7) stable; urgency=medium
|
flexnbd (0.1.7) stable; urgency=medium
|
||||||
|
|
||||||
|
@@ -45,7 +45,8 @@ int build_allocation_map(struct bitset *allocation_map, int fd)
|
|||||||
if ( ioctl( fd, FS_IOC_FIEMAP, fiemap ) < 0 ) {
|
if ( ioctl( fd, FS_IOC_FIEMAP, fiemap ) < 0 ) {
|
||||||
debug( "Couldn't get fiemap, returning no allocation_map" );
|
debug( "Couldn't get fiemap, returning no allocation_map" );
|
||||||
return 0; /* it's up to the caller to free the map */
|
return 0; /* it's up to the caller to free the map */
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
for ( unsigned int i = 0; i < fiemap->fm_mapped_extents; i++ ) {
|
for ( unsigned int i = 0; i < fiemap->fm_mapped_extents; i++ ) {
|
||||||
bitset_set_range( allocation_map,
|
bitset_set_range( allocation_map,
|
||||||
fiemap->fm_extents[i].fe_logical,
|
fiemap->fm_extents[i].fe_logical,
|
||||||
@@ -57,10 +58,12 @@ int build_allocation_map(struct bitset *allocation_map, int fd)
|
|||||||
* if we've actually hit max_offsets.
|
* if we've actually hit max_offsets.
|
||||||
*/
|
*/
|
||||||
if (fiemap->fm_mapped_extents > 0) {
|
if (fiemap->fm_mapped_extents > 0) {
|
||||||
struct fiemap_extent *last =
|
struct fiemap_extent *last = &fiemap->fm_extents[
|
||||||
&fiemap->fm_extents[fiemap->fm_mapped_extents - 1];
|
fiemap->fm_mapped_extents-1
|
||||||
|
];
|
||||||
offset = last->fe_logical + last->fe_length;
|
offset = last->fe_logical + last->fe_length;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
offset += fiemap->fm_length;
|
offset += fiemap->fm_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,8 +74,7 @@ int build_allocation_map(struct bitset *allocation_map, int fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
|
int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **out_map)
|
||||||
void **out_map)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* size and out_size are intentionally of different types.
|
* size and out_size are intentionally of different types.
|
||||||
@@ -83,7 +85,7 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
|
|||||||
off64_t size;
|
off64_t size;
|
||||||
|
|
||||||
/* O_DIRECT should not be used with mmap() */
|
/* O_DIRECT should not be used with mmap() */
|
||||||
*out_fd = open(filename, O_RDWR | O_NOATIME);
|
*out_fd = open(filename, O_RDWR | O_SYNC );
|
||||||
|
|
||||||
if (*out_fd < 1) {
|
if (*out_fd < 1) {
|
||||||
warn("open(%s) failed: does it exist?", filename);
|
warn("open(%s) failed: does it exist?", filename);
|
||||||
@@ -95,15 +97,6 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
|
|||||||
warn("lseek64() failed");
|
warn("lseek64() failed");
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If discs are not in multiples of 512, then odd things happen,
|
|
||||||
* resulting in reads/writes past the ends of files.
|
|
||||||
*/
|
|
||||||
if (size != (size & (~0x1ff))) {
|
|
||||||
warn("file does not fit into 512-byte sectors; the end of the file will be ignored.");
|
|
||||||
size &= ~0x1ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_size) {
|
if (out_size) {
|
||||||
*out_size = size;
|
*out_size = size;
|
||||||
}
|
}
|
||||||
@@ -115,9 +108,9 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
|
|||||||
warn("mmap64() failed");
|
warn("mmap64() failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd,
|
debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd, *out_map);
|
||||||
*out_map);
|
}
|
||||||
} else {
|
else {
|
||||||
debug("opened %s size %ld on fd %d", filename, size, *out_fd);
|
debug("opened %s size %ld on fd %d", filename, size, *out_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,8 +141,7 @@ int readloop(int filedes, void *buffer, size_t size)
|
|||||||
ssize_t result = read(filedes, buffer+readden, size-readden);
|
ssize_t result = read(filedes, buffer+readden, size-readden);
|
||||||
|
|
||||||
if ( result == 0 /* EOF */ ) {
|
if ( result == 0 /* EOF */ ) {
|
||||||
warn("end-of-file detected while reading after %i bytes",
|
warn( "end-of-file detected while reading after %i bytes", readden );
|
||||||
readden);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,9 +161,7 @@ int sendfileloop(int out_fd, int in_fd, off64_t * offset, size_t count)
|
|||||||
size_t sent=0;
|
size_t sent=0;
|
||||||
while (sent < count) {
|
while (sent < count) {
|
||||||
ssize_t result = sendfile64(out_fd, in_fd, offset, count-sent);
|
ssize_t result = sendfile64(out_fd, in_fd, offset, count-sent);
|
||||||
debug
|
debug("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld", out_fd, in_fd, offset, count-sent, result);
|
||||||
("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld",
|
|
||||||
out_fd, in_fd, offset, count - sent, result);
|
|
||||||
|
|
||||||
if (result == -1) {
|
if (result == -1) {
|
||||||
debug( "%s (%i) calling sendfile64()", strerror(errno), errno );
|
debug( "%s (%i) calling sendfile64()", strerror(errno), errno );
|
||||||
@@ -185,8 +175,7 @@ int sendfileloop(int out_fd, int in_fd, off64_t * offset, size_t count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
|
ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2)
|
||||||
loff_t * off_out, size_t len, unsigned int flags2)
|
|
||||||
{
|
{
|
||||||
const unsigned int flags = SPLICE_F_MORE|SPLICE_F_MOVE|flags2;
|
const unsigned int flags = SPLICE_F_MORE|SPLICE_F_MOVE|flags2;
|
||||||
size_t spliced=0;
|
size_t spliced=0;
|
||||||
@@ -194,13 +183,13 @@ ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
|
|||||||
//debug("spliceloop(%d, %ld, %d, %ld, %ld)", fd_in, off_in ? *off_in : 0, fd_out, off_out ? *off_out : 0, len);
|
//debug("spliceloop(%d, %ld, %d, %ld, %ld)", fd_in, off_in ? *off_in : 0, fd_out, off_out ? *off_out : 0, len);
|
||||||
|
|
||||||
while (spliced < len) {
|
while (spliced < len) {
|
||||||
ssize_t result =
|
ssize_t result = splice(fd_in, off_in, fd_out, off_out, len, flags);
|
||||||
splice(fd_in, off_in, fd_out, off_out, len, flags);
|
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
//debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len);
|
//debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len);
|
||||||
if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK) ) {
|
if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK) ) {
|
||||||
return spliced;
|
return spliced;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -224,19 +213,13 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len)
|
|||||||
|
|
||||||
while (spliced < len) {
|
while (spliced < len) {
|
||||||
ssize_t run = len-spliced;
|
ssize_t run = len-spliced;
|
||||||
ssize_t s2, s1 =
|
ssize_t s2, s1 = spliceloop(fd_in, NULL, pipefd[1], NULL, run, SPLICE_F_NONBLOCK);
|
||||||
spliceloop(fd_in, NULL, pipefd[1], NULL, run,
|
|
||||||
SPLICE_F_NONBLOCK);
|
|
||||||
/*if (run > 65535)
|
/*if (run > 65535)
|
||||||
run = 65535;*/
|
run = 65535;*/
|
||||||
if (s1 < 0) {
|
if (s1 < 0) { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0);
|
s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0);
|
||||||
if (s2 < 0) {
|
if (s2 < 0) { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
spliced += s2;
|
spliced += s2;
|
||||||
}
|
}
|
||||||
close(pipefd[0]);
|
close(pipefd[0]);
|
||||||
@@ -258,9 +241,7 @@ int read_until_newline(int fd, char *buf, int bufsize)
|
|||||||
|
|
||||||
for (cur=0; cur < bufsize; cur++) {
|
for (cur=0; cur < bufsize; cur++) {
|
||||||
int result = read(fd, buf+cur, 1);
|
int result = read(fd, buf+cur, 1);
|
||||||
if (result <= 0) {
|
if (result <= 0) { return -1; }
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (buf[cur] == 10) {
|
if (buf[cur] == 10) {
|
||||||
buf[cur] = '\0';
|
buf[cur] = '\0';
|
||||||
break;
|
break;
|
||||||
@@ -285,9 +266,7 @@ int read_lines_until_blankline(int fd, int max_line_length, char ***lines)
|
|||||||
* -1 for an eof
|
* -1 for an eof
|
||||||
* -1 for a read error
|
* -1 for a read error
|
||||||
*/
|
*/
|
||||||
if (readden <= 1) {
|
if (readden <= 1) { return lines_count; }
|
||||||
return lines_count;
|
|
||||||
}
|
|
||||||
*lines = xrealloc(*lines, (lines_count+1) * sizeof(char*));
|
*lines = xrealloc(*lines, (lines_count+1) * sizeof(char*));
|
||||||
(*lines)[lines_count] = strdup(line);
|
(*lines)[lines_count] = strdup(line);
|
||||||
if ((*lines)[lines_count][0] == 0) {
|
if ((*lines)[lines_count][0] == 0) {
|
||||||
@@ -325,8 +304,7 @@ ssize_t iobuf_read(int fd, struct iobuf * iobuf, size_t default_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
left = iobuf->size - iobuf->needle;
|
left = iobuf->size - iobuf->needle;
|
||||||
debug("Reading %" PRIu32 " of %" PRIu32 " bytes from fd %i", left,
|
debug( "Reading %"PRIu32" of %"PRIu32" bytes from fd %i", left, iobuf->size, fd );
|
||||||
iobuf->size, fd);
|
|
||||||
|
|
||||||
count = read( fd, iobuf->buf + iobuf->needle, left );
|
count = read( fd, iobuf->buf + iobuf->needle, left );
|
||||||
|
|
||||||
@@ -354,8 +332,7 @@ ssize_t iobuf_write(int fd, struct iobuf * iobuf)
|
|||||||
size_t left = iobuf->size - iobuf->needle;
|
size_t left = iobuf->size - iobuf->needle;
|
||||||
ssize_t count;
|
ssize_t count;
|
||||||
|
|
||||||
debug("Writing %" PRIu32 " of %" PRIu32 " bytes to fd %i", left,
|
debug( "Writing %"PRIu32" of %"PRIu32" bytes to fd %i", left, iobuf->size, fd );
|
||||||
iobuf->size, fd);
|
|
||||||
count = write( fd, iobuf->buf + iobuf->needle, left );
|
count = write( fd, iobuf->buf + iobuf->needle, left );
|
||||||
|
|
||||||
if ( count >= 0 ) {
|
if ( count >= 0 ) {
|
||||||
|
@@ -38,8 +38,7 @@ int readloop(int filedes, void *buffer, size_t size);
|
|||||||
int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count);
|
int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count);
|
||||||
|
|
||||||
/** Repeat a splice() operation until we have 'len' bytes. */
|
/** Repeat a splice() operation until we have 'len' bytes. */
|
||||||
ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
|
ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2);
|
||||||
loff_t * off_out, size_t len, unsigned int flags2);
|
|
||||||
|
|
||||||
/** Copy ''len'' bytes from ''fd_in'' to ''fd_out'' by creating a temporary
|
/** Copy ''len'' bytes from ''fd_in'' to ''fd_out'' by creating a temporary
|
||||||
* pipe and using the Linux splice call repeatedly until it has transferred
|
* pipe and using the Linux splice call repeatedly until it has transferred
|
||||||
@@ -66,8 +65,7 @@ int read_lines_until_blankline(int fd, int max_line_length, char ***lines);
|
|||||||
* ''out_size'' and the address of the mmap in ''out_map''. If anything goes
|
* ''out_size'' and the address of the mmap in ''out_map''. If anything goes
|
||||||
* wrong, returns -1 setting errno, otherwise 0.
|
* wrong, returns -1 setting errno, otherwise 0.
|
||||||
*/
|
*/
|
||||||
int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
|
int open_and_mmap( const char* filename, int* out_fd, uint64_t* out_size, void **out_map);
|
||||||
void **out_map);
|
|
||||||
|
|
||||||
|
|
||||||
/** Check to see whether the given file descriptor is closed.
|
/** Check to see whether the given file descriptor is closed.
|
||||||
@@ -75,3 +73,4 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
|
|||||||
int fd_is_closed( int fd_in );
|
int fd_is_closed( int fd_in );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -94,3 +94,4 @@ void mode(char *mode, int argc, char **argv);
|
|||||||
char * help_help_text;
|
char * help_help_text;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -13,7 +13,6 @@ void nbd_r2h_init(struct nbd_init_raw *from, struct nbd_init *to)
|
|||||||
memcpy( to->passwd, from->passwd, 8 );
|
memcpy( to->passwd, from->passwd, 8 );
|
||||||
to->magic = be64toh( from->magic );
|
to->magic = be64toh( from->magic );
|
||||||
to->size = be64toh( from->size );
|
to->size = be64toh( from->size );
|
||||||
to->flags = be32toh(from->flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbd_h2r_init( struct nbd_init * from, struct nbd_init_raw * to)
|
void nbd_h2r_init( struct nbd_init * from, struct nbd_init_raw * to)
|
||||||
@@ -21,41 +20,39 @@ void nbd_h2r_init(struct nbd_init *from, struct nbd_init_raw *to)
|
|||||||
memcpy( to->passwd, from->passwd, 8 );
|
memcpy( to->passwd, from->passwd, 8 );
|
||||||
to->magic = htobe64( from->magic );
|
to->magic = htobe64( from->magic );
|
||||||
to->size = htobe64( from->size );
|
to->size = htobe64( from->size );
|
||||||
to->flags = htobe32(from->flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 )
|
||||||
{
|
|
||||||
to->magic = be32toh(from->magic);
|
|
||||||
to->flags = be16toh(from->flags);
|
|
||||||
to->type = be16toh(from->type);
|
|
||||||
to->handle.w = from->handle.w;
|
|
||||||
to->from = be64toh(from->from);
|
|
||||||
to->len = be32toh(from->len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nbd_h2r_request(struct nbd_request *from, struct nbd_request_raw *to)
|
|
||||||
{
|
{
|
||||||
to->magic = htobe32( from->magic );
|
to->magic = htobe32( from->magic );
|
||||||
to->flags = htobe16(from->flags);
|
to->type = htobe32( from->type );
|
||||||
to->type = htobe16(from->type);
|
|
||||||
to->handle.w = from->handle.w;
|
to->handle.w = from->handle.w;
|
||||||
to->from = htobe64( from->from );
|
to->from = htobe64( from->from );
|
||||||
to->len = htobe32( from->len );
|
to->len = htobe32( from->len );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to )
|
||||||
|
{
|
||||||
|
to->magic = be32toh( from->magic );
|
||||||
|
to->type = be32toh( from->type );
|
||||||
|
to->handle.w = from->handle.w;
|
||||||
|
to->from = be64toh( from->from );
|
||||||
|
to->len = be32toh( from->len );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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 )
|
||||||
|
{
|
||||||
|
to->magic = htobe32( from->magic );
|
||||||
|
to->error = htobe32( from->error );
|
||||||
|
to->handle.w = from->handle.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to )
|
||||||
{
|
{
|
||||||
to->magic = be32toh( from->magic );
|
to->magic = be32toh( from->magic );
|
||||||
to->error = be32toh( from->error );
|
to->error = be32toh( from->error );
|
||||||
to->handle.w = from->handle.w;
|
to->handle.w = from->handle.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbd_h2r_reply(struct nbd_reply *from, struct nbd_reply_raw *to)
|
|
||||||
{
|
|
||||||
to->magic = htobe32(from->magic);
|
|
||||||
to->error = htobe32(from->error);
|
|
||||||
to->handle.w = from->handle.w;
|
|
||||||
}
|
|
||||||
|
@@ -7,38 +7,15 @@
|
|||||||
#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
|
||||||
#define REQUEST_FLUSH 3
|
|
||||||
|
|
||||||
/* values for transmission flag field */
|
/* The top 2 bytes of the type field are overloaded and can contain flags */
|
||||||
#define FLAG_HAS_FLAGS (1 << 0) /* Flags are there */
|
#define REQUEST_MASK 0x0000ffff
|
||||||
#define FLAG_SEND_FLUSH (1 << 2) /* Send FLUSH */
|
|
||||||
#define FLAG_SEND_FUA (1 << 3) /* Send FUA (Force Unit Access) */
|
|
||||||
|
|
||||||
/* values for command flag field */
|
|
||||||
#define CMD_FLAG_FUA (1 << 0)
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Not yet implemented by flexnbd */
|
|
||||||
#define REQUEST_TRIM 4
|
|
||||||
#define REQUEST_WRITE_ZEROES 6
|
|
||||||
|
|
||||||
#define FLAG_READ_ONLY (1 << 1) /* Device is read-only */
|
|
||||||
#define FLAG_ROTATIONAL (1 << 4) /* Use elevator algorithm - rotational media */
|
|
||||||
#define FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */
|
|
||||||
#define FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send NBD_CMD_WRITE_ZEROES */
|
|
||||||
#define FLAG_CAN_MULTI_CONN (1 << 8) /* multiple connections are okay */
|
|
||||||
|
|
||||||
#define CMD_FLAG_NO_HOLE (1 << 1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* 32 MiB is the maximum qemu will send you:
|
/* 1MiB is the de-facto standard for maximum size of header + data */
|
||||||
* https://github.com/qemu/qemu/blob/v2.11.0/include/block/nbd.h#L183
|
|
||||||
*/
|
|
||||||
#define NBD_MAX_SIZE ( 32 * 1024 * 1024 )
|
#define NBD_MAX_SIZE ( 32 * 1024 * 1024 )
|
||||||
|
|
||||||
#define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) )
|
#define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) )
|
||||||
@@ -61,14 +38,12 @@ struct nbd_init_raw {
|
|||||||
char passwd[8];
|
char passwd[8];
|
||||||
__be64 magic;
|
__be64 magic;
|
||||||
__be64 size;
|
__be64 size;
|
||||||
__be32 flags;
|
char reserved[128];
|
||||||
char reserved[124];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nbd_request_raw {
|
struct nbd_request_raw {
|
||||||
__be32 magic;
|
__be32 magic;
|
||||||
__be16 flags;
|
__be32 type; /* == READ || == WRITE */
|
||||||
__be16 type; /* == READ || == WRITE || == FLUSH */
|
|
||||||
nbd_handle_t handle;
|
nbd_handle_t handle;
|
||||||
__be64 from;
|
__be64 from;
|
||||||
__be32 len;
|
__be32 len;
|
||||||
@@ -80,18 +55,18 @@ struct nbd_reply_raw {
|
|||||||
nbd_handle_t handle; /* handle you got from request */
|
nbd_handle_t handle; /* handle you got from request */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct nbd_init {
|
struct nbd_init {
|
||||||
char passwd[8];
|
char passwd[8];
|
||||||
uint64_t magic;
|
uint64_t magic;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
uint32_t flags;
|
char reserved[128];
|
||||||
char reserved[124];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nbd_request {
|
struct nbd_request {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint16_t flags;
|
uint32_t type; /* == READ || == WRITE || == DISCONNECT */
|
||||||
uint16_t type; /* == READ || == WRITE || == DISCONNECT || == FLUSH */
|
|
||||||
nbd_handle_t handle;
|
nbd_handle_t handle;
|
||||||
uint64_t from;
|
uint64_t from;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
@@ -112,3 +87,4 @@ void nbd_h2r_request(struct nbd_request *from, struct nbd_request_raw *to);
|
|||||||
void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to );
|
void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -22,9 +22,7 @@ int parse_ip_to_sockaddr(struct sockaddr *out, char *src)
|
|||||||
/* allow user to start with [ and end with any other invalid char */
|
/* allow user to start with [ and end with any other invalid char */
|
||||||
{
|
{
|
||||||
int i=0, j=0;
|
int i=0, j=0;
|
||||||
if (src[i] == '[') {
|
if (src[i] == '[') { i++; }
|
||||||
i++;
|
|
||||||
}
|
|
||||||
for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++) {
|
for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++) {
|
||||||
temp[j++] = src[i];
|
temp[j++] = src[i];
|
||||||
}
|
}
|
||||||
@@ -74,7 +72,8 @@ int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
|
|||||||
if (max == 0) {
|
if (max == 0) {
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
list = xmalloc(max * sizeof(struct ip_and_mask));
|
list = xmalloc(max * sizeof(struct ip_and_mask));
|
||||||
*out = (struct ip_and_mask (*)[])list;
|
*out = (struct ip_and_mask (*)[])list;
|
||||||
debug("acl alloc: %p", *out);
|
debug("acl alloc: %p", *out);
|
||||||
@@ -89,14 +88,16 @@ int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; entries[i][j] && entries[i][j] != '/'; j++); // increment j!
|
for (j=0; entries[i][j] && entries[i][j] != '/'; j++)
|
||||||
|
; // increment j!
|
||||||
|
|
||||||
if (entries[i][j] == '/') {
|
if (entries[i][j] == '/') {
|
||||||
outentry->mask = atoi(entries[i]+j+1);
|
outentry->mask = atoi(entries[i]+j+1);
|
||||||
if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) {
|
if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
outentry->mask = MAX_MASK_BITS;
|
outentry->mask = MAX_MASK_BITS;
|
||||||
}
|
}
|
||||||
# undef MAX_MASK_BITS
|
# undef MAX_MASK_BITS
|
||||||
@@ -123,3 +124,4 @@ void parse_port(char *s_port, struct sockaddr_in *out)
|
|||||||
}
|
}
|
||||||
out->sin_port = htobe16( raw_port );
|
out->sin_port = htobe16( raw_port );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,3 +26,4 @@ int parse_acl(struct ip_and_mask (**out)[], int max, char **entries);
|
|||||||
void parse_port( char *s_port, struct sockaddr_in *out );
|
void parse_port( char *s_port, struct sockaddr_in *out );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -10,9 +10,7 @@
|
|||||||
|
|
||||||
int socket_connect(struct sockaddr* to, struct sockaddr* from)
|
int socket_connect(struct sockaddr* to, struct sockaddr* from)
|
||||||
{
|
{
|
||||||
int fd =
|
int fd = socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0);
|
||||||
socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM,
|
|
||||||
0);
|
|
||||||
if( fd < 0 ){
|
if( fd < 0 ){
|
||||||
warn( "Couldn't create client socket");
|
warn( "Couldn't create client socket");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -43,8 +41,7 @@ int socket_connect(struct sockaddr *to, struct sockaddr *from)
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nbd_check_hello(struct nbd_init_raw *init_raw, uint64_t * out_size,
|
int nbd_check_hello( struct nbd_init_raw* init_raw, uint64_t* out_size )
|
||||||
uint32_t * out_flags)
|
|
||||||
{
|
{
|
||||||
if ( strncmp( init_raw->passwd, INIT_PASSWD, 8 ) != 0 ) {
|
if ( strncmp( init_raw->passwd, INIT_PASSWD, 8 ) != 0 ) {
|
||||||
warn( "wrong passwd" );
|
warn( "wrong passwd" );
|
||||||
@@ -59,18 +56,13 @@ int nbd_check_hello(struct nbd_init_raw *init_raw, uint64_t * out_size,
|
|||||||
*out_size = be64toh( init_raw->size );
|
*out_size = be64toh( init_raw->size );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NULL != out_flags) {
|
|
||||||
*out_flags = be32toh(init_raw->flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int socket_nbd_read_hello(int fd, uint64_t * out_size,
|
int socket_nbd_read_hello( int fd, uint64_t* out_size )
|
||||||
uint32_t * out_flags)
|
|
||||||
{
|
{
|
||||||
struct nbd_init_raw init_raw;
|
struct nbd_init_raw init_raw;
|
||||||
|
|
||||||
@@ -80,18 +72,16 @@ int socket_nbd_read_hello(int fd, uint64_t * out_size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nbd_check_hello(&init_raw, out_size, out_flags);
|
return nbd_check_hello( &init_raw, out_size );
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbd_hello_to_buf(struct nbd_init_raw *buf, off64_t out_size,
|
void nbd_hello_to_buf( struct nbd_init_raw *buf, off64_t out_size )
|
||||||
uint32_t out_flags)
|
|
||||||
{
|
{
|
||||||
struct nbd_init init;
|
struct nbd_init init;
|
||||||
|
|
||||||
memcpy( &init.passwd, INIT_PASSWD, 8 );
|
memcpy( &init.passwd, INIT_PASSWD, 8 );
|
||||||
init.magic = INIT_MAGIC;
|
init.magic = INIT_MAGIC;
|
||||||
init.size = out_size;
|
init.size = out_size;
|
||||||
init.flags = out_flags;
|
|
||||||
|
|
||||||
memset( buf, 0, sizeof( struct nbd_init_raw ) ); // ensure reserved is 0s
|
memset( buf, 0, sizeof( struct nbd_init_raw ) ); // ensure reserved is 0s
|
||||||
nbd_h2r_init( &init, buf );
|
nbd_h2r_init( &init, buf );
|
||||||
@@ -99,10 +89,10 @@ void nbd_hello_to_buf(struct nbd_init_raw *buf, off64_t out_size,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int socket_nbd_write_hello(int fd, off64_t out_size, uint32_t out_flags)
|
int socket_nbd_write_hello(int fd, off64_t out_size)
|
||||||
{
|
{
|
||||||
struct nbd_init_raw init_raw;
|
struct nbd_init_raw init_raw;
|
||||||
nbd_hello_to_buf(&init_raw, out_size, out_flags);
|
nbd_hello_to_buf( &init_raw, out_size );
|
||||||
|
|
||||||
if ( 0 > writeloop( fd, &init_raw, sizeof( init_raw ) ) ) {
|
if ( 0 > writeloop( fd, &init_raw, sizeof( init_raw ) ) ) {
|
||||||
warn( SHOW_ERRNO( "failed to write hello to socket" ) );
|
warn( SHOW_ERRNO( "failed to write hello to socket" ) );
|
||||||
@@ -111,25 +101,20 @@ int socket_nbd_write_hello(int fd, off64_t out_size, uint32_t out_flags)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_request(struct nbd_request_raw *request_raw, uint16_t type,
|
void fill_request(struct nbd_request *request, int type, uint64_t from, uint32_t len)
|
||||||
uint16_t flags, uint64_t from, uint32_t len)
|
|
||||||
{
|
{
|
||||||
request_raw->magic = htobe32(REQUEST_MAGIC);
|
request->magic = htobe32(REQUEST_MAGIC);
|
||||||
request_raw->type = htobe16(type);
|
request->type = htobe32(type);
|
||||||
request_raw->flags = htobe16(flags);
|
request->handle.w = (((uint64_t)rand()) << 32) | ((uint64_t)rand());
|
||||||
request_raw->handle.w =
|
request->from = htobe64(from);
|
||||||
(((uint64_t) rand()) << 32) | ((uint64_t) rand());
|
request->len = htobe32(len);
|
||||||
request_raw->from = htobe64(from);
|
|
||||||
request_raw->len = htobe32(len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_reply(int fd, uint64_t request_raw_handle,
|
void read_reply(int fd, struct nbd_request *request, struct nbd_reply *reply)
|
||||||
struct nbd_reply *reply)
|
|
||||||
{
|
{
|
||||||
struct nbd_reply_raw reply_raw;
|
struct nbd_reply_raw reply_raw;
|
||||||
|
|
||||||
ERROR_IF_NEGATIVE(readloop
|
ERROR_IF_NEGATIVE(readloop(fd, &reply_raw, sizeof(struct nbd_reply_raw)),
|
||||||
(fd, &reply_raw, sizeof(struct nbd_reply_raw)),
|
|
||||||
"Couldn't read reply");
|
"Couldn't read reply");
|
||||||
|
|
||||||
nbd_r2h_reply( &reply_raw, reply );
|
nbd_r2h_reply( &reply_raw, reply );
|
||||||
@@ -140,7 +125,7 @@ void read_reply(int fd, uint64_t request_raw_handle,
|
|||||||
if (reply->error != 0) {
|
if (reply->error != 0) {
|
||||||
error("Server replied with error %d", reply->error);
|
error("Server replied with error %d", reply->error);
|
||||||
}
|
}
|
||||||
if (request_raw_handle != reply_raw.handle.w) {
|
if (request->handle.w != reply->handle.w) {
|
||||||
error("Did not reply with correct handle");
|
error("Did not reply with correct handle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,77 +139,81 @@ void wait_for_data(int fd, int timeout_secs)
|
|||||||
FD_ZERO( &fds );
|
FD_ZERO( &fds );
|
||||||
FD_SET( fd, &fds );
|
FD_SET( fd, &fds );
|
||||||
|
|
||||||
selected =
|
selected = sock_try_select(
|
||||||
sock_try_select(FD_SETSIZE, &fds, NULL, NULL,
|
FD_SETSIZE, &fds, NULL, NULL, timeout_secs >=0 ? &tv : NULL
|
||||||
timeout_secs >= 0 ? &tv : NULL);
|
);
|
||||||
|
|
||||||
FATAL_IF( -1 == selected, "Select failed" );
|
FATAL_IF( -1 == selected, "Select failed" );
|
||||||
ERROR_IF( 0 == selected, "Timed out waiting for reply" );
|
ERROR_IF( 0 == selected, "Timed out waiting for reply" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd,
|
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs)
|
||||||
void *out_buf, int timeout_secs)
|
|
||||||
{
|
{
|
||||||
struct nbd_request_raw request_raw;
|
struct nbd_request request;
|
||||||
struct nbd_reply reply;
|
struct nbd_reply reply;
|
||||||
|
|
||||||
fill_request(&request_raw, REQUEST_READ, 0, from, len);
|
fill_request(&request, REQUEST_READ, from, len);
|
||||||
FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
|
FATAL_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)),
|
||||||
"Couldn't write request");
|
"Couldn't write request");
|
||||||
|
|
||||||
|
|
||||||
wait_for_data( fd, timeout_secs );
|
wait_for_data( fd, timeout_secs );
|
||||||
read_reply(fd, request_raw.handle.w, &reply);
|
read_reply(fd, &request, &reply);
|
||||||
|
|
||||||
if (out_buf) {
|
if (out_buf) {
|
||||||
FATAL_IF_NEGATIVE(readloop(fd, out_buf, len), "Read failed");
|
FATAL_IF_NEGATIVE(readloop(fd, out_buf, len),
|
||||||
} else {
|
"Read failed");
|
||||||
FATAL_IF_NEGATIVE(splice_via_pipe_loop(fd, out_fd, len),
|
}
|
||||||
"Splice failed");
|
else {
|
||||||
|
FATAL_IF_NEGATIVE(
|
||||||
|
splice_via_pipe_loop(fd, out_fd, len),
|
||||||
|
"Splice failed"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd,
|
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd, void* in_buf, int timeout_secs)
|
||||||
void *in_buf, int timeout_secs)
|
|
||||||
{
|
{
|
||||||
struct nbd_request_raw request_raw;
|
struct nbd_request request;
|
||||||
struct nbd_reply reply;
|
struct nbd_reply reply;
|
||||||
|
|
||||||
fill_request(&request_raw, REQUEST_WRITE, 0, from, len);
|
fill_request(&request, REQUEST_WRITE, from, len);
|
||||||
ERROR_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
|
ERROR_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)),
|
||||||
"Couldn't write request");
|
"Couldn't write request");
|
||||||
|
|
||||||
if (in_buf) {
|
if (in_buf) {
|
||||||
ERROR_IF_NEGATIVE(writeloop(fd, in_buf, len), "Write failed");
|
ERROR_IF_NEGATIVE(writeloop(fd, in_buf, len),
|
||||||
} else {
|
"Write failed");
|
||||||
ERROR_IF_NEGATIVE(splice_via_pipe_loop(in_fd, fd, len),
|
}
|
||||||
"Splice failed");
|
else {
|
||||||
|
ERROR_IF_NEGATIVE(
|
||||||
|
splice_via_pipe_loop(in_fd, fd, len),
|
||||||
|
"Splice failed"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_for_data( fd, timeout_secs );
|
wait_for_data( fd, timeout_secs );
|
||||||
read_reply(fd, request_raw.handle.w, &reply);
|
read_reply(fd, &request, &reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int socket_nbd_disconnect( int fd )
|
int socket_nbd_disconnect( int fd )
|
||||||
{
|
{
|
||||||
int success = 1;
|
int success = 1;
|
||||||
struct nbd_request_raw request_raw;
|
struct nbd_request request;
|
||||||
|
|
||||||
fill_request(&request_raw, REQUEST_DISCONNECT, 0, 0, 0);
|
fill_request( &request, REQUEST_DISCONNECT, 0, 0 );
|
||||||
/* FIXME: This shouldn't be a FATAL error. We should just drop
|
/* FIXME: This shouldn't be a FATAL error. We should just drop
|
||||||
* the mirror without affecting the main server.
|
* the mirror without affecting the main server.
|
||||||
*/
|
*/
|
||||||
FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
|
FATAL_IF_NEGATIVE( writeloop( fd, &request, sizeof( request ) ),
|
||||||
"Failed to write the disconnect request." );
|
"Failed to write the disconnect request." );
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_RANGE(error_type) { \
|
#define CHECK_RANGE(error_type) { \
|
||||||
uint64_t size;\
|
uint64_t size;\
|
||||||
uint32_t flags;\
|
int success = socket_nbd_read_hello(params->client, &size); \
|
||||||
int success = socket_nbd_read_hello(params->client, &size, &flags); \
|
|
||||||
if ( success ) {\
|
if ( success ) {\
|
||||||
uint64_t endpoint = params->from + params->len; \
|
uint64_t endpoint = params->from + params->len; \
|
||||||
if (endpoint > size || \
|
if (endpoint > size || \
|
||||||
@@ -242,9 +231,7 @@ int socket_nbd_disconnect(int fd)
|
|||||||
|
|
||||||
void do_read(struct mode_readwrite_params* params)
|
void do_read(struct mode_readwrite_params* params)
|
||||||
{
|
{
|
||||||
params->client =
|
params->client = socket_connect(¶ms->connect_to.generic, ¶ms->connect_from.generic);
|
||||||
socket_connect(¶ms->connect_to.generic,
|
|
||||||
¶ms->connect_from.generic);
|
|
||||||
FATAL_IF_NEGATIVE( params->client, "Couldn't connect." );
|
FATAL_IF_NEGATIVE( params->client, "Couldn't connect." );
|
||||||
CHECK_RANGE("read");
|
CHECK_RANGE("read");
|
||||||
socket_nbd_read(params->client, params->from, params->len,
|
socket_nbd_read(params->client, params->from, params->len,
|
||||||
@@ -254,12 +241,11 @@ void do_read(struct mode_readwrite_params *params)
|
|||||||
|
|
||||||
void do_write(struct mode_readwrite_params* params)
|
void do_write(struct mode_readwrite_params* params)
|
||||||
{
|
{
|
||||||
params->client =
|
params->client = socket_connect(¶ms->connect_to.generic, ¶ms->connect_from.generic);
|
||||||
socket_connect(¶ms->connect_to.generic,
|
|
||||||
¶ms->connect_from.generic);
|
|
||||||
FATAL_IF_NEGATIVE( params->client, "Couldn't connect." );
|
FATAL_IF_NEGATIVE( params->client, "Couldn't connect." );
|
||||||
CHECK_RANGE("write");
|
CHECK_RANGE("write");
|
||||||
socket_nbd_write(params->client, params->from, params->len,
|
socket_nbd_write(params->client, params->from, params->len,
|
||||||
params->data_fd, NULL, 10);
|
params->data_fd, NULL, 10);
|
||||||
close(params->client);
|
close(params->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,20 +7,17 @@
|
|||||||
#include "nbdtypes.h"
|
#include "nbdtypes.h"
|
||||||
|
|
||||||
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, uint64_t * size, uint32_t * flags);
|
int socket_nbd_read_hello(int fd, uint64_t* size);
|
||||||
int socket_nbd_write_hello(int fd, uint64_t size, uint32_t flags);
|
int socket_nbd_write_hello(int fd, uint64_t size);
|
||||||
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd,
|
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs);
|
||||||
void *out_buf, int timeout_secs);
|
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs);
|
||||||
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int out_fd,
|
|
||||||
void *out_buf, int timeout_secs);
|
|
||||||
int socket_nbd_disconnect( int fd );
|
int socket_nbd_disconnect( int fd );
|
||||||
|
|
||||||
/* as you can see, we're slowly accumulating code that should really be in an
|
/* as you can see, we're slowly accumulating code that should really be in an
|
||||||
* NBD library */
|
* NBD library */
|
||||||
|
|
||||||
void nbd_hello_to_buf(struct nbd_init_raw *buf, uint64_t out_size,
|
void nbd_hello_to_buf( struct nbd_init_raw* buf, uint64_t out_size );
|
||||||
uint32_t out_flags);
|
int nbd_check_hello( struct nbd_init_raw* init_raw, uint64_t* out_size );
|
||||||
int nbd_check_hello(struct nbd_init_raw *init_raw, uint64_t * out_size,
|
|
||||||
uint32_t * out_flags);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -24,8 +24,7 @@ void print_response(const char *response)
|
|||||||
fprintf(out, "%s\n", response_text + 2);
|
fprintf(out, "%s\n", response_text + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_remote_command(char *command, char *socket_name, int argc,
|
void do_remote_command(char* command, char* socket_name, int argc, char** argv)
|
||||||
char **argv)
|
|
||||||
{
|
{
|
||||||
char newline=10;
|
char newline=10;
|
||||||
int i;
|
int i;
|
||||||
@@ -41,10 +40,10 @@ void do_remote_command(char *command, char *socket_name, int argc,
|
|||||||
address.sun_family = AF_UNIX;
|
address.sun_family = AF_UNIX;
|
||||||
strncpy(address.sun_path, socket_name, sizeof(address.sun_path));
|
strncpy(address.sun_path, socket_name, sizeof(address.sun_path));
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(connect
|
FATAL_IF_NEGATIVE(
|
||||||
(remote, (struct sockaddr *) &address,
|
connect(remote, (struct sockaddr*) &address, sizeof(address)),
|
||||||
sizeof(address)), "Couldn't connect to %s",
|
"Couldn't connect to %s", socket_name
|
||||||
socket_name);
|
);
|
||||||
|
|
||||||
write(remote, command, strlen(command));
|
write(remote, command, strlen(command));
|
||||||
write(remote, &newline, 1);
|
write(remote, &newline, 1);
|
||||||
@@ -56,10 +55,13 @@ void do_remote_command(char *command, char *socket_name, int argc,
|
|||||||
}
|
}
|
||||||
write(remote, &newline, 1);
|
write(remote, &newline, 1);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(read_until_newline(remote, response, max_response),
|
FATAL_IF_NEGATIVE(
|
||||||
"Couldn't read response from %s", socket_name);
|
read_until_newline(remote, response, max_response),
|
||||||
|
"Couldn't read response from %s", socket_name
|
||||||
|
);
|
||||||
|
|
||||||
print_response( response );
|
print_response( response );
|
||||||
|
|
||||||
exit(atoi(response));
|
exit(atoi(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -52,9 +52,7 @@ struct self_pipe *self_pipe_create(void)
|
|||||||
struct self_pipe *sig = xmalloc( sizeof( struct self_pipe ) );
|
struct self_pipe *sig = xmalloc( sizeof( struct self_pipe ) );
|
||||||
int fds[2];
|
int fds[2];
|
||||||
|
|
||||||
if (NULL == sig) {
|
if ( NULL == sig ) { return NULL; }
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pipe( fds ) ) {
|
if ( pipe( fds ) ) {
|
||||||
free( sig );
|
free( sig );
|
||||||
@@ -62,8 +60,7 @@ struct self_pipe *self_pipe_create(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fcntl(fds[0], F_SETFL, O_NONBLOCK)
|
if ( fcntl( fds[0], F_SETFL, O_NONBLOCK ) || fcntl( fds[1], F_SETFL, O_NONBLOCK ) ) {
|
||||||
|| fcntl(fds[1], F_SETFL, O_NONBLOCK)) {
|
|
||||||
int fcntl_err = errno;
|
int fcntl_err = errno;
|
||||||
while( close( fds[0] ) == -1 && errno == EINTR );
|
while( close( fds[0] ) == -1 && errno == EINTR );
|
||||||
while( close( fds[1] ) == -1 && errno == EINTR );
|
while( close( fds[1] ) == -1 && errno == EINTR );
|
||||||
|
@@ -29,8 +29,7 @@ size_t sockaddr_size(const struct sockaddr * sa)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
|
const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size_t len )
|
||||||
size_t len)
|
|
||||||
{
|
{
|
||||||
NULLCHECK( sa );
|
NULLCHECK( sa );
|
||||||
NULLCHECK( dest );
|
NULLCHECK( dest );
|
||||||
@@ -66,8 +65,7 @@ const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
|
|||||||
|
|
||||||
int sock_set_reuseaddr( int fd, int optval )
|
int sock_set_reuseaddr( int fd, int optval )
|
||||||
{
|
{
|
||||||
return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval,
|
return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
|
||||||
sizeof(optval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_keepalive_params( int fd, int time, int intvl, int probes)
|
int sock_set_keepalive_params( int fd, int time, int intvl, int probes)
|
||||||
@@ -83,33 +81,28 @@ int sock_set_keepalive_params(int fd, int time, int intvl, int probes)
|
|||||||
|
|
||||||
int sock_set_keepalive( int fd, int optval )
|
int sock_set_keepalive( int fd, int optval )
|
||||||
{
|
{
|
||||||
return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval,
|
return setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) );
|
||||||
sizeof(optval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_tcp_keepidle( int fd, int optval )
|
int sock_set_tcp_keepidle( int fd, int optval )
|
||||||
{
|
{
|
||||||
return setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval,
|
return setsockopt( fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval) );
|
||||||
sizeof(optval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_tcp_keepintvl( int fd, int optval )
|
int sock_set_tcp_keepintvl( int fd, int optval )
|
||||||
{
|
{
|
||||||
return setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval,
|
return setsockopt( fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval) );
|
||||||
sizeof(optval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_tcp_keepcnt( int fd, int optval )
|
int sock_set_tcp_keepcnt( int fd, int optval )
|
||||||
{
|
{
|
||||||
return setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &optval,
|
return setsockopt( fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval) );
|
||||||
sizeof(optval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the tcp_nodelay option */
|
/* Set the tcp_nodelay option */
|
||||||
int sock_set_tcp_nodelay( int fd, int optval )
|
int sock_set_tcp_nodelay( int fd, int optval )
|
||||||
{
|
{
|
||||||
return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval,
|
return setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval) );
|
||||||
sizeof(optval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_set_tcp_cork( int fd, int optval )
|
int sock_set_tcp_cork( int fd, int optval )
|
||||||
@@ -147,7 +140,8 @@ int sock_try_bind(int fd, const struct sockaddr *sa)
|
|||||||
if ( 0 == bind_result ) {
|
if ( 0 == bind_result ) {
|
||||||
info( "Bound to %s", s_address );
|
info( "Bound to %s", s_address );
|
||||||
break;
|
break;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
warn( SHOW_ERRNO( "Couldn't bind to %s", s_address ) );
|
warn( SHOW_ERRNO( "Couldn't bind to %s", s_address ) );
|
||||||
|
|
||||||
switch ( errno ) {
|
switch ( errno ) {
|
||||||
@@ -183,8 +177,7 @@ int sock_try_bind(int fd, const struct sockaddr *sa)
|
|||||||
return bind_result;
|
return bind_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
|
int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
|
||||||
fd_set * exceptfds, struct timeval *timeout)
|
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
@@ -199,16 +192,14 @@ int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
|
int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait )
|
||||||
int wait)
|
|
||||||
{
|
{
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
struct timeval tv = { wait, 0 };
|
struct timeval tv = { wait, 0 };
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
if ( sock_set_nonblock( fd, 1 ) == -1 ) {
|
if ( sock_set_nonblock( fd, 1 ) == -1 ) {
|
||||||
warn(SHOW_ERRNO
|
warn( SHOW_ERRNO( "Failed to set socket non-blocking for connect()" ) );
|
||||||
("Failed to set socket non-blocking for connect()"));
|
|
||||||
return connect( fd, to, addrlen );
|
return connect( fd, to, addrlen );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,3 +284,4 @@ int sock_try_close(int fd)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,8 +12,7 @@ size_t sockaddr_size(const struct sockaddr *sa);
|
|||||||
/* Convert a sockaddr into an address. Like inet_ntop, it returns dest if
|
/* Convert a sockaddr into an address. Like inet_ntop, it returns dest if
|
||||||
* successful, NULL otherwise. In the latter case, dest will contain "???"
|
* successful, NULL otherwise. In the latter case, dest will contain "???"
|
||||||
*/
|
*/
|
||||||
const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
|
const char* sockaddr_address_string(const struct sockaddr* sa, char* dest, size_t len);
|
||||||
size_t len);
|
|
||||||
|
|
||||||
/* Configure TCP keepalive on a socket */
|
/* Configure TCP keepalive on a socket */
|
||||||
int sock_set_keepalive_params( int fd, int time, int intvl, int probes);
|
int sock_set_keepalive_params( int fd, int time, int intvl, int probes);
|
||||||
@@ -45,14 +44,13 @@ int sock_set_nonblock(int fd, int optval);
|
|||||||
int sock_try_bind(int fd, const struct sockaddr* sa);
|
int sock_try_bind(int fd, const struct sockaddr* sa);
|
||||||
|
|
||||||
/* Try to call select(), retrying EINTR */
|
/* Try to call select(), retrying EINTR */
|
||||||
int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
|
int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
|
||||||
fd_set * exceptfds, struct timeval *timeout);
|
|
||||||
|
|
||||||
/* Try to call connect(), timing out after wait seconds */
|
/* Try to call connect(), timing out after wait seconds */
|
||||||
int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
|
int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait );
|
||||||
int wait);
|
|
||||||
|
|
||||||
/* Try to call close(), retrying EINTR */
|
/* Try to call close(), retrying EINTR */
|
||||||
int sock_try_close( int fd );
|
int sock_try_close( int fd );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -26,12 +26,10 @@ void error_handler(int fatal)
|
|||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
longjmp(context->jmp, fatal ? 1 : 2 );
|
longjmp(context->jmp, fatal ? 1 : 2 );
|
||||||
} else {
|
|
||||||
if (fatal) {
|
|
||||||
abort();
|
|
||||||
} else {
|
|
||||||
pthread_exit((void *) 1);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if ( fatal ) { abort(); }
|
||||||
|
else { pthread_exit((void*) 1); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +43,7 @@ void exit_err(const char *msg)
|
|||||||
|
|
||||||
void mylog(int line_level, const char* format, ...)
|
void mylog(int line_level, const char* format, ...)
|
||||||
{
|
{
|
||||||
if (line_level < log_level) {
|
if (line_level < log_level) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
|
|
||||||
@@ -61,7 +57,8 @@ uint64_t monotonic_time_ms()
|
|||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
uint64_t seconds_ms, nanoseconds_ms;
|
uint64_t seconds_ms, nanoseconds_ms;
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(clock_gettime(CLOCK_MONOTONIC, &ts),
|
FATAL_IF_NEGATIVE(
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts),
|
||||||
SHOW_ERRNO( "clock_gettime failed" )
|
SHOW_ERRNO( "clock_gettime failed" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -78,8 +75,7 @@ uint64_t monotonic_time_ms()
|
|||||||
void* xrealloc(void* ptr, size_t size)
|
void* xrealloc(void* ptr, size_t size)
|
||||||
{
|
{
|
||||||
void* p = realloc(ptr, size);
|
void* p = realloc(ptr, size);
|
||||||
FATAL_IF_NULL(p, "couldn't xrealloc %d bytes",
|
FATAL_IF_NULL(p, "couldn't xrealloc %d bytes", ptr ? "realloc" : "malloc", size);
|
||||||
ptr ? "realloc" : "malloc", size);
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,3 +85,4 @@ void *xmalloc(size_t size)
|
|||||||
memset(p, 0, size);
|
memset(p, 0, size);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,8 +13,7 @@
|
|||||||
void* xrealloc(void* ptr, size_t size);
|
void* xrealloc(void* ptr, size_t size);
|
||||||
void* xmalloc(size_t size);
|
void* xmalloc(size_t size);
|
||||||
|
|
||||||
typedef void (cleanup_handler) (void * /* context */ ,
|
typedef void (cleanup_handler)(void* /* context */, int /* is fatal? */);
|
||||||
int /* is fatal? */ );
|
|
||||||
|
|
||||||
/* set from 0 - 5 depending on what level of verbosity you want */
|
/* set from 0 - 5 depending on what level of verbosity you want */
|
||||||
extern int log_level;
|
extern int log_level;
|
||||||
@@ -163,3 +162,4 @@ uint64_t monotonic_time_ms(void);
|
|||||||
#define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); }
|
#define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -19,3 +19,4 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@ static struct option proxy_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
|
static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char proxy_help_text[] =
|
static char proxy_help_text[] =
|
||||||
"Usage: flexnbd-proxy <options>\n\n"
|
"Usage: flexnbd-proxy <options>\n\n"
|
||||||
@@ -27,24 +26,24 @@ static char proxy_help_text[] =
|
|||||||
"We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n"
|
"We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n"
|
||||||
HELP_LINE
|
HELP_LINE
|
||||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n"
|
"\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n"
|
||||||
"\t--" OPT_PORT
|
"\t--" OPT_PORT ",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
|
||||||
",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
|
|
||||||
"\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
|
"\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
|
||||||
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n"
|
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n"
|
||||||
"\t--" OPT_BIND
|
"\t--" OPT_BIND ",-b <ADDR>\tThe address we connect from, as a proxy.\n"
|
||||||
",-b <ADDR>\tThe address we connect from, as a proxy.\n" "\t--"
|
"\t--" OPT_CACHE ",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n"
|
||||||
OPT_CACHE
|
QUIET_LINE
|
||||||
",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n"
|
VERBOSE_LINE;
|
||||||
QUIET_LINE VERBOSE_LINE;
|
|
||||||
|
|
||||||
static char proxy_default_cache_size[] = "4096";
|
static char proxy_default_cache_size[] = "4096";
|
||||||
|
|
||||||
void read_proxy_param(int c,
|
void read_proxy_param(
|
||||||
|
int c,
|
||||||
char **downstream_addr,
|
char **downstream_addr,
|
||||||
char **downstream_port,
|
char **downstream_port,
|
||||||
char **upstream_addr,
|
char **upstream_addr,
|
||||||
char **upstream_port,
|
char **upstream_port,
|
||||||
char **bind_addr, char **cache_bytes)
|
char **bind_addr,
|
||||||
|
char **cache_bytes)
|
||||||
{
|
{
|
||||||
switch( c ) {
|
switch( c ) {
|
||||||
case 'h' :
|
case 'h' :
|
||||||
@@ -117,31 +116,34 @@ int main(int argc, char *argv[])
|
|||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long(argc, argv, proxy_short_options, proxy_options,
|
c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL );
|
||||||
NULL);
|
if ( -1 == c ) { break; }
|
||||||
if (-1 == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
read_proxy_param( c,
|
read_proxy_param( c,
|
||||||
&downstream_addr,
|
&downstream_addr,
|
||||||
&downstream_port,
|
&downstream_port,
|
||||||
&upstream_addr,
|
&upstream_addr,
|
||||||
&upstream_port, &bind_addr, &cache_bytes);
|
&upstream_port,
|
||||||
|
&bind_addr,
|
||||||
|
&cache_bytes
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == downstream_addr ){
|
if ( NULL == downstream_addr ){
|
||||||
fprintf( stderr, "--addr is required.\n" );
|
fprintf( stderr, "--addr is required.\n" );
|
||||||
exit_err( proxy_help_text );
|
exit_err( proxy_help_text );
|
||||||
} else if ( NULL == upstream_addr || NULL == upstream_port ){
|
} else if ( NULL == upstream_addr || NULL == upstream_port ){
|
||||||
fprintf(stderr,
|
fprintf( stderr, "both --conn-addr and --conn-port are required.\n" );
|
||||||
"both --conn-addr and --conn-port are required.\n");
|
|
||||||
exit_err( proxy_help_text );
|
exit_err( proxy_help_text );
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy = proxy_create(downstream_addr,
|
proxy = proxy_create(
|
||||||
|
downstream_addr,
|
||||||
downstream_port,
|
downstream_port,
|
||||||
upstream_addr,
|
upstream_addr,
|
||||||
upstream_port, bind_addr, cache_bytes);
|
upstream_port,
|
||||||
|
bind_addr,
|
||||||
|
cache_bytes
|
||||||
|
);
|
||||||
|
|
||||||
/* Set these *after* proxy has been assigned to */
|
/* Set these *after* proxy has been assigned to */
|
||||||
sigaction(SIGTERM, &exit_action, NULL);
|
sigaction(SIGTERM, &exit_action, NULL);
|
||||||
@@ -150,12 +152,15 @@ int main(int argc, char *argv[])
|
|||||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||||
|
|
||||||
if ( NULL != downstream_port ) {
|
if ( NULL != downstream_port ) {
|
||||||
info("Proxying between %s %s (downstream) and %s %s (upstream)",
|
info(
|
||||||
downstream_addr, downstream_port, upstream_addr,
|
"Proxying between %s %s (downstream) and %s %s (upstream)",
|
||||||
upstream_port);
|
downstream_addr, downstream_port, upstream_addr, upstream_port
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
info("Proxying between %s (downstream) and %s %s (upstream)",
|
info(
|
||||||
downstream_addr, upstream_addr, upstream_port);
|
"Proxying between %s (downstream) and %s %s (upstream)",
|
||||||
|
downstream_addr, upstream_addr, upstream_port
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
success = do_proxy( proxy );
|
success = do_proxy( proxy );
|
||||||
@@ -163,3 +168,4 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
return success ? 0 : 1;
|
return success ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,8 +2,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
struct prefetch *prefetch_create(size_t size_bytes)
|
struct prefetch* prefetch_create( size_t size_bytes ){
|
||||||
{
|
|
||||||
|
|
||||||
struct prefetch* out = xmalloc( sizeof( struct prefetch ) );
|
struct prefetch* out = xmalloc( sizeof( struct prefetch ) );
|
||||||
NULLCHECK( out );
|
NULLCHECK( out );
|
||||||
@@ -20,16 +19,14 @@ struct prefetch *prefetch_create(size_t size_bytes)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void prefetch_destroy(struct prefetch *prefetch)
|
void prefetch_destroy( struct prefetch *prefetch ) {
|
||||||
{
|
|
||||||
if( prefetch ) {
|
if( prefetch ) {
|
||||||
free( prefetch->buffer );
|
free( prefetch->buffer );
|
||||||
free( prefetch );
|
free( prefetch );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t prefetch_size(struct prefetch *prefetch)
|
size_t prefetch_size( struct prefetch *prefetch){
|
||||||
{
|
|
||||||
if ( prefetch ) {
|
if ( prefetch ) {
|
||||||
return prefetch->size;
|
return prefetch->size;
|
||||||
} else {
|
} else {
|
||||||
@@ -37,25 +34,21 @@ size_t prefetch_size(struct prefetch *prefetch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void prefetch_set_is_empty(struct prefetch *prefetch)
|
void prefetch_set_is_empty( struct prefetch *prefetch ){
|
||||||
{
|
|
||||||
prefetch_set_full( prefetch, 0 );
|
prefetch_set_full( prefetch, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void prefetch_set_is_full(struct prefetch *prefetch)
|
void prefetch_set_is_full( struct prefetch *prefetch ){
|
||||||
{
|
|
||||||
prefetch_set_full( prefetch, 1 );
|
prefetch_set_full( prefetch, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void prefetch_set_full(struct prefetch *prefetch, int val)
|
void prefetch_set_full( struct prefetch *prefetch, int val ){
|
||||||
{
|
|
||||||
if( prefetch ) {
|
if( prefetch ) {
|
||||||
prefetch->is_full = val;
|
prefetch->is_full = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int prefetch_is_full(struct prefetch *prefetch)
|
int prefetch_is_full( struct prefetch *prefetch ){
|
||||||
{
|
|
||||||
if( prefetch ) {
|
if( prefetch ) {
|
||||||
return prefetch->is_full;
|
return prefetch->is_full;
|
||||||
} else {
|
} else {
|
||||||
@@ -63,16 +56,13 @@ int prefetch_is_full(struct prefetch *prefetch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int prefetch_contains(struct prefetch *prefetch, uint64_t from,
|
int prefetch_contains( struct prefetch *prefetch, uint64_t from, uint32_t len ){
|
||||||
uint32_t len)
|
|
||||||
{
|
|
||||||
NULLCHECK( prefetch );
|
NULLCHECK( prefetch );
|
||||||
return from >= prefetch->from &&
|
return from >= prefetch->from &&
|
||||||
from + len <= prefetch->from + prefetch->len;
|
from + len <= prefetch->from + prefetch->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *prefetch_offset(struct prefetch *prefetch, uint64_t from)
|
char *prefetch_offset( struct prefetch *prefetch, uint64_t from ){
|
||||||
{
|
|
||||||
NULLCHECK( prefetch );
|
NULLCHECK( prefetch );
|
||||||
return prefetch->buffer + (from - prefetch->from);
|
return prefetch->buffer + (from - prefetch->from);
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,7 @@ void prefetch_set_is_empty(struct prefetch *prefetch);
|
|||||||
void prefetch_set_is_full( struct prefetch *prefetch );
|
void prefetch_set_is_full( struct prefetch *prefetch );
|
||||||
void prefetch_set_full( struct prefetch *prefetch, int val );
|
void prefetch_set_full( struct prefetch *prefetch, int val );
|
||||||
int prefetch_is_full( struct prefetch *prefetch );
|
int prefetch_is_full( struct prefetch *prefetch );
|
||||||
int prefetch_contains(struct prefetch *prefetch, uint64_t from,
|
int prefetch_contains( struct prefetch *prefetch, uint64_t from, uint32_t len );
|
||||||
uint32_t len);
|
|
||||||
char *prefetch_offset( struct prefetch *prefetch, uint64_t from );
|
char *prefetch_offset( struct prefetch *prefetch, uint64_t from );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -13,11 +13,13 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
struct proxier *proxy_create(char *s_downstream_address,
|
struct proxier* proxy_create(
|
||||||
|
char* s_downstream_address,
|
||||||
char* s_downstream_port,
|
char* s_downstream_port,
|
||||||
char* s_upstream_address,
|
char* s_upstream_address,
|
||||||
char* s_upstream_port,
|
char* s_upstream_port,
|
||||||
char *s_upstream_bind, char *s_cache_bytes)
|
char* s_upstream_bind,
|
||||||
|
char* s_cache_bytes )
|
||||||
{
|
{
|
||||||
struct proxier* out;
|
struct proxier* out;
|
||||||
out = xmalloc( sizeof( struct proxier ) );
|
out = xmalloc( sizeof( struct proxier ) );
|
||||||
@@ -25,9 +27,10 @@ struct proxier *proxy_create(char *s_downstream_address,
|
|||||||
FATAL_IF_NULL(s_downstream_address, "Listen address not specified");
|
FATAL_IF_NULL(s_downstream_address, "Listen address not specified");
|
||||||
NULLCHECK( s_downstream_address );
|
NULLCHECK( s_downstream_address );
|
||||||
|
|
||||||
FATAL_UNLESS(parse_to_sockaddr
|
FATAL_UNLESS(
|
||||||
(&out->listen_on.generic, s_downstream_address),
|
parse_to_sockaddr( &out->listen_on.generic, s_downstream_address ),
|
||||||
"Couldn't parse downstream address %s");
|
"Couldn't parse downstream address %s"
|
||||||
|
);
|
||||||
|
|
||||||
if ( out->listen_on.family != AF_UNIX ) {
|
if ( out->listen_on.family != AF_UNIX ) {
|
||||||
FATAL_IF_NULL( s_downstream_port, "Downstream port not specified" );
|
FATAL_IF_NULL( s_downstream_port, "Downstream port not specified" );
|
||||||
@@ -38,19 +41,22 @@ struct proxier *proxy_create(char *s_downstream_address,
|
|||||||
FATAL_IF_NULL(s_upstream_address, "Upstream address not specified");
|
FATAL_IF_NULL(s_upstream_address, "Upstream address not specified");
|
||||||
NULLCHECK( s_upstream_address );
|
NULLCHECK( s_upstream_address );
|
||||||
|
|
||||||
FATAL_UNLESS(parse_ip_to_sockaddr
|
FATAL_UNLESS(
|
||||||
(&out->connect_to.generic, s_upstream_address),
|
parse_ip_to_sockaddr( &out->connect_to.generic, s_upstream_address ),
|
||||||
"Couldn't parse upstream address '%s'",
|
"Couldn't parse upstream address '%s'",
|
||||||
s_upstream_address);
|
s_upstream_address
|
||||||
|
);
|
||||||
|
|
||||||
FATAL_IF_NULL( s_upstream_port, "Upstream port not specified" );
|
FATAL_IF_NULL( s_upstream_port, "Upstream port not specified" );
|
||||||
NULLCHECK( s_upstream_port );
|
NULLCHECK( s_upstream_port );
|
||||||
parse_port( s_upstream_port, &out->connect_to.v4 );
|
parse_port( s_upstream_port, &out->connect_to.v4 );
|
||||||
|
|
||||||
if ( s_upstream_bind ) {
|
if ( s_upstream_bind ) {
|
||||||
FATAL_IF_ZERO(parse_ip_to_sockaddr
|
FATAL_IF_ZERO(
|
||||||
(&out->connect_from.generic, s_upstream_bind),
|
parse_ip_to_sockaddr( &out->connect_from.generic, s_upstream_bind ),
|
||||||
"Couldn't parse bind address '%s'", s_upstream_bind);
|
"Couldn't parse bind address '%s'",
|
||||||
|
s_upstream_bind
|
||||||
|
);
|
||||||
out->bind = 1;
|
out->bind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,21 +83,18 @@ struct proxier *proxy_create(char *s_downstream_address,
|
|||||||
out->req.buf = xmalloc( NBD_MAX_SIZE + NBD_REQUEST_SIZE );
|
out->req.buf = xmalloc( NBD_MAX_SIZE + NBD_REQUEST_SIZE );
|
||||||
out->rsp.buf = xmalloc( NBD_MAX_SIZE + NBD_REPLY_SIZE );
|
out->rsp.buf = xmalloc( NBD_MAX_SIZE + NBD_REPLY_SIZE );
|
||||||
|
|
||||||
log_context =
|
log_context = xmalloc( strlen(s_upstream_address) + strlen(s_upstream_port) + 2 );
|
||||||
xmalloc(strlen(s_upstream_address) + strlen(s_upstream_port) + 2);
|
|
||||||
sprintf(log_context, "%s:%s", s_upstream_address, s_upstream_port);
|
sprintf(log_context, "%s:%s", s_upstream_address, s_upstream_port);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int proxy_prefetches(struct proxier *proxy)
|
int proxy_prefetches( struct proxier* proxy ) {
|
||||||
{
|
|
||||||
NULLCHECK( proxy );
|
NULLCHECK( proxy );
|
||||||
return proxy->prefetch != NULL;
|
return proxy->prefetch != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int proxy_prefetch_bufsize(struct proxier *proxy)
|
int proxy_prefetch_bufsize( struct proxier* proxy ){
|
||||||
{
|
|
||||||
NULLCHECK( proxy );
|
NULLCHECK( proxy );
|
||||||
return prefetch_size( proxy->prefetch );
|
return prefetch_size( proxy->prefetch );
|
||||||
}
|
}
|
||||||
@@ -107,8 +110,7 @@ void proxy_destroy(struct proxier *proxy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Shared between our two different connect_to_upstream paths */
|
/* Shared between our two different connect_to_upstream paths */
|
||||||
void proxy_finish_connect_to_upstream(struct proxier *proxy, uint64_t size,
|
void proxy_finish_connect_to_upstream( struct proxier *proxy, uint64_t size );
|
||||||
uint32_t flags);
|
|
||||||
|
|
||||||
/* Try to establish a connection to our upstream server. Return 1 on success,
|
/* Try to establish a connection to our upstream server. Return 1 on success,
|
||||||
* 0 on failure. this is a blocking call that returns a non-blocking socket.
|
* 0 on failure. this is a blocking call that returns a non-blocking socket.
|
||||||
@@ -122,22 +124,22 @@ int proxy_connect_to_upstream(struct proxier *proxy)
|
|||||||
|
|
||||||
int fd = socket_connect( &proxy->connect_to.generic, connect_from );
|
int fd = socket_connect( &proxy->connect_to.generic, connect_from );
|
||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
uint32_t flags = 0;
|
|
||||||
|
|
||||||
if ( -1 == fd ) {
|
if ( -1 == fd ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!socket_nbd_read_hello(fd, &size, &flags)) {
|
if( !socket_nbd_read_hello( fd, &size ) ) {
|
||||||
WARN_IF_NEGATIVE(sock_try_close(fd),
|
WARN_IF_NEGATIVE(
|
||||||
"Couldn't close() after failed read of NBD hello on fd %i",
|
sock_try_close( fd ),
|
||||||
fd);
|
"Couldn't close() after failed read of NBD hello on fd %i", fd
|
||||||
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy->upstream_fd = fd;
|
proxy->upstream_fd = fd;
|
||||||
sock_set_nonblock( fd, 1 );
|
sock_set_nonblock( fd, 1 );
|
||||||
proxy_finish_connect_to_upstream(proxy, size, flags);
|
proxy_finish_connect_to_upstream( proxy, size );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -158,8 +160,7 @@ void proxy_start_connect_to_upstream(struct proxier *proxy)
|
|||||||
fd = socket( to->sa_family , SOCK_STREAM, 0 );
|
fd = socket( to->sa_family , SOCK_STREAM, 0 );
|
||||||
|
|
||||||
if( fd < 0 ) {
|
if( fd < 0 ) {
|
||||||
warn(SHOW_ERRNO
|
warn( SHOW_ERRNO( "Couldn't create socket to reconnect to upstream" ) );
|
||||||
("Couldn't create socket to reconnect to upstream"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,28 +195,19 @@ void proxy_start_connect_to_upstream(struct proxier *proxy)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void proxy_finish_connect_to_upstream(struct proxier *proxy, uint64_t size,
|
void proxy_finish_connect_to_upstream( struct proxier *proxy, uint64_t size ) {
|
||||||
uint32_t flags)
|
|
||||||
{
|
|
||||||
|
|
||||||
if ( proxy->upstream_size == 0 ) {
|
if ( proxy->upstream_size == 0 ) {
|
||||||
info( "Size of upstream image is %"PRIu64" bytes", size );
|
info( "Size of upstream image is %"PRIu64" bytes", size );
|
||||||
} else if ( proxy->upstream_size != size ) {
|
} else if ( proxy->upstream_size != size ) {
|
||||||
warn("Size changed from %" PRIu64 " to %" PRIu64 " bytes",
|
warn(
|
||||||
proxy->upstream_size, size);
|
"Size changed from %"PRIu64" to %"PRIu64" bytes",
|
||||||
|
proxy->upstream_size, size
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy->upstream_size = size;
|
proxy->upstream_size = size;
|
||||||
|
|
||||||
if (proxy->upstream_flags == 0) {
|
|
||||||
info("Upstream transmission flags set to %" PRIu32 "", flags);
|
|
||||||
} else if (proxy->upstream_flags != flags) {
|
|
||||||
warn("Upstream transmission flags changed from %" PRIu32 " to %"
|
|
||||||
PRIu32 "", proxy->upstream_flags, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy->upstream_flags = flags;
|
|
||||||
|
|
||||||
if ( AF_UNIX != proxy->connect_to.family ) {
|
if ( AF_UNIX != proxy->connect_to.family ) {
|
||||||
if ( sock_set_tcp_nodelay( proxy->upstream_fd, 1 ) == -1 ) {
|
if ( sock_set_tcp_nodelay( proxy->upstream_fd, 1 ) == -1 ) {
|
||||||
warn( SHOW_ERRNO( "Failed to set TCP_NODELAY" ) );
|
warn( SHOW_ERRNO( "Failed to set TCP_NODELAY" ) );
|
||||||
@@ -233,9 +225,11 @@ void proxy_disconnect_from_upstream(struct proxier *proxy)
|
|||||||
info("Closing upstream connection on fd %i", proxy->upstream_fd );
|
info("Closing upstream connection on fd %i", proxy->upstream_fd );
|
||||||
|
|
||||||
/* TODO: An NBD disconnect would be pleasant here */
|
/* TODO: An NBD disconnect would be pleasant here */
|
||||||
WARN_IF_NEGATIVE(sock_try_close(proxy->upstream_fd),
|
WARN_IF_NEGATIVE(
|
||||||
|
sock_try_close( proxy->upstream_fd ),
|
||||||
"Failed to close() fd %i when disconnecting from upstream",
|
"Failed to close() fd %i when disconnecting from upstream",
|
||||||
proxy->upstream_fd);
|
proxy->upstream_fd
|
||||||
|
);
|
||||||
proxy->upstream_fd = -1;
|
proxy->upstream_fd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,28 +241,31 @@ void proxy_open_listen_socket(struct proxier *params)
|
|||||||
NULLCHECK( params );
|
NULLCHECK( params );
|
||||||
|
|
||||||
params->listen_fd = socket(params->listen_on.family, SOCK_STREAM, 0);
|
params->listen_fd = socket(params->listen_on.family, SOCK_STREAM, 0);
|
||||||
FATAL_IF_NEGATIVE(params->listen_fd,
|
FATAL_IF_NEGATIVE(
|
||||||
SHOW_ERRNO("Couldn't create listen socket")
|
params->listen_fd, SHOW_ERRNO( "Couldn't create listen socket" )
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Allow us to restart quickly */
|
/* Allow us to restart quickly */
|
||||||
FATAL_IF_NEGATIVE(sock_set_reuseaddr(params->listen_fd, 1),
|
FATAL_IF_NEGATIVE(
|
||||||
|
sock_set_reuseaddr(params->listen_fd, 1),
|
||||||
SHOW_ERRNO( "Couldn't set SO_REUSEADDR" )
|
SHOW_ERRNO( "Couldn't set SO_REUSEADDR" )
|
||||||
);
|
);
|
||||||
|
|
||||||
if( AF_UNIX != params->listen_on.family ) {
|
if( AF_UNIX != params->listen_on.family ) {
|
||||||
FATAL_IF_NEGATIVE(sock_set_tcp_nodelay(params->listen_fd, 1),
|
FATAL_IF_NEGATIVE(
|
||||||
|
sock_set_tcp_nodelay(params->listen_fd, 1),
|
||||||
SHOW_ERRNO( "Couldn't set TCP_NODELAY" )
|
SHOW_ERRNO( "Couldn't set TCP_NODELAY" )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FATAL_UNLESS_ZERO(sock_try_bind
|
FATAL_UNLESS_ZERO(
|
||||||
(params->listen_fd, ¶ms->listen_on.generic),
|
sock_try_bind( params->listen_fd, ¶ms->listen_on.generic ),
|
||||||
SHOW_ERRNO( "Failed to bind to listening socket" )
|
SHOW_ERRNO( "Failed to bind to listening socket" )
|
||||||
);
|
);
|
||||||
|
|
||||||
/* We're only serving one client at a time, hence backlog of 1 */
|
/* We're only serving one client at a time, hence backlog of 1 */
|
||||||
FATAL_IF_NEGATIVE(listen(params->listen_fd, 1),
|
FATAL_IF_NEGATIVE(
|
||||||
|
listen(params->listen_fd, 1),
|
||||||
SHOW_ERRNO( "Failed to listen on listening socket" )
|
SHOW_ERRNO( "Failed to listen on listening socket" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -299,8 +296,8 @@ static char *proxy_session_state_names[] = {
|
|||||||
|
|
||||||
static inline int proxy_state_upstream( int state )
|
static inline int proxy_state_upstream( int state )
|
||||||
{
|
{
|
||||||
return state == CONNECT_TO_UPSTREAM || state == READ_INIT_FROM_UPSTREAM
|
return state == CONNECT_TO_UPSTREAM || state == READ_INIT_FROM_UPSTREAM ||
|
||||||
|| state == WRITE_TO_UPSTREAM || state == READ_FROM_UPSTREAM;
|
state == WRITE_TO_UPSTREAM || state == READ_FROM_UPSTREAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
int proxy_prefetch_for_request( struct proxier* proxy, int state )
|
int proxy_prefetch_for_request( struct proxier* proxy, int state )
|
||||||
@@ -309,12 +306,10 @@ int proxy_prefetch_for_request(struct proxier *proxy, int state)
|
|||||||
struct nbd_request* req = &proxy->req_hdr;
|
struct nbd_request* req = &proxy->req_hdr;
|
||||||
struct nbd_reply* rsp = &proxy->rsp_hdr;
|
struct nbd_reply* rsp = &proxy->rsp_hdr;
|
||||||
|
|
||||||
struct nbd_request_raw *req_raw =
|
struct nbd_request_raw* req_raw = (struct nbd_request_raw*) proxy->req.buf;
|
||||||
(struct nbd_request_raw *) proxy->req.buf;
|
struct nbd_reply_raw *rsp_raw = (struct nbd_reply_raw*) proxy->rsp.buf;
|
||||||
struct nbd_reply_raw *rsp_raw =
|
|
||||||
(struct nbd_reply_raw *) proxy->rsp.buf;
|
|
||||||
|
|
||||||
int is_read = req->type == REQUEST_READ;
|
int is_read = ( req->type & REQUEST_MASK ) == REQUEST_READ;
|
||||||
|
|
||||||
if ( is_read ) {
|
if ( is_read ) {
|
||||||
/* See if we can respond with what's in our prefetch
|
/* See if we can respond with what's in our prefetch
|
||||||
@@ -333,8 +328,11 @@ int proxy_prefetch_for_request(struct proxier *proxy, int state)
|
|||||||
nbd_h2r_reply( rsp, rsp_raw );
|
nbd_h2r_reply( rsp, rsp_raw );
|
||||||
|
|
||||||
/* and the data */
|
/* and the data */
|
||||||
memcpy(proxy->rsp.buf + NBD_REPLY_SIZE,
|
memcpy(
|
||||||
prefetch_offset(proxy->prefetch, req->from), req->len);
|
proxy->rsp.buf + NBD_REPLY_SIZE,
|
||||||
|
prefetch_offset( proxy->prefetch, req->from ),
|
||||||
|
req->len
|
||||||
|
);
|
||||||
|
|
||||||
proxy->rsp.size = NBD_REPLY_SIZE + req->len;
|
proxy->rsp.size = NBD_REPLY_SIZE + req->len;
|
||||||
proxy->rsp.needle = 0;
|
proxy->rsp.needle = 0;
|
||||||
@@ -342,14 +340,14 @@ int proxy_prefetch_for_request(struct proxier *proxy, int state)
|
|||||||
/* return early, our work here is done */
|
/* return early, our work here is done */
|
||||||
return WRITE_TO_DOWNSTREAM;
|
return WRITE_TO_DOWNSTREAM;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
/* Safety catch. If we're sending a write request, we
|
/* Safety catch. If we're sending a write request, we
|
||||||
* blow away the cache. This is very pessimistic, but
|
* blow away the cache. This is very pessimistic, but
|
||||||
* it's simpler (and therefore safer) than working out
|
* it's simpler (and therefore safer) than working out
|
||||||
* whether we can keep it or not.
|
* whether we can keep it or not.
|
||||||
*/
|
*/
|
||||||
debug("Blowing away prefetch cache on type %d request.",
|
debug( "Blowing away prefetch cache on type %d request.", req->type );
|
||||||
req->type);
|
|
||||||
prefetch_set_is_empty( proxy->prefetch );
|
prefetch_set_is_empty( proxy->prefetch );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,12 +395,13 @@ int proxy_prefetch_for_reply(struct proxier *proxy, int state)
|
|||||||
prefetched_bytes = proxy->req_hdr.len - proxy->prefetch_req_orig_len;
|
prefetched_bytes = proxy->req_hdr.len - proxy->prefetch_req_orig_len;
|
||||||
|
|
||||||
debug( "Prefetched additional %d bytes", prefetched_bytes );
|
debug( "Prefetched additional %d bytes", prefetched_bytes );
|
||||||
memcpy(proxy->prefetch->buffer,
|
memcpy(
|
||||||
|
proxy->prefetch->buffer,
|
||||||
proxy->rsp.buf + proxy->prefetch_req_orig_len + NBD_REPLY_SIZE,
|
proxy->rsp.buf + proxy->prefetch_req_orig_len + NBD_REPLY_SIZE,
|
||||||
prefetched_bytes);
|
prefetched_bytes
|
||||||
|
);
|
||||||
|
|
||||||
proxy->prefetch->from =
|
proxy->prefetch->from = proxy->req_hdr.from + proxy->prefetch_req_orig_len;
|
||||||
proxy->req_hdr.from + proxy->prefetch_req_orig_len;
|
|
||||||
proxy->prefetch->len = prefetched_bytes;
|
proxy->prefetch->len = prefetched_bytes;
|
||||||
|
|
||||||
/* We've finished with proxy->req by now, so don't need to alter it to make
|
/* We've finished with proxy->req by now, so don't need to alter it to make
|
||||||
@@ -425,14 +424,12 @@ int proxy_read_from_downstream(struct proxier *proxy, int state)
|
|||||||
{
|
{
|
||||||
ssize_t count;
|
ssize_t count;
|
||||||
|
|
||||||
struct nbd_request_raw *request_raw =
|
struct nbd_request_raw* request_raw = (struct nbd_request_raw*) proxy->req.buf;
|
||||||
(struct nbd_request_raw *) proxy->req.buf;
|
|
||||||
struct nbd_request* request = &(proxy->req_hdr);
|
struct nbd_request* request = &(proxy->req_hdr);
|
||||||
|
|
||||||
// assert( state == READ_FROM_DOWNSTREAM );
|
// assert( state == READ_FROM_DOWNSTREAM );
|
||||||
|
|
||||||
count =
|
count = iobuf_read( proxy->downstream_fd, &proxy->req, NBD_REQUEST_SIZE );
|
||||||
iobuf_read(proxy->downstream_fd, &proxy->req, NBD_REQUEST_SIZE);
|
|
||||||
|
|
||||||
if ( count == -1 ) {
|
if ( count == -1 ) {
|
||||||
warn( SHOW_ERRNO( "Couldn't read request from downstream" ) );
|
warn( SHOW_ERRNO( "Couldn't read request from downstream" ) );
|
||||||
@@ -442,7 +439,7 @@ int proxy_read_from_downstream(struct proxier *proxy, int state)
|
|||||||
if ( proxy->req.needle == NBD_REQUEST_SIZE ) {
|
if ( proxy->req.needle == NBD_REQUEST_SIZE ) {
|
||||||
nbd_r2h_request( request_raw, request );
|
nbd_r2h_request( request_raw, request );
|
||||||
|
|
||||||
if (request->type == REQUEST_DISCONNECT) {
|
if ( ( request->type & REQUEST_MASK ) == REQUEST_DISCONNECT ) {
|
||||||
info( "Received disconnect request from client" );
|
info( "Received disconnect request from client" );
|
||||||
return EXIT;
|
return EXIT;
|
||||||
}
|
}
|
||||||
@@ -451,17 +448,15 @@ int proxy_read_from_downstream(struct proxier *proxy, int state)
|
|||||||
* been taken into account in the xmalloc, so no need to worry
|
* been taken into account in the xmalloc, so no need to worry
|
||||||
* about them here
|
* about them here
|
||||||
*/
|
*/
|
||||||
if (request->type == REQUEST_READ) {
|
if ( ( request->type & REQUEST_MASK ) == REQUEST_READ ) {
|
||||||
if ( request->len > NBD_MAX_SIZE ) {
|
if ( request->len > NBD_MAX_SIZE ) {
|
||||||
warn("NBD read request size %" PRIu32 " too large",
|
warn( "NBD read request size %"PRIu32" too large", request->len );
|
||||||
request->len);
|
|
||||||
return EXIT;
|
return EXIT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (request->type == REQUEST_WRITE) {
|
if ( (request->type & REQUEST_MASK ) == REQUEST_WRITE ) {
|
||||||
if ( request->len > NBD_MAX_SIZE ) {
|
if ( request->len > NBD_MAX_SIZE ) {
|
||||||
warn("NBD write request size %" PRIu32 " too large",
|
warn( "NBD write request size %"PRIu32" too large", request->len );
|
||||||
request->len);
|
|
||||||
return EXIT;
|
return EXIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,9 +465,10 @@ int proxy_read_from_downstream(struct proxier *proxy, int state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( proxy->req.needle == proxy->req.size ) {
|
if ( proxy->req.needle == proxy->req.size ) {
|
||||||
debug("Received NBD request from downstream. type=%" PRIu16
|
debug(
|
||||||
" flags=%" PRIu16 " from=%" PRIu64 " len=%" PRIu32,
|
"Received NBD request from downstream. type=%"PRIu32" from=%"PRIu64" len=%"PRIu32,
|
||||||
request->type, request->flags, request->from, request->len);
|
request->type, request->from, request->len
|
||||||
|
);
|
||||||
|
|
||||||
/* Finished reading, so advance state. Leave size untouched so the next
|
/* Finished reading, so advance state. Leave size untouched so the next
|
||||||
* state knows how many bytes to write */
|
* state knows how many bytes to write */
|
||||||
@@ -490,8 +486,9 @@ int proxy_continue_connecting_to_upstream(struct proxier *proxy, int state)
|
|||||||
|
|
||||||
// assert( state == CONNECT_TO_UPSTREAM );
|
// assert( state == CONNECT_TO_UPSTREAM );
|
||||||
|
|
||||||
result =
|
result = getsockopt(
|
||||||
getsockopt(proxy->upstream_fd, SOL_SOCKET, SO_ERROR, &error, &len);
|
proxy->upstream_fd, SOL_SOCKET, SO_ERROR, &error, &len
|
||||||
|
);
|
||||||
|
|
||||||
if ( result == -1 ) {
|
if ( result == -1 ) {
|
||||||
warn( SHOW_ERRNO( "Failed to tell if connected to upstream" ) );
|
warn( SHOW_ERRNO( "Failed to tell if connected to upstream" ) );
|
||||||
@@ -517,9 +514,7 @@ int proxy_read_init_from_upstream(struct proxier *proxy, int state)
|
|||||||
|
|
||||||
// assert( state == READ_INIT_FROM_UPSTREAM );
|
// assert( state == READ_INIT_FROM_UPSTREAM );
|
||||||
|
|
||||||
count =
|
count = iobuf_read( proxy->upstream_fd, &proxy->init, sizeof( struct nbd_init_raw ) );
|
||||||
iobuf_read(proxy->upstream_fd, &proxy->init,
|
|
||||||
sizeof(struct nbd_init_raw));
|
|
||||||
|
|
||||||
if ( count == -1 ) {
|
if ( count == -1 ) {
|
||||||
warn( SHOW_ERRNO( "Failed to read init from upstream" ) );
|
warn( SHOW_ERRNO( "Failed to read init from upstream" ) );
|
||||||
@@ -528,18 +523,11 @@ int proxy_read_init_from_upstream(struct proxier *proxy, int state)
|
|||||||
|
|
||||||
if ( proxy->init.needle == proxy->init.size ) {
|
if ( proxy->init.needle == proxy->init.size ) {
|
||||||
uint64_t upstream_size;
|
uint64_t upstream_size;
|
||||||
uint32_t upstream_flags;
|
if ( !nbd_check_hello( (struct nbd_init_raw*) proxy->init.buf, &upstream_size ) ) {
|
||||||
if (!nbd_check_hello
|
|
||||||
((struct nbd_init_raw *) proxy->init.buf, &upstream_size,
|
|
||||||
&upstream_flags)) {
|
|
||||||
warn( "Upstream sent invalid init" );
|
warn( "Upstream sent invalid init" );
|
||||||
goto disconnect;
|
goto disconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* record the flags, and log the reconnection, set TCP_NODELAY */
|
|
||||||
proxy_finish_connect_to_upstream(proxy, upstream_size,
|
|
||||||
upstream_flags);
|
|
||||||
|
|
||||||
/* Currently, we only get disconnected from upstream (so needing to come
|
/* Currently, we only get disconnected from upstream (so needing to come
|
||||||
* here) when we have an outstanding request. If that becomes false,
|
* here) when we have an outstanding request. If that becomes false,
|
||||||
* we'll need to choose the right state to return to here */
|
* we'll need to choose the right state to return to here */
|
||||||
@@ -603,8 +591,7 @@ int proxy_read_from_upstream(struct proxier *proxy, int state)
|
|||||||
ssize_t count;
|
ssize_t count;
|
||||||
|
|
||||||
struct nbd_reply* reply = &(proxy->rsp_hdr);
|
struct nbd_reply* reply = &(proxy->rsp_hdr);
|
||||||
struct nbd_reply_raw *reply_raw =
|
struct nbd_reply_raw* reply_raw = (struct nbd_reply_raw*) proxy->rsp.buf;
|
||||||
(struct nbd_reply_raw *) proxy->rsp.buf;
|
|
||||||
|
|
||||||
/* We can't assume the NBD_REPLY_SIZE + req->len is what we'll get back */
|
/* We can't assume the NBD_REPLY_SIZE + req->len is what we'll get back */
|
||||||
count = iobuf_read( proxy->upstream_fd, &proxy->rsp, NBD_REPLY_SIZE );
|
count = iobuf_read( proxy->upstream_fd, &proxy->rsp, NBD_REPLY_SIZE );
|
||||||
@@ -622,7 +609,12 @@ int proxy_read_from_upstream(struct proxier *proxy, int state)
|
|||||||
goto disconnect;
|
goto disconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proxy->req_hdr.type == REQUEST_READ) {
|
if ( reply->error != 0 ) {
|
||||||
|
warn( "NBD error returned from upstream: %"PRIu32, reply->error );
|
||||||
|
goto disconnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ( proxy->req_hdr.type & REQUEST_MASK ) == REQUEST_READ ) {
|
||||||
/* Get the read reply data too. */
|
/* Get the read reply data too. */
|
||||||
proxy->rsp.size += proxy->req_hdr.len;
|
proxy->rsp.size += proxy->req_hdr.len;
|
||||||
}
|
}
|
||||||
@@ -698,8 +690,7 @@ void proxy_session(struct proxier *proxy)
|
|||||||
|
|
||||||
|
|
||||||
/* First action: Write hello to downstream */
|
/* First action: Write hello to downstream */
|
||||||
nbd_hello_to_buf((struct nbd_init_raw *) proxy->rsp.buf,
|
nbd_hello_to_buf( (struct nbd_init_raw *) proxy->rsp.buf, proxy->upstream_size );
|
||||||
proxy->upstream_size, proxy->upstream_flags);
|
|
||||||
proxy->rsp.size = sizeof( struct nbd_init_raw );
|
proxy->rsp.size = sizeof( struct nbd_init_raw );
|
||||||
proxy->rsp.needle = 0;
|
proxy->rsp.needle = 0;
|
||||||
state = WRITE_TO_DOWNSTREAM;
|
state = WRITE_TO_DOWNSTREAM;
|
||||||
@@ -727,13 +718,13 @@ void proxy_session(struct proxier *proxy)
|
|||||||
if ( state != old_state ) {
|
if ( state != old_state ) {
|
||||||
state_started = monotonic_time_ms();
|
state_started = monotonic_time_ms();
|
||||||
|
|
||||||
debug("State transition from %s to %s",
|
debug(
|
||||||
|
"State transition from %s to %s",
|
||||||
proxy_session_state_names[old_state],
|
proxy_session_state_names[old_state],
|
||||||
proxy_session_state_names[state]
|
proxy_session_state_names[state]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debug("Proxy is in state %s", proxy_session_state_names[state],
|
debug( "Proxy is in state %s", proxy_session_state_names[state], state );
|
||||||
state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
old_state = state;
|
old_state = state;
|
||||||
@@ -778,9 +769,7 @@ void proxy_session(struct proxier *proxy)
|
|||||||
select_timeout_ptr = &select_timeout;
|
select_timeout_ptr = &select_timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
result =
|
result = sock_try_select( FD_SETSIZE, &rfds, &wfds, NULL, select_timeout_ptr );
|
||||||
sock_try_select(FD_SETSIZE, &rfds, &wfds, NULL,
|
|
||||||
select_timeout_ptr);
|
|
||||||
|
|
||||||
if ( result == -1 ) {
|
if ( result == -1 ) {
|
||||||
warn( SHOW_ERRNO( "select() failed: " ) );
|
warn( SHOW_ERRNO( "select() failed: " ) );
|
||||||
@@ -806,8 +795,7 @@ void proxy_session(struct proxier *proxy)
|
|||||||
break;
|
break;
|
||||||
case CONNECT_TO_UPSTREAM:
|
case CONNECT_TO_UPSTREAM:
|
||||||
if ( FD_ISSET( proxy->upstream_fd, &wfds ) ) {
|
if ( FD_ISSET( proxy->upstream_fd, &wfds ) ) {
|
||||||
state =
|
state = proxy_continue_connecting_to_upstream( proxy, state );
|
||||||
proxy_continue_connecting_to_upstream(proxy, state);
|
|
||||||
}
|
}
|
||||||
/* Leaving state untouched will retry connecting to upstream -
|
/* Leaving state untouched will retry connecting to upstream -
|
||||||
* so introduce a bit of sleep */
|
* so introduce a bit of sleep */
|
||||||
@@ -849,7 +837,9 @@ void proxy_session(struct proxier *proxy)
|
|||||||
*/
|
*/
|
||||||
if ( old_state == state && proxy_state_upstream( state ) ) {
|
if ( old_state == state && proxy_state_upstream( state ) ) {
|
||||||
if ( ( monotonic_time_ms() ) - state_started > UPSTREAM_TIMEOUT ) {
|
if ( ( monotonic_time_ms() ) - state_started > UPSTREAM_TIMEOUT ) {
|
||||||
warn("Timed out in state %s while communicating with upstream", proxy_session_state_names[state]
|
warn(
|
||||||
|
"Timed out in state %s while communicating with upstream",
|
||||||
|
proxy_session_state_names[state]
|
||||||
);
|
);
|
||||||
state = CONNECT_TO_UPSTREAM;
|
state = CONNECT_TO_UPSTREAM;
|
||||||
|
|
||||||
@@ -863,8 +853,10 @@ void proxy_session(struct proxier *proxy)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info("Finished proxy session on fd %i after %" PRIu64
|
info(
|
||||||
" successful request(s)", proxy->downstream_fd, proxy->req_count);
|
"Finished proxy session on fd %i after %"PRIu64" successful request(s)",
|
||||||
|
proxy->downstream_fd, proxy->req_count
|
||||||
|
);
|
||||||
|
|
||||||
/* Reset these two for the next session */
|
/* Reset these two for the next session */
|
||||||
proxy->req_count = 0;
|
proxy->req_count = 0;
|
||||||
@@ -889,13 +881,13 @@ int proxy_accept(struct proxier *params)
|
|||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(params->listen_fd, &fds);
|
FD_SET(params->listen_fd, &fds);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
|
FATAL_IF_NEGATIVE(
|
||||||
|
sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
|
||||||
SHOW_ERRNO( "select() failed" )
|
SHOW_ERRNO( "select() failed" )
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( FD_ISSET( params->listen_fd, &fds ) ) {
|
if ( FD_ISSET( params->listen_fd, &fds ) ) {
|
||||||
client_fd =
|
client_fd = accept( params->listen_fd, &client_address.generic, &socklen );
|
||||||
accept(params->listen_fd, &client_address.generic, &socklen);
|
|
||||||
|
|
||||||
if ( client_address.family != AF_UNIX ) {
|
if ( client_address.family != AF_UNIX ) {
|
||||||
if ( sock_set_tcp_nodelay(client_fd, 1) == -1 ) {
|
if ( sock_set_tcp_nodelay(client_fd, 1) == -1 ) {
|
||||||
@@ -908,9 +900,11 @@ int proxy_accept(struct proxier *params)
|
|||||||
params->downstream_fd = client_fd;
|
params->downstream_fd = client_fd;
|
||||||
proxy_session( params );
|
proxy_session( params );
|
||||||
|
|
||||||
WARN_IF_NEGATIVE(sock_try_close(params->downstream_fd),
|
WARN_IF_NEGATIVE(
|
||||||
|
sock_try_close( params->downstream_fd ),
|
||||||
"Couldn't close() downstram fd %i after proxy session",
|
"Couldn't close() downstram fd %i after proxy session",
|
||||||
params->downstream_fd);
|
params->downstream_fd
|
||||||
|
);
|
||||||
params->downstream_fd = -1;
|
params->downstream_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,31 +929,33 @@ void proxy_cleanup(struct proxier *proxy)
|
|||||||
|
|
||||||
if ( AF_UNIX == proxy->listen_on.family ) {
|
if ( AF_UNIX == proxy->listen_on.family ) {
|
||||||
if ( -1 == unlink( proxy->listen_on.un.sun_path ) ) {
|
if ( -1 == unlink( proxy->listen_on.un.sun_path ) ) {
|
||||||
warn(SHOW_ERRNO
|
warn( SHOW_ERRNO( "Failed to unlink %s", proxy->listen_on.un.sun_path ) );
|
||||||
("Failed to unlink %s",
|
|
||||||
proxy->listen_on.un.sun_path));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN_IF_NEGATIVE(sock_try_close(proxy->listen_fd),
|
WARN_IF_NEGATIVE(
|
||||||
SHOW_ERRNO("Failed to close() listen fd %i",
|
sock_try_close( proxy->listen_fd ),
|
||||||
proxy->listen_fd)
|
SHOW_ERRNO( "Failed to close() listen fd %i", proxy->listen_fd )
|
||||||
);
|
);
|
||||||
proxy->listen_fd = -1;
|
proxy->listen_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( -1 != proxy->downstream_fd ) {
|
if ( -1 != proxy->downstream_fd ) {
|
||||||
WARN_IF_NEGATIVE(sock_try_close(proxy->downstream_fd),
|
WARN_IF_NEGATIVE(
|
||||||
SHOW_ERRNO("Failed to close() downstream fd %i",
|
sock_try_close( proxy->downstream_fd ),
|
||||||
proxy->downstream_fd)
|
SHOW_ERRNO(
|
||||||
|
"Failed to close() downstream fd %i", proxy->downstream_fd
|
||||||
|
)
|
||||||
);
|
);
|
||||||
proxy->downstream_fd = -1;
|
proxy->downstream_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( -1 != proxy->upstream_fd ) {
|
if ( -1 != proxy->upstream_fd ) {
|
||||||
WARN_IF_NEGATIVE(sock_try_close(proxy->upstream_fd),
|
WARN_IF_NEGATIVE(
|
||||||
SHOW_ERRNO("Failed to close() upstream fd %i",
|
sock_try_close( proxy->upstream_fd ),
|
||||||
proxy->upstream_fd)
|
SHOW_ERRNO(
|
||||||
|
"Failed to close() upstream fd %i", proxy->upstream_fd
|
||||||
|
)
|
||||||
);
|
);
|
||||||
proxy->upstream_fd = -1;
|
proxy->upstream_fd = -1;
|
||||||
}
|
}
|
||||||
@@ -986,3 +982,4 @@ int do_proxy(struct proxier *params)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,9 +46,6 @@ struct proxier {
|
|||||||
/* This is the size we advertise to the downstream server */
|
/* This is the size we advertise to the downstream server */
|
||||||
uint64_t upstream_size;
|
uint64_t upstream_size;
|
||||||
|
|
||||||
/* These are the transmission flags sent as part of the handshake */
|
|
||||||
uint32_t upstream_flags;
|
|
||||||
|
|
||||||
/* We transform the raw request header into here */
|
/* We transform the raw request header into here */
|
||||||
struct nbd_request req_hdr;
|
struct nbd_request req_hdr;
|
||||||
|
|
||||||
@@ -85,13 +82,16 @@ struct proxier {
|
|||||||
/** */
|
/** */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct proxier *proxy_create(char *s_downstream_address,
|
struct proxier* proxy_create(
|
||||||
|
char* s_downstream_address,
|
||||||
char* s_downstream_port,
|
char* s_downstream_port,
|
||||||
char* s_upstream_address,
|
char* s_upstream_address,
|
||||||
char* s_upstream_port,
|
char* s_upstream_port,
|
||||||
char *s_upstream_bind, char *s_cache_bytes);
|
char* s_upstream_bind,
|
||||||
|
char* s_cache_bytes);
|
||||||
int do_proxy( struct proxier* proxy );
|
int do_proxy( struct proxier* proxy );
|
||||||
void proxy_cleanup( struct proxier* proxy );
|
void proxy_cleanup( struct proxier* proxy );
|
||||||
void proxy_destroy( struct proxier* proxy );
|
void proxy_destroy( struct proxier* proxy );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -22,9 +22,7 @@ static int testmasks[9] = { 0, 128, 192, 224, 240, 248, 252, 254, 255 };
|
|||||||
/** Test whether AF_INET or AF_INET6 sockaddr is included in the given access
|
/** Test whether AF_INET or AF_INET6 sockaddr is included in the given access
|
||||||
* control list, returning 1 if it is, and 0 if not.
|
* control list, returning 1 if it is, and 0 if not.
|
||||||
*/
|
*/
|
||||||
static int is_included_in_acl(int list_length,
|
static int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], union mysockaddr* test)
|
||||||
struct ip_and_mask (*list)[],
|
|
||||||
union mysockaddr *test)
|
|
||||||
{
|
{
|
||||||
NULLCHECK( test );
|
NULLCHECK( test );
|
||||||
|
|
||||||
@@ -35,8 +33,7 @@ static int is_included_in_acl(int list_length,
|
|||||||
int testbits;
|
int testbits;
|
||||||
unsigned char *raw_address1 = NULL, *raw_address2 = NULL;
|
unsigned char *raw_address1 = NULL, *raw_address2 = NULL;
|
||||||
|
|
||||||
debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family,
|
debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family, entry->ip.family);
|
||||||
entry->ip.family);
|
|
||||||
|
|
||||||
if (test->generic.sa_family != entry->ip.family) {
|
if (test->generic.sa_family != entry->ip.family) {
|
||||||
continue;
|
continue;
|
||||||
@@ -46,24 +43,24 @@ static int is_included_in_acl(int list_length,
|
|||||||
debug("it's an AF_INET");
|
debug("it's an AF_INET");
|
||||||
raw_address1 = (unsigned char*) &test->v4.sin_addr;
|
raw_address1 = (unsigned char*) &test->v4.sin_addr;
|
||||||
raw_address2 = (unsigned char*) &entry->ip.v4.sin_addr;
|
raw_address2 = (unsigned char*) &entry->ip.v4.sin_addr;
|
||||||
} else if (test->generic.sa_family == AF_INET6) {
|
}
|
||||||
|
else if (test->generic.sa_family == AF_INET6) {
|
||||||
debug("it's an AF_INET6");
|
debug("it's an AF_INET6");
|
||||||
raw_address1 = (unsigned char*) &test->v6.sin6_addr;
|
raw_address1 = (unsigned char*) &test->v6.sin6_addr;
|
||||||
raw_address2 = (unsigned char*) &entry->ip.v6.sin6_addr;
|
raw_address2 = (unsigned char*) &entry->ip.v6.sin6_addr;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
fatal( "Can't check an ACL for this address type." );
|
fatal( "Can't check an ACL for this address type." );
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("testbits=%d", entry->mask);
|
debug("testbits=%d", entry->mask);
|
||||||
|
|
||||||
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
|
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
|
||||||
debug("testbits=%d, c1=%02x, c2=%02x", testbits,
|
debug("testbits=%d, c1=%02x, c2=%02x", testbits, raw_address1[0], raw_address2[0]);
|
||||||
raw_address1[0], raw_address2[0]);
|
|
||||||
if (testbits >= 8) {
|
if (testbits >= 8) {
|
||||||
if (raw_address1[0] != raw_address2[0]) {
|
if (raw_address1[0] != raw_address2[0]) { goto no_match; }
|
||||||
goto no_match;
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
if ((raw_address1[0] & testmasks[testbits%8]) !=
|
if ((raw_address1[0] & testmasks[testbits%8]) !=
|
||||||
(raw_address2[0] & testmasks[testbits%8]) ) {
|
(raw_address2[0] & testmasks[testbits%8]) ) {
|
||||||
goto no_match;
|
goto no_match;
|
||||||
@@ -89,7 +86,8 @@ int acl_includes(struct acl *acl, union mysockaddr *addr)
|
|||||||
|
|
||||||
if ( 0 == acl->len ) {
|
if ( 0 == acl->len ) {
|
||||||
return !( acl->default_deny );
|
return !( acl->default_deny );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return is_included_in_acl( acl->len, acl->entries, addr );
|
return is_included_in_acl( acl->len, acl->entries, addr );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,3 +105,4 @@ void acl_destroy(struct acl *acl)
|
|||||||
acl->entries = NULL;
|
acl->entries = NULL;
|
||||||
free( acl );
|
free( acl );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,41 +30,30 @@ typedef bitfield_word_t *bitfield_p;
|
|||||||
((_bytes + (BITFIELD_WORD_SIZE-1)) / BITFIELD_WORD_SIZE)
|
((_bytes + (BITFIELD_WORD_SIZE-1)) / BITFIELD_WORD_SIZE)
|
||||||
|
|
||||||
/** Return the bit value ''idx'' in array ''b'' */
|
/** Return the bit value ''idx'' in array ''b'' */
|
||||||
static inline int bit_get(bitfield_p b, uint64_t idx)
|
static inline int bit_get(bitfield_p b, uint64_t idx) {
|
||||||
{
|
|
||||||
return (BIT_WORD(b, idx) >> (idx & (BITS_PER_WORD-1))) & 1;
|
return (BIT_WORD(b, idx) >> (idx & (BITS_PER_WORD-1))) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return 1 if the bit at ''idx'' in array ''b'' is set */
|
/** Return 1 if the bit at ''idx'' in array ''b'' is set */
|
||||||
static inline int bit_is_set(bitfield_p b, uint64_t idx)
|
static inline int bit_is_set(bitfield_p b, uint64_t idx) {
|
||||||
{
|
|
||||||
return bit_get(b, idx);
|
return bit_get(b, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return 1 if the bit at ''idx'' in array ''b'' is clear */
|
/** Return 1 if the bit at ''idx'' in array ''b'' is clear */
|
||||||
static inline int bit_is_clear(bitfield_p b, uint64_t idx)
|
static inline int bit_is_clear(bitfield_p b, uint64_t idx) {
|
||||||
{
|
|
||||||
return !bit_get(b, idx);
|
return !bit_get(b, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tests whether the bit at ''idx'' in array ''b'' has value ''value'' */
|
/** Tests whether the bit at ''idx'' in array ''b'' has value ''value'' */
|
||||||
static inline int bit_has_value(bitfield_p b, uint64_t idx, int value)
|
static inline int bit_has_value(bitfield_p b, uint64_t idx, int value) {
|
||||||
{
|
|
||||||
return bit_get(b, idx) == !!value;
|
return bit_get(b, idx) == !!value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the bit ''idx'' in array ''b'' */
|
/** Sets the bit ''idx'' in array ''b'' */
|
||||||
static inline void bit_set(bitfield_p b, uint64_t idx)
|
static inline void bit_set(bitfield_p b, uint64_t idx) {
|
||||||
{
|
|
||||||
BIT_WORD(b, idx) |= BIT_MASK(idx);
|
BIT_WORD(b, idx) |= BIT_MASK(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears the bit ''idx'' in array ''b'' */
|
/** Clears the bit ''idx'' in array ''b'' */
|
||||||
static inline void bit_clear(bitfield_p b, uint64_t idx)
|
static inline void bit_clear(bitfield_p b, uint64_t idx) {
|
||||||
{
|
|
||||||
BIT_WORD(b, idx) &= ~BIT_MASK(idx);
|
BIT_WORD(b, idx) &= ~BIT_MASK(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets ''len'' bits in array ''b'' starting at offset ''from'' */
|
/** Sets ''len'' bits in array ''b'' starting at offset ''from'' */
|
||||||
static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len)
|
static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len)
|
||||||
{
|
{
|
||||||
@@ -83,10 +72,8 @@ static inline void bit_set_range(bitfield_p b, uint64_t from, uint64_t len)
|
|||||||
bit_set( b, from++ );
|
bit_set( b, from++ );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears ''len'' bits in array ''b'' starting at offset ''from'' */
|
/** Clears ''len'' bits in array ''b'' starting at offset ''from'' */
|
||||||
static inline void bit_clear_range(bitfield_p b, uint64_t from,
|
static inline void bit_clear_range(bitfield_p b, uint64_t from, uint64_t len)
|
||||||
uint64_t len)
|
|
||||||
{
|
{
|
||||||
for ( ; (from % BITS_PER_WORD) != 0 && len > 0 ; len-- ) {
|
for ( ; (from % BITS_PER_WORD) != 0 && len > 0 ; len-- ) {
|
||||||
bit_clear( b, from++ );
|
bit_clear( b, from++ );
|
||||||
@@ -109,9 +96,7 @@ static inline void bit_clear_range(bitfield_p b, uint64_t from,
|
|||||||
* bits that are the same as the first one specified. If ''run_is_set'' is
|
* bits that are the same as the first one specified. If ''run_is_set'' is
|
||||||
* non-NULL, the value of that bit is placed into it.
|
* non-NULL, the value of that bit is placed into it.
|
||||||
*/
|
*/
|
||||||
static inline uint64_t bit_run_count(bitfield_p b, uint64_t from,
|
static inline uint64_t bit_run_count(bitfield_p b, uint64_t from, uint64_t len, int *run_is_set) {
|
||||||
uint64_t len, int *run_is_set)
|
|
||||||
{
|
|
||||||
uint64_t count = 0;
|
uint64_t count = 0;
|
||||||
int first_value = bit_get(b, from);
|
int first_value = bit_get(b, from);
|
||||||
bitfield_word_t word_match = first_value ? -1 : 0;
|
bitfield_word_t word_match = first_value ? -1 : 0;
|
||||||
@@ -200,10 +185,8 @@ static inline struct bitset *bitset_alloc(uint64_t size, int resolution)
|
|||||||
// calculate a size to allocate that is a multiple of the size of the
|
// calculate a size to allocate that is a multiple of the size of the
|
||||||
// bitfield word
|
// bitfield word
|
||||||
size_t bitfield_size =
|
size_t bitfield_size =
|
||||||
BIT_WORDS_FOR_SIZE(((size + resolution -
|
BIT_WORDS_FOR_SIZE((( size + resolution - 1 ) / resolution)) * sizeof( bitfield_word_t );
|
||||||
1) / resolution)) * sizeof(bitfield_word_t);
|
struct bitset *bitset = xmalloc(sizeof( struct bitset ) + ( bitfield_size / 8 ) );
|
||||||
struct bitset *bitset =
|
|
||||||
xmalloc(sizeof(struct bitset) + (bitfield_size / 8));
|
|
||||||
|
|
||||||
bitset->size = size;
|
bitset->size = size;
|
||||||
bitset->resolution = resolution;
|
bitset->resolution = resolution;
|
||||||
@@ -241,9 +224,12 @@ static inline void bitset_free(struct bitset *set)
|
|||||||
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
|
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
|
||||||
|
|
||||||
|
|
||||||
static inline void bitset_stream_enqueue(struct bitset *set,
|
static inline void bitset_stream_enqueue(
|
||||||
|
struct bitset * set,
|
||||||
enum bitset_stream_events event,
|
enum bitset_stream_events event,
|
||||||
uint64_t from, uint64_t len)
|
uint64_t from,
|
||||||
|
uint64_t len
|
||||||
|
)
|
||||||
{
|
{
|
||||||
struct bitset_stream * stream = set->stream;
|
struct bitset_stream * stream = set->stream;
|
||||||
|
|
||||||
@@ -268,8 +254,10 @@ static inline void bitset_stream_enqueue(struct bitset *set,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void bitset_stream_dequeue(struct bitset *set,
|
static inline void bitset_stream_dequeue(
|
||||||
struct bitset_stream_entry *out)
|
struct bitset * set,
|
||||||
|
struct bitset_stream_entry * out
|
||||||
|
)
|
||||||
{
|
{
|
||||||
struct bitset_stream * stream = set->stream;
|
struct bitset_stream * stream = set->stream;
|
||||||
struct bitset_stream_entry * dequeued;
|
struct bitset_stream_entry * dequeued;
|
||||||
@@ -310,9 +298,10 @@ static inline size_t bitset_stream_size(struct bitset *set)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint64_t bitset_stream_queued_bytes(struct bitset *set,
|
static inline uint64_t bitset_stream_queued_bytes(
|
||||||
enum bitset_stream_events
|
struct bitset * set,
|
||||||
event)
|
enum bitset_stream_events event
|
||||||
|
)
|
||||||
{
|
{
|
||||||
uint64_t total;
|
uint64_t total;
|
||||||
|
|
||||||
@@ -342,8 +331,10 @@ static inline void bitset_disable_stream(struct bitset *set)
|
|||||||
/** Set the bits in a bitset which correspond to the given bytes in the larger
|
/** Set the bits in a bitset which correspond to the given bytes in the larger
|
||||||
* file.
|
* file.
|
||||||
*/
|
*/
|
||||||
static inline void bitset_set_range(struct bitset *set,
|
static inline void bitset_set_range(
|
||||||
uint64_t from, uint64_t len)
|
struct bitset * set,
|
||||||
|
uint64_t from,
|
||||||
|
uint64_t len)
|
||||||
{
|
{
|
||||||
INT_FIRST_AND_LAST;
|
INT_FIRST_AND_LAST;
|
||||||
BITSET_LOCK;
|
BITSET_LOCK;
|
||||||
@@ -366,8 +357,10 @@ static inline void bitset_set(struct bitset *set)
|
|||||||
/** Clear the bits in a bitset which correspond to the given bytes in the
|
/** Clear the bits in a bitset which correspond to the given bytes in the
|
||||||
* larger file.
|
* larger file.
|
||||||
*/
|
*/
|
||||||
static inline void bitset_clear_range(struct bitset *set,
|
static inline void bitset_clear_range(
|
||||||
uint64_t from, uint64_t len)
|
struct bitset * set,
|
||||||
|
uint64_t from,
|
||||||
|
uint64_t len)
|
||||||
{
|
{
|
||||||
INT_FIRST_AND_LAST;
|
INT_FIRST_AND_LAST;
|
||||||
BITSET_LOCK;
|
BITSET_LOCK;
|
||||||
@@ -390,9 +383,12 @@ static inline void bitset_clear(struct bitset *set)
|
|||||||
/** As per bitset_run_count but also tells you whether the run it found was set
|
/** As per bitset_run_count but also tells you whether the run it found was set
|
||||||
* or unset, atomically.
|
* or unset, atomically.
|
||||||
*/
|
*/
|
||||||
static inline uint64_t bitset_run_count_ex(struct bitset *set,
|
static inline uint64_t bitset_run_count_ex(
|
||||||
|
struct bitset * set,
|
||||||
uint64_t from,
|
uint64_t from,
|
||||||
uint64_t len, int *run_is_set)
|
uint64_t len,
|
||||||
|
int* run_is_set
|
||||||
|
)
|
||||||
{
|
{
|
||||||
uint64_t run;
|
uint64_t run;
|
||||||
|
|
||||||
@@ -405,9 +401,7 @@ static inline uint64_t bitset_run_count_ex(struct bitset *set,
|
|||||||
INT_FIRST_AND_LAST;
|
INT_FIRST_AND_LAST;
|
||||||
|
|
||||||
BITSET_LOCK;
|
BITSET_LOCK;
|
||||||
run =
|
run = bit_run_count(set->bits, first, bitlen, run_is_set) * set->resolution;
|
||||||
bit_run_count(set->bits, first, bitlen,
|
|
||||||
run_is_set) * set->resolution;
|
|
||||||
run -= (from % set->resolution);
|
run -= (from % set->resolution);
|
||||||
BITSET_UNLOCK;
|
BITSET_UNLOCK;
|
||||||
|
|
||||||
@@ -417,8 +411,10 @@ static inline uint64_t bitset_run_count_ex(struct bitset *set,
|
|||||||
/** Counts the number of contiguous bytes that are represented as a run in
|
/** Counts the number of contiguous bytes that are represented as a run in
|
||||||
* the bit field.
|
* the bit field.
|
||||||
*/
|
*/
|
||||||
static inline uint64_t bitset_run_count(struct bitset *set,
|
static inline uint64_t bitset_run_count(
|
||||||
uint64_t from, uint64_t len)
|
struct bitset * set,
|
||||||
|
uint64_t from,
|
||||||
|
uint64_t len)
|
||||||
{
|
{
|
||||||
return bitset_run_count_ex( set, from, len, NULL );
|
return bitset_run_count_ex( set, from, len, NULL );
|
||||||
}
|
}
|
||||||
@@ -439,3 +435,4 @@ static inline int bitset_is_set_at(struct bitset *set, uint64_t at)
|
|||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -18,16 +18,14 @@
|
|||||||
|
|
||||||
// When this signal is invoked, we call shutdown() on the client fd, which
|
// When this signal is invoked, we call shutdown() on the client fd, which
|
||||||
// results in the thread being wound up
|
// results in the thread being wound up
|
||||||
void client_killswitch_hit(int signal
|
void client_killswitch_hit(int signal __attribute__ ((unused)), siginfo_t *info, void *ptr __attribute__ ((unused)))
|
||||||
__attribute__ ((unused)), siginfo_t * info,
|
|
||||||
void *ptr __attribute__ ((unused)))
|
|
||||||
{
|
{
|
||||||
int fd = info->si_value.sival_int;
|
int fd = info->si_value.sival_int;
|
||||||
warn( "Killswitch for fd %i activated, calling shutdown on socket", fd );
|
warn( "Killswitch for fd %i activated, calling shutdown on socket", fd );
|
||||||
|
|
||||||
FATAL_IF(-1 == shutdown(fd, SHUT_RDWR),
|
FATAL_IF(
|
||||||
SHOW_ERRNO
|
-1 == shutdown( fd, SHUT_RDWR ),
|
||||||
("Failed to shutdown() the socket, killing the server")
|
SHOW_ERRNO( "Failed to shutdown() the socket, killing the server" )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +53,8 @@ struct client *client_create(struct server *serve, int socket)
|
|||||||
|
|
||||||
c->stop_signal = self_pipe_create();
|
c->stop_signal = self_pipe_create();
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(timer_create
|
FATAL_IF_NEGATIVE(
|
||||||
(CLOCK_MONOTONIC, &evp, &(c->killswitch)),
|
timer_create( CLOCK_MONOTONIC, &evp, &(c->killswitch) ),
|
||||||
SHOW_ERRNO( "Failed to create killswitch timer" )
|
SHOW_ERRNO( "Failed to create killswitch timer" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -69,8 +67,7 @@ void client_signal_stop(struct client *c)
|
|||||||
{
|
{
|
||||||
NULLCHECK( c);
|
NULLCHECK( c);
|
||||||
|
|
||||||
debug("client %p: signal stop (%d, %d)", c, c->stop_signal->read_fd,
|
debug("client %p: signal stop (%d, %d)", c,c->stop_signal->read_fd, c->stop_signal->write_fd );
|
||||||
c->stop_signal->write_fd);
|
|
||||||
self_pipe_signal( c->stop_signal );
|
self_pipe_signal( c->stop_signal );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +75,8 @@ void client_destroy(struct client *client)
|
|||||||
{
|
{
|
||||||
NULLCHECK( client );
|
NULLCHECK( client );
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(timer_delete(client->killswitch),
|
FATAL_IF_NEGATIVE(
|
||||||
|
timer_delete( client->killswitch ),
|
||||||
SHOW_ERRNO( "Couldn't delete killswitch" )
|
SHOW_ERRNO( "Couldn't delete killswitch" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -121,8 +119,7 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
|
|||||||
|
|
||||||
uint64_t run = bitset_run_count(map, from, len);
|
uint64_t run = bitset_run_count(map, from, len);
|
||||||
|
|
||||||
debug("write_not_zeroes: from=%ld, len=%d, run=%d", from, len,
|
debug("write_not_zeroes: from=%ld, len=%d, run=%d", from, len, run);
|
||||||
run);
|
|
||||||
|
|
||||||
if (run > len) {
|
if (run > len) {
|
||||||
run = len;
|
run = len;
|
||||||
@@ -167,7 +164,8 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
|
|||||||
bitset_set_range( map, from, run );
|
bitset_set_range( map, from, run );
|
||||||
len -= run;
|
len -= run;
|
||||||
from += run;
|
from += run;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
char zerobuffer[block_allocation_resolution];
|
char zerobuffer[block_allocation_resolution];
|
||||||
/* not allocated, read in block_allocation_resoution */
|
/* not allocated, read in block_allocation_resoution */
|
||||||
while (run > 0) {
|
while (run > 0) {
|
||||||
@@ -185,8 +183,7 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
int all_zeros = (zerobuffer[0] == 0) &&
|
int all_zeros = (zerobuffer[0] == 0) &&
|
||||||
(0 ==
|
(0 == memcmp( zerobuffer, zerobuffer+1, blockrun-1 ));
|
||||||
memcmp(zerobuffer, zerobuffer + 1, blockrun - 1));
|
|
||||||
|
|
||||||
if ( !all_zeros ) {
|
if ( !all_zeros ) {
|
||||||
memcpy(client->mapped+from, zerobuffer, blockrun);
|
memcpy(client->mapped+from, zerobuffer, blockrun);
|
||||||
@@ -220,8 +217,7 @@ int fd_read_request(int fd, struct nbd_request_raw *out_request)
|
|||||||
|
|
||||||
/* Returns 1 if *request was filled with a valid request which we should
|
/* Returns 1 if *request was filled with a valid request which we should
|
||||||
* try to honour. 0 otherwise. */
|
* try to honour. 0 otherwise. */
|
||||||
int client_read_request(struct client *client,
|
int client_read_request( struct client * client , struct nbd_request *out_request, int * disconnected )
|
||||||
struct nbd_request *out_request, int *disconnected)
|
|
||||||
{
|
{
|
||||||
NULLCHECK( client );
|
NULLCHECK( client );
|
||||||
NULLCHECK( out_request );
|
NULLCHECK( out_request );
|
||||||
@@ -232,13 +228,11 @@ int client_read_request(struct client *client,
|
|||||||
*disconnected = 1;
|
*disconnected = 1;
|
||||||
switch( errno ){
|
switch( errno ){
|
||||||
case 0:
|
case 0:
|
||||||
warn("EOF while reading request");
|
debug( "EOF while reading request" );
|
||||||
return 0;
|
return 0;
|
||||||
case ECONNRESET:
|
case ECONNRESET:
|
||||||
warn("Connection reset while" " reading request");
|
debug( "Connection reset while"
|
||||||
return 0;
|
" reading request" );
|
||||||
case ETIMEDOUT:
|
|
||||||
warn("Connection timed out while" " reading request");
|
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
/* FIXME: I've seen this happen, but I
|
/* FIXME: I've seen this happen, but I
|
||||||
@@ -248,7 +242,9 @@ int client_read_request(struct client *client,
|
|||||||
* again. It should *probably* be an
|
* again. It should *probably* be an
|
||||||
* error() call, but I want to be sure.
|
* error() call, but I want to be sure.
|
||||||
* */
|
* */
|
||||||
fatal("Error reading request: %d, %s", errno, strerror(errno));
|
fatal("Error reading request: %d, %s",
|
||||||
|
errno,
|
||||||
|
strerror( errno ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,8 +289,7 @@ int fd_write_reply(int fd, uint64_t handle, int error)
|
|||||||
* Returns 1; we don't check for errors on the write.
|
* Returns 1; we don't check for errors on the write.
|
||||||
* TODO: Check for errors on the write.
|
* TODO: Check for errors on the write.
|
||||||
*/
|
*/
|
||||||
int client_write_reply(struct client *client, struct nbd_request *request,
|
int client_write_reply( struct client * client, struct nbd_request *request, int error )
|
||||||
int error)
|
|
||||||
{
|
{
|
||||||
return fd_write_reply( client->socket, request->handle.w, error);
|
return fd_write_reply( client->socket, request->handle.w, error);
|
||||||
}
|
}
|
||||||
@@ -308,17 +303,14 @@ void client_write_init(struct client *client, uint64_t size)
|
|||||||
memcpy( init.passwd, INIT_PASSWD, sizeof( init.passwd ) );
|
memcpy( init.passwd, INIT_PASSWD, sizeof( init.passwd ) );
|
||||||
init.magic = INIT_MAGIC;
|
init.magic = INIT_MAGIC;
|
||||||
init.size = size;
|
init.size = size;
|
||||||
/* As more features are implemented, this is the place to advertise
|
memset( init.reserved, 0, 128 );
|
||||||
* them.
|
|
||||||
*/
|
|
||||||
init.flags = FLAG_HAS_FLAGS | FLAG_SEND_FLUSH | FLAG_SEND_FUA;
|
|
||||||
memset(init.reserved, 0, 124);
|
|
||||||
|
|
||||||
nbd_h2r_init( &init, &init_raw );
|
nbd_h2r_init( &init, &init_raw );
|
||||||
|
|
||||||
ERROR_IF_NEGATIVE(writeloop
|
ERROR_IF_NEGATIVE(
|
||||||
(client->socket, &init_raw, sizeof(init_raw)),
|
writeloop(client->socket, &init_raw, sizeof(init_raw)),
|
||||||
"Couldn't send hello");
|
"Couldn't send hello"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -338,15 +330,23 @@ void client_flush(struct client *client, size_t len)
|
|||||||
size_t spliced = 0;
|
size_t spliced = 0;
|
||||||
|
|
||||||
while ( spliced < len ) {
|
while ( spliced < len ) {
|
||||||
ssize_t received = splice(client->socket, NULL,
|
ssize_t received = splice(
|
||||||
|
client->socket, NULL,
|
||||||
pipes[1], NULL,
|
pipes[1], NULL,
|
||||||
len-spliced, flags );
|
len-spliced, flags );
|
||||||
FATAL_IF_NEGATIVE(received, "splice error: %s", strerror(errno));
|
FATAL_IF_NEGATIVE( received,
|
||||||
|
"splice error: %s",
|
||||||
|
strerror(errno));
|
||||||
ssize_t junked = 0;
|
ssize_t junked = 0;
|
||||||
while( junked < received ) {
|
while( junked < received ) {
|
||||||
ssize_t junk;
|
ssize_t junk;
|
||||||
junk = splice(pipes[0], NULL, devnull, NULL, received, flags);
|
junk = splice(
|
||||||
FATAL_IF_NEGATIVE(junk, "splice error: %s", strerror(errno));
|
pipes[0], NULL,
|
||||||
|
devnull, NULL,
|
||||||
|
received, flags );
|
||||||
|
FATAL_IF_NEGATIVE( junk,
|
||||||
|
"splice error: %s",
|
||||||
|
strerror(errno));
|
||||||
junked += junk;
|
junked += junk;
|
||||||
}
|
}
|
||||||
spliced += received;
|
spliced += received;
|
||||||
@@ -378,26 +378,27 @@ int client_request_needs_reply(struct client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("request type=%" PRIu16 ", flags=%" PRIu16 ", from=%" PRIu64
|
debug(
|
||||||
", len=%" PRIu32 ", handle=0x%08X", request.type, request.flags,
|
"request type=%"PRIu32", from=%"PRIu64", len=%"PRIu32", handle=0x%08X",
|
||||||
request.from, request.len, request.handle);
|
request.type, request.from, request.len, request.handle
|
||||||
|
);
|
||||||
|
|
||||||
/* check it's not out of range. NBD protocol requires ENOSPC to be
|
/* check it's not out of range */
|
||||||
* returned in this instance
|
|
||||||
*/
|
|
||||||
if ( request.from+request.len > client->serve->size) {
|
if ( request.from+request.len > client->serve->size) {
|
||||||
warn("write request %"PRIu64"+%"PRIu32" out of range",
|
warn("write request %"PRIu64"+%"PRIu32" out of range",
|
||||||
request.from, request.len);
|
request.from, request.len
|
||||||
|
);
|
||||||
if ( request.type == REQUEST_WRITE ) {
|
if ( request.type == REQUEST_WRITE ) {
|
||||||
client_flush( client, request.len );
|
client_flush( client, request.len );
|
||||||
}
|
}
|
||||||
client_write_reply(client, &request, ENOSPC);
|
client_write_reply( client, &request, EPERM ); /* TODO: Change to ERANGE ? */
|
||||||
client->disconnect = 0;
|
client->disconnect = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (request.type) {
|
switch (request.type)
|
||||||
|
{
|
||||||
case REQUEST_READ:
|
case REQUEST_READ:
|
||||||
break;
|
break;
|
||||||
case REQUEST_WRITE:
|
case REQUEST_WRITE:
|
||||||
@@ -406,22 +407,15 @@ int client_request_needs_reply(struct client *client,
|
|||||||
debug("request disconnect");
|
debug("request disconnect");
|
||||||
client->disconnect = 1;
|
client->disconnect = 1;
|
||||||
return 0;
|
return 0;
|
||||||
case REQUEST_FLUSH:
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
/* NBD prototcol says servers SHOULD return EINVAL to unknown
|
fatal("Unknown request 0x%08X", request.type);
|
||||||
* commands */
|
|
||||||
warn("Unknown request 0x%08X", request.type);
|
|
||||||
client_write_reply(client, &request, EINVAL);
|
|
||||||
client->disconnect = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void client_reply_to_read(struct client *client,
|
void client_reply_to_read( struct client* client, struct nbd_request request )
|
||||||
struct nbd_request request)
|
|
||||||
{
|
{
|
||||||
off64_t offset;
|
off64_t offset;
|
||||||
|
|
||||||
@@ -434,74 +428,62 @@ void client_reply_to_read(struct client *client,
|
|||||||
/* If we get cut off partway through this sendfile, we don't
|
/* If we get cut off partway through this sendfile, we don't
|
||||||
* want to kill the server. This should be an error.
|
* want to kill the server. This should be an error.
|
||||||
*/
|
*/
|
||||||
ERROR_IF_NEGATIVE(sendfileloop(client->socket,
|
ERROR_IF_NEGATIVE(
|
||||||
|
sendfileloop(
|
||||||
|
client->socket,
|
||||||
client->fileno,
|
client->fileno,
|
||||||
&offset,
|
&offset,
|
||||||
request.len),
|
request.len),
|
||||||
"sendfile failed from=%ld, len=%d",
|
"sendfile failed from=%ld, len=%d",
|
||||||
offset, request.len);
|
offset,
|
||||||
|
request.len);
|
||||||
|
|
||||||
sock_set_tcp_cork( client->socket, 0 );
|
sock_set_tcp_cork( client->socket, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void client_reply_to_write(struct client *client,
|
void client_reply_to_write( struct client* client, struct nbd_request request )
|
||||||
struct nbd_request request)
|
|
||||||
{
|
{
|
||||||
debug("request write from=%" PRIu64 ", len=%" PRIu32 ", handle=0x%08X",
|
debug("request write from=%"PRIu64", len=%"PRIu32", handle=0x%08X", request.from, request.len, request.handle);
|
||||||
request.from, request.len, request.handle);
|
|
||||||
if (client->serve->allocation_map_built) {
|
if (client->serve->allocation_map_built) {
|
||||||
write_not_zeroes( client, request.from, request.len );
|
write_not_zeroes( client, request.from, request.len );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
debug("No allocation map, writing directly.");
|
debug("No allocation map, writing directly.");
|
||||||
/* If we get cut off partway through reading this data:
|
/* If we get cut off partway through reading this data:
|
||||||
* */
|
* */
|
||||||
ERROR_IF_NEGATIVE(readloop(client->socket,
|
ERROR_IF_NEGATIVE(
|
||||||
|
readloop( client->socket,
|
||||||
client->mapped + request.from,
|
client->mapped + request.from,
|
||||||
request.len),
|
request.len),
|
||||||
"reading write data failed from=%ld, len=%d",
|
"reading write data failed from=%ld, len=%d",
|
||||||
request.from, request.len);
|
request.from,
|
||||||
|
request.len
|
||||||
|
);
|
||||||
|
|
||||||
/* the allocation_map is shared between client threads, and may be
|
/* the allocation_map is shared between client threads, and may be
|
||||||
* being built. We need to reflect the write in it, as it may be in
|
* being built. We need to reflect the write in it, as it may be in
|
||||||
* a position the builder has already gone over.
|
* a position the builder has already gone over.
|
||||||
*/
|
*/
|
||||||
bitset_set_range(client->serve->allocation_map, request.from,
|
bitset_set_range(client->serve->allocation_map, request.from, request.len);
|
||||||
request.len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only flush if FUA is set -- overridden for now to force flush after each
|
if (1) /* not sure whether this is necessary... */
|
||||||
// write.
|
{
|
||||||
// if (request.flags & CMD_FLAG_FUA) {
|
/* multiple of 4K page size */
|
||||||
if (1) {
|
uint64_t from_rounded = request.from & (!0xfff);
|
||||||
/* multiple of page size */
|
|
||||||
uint64_t from_rounded =
|
|
||||||
request.from & (~(sysconf(_SC_PAGE_SIZE) - 1));
|
|
||||||
uint64_t len_rounded = request.len + (request.from - from_rounded);
|
uint64_t len_rounded = request.len + (request.from - from_rounded);
|
||||||
debug("Calling msync from=%" PRIu64 ", len=%" PRIu64 "",
|
|
||||||
from_rounded, len_rounded);
|
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(msync(client->mapped + from_rounded,
|
FATAL_IF_NEGATIVE(
|
||||||
|
msync( client->mapped + from_rounded,
|
||||||
len_rounded,
|
len_rounded,
|
||||||
MS_SYNC | MS_INVALIDATE),
|
MS_SYNC | MS_INVALIDATE),
|
||||||
"msync failed %ld %ld", request.from,
|
"msync failed %ld %ld", request.from, request.len
|
||||||
request.len);
|
);
|
||||||
}
|
}
|
||||||
client_write_reply( client, &request, 0);
|
client_write_reply( client, &request, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void client_reply_to_flush(struct client *client,
|
|
||||||
struct nbd_request request)
|
|
||||||
{
|
|
||||||
debug("request flush from=%" PRIu64 ", len=%" PRIu32 ", handle=0x%08X",
|
|
||||||
request.from, request.len, request.handle);
|
|
||||||
|
|
||||||
ERROR_IF_NEGATIVE(msync
|
|
||||||
(client->mapped, client->mapped_size,
|
|
||||||
MS_SYNC | MS_INVALIDATE), "flush failed");
|
|
||||||
|
|
||||||
client_write_reply(client, &request, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void client_reply( struct client* client, struct nbd_request request )
|
void client_reply( struct client* client, struct nbd_request request )
|
||||||
{
|
{
|
||||||
@@ -512,9 +494,6 @@ void client_reply(struct client *client, struct nbd_request request)
|
|||||||
case REQUEST_WRITE:
|
case REQUEST_WRITE:
|
||||||
client_reply_to_write( client, request );
|
client_reply_to_write( client, request );
|
||||||
break;
|
break;
|
||||||
case REQUEST_FLUSH:
|
|
||||||
client_reply_to_flush(client, request);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +514,8 @@ void client_arm_killswitch(struct client *client)
|
|||||||
|
|
||||||
debug( "Arming killswitch" );
|
debug( "Arming killswitch" );
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(timer_settime(client->killswitch, 0, &its, NULL),
|
FATAL_IF_NEGATIVE(
|
||||||
|
timer_settime( client->killswitch, 0, &its, NULL ),
|
||||||
SHOW_ERRNO( "Failed to arm killswitch" )
|
SHOW_ERRNO( "Failed to arm killswitch" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -555,7 +535,8 @@ void client_disarm_killswitch(struct client *client)
|
|||||||
|
|
||||||
debug( "Disarming killswitch" );
|
debug( "Disarming killswitch" );
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(timer_settime(client->killswitch, 0, &its, NULL),
|
FATAL_IF_NEGATIVE(
|
||||||
|
timer_settime( client->killswitch, 0, &its, NULL ),
|
||||||
SHOW_ERRNO( "Failed to disarm killswitch" )
|
SHOW_ERRNO( "Failed to disarm killswitch" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -591,9 +572,8 @@ int client_serve_request(struct client *client)
|
|||||||
if ( fd_count == 0 ) {
|
if ( fd_count == 0 ) {
|
||||||
/* This "can't ever happen" */
|
/* This "can't ever happen" */
|
||||||
fatal( "No FDs selected, and no timeout!" );
|
fatal( "No FDs selected, and no timeout!" );
|
||||||
} else if (fd_count < 0) {
|
|
||||||
fatal("Select failed");
|
|
||||||
}
|
}
|
||||||
|
else if ( fd_count < 0 ) { fatal( "Select failed" ); }
|
||||||
|
|
||||||
if ( self_pipe_fd_isset( client->stop_signal, &rfds ) ){
|
if ( self_pipe_fd_isset( client->stop_signal, &rfds ) ){
|
||||||
debug("Client received stop signal.");
|
debug("Client received stop signal.");
|
||||||
@@ -671,14 +651,13 @@ void client_cleanup(struct client *client,
|
|||||||
}
|
}
|
||||||
if (client->fileno) {
|
if (client->fileno) {
|
||||||
FATAL_IF_NEGATIVE( close(client->fileno),
|
FATAL_IF_NEGATIVE( close(client->fileno),
|
||||||
"Error closing file %d", client->fileno);
|
"Error closing file %d",
|
||||||
|
client->fileno );
|
||||||
debug("Closed client file fd %d", client->fileno );
|
debug("Closed client file fd %d", client->fileno );
|
||||||
client->fileno = -1;
|
client->fileno = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_acl_locked(client->serve)) {
|
if ( server_acl_locked( client->serve ) ) { server_unlock_acl( client->serve ); }
|
||||||
server_unlock_acl(client->serve);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,18 +668,19 @@ void *client_serve(void *client_uncast)
|
|||||||
error_set_handler((cleanup_handler*) client_cleanup, client);
|
error_set_handler((cleanup_handler*) client_cleanup, client);
|
||||||
|
|
||||||
info("client: mmaping file");
|
info("client: mmaping file");
|
||||||
FATAL_IF_NEGATIVE(open_and_mmap(client->serve->filename,
|
FATAL_IF_NEGATIVE(
|
||||||
|
open_and_mmap(
|
||||||
|
client->serve->filename,
|
||||||
&client->fileno,
|
&client->fileno,
|
||||||
&client->mapped_size,
|
NULL,
|
||||||
(void **) &client->mapped),
|
(void**) &client->mapped
|
||||||
"Couldn't open/mmap file %s: %s",
|
),
|
||||||
client->serve->filename, strerror(errno)
|
"Couldn't open/mmap file %s: %s", client->serve->filename, strerror( errno )
|
||||||
);
|
);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(madvise
|
FATAL_IF_NEGATIVE(
|
||||||
(client->mapped, client->serve->size, MADV_RANDOM),
|
madvise( client->mapped, client->serve->size, MADV_RANDOM ),
|
||||||
SHOW_ERRNO("Failed to madvise() %s",
|
SHOW_ERRNO( "Failed to madvise() %s", client->serve->filename )
|
||||||
client->serve->filename)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
debug( "Opened client file fd %d", client->fileno);
|
debug( "Opened client file fd %d", client->fileno);
|
||||||
@@ -708,7 +688,8 @@ void *client_serve(void *client_uncast)
|
|||||||
client_send_hello(client);
|
client_send_hello(client);
|
||||||
|
|
||||||
debug("client: serving requests");
|
debug("client: serving requests");
|
||||||
while (client_serve_request(client) == 0);
|
while (client_serve_request(client) == 0)
|
||||||
|
;
|
||||||
debug("client: stopped serving requests");
|
debug("client: stopped serving requests");
|
||||||
client->stopped = 1;
|
client->stopped = 1;
|
||||||
|
|
||||||
@@ -717,10 +698,10 @@ void *client_serve(void *client_uncast)
|
|||||||
server_control_arrived( client->serve );
|
server_control_arrived( client->serve );
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Cleaning client %p up normally in thread %p", client,
|
debug("Cleaning client %p up normally in thread %p", client, pthread_self());
|
||||||
pthread_self());
|
|
||||||
client_cleanup(client, 0);
|
client_cleanup(client, 0);
|
||||||
debug("Client thread done" );
|
debug("Client thread done" );
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
/** CLIENT_HANDLER_TIMEOUT
|
/** CLIENT_HANDLER_TIMEOUT
|
||||||
* This is the length of time (in seconds) any request can be outstanding for.
|
* This is the length of time (in seconds) any request can be outstanding for.
|
||||||
@@ -32,8 +31,6 @@ struct client {
|
|||||||
int fileno;
|
int fileno;
|
||||||
char* mapped;
|
char* mapped;
|
||||||
|
|
||||||
uint64_t mapped_size;
|
|
||||||
|
|
||||||
struct self_pipe * stop_signal;
|
struct self_pipe * stop_signal;
|
||||||
|
|
||||||
struct server* serve; /* FIXME: remove above duplication */
|
struct server* serve; /* FIXME: remove above duplication */
|
||||||
@@ -56,3 +53,4 @@ void client_destroy(struct client *client);
|
|||||||
void client_signal_stop( struct client * client );
|
void client_signal_stop( struct client * client );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -44,7 +44,9 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
struct control *control_create(struct flexnbd *flexnbd, const char *csn)
|
struct control * control_create(
|
||||||
|
struct flexnbd * flexnbd,
|
||||||
|
const char * csn)
|
||||||
{
|
{
|
||||||
struct control * control = xmalloc( sizeof( struct control ) );
|
struct control * control = xmalloc( sizeof( struct control ) );
|
||||||
|
|
||||||
@@ -78,7 +80,8 @@ void control_destroy(struct control *control)
|
|||||||
free( control );
|
free( control );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct control_client *control_client_create(struct flexnbd *flexnbd,
|
struct control_client * control_client_create(
|
||||||
|
struct flexnbd * flexnbd,
|
||||||
int client_fd ,
|
int client_fd ,
|
||||||
struct mbox * state_mbox )
|
struct mbox * state_mbox )
|
||||||
{
|
{
|
||||||
@@ -109,7 +112,8 @@ void control_handle_client(struct control *control, int client_fd)
|
|||||||
NULLCHECK( control );
|
NULLCHECK( control );
|
||||||
NULLCHECK( control->flexnbd );
|
NULLCHECK( control->flexnbd );
|
||||||
struct control_client * control_client =
|
struct control_client * control_client =
|
||||||
control_client_create(control->flexnbd,
|
control_client_create(
|
||||||
|
control->flexnbd,
|
||||||
client_fd ,
|
client_fd ,
|
||||||
control->mirror_state_mbox);
|
control->mirror_state_mbox);
|
||||||
|
|
||||||
@@ -128,8 +132,7 @@ void control_accept_client(struct control *control)
|
|||||||
union mysockaddr client_address;
|
union mysockaddr client_address;
|
||||||
socklen_t addrlen = sizeof( union mysockaddr );
|
socklen_t addrlen = sizeof( union mysockaddr );
|
||||||
|
|
||||||
client_fd =
|
client_fd = accept( control->control_fd, &client_address.generic, &addrlen );
|
||||||
accept(control->control_fd, &client_address.generic, &addrlen);
|
|
||||||
FATAL_IF( -1 == client_fd, "control accept failed" );
|
FATAL_IF( -1 == client_fd, "control accept failed" );
|
||||||
|
|
||||||
control_handle_client( control, client_fd );
|
control_handle_client( control, client_fd );
|
||||||
@@ -175,23 +178,25 @@ int open_control_socket(const char *socket_name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
FATAL_IF_NEGATIVE(control_fd, "Couldn't create control socket");
|
FATAL_IF_NEGATIVE(control_fd ,
|
||||||
|
"Couldn't create control socket");
|
||||||
|
|
||||||
memset(&bind_address, 0, sizeof(struct sockaddr_un));
|
memset(&bind_address, 0, sizeof(struct sockaddr_un));
|
||||||
bind_address.sun_family = AF_UNIX;
|
bind_address.sun_family = AF_UNIX;
|
||||||
strncpy(bind_address.sun_path, socket_name,
|
strncpy(bind_address.sun_path, socket_name, sizeof(bind_address.sun_path)-1);
|
||||||
sizeof(bind_address.sun_path) - 1);
|
|
||||||
|
|
||||||
//unlink(socket_name); /* ignore failure */
|
//unlink(socket_name); /* ignore failure */
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(bind
|
FATAL_IF_NEGATIVE(
|
||||||
(control_fd, &bind_address, sizeof(bind_address)),
|
bind(control_fd , &bind_address, sizeof(bind_address)),
|
||||||
"Couldn't bind control socket to %s: %s",
|
"Couldn't bind control socket to %s: %s",
|
||||||
socket_name, strerror( errno )
|
socket_name, strerror( errno )
|
||||||
);
|
);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(listen(control_fd, 5),
|
FATAL_IF_NEGATIVE(
|
||||||
"Couldn't listen on control socket");
|
listen(control_fd , 5),
|
||||||
|
"Couldn't listen on control socket"
|
||||||
|
);
|
||||||
return control_fd;
|
return control_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +229,8 @@ void control_serve(struct control *control)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void control_cleanup(struct control *control,
|
void control_cleanup(
|
||||||
|
struct control * control,
|
||||||
int fatal __attribute__((unused)) )
|
int fatal __attribute__((unused)) )
|
||||||
{
|
{
|
||||||
NULLCHECK( control );
|
NULLCHECK( control );
|
||||||
@@ -250,8 +256,7 @@ void *control_runner(void *control_uncast)
|
|||||||
|
|
||||||
#define write_socket(msg) write(client_fd, (msg "\n"), strlen((msg))+1)
|
#define write_socket(msg) write(client_fd, (msg "\n"), strlen((msg))+1)
|
||||||
|
|
||||||
void control_write_mirror_response(enum mirror_state mirror_state,
|
void control_write_mirror_response( enum mirror_state mirror_state, int client_fd )
|
||||||
int client_fd)
|
|
||||||
{
|
{
|
||||||
switch (mirror_state) {
|
switch (mirror_state) {
|
||||||
case MS_INIT:
|
case MS_INIT:
|
||||||
@@ -281,12 +286,12 @@ void control_write_mirror_response(enum mirror_state mirror_state,
|
|||||||
fatal( "Unhandled mirror state: %d", mirror_state );
|
fatal( "Unhandled mirror state: %d", mirror_state );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef write_socket
|
#undef write_socket
|
||||||
|
|
||||||
|
|
||||||
/* Call this in the thread where you want to receive the mirror state */
|
/* Call this in the thread where you want to receive the mirror state */
|
||||||
enum mirror_state control_client_mirror_wait(struct control_client *client)
|
enum mirror_state control_client_mirror_wait(
|
||||||
|
struct control_client* client)
|
||||||
{
|
{
|
||||||
NULLCHECK( client );
|
NULLCHECK( client );
|
||||||
NULLCHECK( client->mirror_state_mbox );
|
NULLCHECK( client->mirror_state_mbox );
|
||||||
@@ -340,11 +345,14 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
|
|||||||
if (linesc > 2) {
|
if (linesc > 2) {
|
||||||
if (strcmp("exit", lines[2]) == 0) {
|
if (strcmp("exit", lines[2]) == 0) {
|
||||||
action_at_finish = ACTION_EXIT;
|
action_at_finish = ACTION_EXIT;
|
||||||
} else if (strcmp("unlink", lines[2]) == 0) {
|
}
|
||||||
|
else if (strcmp( "unlink", lines[2]) == 0 ) {
|
||||||
action_at_finish = ACTION_UNLINK;
|
action_at_finish = ACTION_UNLINK;
|
||||||
} else if (strcmp("nothing", lines[2]) == 0) {
|
}
|
||||||
|
else if (strcmp("nothing", lines[2]) == 0) {
|
||||||
action_at_finish = ACTION_NOTHING;
|
action_at_finish = ACTION_NOTHING;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
write_socket("1: action must be 'exit' or 'nothing'");
|
write_socket("1: action must be 'exit' or 'nothing'");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -381,13 +389,13 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
|
|||||||
server_lock_start_mirror( serve );
|
server_lock_start_mirror( serve );
|
||||||
{
|
{
|
||||||
if ( server_mirror_can_start( serve ) ) {
|
if ( server_mirror_can_start( serve ) ) {
|
||||||
serve->mirror_super = mirror_super_create(serve->filename,
|
serve->mirror_super = mirror_super_create(
|
||||||
|
serve->filename,
|
||||||
connect_to,
|
connect_to,
|
||||||
connect_from,
|
connect_from,
|
||||||
max_Bps ,
|
max_Bps ,
|
||||||
action_at_finish,
|
action_at_finish,
|
||||||
client->
|
client->mirror_state_mbox );
|
||||||
mirror_state_mbox);
|
|
||||||
serve->mirror = serve->mirror_super->mirror;
|
serve->mirror = serve->mirror_super->mirror;
|
||||||
server_prevent_mirror_start( serve );
|
server_prevent_mirror_start( serve );
|
||||||
} else {
|
} else {
|
||||||
@@ -407,14 +415,18 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
|
|||||||
* sighandler can block the serve thread
|
* sighandler can block the serve thread
|
||||||
*/
|
*/
|
||||||
if ( serve->mirror_super ) {
|
if ( serve->mirror_super ) {
|
||||||
FATAL_IF(0 != pthread_create(&serve->mirror_super->thread,
|
FATAL_IF( 0 != pthread_create(
|
||||||
|
&serve->mirror_super->thread,
|
||||||
NULL,
|
NULL,
|
||||||
mirror_super_runner,
|
mirror_super_runner,
|
||||||
serve),
|
serve
|
||||||
"Failed to create mirror thread");
|
),
|
||||||
|
"Failed to create mirror thread"
|
||||||
|
);
|
||||||
|
|
||||||
debug("Control thread mirror super waiting");
|
debug("Control thread mirror super waiting");
|
||||||
enum mirror_state state = control_client_mirror_wait(client);
|
enum mirror_state state =
|
||||||
|
control_client_mirror_wait( client );
|
||||||
debug("Control thread writing response");
|
debug("Control thread writing response");
|
||||||
control_write_mirror_response( state, client->socket );
|
control_write_mirror_response( state, client->socket );
|
||||||
}
|
}
|
||||||
@@ -424,8 +436,7 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int control_mirror_max_bps(struct control_client *client, int linesc,
|
int control_mirror_max_bps( struct control_client* client, int linesc, char** lines )
|
||||||
char **lines)
|
|
||||||
{
|
{
|
||||||
NULLCHECK( client );
|
NULLCHECK( client );
|
||||||
NULLCHECK( client->flexnbd );
|
NULLCHECK( client->flexnbd );
|
||||||
@@ -478,7 +489,8 @@ int control_acl(struct control_client *client, int linesc, char **lines)
|
|||||||
strlen(lines[new_acl->len]));
|
strlen(lines[new_acl->len]));
|
||||||
write(client->socket, "\n", 1);
|
write(client->socket, "\n", 1);
|
||||||
acl_destroy( new_acl );
|
acl_destroy( new_acl );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
flexnbd_replace_acl( flexnbd, new_acl );
|
flexnbd_replace_acl( flexnbd, new_acl );
|
||||||
info("ACL set");
|
info("ACL set");
|
||||||
write( client->socket, "0: updated\n", 11);
|
write( client->socket, "0: updated\n", 11);
|
||||||
@@ -488,7 +500,8 @@ int control_acl(struct control_client *client, int linesc, char **lines)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int control_break(struct control_client *client,
|
int control_break(
|
||||||
|
struct control_client* client,
|
||||||
int linesc __attribute__ ((unused)),
|
int linesc __attribute__ ((unused)),
|
||||||
char** lines __attribute__((unused))
|
char** lines __attribute__((unused))
|
||||||
)
|
)
|
||||||
@@ -511,10 +524,13 @@ int control_break(struct control_client *client,
|
|||||||
|
|
||||||
if ( server_is_closed( serve ) ) {
|
if ( server_is_closed( serve ) ) {
|
||||||
info( "Mirror completed while canceling" );
|
info( "Mirror completed while canceling" );
|
||||||
write(client->socket, "1: mirror completed\n", 20);
|
write( client->socket,
|
||||||
} else {
|
"1: mirror completed\n", 20 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
info( "Mirror successfully stopped." );
|
info( "Mirror successfully stopped." );
|
||||||
write(client->socket, "0: mirror stopped\n", 18);
|
write( client->socket,
|
||||||
|
"0: mirror stopped\n", 18 );
|
||||||
result = 1;
|
result = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,7 +546,8 @@ int control_break(struct control_client *client,
|
|||||||
|
|
||||||
|
|
||||||
/** FIXME: add some useful statistics */
|
/** FIXME: add some useful statistics */
|
||||||
int control_status(struct control_client *client,
|
int control_status(
|
||||||
|
struct control_client* client,
|
||||||
int linesc __attribute__ ((unused)),
|
int linesc __attribute__ ((unused)),
|
||||||
char** lines __attribute__((unused))
|
char** lines __attribute__((unused))
|
||||||
)
|
)
|
||||||
@@ -549,14 +566,10 @@ int control_status(struct control_client *client,
|
|||||||
void control_client_cleanup(struct control_client* client,
|
void control_client_cleanup(struct control_client* client,
|
||||||
int fatal __attribute__ ((unused)) )
|
int fatal __attribute__ ((unused)) )
|
||||||
{
|
{
|
||||||
if (client->socket) {
|
if (client->socket) { close(client->socket); }
|
||||||
close(client->socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is wrongness */
|
/* This is wrongness */
|
||||||
if (server_acl_locked(client->flexnbd->serve)) {
|
if ( server_acl_locked( client->flexnbd->serve ) ) { server_unlock_acl( client->flexnbd->serve ); }
|
||||||
server_unlock_acl(client->flexnbd->serve);
|
|
||||||
}
|
|
||||||
|
|
||||||
control_client_destroy( client );
|
control_client_destroy( client );
|
||||||
}
|
}
|
||||||
@@ -571,25 +584,30 @@ void control_respond(struct control_client *client)
|
|||||||
int i, linesc;
|
int i, linesc;
|
||||||
linesc = read_lines_until_blankline(client->socket, 256, &lines);
|
linesc = read_lines_until_blankline(client->socket, 256, &lines);
|
||||||
|
|
||||||
if (linesc < 1) {
|
if (linesc < 1)
|
||||||
|
{
|
||||||
write(client->socket, "9: missing command\n", 19);
|
write(client->socket, "9: missing command\n", 19);
|
||||||
/* ignore failure */
|
/* ignore failure */
|
||||||
} else if (strcmp(lines[0], "acl") == 0) {
|
}
|
||||||
|
else if (strcmp(lines[0], "acl") == 0) {
|
||||||
info("acl command received" );
|
info("acl command received" );
|
||||||
if (control_acl(client, linesc-1, lines+1) < 0) {
|
if (control_acl(client, linesc-1, lines+1) < 0) {
|
||||||
debug("acl command failed");
|
debug("acl command failed");
|
||||||
}
|
}
|
||||||
} else if (strcmp(lines[0], "mirror") == 0) {
|
}
|
||||||
|
else if (strcmp(lines[0], "mirror") == 0) {
|
||||||
info("mirror command received" );
|
info("mirror command received" );
|
||||||
if (control_mirror(client, linesc-1, lines+1) < 0) {
|
if (control_mirror(client, linesc-1, lines+1) < 0) {
|
||||||
debug("mirror command failed");
|
debug("mirror command failed");
|
||||||
}
|
}
|
||||||
} else if (strcmp(lines[0], "break") == 0) {
|
}
|
||||||
|
else if (strcmp(lines[0], "break") == 0) {
|
||||||
info( "break command received" );
|
info( "break command received" );
|
||||||
if ( control_break( client, linesc-1, lines+1) < 0) {
|
if ( control_break( client, linesc-1, lines+1) < 0) {
|
||||||
debug( "break command failed" );
|
debug( "break command failed" );
|
||||||
}
|
}
|
||||||
} else if (strcmp(lines[0], "status") == 0) {
|
}
|
||||||
|
else if (strcmp(lines[0], "status") == 0) {
|
||||||
info("status command received" );
|
info("status command received" );
|
||||||
if (control_status(client, linesc-1, lines+1) < 0) {
|
if (control_status(client, linesc-1, lines+1) < 0) {
|
||||||
debug("status command failed");
|
debug("status command failed");
|
||||||
@@ -599,7 +617,8 @@ void control_respond(struct control_client *client)
|
|||||||
if( control_mirror_max_bps( client, linesc-1, lines+1 ) < 0 ) {
|
if( control_mirror_max_bps( client, linesc-1, lines+1 ) < 0 ) {
|
||||||
debug( "mirror_max_bps command failed" );
|
debug( "mirror_max_bps command failed" );
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
write(client->socket, "10: unknown command\n", 23);
|
write(client->socket, "10: unknown command\n", 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,3 +630,4 @@ void control_respond(struct control_client *client)
|
|||||||
control_client_cleanup(client, 0);
|
control_client_cleanup(client, 0);
|
||||||
debug("control command handled" );
|
debug("control command handled" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,15 +44,16 @@ struct control_client {
|
|||||||
struct mbox * mirror_state_mbox;
|
struct mbox * mirror_state_mbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct control *control_create(struct flexnbd *,
|
struct control * control_create(
|
||||||
|
struct flexnbd *,
|
||||||
const char * control_socket_name );
|
const char * control_socket_name );
|
||||||
void control_signal_close( struct control * );
|
void control_signal_close( struct control * );
|
||||||
void control_destroy( struct control * );
|
void control_destroy( struct control * );
|
||||||
|
|
||||||
void * control_runner( void * );
|
void * control_runner( void * );
|
||||||
|
|
||||||
void accept_control_connection(struct server *params, int client_fd,
|
void accept_control_connection(struct server* params, int client_fd, union mysockaddr* client_address);
|
||||||
union mysockaddr *client_address);
|
|
||||||
void serve_open_control_socket(struct server* params);
|
void serve_open_control_socket(struct server* params);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -61,13 +61,16 @@ int flexnbd_build_signal_fd(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void flexnbd_create_shared(struct flexnbd *flexnbd,
|
void flexnbd_create_shared(
|
||||||
|
struct flexnbd * flexnbd,
|
||||||
const char * s_ctrl_sock)
|
const char * s_ctrl_sock)
|
||||||
{
|
{
|
||||||
NULLCHECK( flexnbd );
|
NULLCHECK( flexnbd );
|
||||||
if ( s_ctrl_sock ){
|
if ( s_ctrl_sock ){
|
||||||
flexnbd->control = control_create(flexnbd, s_ctrl_sock);
|
flexnbd->control =
|
||||||
} else {
|
control_create( flexnbd, s_ctrl_sock );
|
||||||
|
}
|
||||||
|
else {
|
||||||
flexnbd->control = NULL;
|
flexnbd->control = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +78,8 @@ void flexnbd_create_shared(struct flexnbd *flexnbd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct flexnbd *flexnbd_create_serving(char *s_ip_address,
|
struct flexnbd * flexnbd_create_serving(
|
||||||
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
char* s_ctrl_sock,
|
char* s_ctrl_sock,
|
||||||
@@ -86,14 +90,17 @@ struct flexnbd *flexnbd_create_serving(char *s_ip_address,
|
|||||||
int use_killswitch)
|
int use_killswitch)
|
||||||
{
|
{
|
||||||
struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) );
|
struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) );
|
||||||
flexnbd->serve = server_create(flexnbd,
|
flexnbd->serve = server_create(
|
||||||
|
flexnbd,
|
||||||
s_ip_address,
|
s_ip_address,
|
||||||
s_port,
|
s_port,
|
||||||
s_file,
|
s_file,
|
||||||
default_deny,
|
default_deny,
|
||||||
acl_entries,
|
acl_entries,
|
||||||
s_acl_entries,
|
s_acl_entries,
|
||||||
max_nbd_clients, use_killswitch, 1);
|
max_nbd_clients,
|
||||||
|
use_killswitch,
|
||||||
|
1);
|
||||||
flexnbd_create_shared( flexnbd, s_ctrl_sock );
|
flexnbd_create_shared( flexnbd, s_ctrl_sock );
|
||||||
|
|
||||||
// Beats installing one handler per client instance
|
// Beats installing one handler per client instance
|
||||||
@@ -103,14 +110,17 @@ struct flexnbd *flexnbd_create_serving(char *s_ip_address,
|
|||||||
.sa_flags = SA_RESTART | SA_SIGINFO
|
.sa_flags = SA_RESTART | SA_SIGINFO
|
||||||
};
|
};
|
||||||
|
|
||||||
FATAL_UNLESS(0 == sigaction(CLIENT_KILLSWITCH_SIGNAL, &act, NULL),
|
FATAL_UNLESS(
|
||||||
"Installing client killswitch signal failed");
|
0 == sigaction( CLIENT_KILLSWITCH_SIGNAL, &act, NULL ),
|
||||||
|
"Installing client killswitch signal failed"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return flexnbd;
|
return flexnbd;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct flexnbd *flexnbd_create_listening(char *s_ip_address,
|
struct flexnbd * flexnbd_create_listening(
|
||||||
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
char* s_ctrl_sock,
|
char* s_ctrl_sock,
|
||||||
@@ -119,12 +129,15 @@ struct flexnbd *flexnbd_create_listening(char *s_ip_address,
|
|||||||
char** s_acl_entries )
|
char** s_acl_entries )
|
||||||
{
|
{
|
||||||
struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) );
|
struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) );
|
||||||
flexnbd->serve = server_create(flexnbd,
|
flexnbd->serve = server_create(
|
||||||
|
flexnbd,
|
||||||
s_ip_address,
|
s_ip_address,
|
||||||
s_port,
|
s_port,
|
||||||
s_file,
|
s_file,
|
||||||
default_deny,
|
default_deny,
|
||||||
acl_entries, s_acl_entries, 1, 0, 0);
|
acl_entries,
|
||||||
|
s_acl_entries,
|
||||||
|
1, 0, 0);
|
||||||
flexnbd_create_shared( flexnbd, s_ctrl_sock );
|
flexnbd_create_shared( flexnbd, s_ctrl_sock );
|
||||||
|
|
||||||
// listen can't use killswitch, as mirror may pause on sending things
|
// listen can't use killswitch, as mirror may pause on sending things
|
||||||
@@ -140,7 +153,8 @@ void flexnbd_spawn_control(struct flexnbd *flexnbd)
|
|||||||
|
|
||||||
pthread_t * control_thread = &flexnbd->control->thread;
|
pthread_t * control_thread = &flexnbd->control->thread;
|
||||||
|
|
||||||
FATAL_UNLESS(0 == pthread_create(control_thread,
|
FATAL_UNLESS( 0 == pthread_create(
|
||||||
|
control_thread,
|
||||||
NULL,
|
NULL,
|
||||||
control_runner,
|
control_runner,
|
||||||
flexnbd->control ),
|
flexnbd->control ),
|
||||||
@@ -219,7 +233,9 @@ void make_writable(const char *filename)
|
|||||||
{
|
{
|
||||||
NULLCHECK( filename );
|
NULLCHECK( filename );
|
||||||
FATAL_IF_NEGATIVE( chmod( filename, S_IWUSR ),
|
FATAL_IF_NEGATIVE( chmod( filename, S_IWUSR ),
|
||||||
"Couldn't chmod %s: %s", filename, strerror(errno));
|
"Couldn't chmod %s: %s",
|
||||||
|
filename,
|
||||||
|
strerror( errno ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -246,3 +262,4 @@ int flexnbd_serve(struct flexnbd *flexnbd)
|
|||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,8 @@ struct flexnbd {
|
|||||||
|
|
||||||
|
|
||||||
struct flexnbd * flexnbd_create(void);
|
struct flexnbd * flexnbd_create(void);
|
||||||
struct flexnbd *flexnbd_create_serving(char *s_ip_address,
|
struct flexnbd * flexnbd_create_serving(
|
||||||
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
char* s_ctrl_sock,
|
char* s_ctrl_sock,
|
||||||
@@ -39,7 +40,8 @@ struct flexnbd *flexnbd_create_serving(char *s_ip_address,
|
|||||||
int max_nbd_clients,
|
int max_nbd_clients,
|
||||||
int use_killswitch);
|
int use_killswitch);
|
||||||
|
|
||||||
struct flexnbd *flexnbd_create_listening(char *s_ip_address,
|
struct flexnbd * flexnbd_create_listening(
|
||||||
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
char* s_ctrl_sock,
|
char* s_ctrl_sock,
|
||||||
@@ -61,3 +63,4 @@ struct server *flexnbd_server(struct flexnbd *flexnbd);
|
|||||||
void flexnbd_replace_acl( struct flexnbd * flexnbd, struct acl * acl );
|
void flexnbd_replace_acl( struct flexnbd * flexnbd, struct acl * acl );
|
||||||
struct status * flexnbd_status_create( struct flexnbd * flexnbd );
|
struct status * flexnbd_status_create( struct flexnbd * flexnbd );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -22,14 +22,15 @@ void flexthread_mutex_destroy(struct flexthread_mutex *ftm)
|
|||||||
|
|
||||||
if( flexthread_mutex_held( ftm ) ) {
|
if( flexthread_mutex_held( ftm ) ) {
|
||||||
flexthread_mutex_unlock( ftm );
|
flexthread_mutex_unlock( ftm );
|
||||||
} else if ((pthread_t) NULL != ftm->holder) {
|
}
|
||||||
|
else if ( (pthread_t)NULL != ftm->holder ) {
|
||||||
/* This "should never happen": if we can try to destroy
|
/* This "should never happen": if we can try to destroy
|
||||||
* a mutex currently held by another thread, there's a
|
* a mutex currently held by another thread, there's a
|
||||||
* logic bug somewhere. I know the test here is racy,
|
* logic bug somewhere. I know the test here is racy,
|
||||||
* but there's not a lot we can do about it at this
|
* but there's not a lot we can do about it at this
|
||||||
* point.
|
* point.
|
||||||
*/
|
*/
|
||||||
fatal("Attempted to destroy a flexthread_mutex"
|
fatal( "Attempted to destroy a flexthread_mutex"\
|
||||||
" held by another thread!" );
|
" held by another thread!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,3 +72,4 @@ int flexthread_mutex_held(struct flexthread_mutex *ftm)
|
|||||||
NULLCHECK( ftm );
|
NULLCHECK( ftm );
|
||||||
return pthread_self() == ftm->holder;
|
return pthread_self() == ftm->holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -90,7 +90,8 @@ struct mirror_ctrl {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mirror *mirror_alloc(union mysockaddr *connect_to,
|
struct mirror * mirror_alloc(
|
||||||
|
union mysockaddr * connect_to,
|
||||||
union mysockaddr * connect_from,
|
union mysockaddr * connect_from,
|
||||||
uint64_t max_Bps,
|
uint64_t max_Bps,
|
||||||
enum mirror_finish_action action_at_finish,
|
enum mirror_finish_action action_at_finish,
|
||||||
@@ -143,13 +144,19 @@ void mirror_init(struct mirror *mirror, const char *filename)
|
|||||||
NULLCHECK( mirror );
|
NULLCHECK( mirror );
|
||||||
NULLCHECK( filename );
|
NULLCHECK( filename );
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(open_and_mmap(filename,
|
FATAL_IF_NEGATIVE(
|
||||||
|
open_and_mmap(
|
||||||
|
filename,
|
||||||
&map_fd,
|
&map_fd,
|
||||||
&size,
|
&size,
|
||||||
(void **) &mirror->mapped),
|
(void**) &mirror->mapped
|
||||||
"Failed to open and mmap %s", filename);
|
),
|
||||||
|
"Failed to open and mmap %s",
|
||||||
|
filename
|
||||||
|
);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(madvise(mirror->mapped, size, MADV_SEQUENTIAL),
|
FATAL_IF_NEGATIVE(
|
||||||
|
madvise( mirror->mapped, size, MADV_SEQUENTIAL ),
|
||||||
SHOW_ERRNO( "Failed to madvise() %s", filename )
|
SHOW_ERRNO( "Failed to madvise() %s", filename )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -169,7 +176,8 @@ void mirror_reset(struct mirror *mirror)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct mirror *mirror_create(const char *filename,
|
struct mirror * mirror_create(
|
||||||
|
const char * filename,
|
||||||
union mysockaddr * connect_to,
|
union mysockaddr * connect_to,
|
||||||
union mysockaddr * connect_from,
|
union mysockaddr * connect_from,
|
||||||
uint64_t max_Bps,
|
uint64_t max_Bps,
|
||||||
@@ -181,7 +189,9 @@ struct mirror *mirror_create(const char *filename,
|
|||||||
|
|
||||||
mirror = mirror_alloc( connect_to,
|
mirror = mirror_alloc( connect_to,
|
||||||
connect_from,
|
connect_from,
|
||||||
max_Bps, action_at_finish, commit_signal);
|
max_Bps,
|
||||||
|
action_at_finish,
|
||||||
|
commit_signal);
|
||||||
|
|
||||||
mirror_init( mirror, filename );
|
mirror_init( mirror, filename );
|
||||||
mirror_reset( mirror );
|
mirror_reset( mirror );
|
||||||
@@ -271,8 +281,7 @@ int mirror_connect(struct mirror *mirror, uint64_t local_size)
|
|||||||
|
|
||||||
NULLCHECK( mirror->connect_to );
|
NULLCHECK( mirror->connect_to );
|
||||||
|
|
||||||
mirror->client =
|
mirror->client = socket_connect(&mirror->connect_to->generic, connect_from);
|
||||||
socket_connect(&mirror->connect_to->generic, connect_from);
|
|
||||||
if ( 0 < mirror->client ) {
|
if ( 0 < mirror->client ) {
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
struct timeval tv = { MS_HELLO_TIME_SECS, 0};
|
struct timeval tv = { MS_HELLO_TIME_SECS, 0};
|
||||||
@@ -284,30 +293,30 @@ int mirror_connect(struct mirror *mirror, uint64_t local_size)
|
|||||||
|
|
||||||
if( FD_ISSET( mirror->client, &fds ) ){
|
if( FD_ISSET( mirror->client, &fds ) ){
|
||||||
uint64_t remote_size;
|
uint64_t remote_size;
|
||||||
uint32_t remote_flags;
|
if ( socket_nbd_read_hello( mirror->client, &remote_size ) ) {
|
||||||
if (socket_nbd_read_hello
|
|
||||||
(mirror->client, &remote_size, &remote_flags)) {
|
|
||||||
if( remote_size == local_size ){
|
if( remote_size == local_size ){
|
||||||
connected = 1;
|
connected = 1;
|
||||||
mirror_set_state( mirror, MS_GO );
|
mirror_set_state( mirror, MS_GO );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
warn("Remote size (%d) doesn't match local (%d)",
|
warn("Remote size (%d) doesn't match local (%d)",
|
||||||
remote_size, local_size );
|
remote_size, local_size );
|
||||||
mirror_set_state( mirror, MS_FAIL_SIZE_MISMATCH );
|
mirror_set_state( mirror, MS_FAIL_SIZE_MISMATCH );
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
warn( "Mirror attempt rejected." );
|
warn( "Mirror attempt rejected." );
|
||||||
mirror_set_state( mirror, MS_FAIL_REJECTED );
|
mirror_set_state( mirror, MS_FAIL_REJECTED );
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
warn( "No NBD Hello received." );
|
warn( "No NBD Hello received." );
|
||||||
mirror_set_state( mirror, MS_FAIL_NO_HELLO );
|
mirror_set_state( mirror, MS_FAIL_NO_HELLO );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connected) {
|
if ( !connected ) { close( mirror->client ); }
|
||||||
close(mirror->client);
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
warn( "Mirror failed to connect.");
|
warn( "Mirror failed to connect.");
|
||||||
mirror_set_state( mirror, MS_FAIL_CONNECT );
|
mirror_set_state( mirror, MS_FAIL_CONNECT );
|
||||||
}
|
}
|
||||||
@@ -362,15 +371,12 @@ int mirror_setup_next_xfer(struct mirror_ctrl *ctrl)
|
|||||||
* full, and stop when it's a quarter full. This stops a busy client from
|
* full, and stop when it's a quarter full. This stops a busy client from
|
||||||
* stalling a migration forever. FIXME: made-up numbers.
|
* stalling a migration forever. FIXME: made-up numbers.
|
||||||
*/
|
*/
|
||||||
if (mirror->offset < serve->size
|
if ( mirror->offset < serve->size && bitset_stream_size( serve->allocation_map ) > BITSET_STREAM_SIZE / 2 ) {
|
||||||
&& bitset_stream_size(serve->allocation_map) >
|
|
||||||
BITSET_STREAM_SIZE / 2) {
|
|
||||||
ctrl->clear_events = 1;
|
ctrl->clear_events = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
while ((mirror->offset == serve->size || ctrl->clear_events)
|
while ( ( mirror->offset == serve->size || ctrl->clear_events ) && e.event != BITSET_STREAM_SET ) {
|
||||||
&& e.event != BITSET_STREAM_SET) {
|
|
||||||
uint64_t events = bitset_stream_size( serve->allocation_map );
|
uint64_t events = bitset_stream_size( serve->allocation_map );
|
||||||
|
|
||||||
if ( events == 0 ) {
|
if ( events == 0 ) {
|
||||||
@@ -402,8 +408,7 @@ int mirror_setup_next_xfer(struct mirror_ctrl *ctrl)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Next transfer: current=%" PRIu64 ", run=%" PRIu64, current,
|
debug( "Next transfer: current=%"PRIu64", run=%"PRIu64, current, run );
|
||||||
run);
|
|
||||||
struct nbd_request req = {
|
struct nbd_request req = {
|
||||||
.magic = REQUEST_MAGIC,
|
.magic = REQUEST_MAGIC,
|
||||||
.type = REQUEST_WRITE,
|
.type = REQUEST_WRITE,
|
||||||
@@ -455,8 +460,7 @@ static void mirror_write_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Mirror write callback invoked with events %d. fd: %i", revents,
|
debug( "Mirror write callback invoked with events %d. fd: %i", revents, ctrl->mirror->client );
|
||||||
ctrl->mirror->client);
|
|
||||||
|
|
||||||
/* FIXME: We can end up corking multiple times in unusual circumstances; this
|
/* FIXME: We can end up corking multiple times in unusual circumstances; this
|
||||||
* is annoying, but harmless */
|
* is annoying, but harmless */
|
||||||
@@ -468,8 +472,7 @@ static void mirror_write_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
data_loc = ( (char*) &xfer->hdr.req_raw ) + ctrl->xfer.written;
|
data_loc = ( (char*) &xfer->hdr.req_raw ) + ctrl->xfer.written;
|
||||||
to_write = hdr_size - xfer->written;
|
to_write = hdr_size - xfer->written;
|
||||||
} else {
|
} else {
|
||||||
data_loc =
|
data_loc = ctrl->mirror->mapped + xfer->from + ( xfer->written - hdr_size );
|
||||||
ctrl->mirror->mapped + xfer->from + (xfer->written - hdr_size);
|
|
||||||
to_write = xfer->len - ( ctrl->xfer.written - hdr_size );
|
to_write = xfer->len - ( ctrl->xfer.written - hdr_size );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,14 +485,14 @@ static void mirror_write_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debug( "Wrote %"PRIu64" bytes", count );
|
debug( "Wrote %"PRIu64" bytes", count );
|
||||||
debug("to_write was %" PRIu64 ", xfer->written was %" PRIu64, to_write,
|
debug( "to_write was %"PRIu64", xfer->written was %"PRIu64, to_write, xfer->written );
|
||||||
xfer->written);
|
|
||||||
|
|
||||||
// We wrote some bytes, so reset the timer and keep track for the next pass
|
// We wrote some bytes, so reset the timer and keep track for the next pass
|
||||||
if ( count > 0 ) {
|
if ( count > 0 ) {
|
||||||
ctrl->xfer.written += count;
|
ctrl->xfer.written += count;
|
||||||
ev_timer_again( ctrl->ev_loop, &ctrl->timeout_watcher );
|
ev_timer_again( ctrl->ev_loop, &ctrl->timeout_watcher );
|
||||||
}
|
}
|
||||||
|
|
||||||
// All bytes written, so now we need to read the NBD reply back.
|
// All bytes written, so now we need to read the NBD reply back.
|
||||||
if ( ctrl->xfer.written == ctrl->xfer.len + hdr_size ) {
|
if ( ctrl->xfer.written == ctrl->xfer.len + hdr_size ) {
|
||||||
sock_set_tcp_cork( ctrl->mirror->client, 0 ) ;
|
sock_set_tcp_cork( ctrl->mirror->client, 0 ) ;
|
||||||
@@ -520,13 +523,10 @@ static void mirror_read_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
ssize_t count;
|
ssize_t count;
|
||||||
uint64_t left = sizeof( struct nbd_reply_raw ) - xfer->read;
|
uint64_t left = sizeof( struct nbd_reply_raw ) - xfer->read;
|
||||||
|
|
||||||
debug("Mirror read callback invoked with events %d. fd:%i", revents,
|
debug( "Mirror read callback invoked with events %d. fd:%i", revents, m->client );
|
||||||
m->client);
|
|
||||||
|
|
||||||
/* Start / continue reading the NBD response from the mirror. */
|
/* Start / continue reading the NBD response from the mirror. */
|
||||||
if ((count =
|
if ( ( count = read( m->client, ((void*) &xfer->hdr.rsp_raw) + xfer->read, left ) ) < 0 ) {
|
||||||
read(m->client, ((void *) &xfer->hdr.rsp_raw) + xfer->read,
|
|
||||||
left)) < 0) {
|
|
||||||
if ( errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR ) {
|
if ( errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR ) {
|
||||||
warn( SHOW_ERRNO( "Couldn't read from listener" ) );
|
warn( SHOW_ERRNO( "Couldn't read from listener" ) );
|
||||||
ev_break( loop, EVBREAK_ONE );
|
ev_break( loop, EVBREAK_ONE );
|
||||||
@@ -540,12 +540,12 @@ static void mirror_read_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
ev_break( loop, EVBREAK_ONE );
|
ev_break( loop, EVBREAK_ONE );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We read some bytes, so reset the timer
|
// We read some bytes, so reset the timer
|
||||||
ev_timer_again( ctrl->ev_loop, &ctrl->timeout_watcher );
|
ev_timer_again( ctrl->ev_loop, &ctrl->timeout_watcher );
|
||||||
|
|
||||||
debug( "Read %i bytes", count );
|
debug( "Read %i bytes", count );
|
||||||
debug("left was %" PRIu64 ", xfer->read was %" PRIu64, left,
|
debug( "left was %"PRIu64", xfer->read was %"PRIu64, left, xfer->read );
|
||||||
xfer->read);
|
|
||||||
xfer->read += count;
|
xfer->read += count;
|
||||||
|
|
||||||
if ( xfer->read < sizeof( struct nbd_reply_raw ) ) {
|
if ( xfer->read < sizeof( struct nbd_reply_raw ) ) {
|
||||||
@@ -602,9 +602,7 @@ static void mirror_read_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
debug( "next_xfer: %d", next_xfer );
|
debug( "next_xfer: %d", next_xfer );
|
||||||
|
|
||||||
/* Regardless of time estimates, if there's no waiting transfer, we can start closing clients down. */
|
/* Regardless of time estimates, if there's no waiting transfer, we can start closing clients down. */
|
||||||
if (!ctrl->clients_closed
|
if ( !ctrl->clients_closed && ( !next_xfer || server_mirror_eta( ctrl->serve ) < MS_CONVERGE_TIME_SECS ) ) {
|
||||||
&& (!next_xfer
|
|
||||||
|| server_mirror_eta(ctrl->serve) < MS_CONVERGE_TIME_SECS)) {
|
|
||||||
info( "Closing clients to allow mirroring to converge" );
|
info( "Closing clients to allow mirroring to converge" );
|
||||||
server_forbid_new_clients( ctrl->serve );
|
server_forbid_new_clients( ctrl->serve );
|
||||||
server_close_clients( ctrl->serve );
|
server_close_clients( ctrl->serve );
|
||||||
@@ -645,8 +643,7 @@ static void mirror_read_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mirror_timeout_cb(struct ev_loop *loop, ev_timer * w
|
static void mirror_timeout_cb( struct ev_loop *loop, ev_timer *w __attribute__((unused)), int revents )
|
||||||
__attribute__ ((unused)), int revents)
|
|
||||||
{
|
{
|
||||||
if ( !(revents & EV_TIMER ) ) {
|
if ( !(revents & EV_TIMER ) ) {
|
||||||
warn( "Mirror timeout called but no timer event signalled" );
|
warn( "Mirror timeout called but no timer event signalled" );
|
||||||
@@ -676,8 +673,7 @@ static void mirror_abandon_cb(struct ev_loop *loop, ev_io * w, int revents)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void mirror_limit_cb(struct ev_loop *loop, ev_timer * w,
|
static void mirror_limit_cb( struct ev_loop *loop, ev_timer *w, int revents )
|
||||||
int revents)
|
|
||||||
{
|
{
|
||||||
struct mirror_ctrl* ctrl = (struct mirror_ctrl*) w->data;
|
struct mirror_ctrl* ctrl = (struct mirror_ctrl*) w->data;
|
||||||
NULLCHECK( ctrl );
|
NULLCHECK( ctrl );
|
||||||
@@ -688,8 +684,7 @@ static void mirror_limit_cb(struct ev_loop *loop, ev_timer * w,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( mirror_should_wait( ctrl ) ) {
|
if ( mirror_should_wait( ctrl ) ) {
|
||||||
debug("max_bps exceeded, waiting",
|
debug( "max_bps exceeded, waiting", ctrl->mirror->max_bytes_per_second );
|
||||||
ctrl->mirror->max_bytes_per_second);
|
|
||||||
ev_timer_again( loop, w );
|
ev_timer_again( loop, w );
|
||||||
} else {
|
} else {
|
||||||
/* We're below the limit, so do the next request */
|
/* We're below the limit, so do the next request */
|
||||||
@@ -706,8 +701,7 @@ static void mirror_limit_cb(struct ev_loop *loop, ev_timer * w,
|
|||||||
* if it has, start migrating. If it's not finished, then enabling the bitset
|
* if it has, start migrating. If it's not finished, then enabling the bitset
|
||||||
* stream does not go well for us.
|
* stream does not go well for us.
|
||||||
*/
|
*/
|
||||||
static void mirror_begin_cb(struct ev_loop *loop, ev_timer * w,
|
static void mirror_begin_cb( struct ev_loop *loop, ev_timer *w, int revents )
|
||||||
int revents)
|
|
||||||
{
|
{
|
||||||
struct mirror_ctrl* ctrl = (struct mirror_ctrl*) w->data;
|
struct mirror_ctrl* ctrl = (struct mirror_ctrl*) w->data;
|
||||||
NULLCHECK( ctrl );
|
NULLCHECK( ctrl );
|
||||||
@@ -717,8 +711,7 @@ static void mirror_begin_cb(struct ev_loop *loop, ev_timer * w,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctrl->serve->allocation_map_built
|
if ( ctrl->serve->allocation_map_built || ctrl->serve->allocation_map_not_built ) {
|
||||||
|| ctrl->serve->allocation_map_not_built) {
|
|
||||||
info( "allocation map builder is finished, beginning migration" );
|
info( "allocation map builder is finished, beginning migration" );
|
||||||
ev_timer_stop( loop, w );
|
ev_timer_stop( loop, w );
|
||||||
/* Start by writing xfer 0 to the listener */
|
/* Start by writing xfer 0 to the listener */
|
||||||
@@ -803,8 +796,10 @@ void mirror_run(struct server *serve)
|
|||||||
ctrl.abandon_watcher.data = (void*) &ctrl;
|
ctrl.abandon_watcher.data = (void*) &ctrl;
|
||||||
ev_io_start( ctrl.ev_loop, &ctrl.abandon_watcher );
|
ev_io_start( ctrl.ev_loop, &ctrl.abandon_watcher );
|
||||||
|
|
||||||
ERROR_UNLESS(mirror_setup_next_xfer(&ctrl),
|
ERROR_UNLESS(
|
||||||
"Couldn't find first transfer for mirror!");
|
mirror_setup_next_xfer( &ctrl ),
|
||||||
|
"Couldn't find first transfer for mirror!"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if ( serve->allocation_map_built ) {
|
if ( serve->allocation_map_built ) {
|
||||||
@@ -835,8 +830,7 @@ void mirror_run(struct server *serve)
|
|||||||
* it to something sane - they just terminate the event loop with state !=
|
* it to something sane - they just terminate the event loop with state !=
|
||||||
* MS_DONE. We re-allow new clients here if necessary.
|
* MS_DONE. We re-allow new clients here if necessary.
|
||||||
*/
|
*/
|
||||||
if (m->action_at_finish == ACTION_NOTHING
|
if ( m->action_at_finish == ACTION_NOTHING || m->commit_state != MS_DONE ) {
|
||||||
|| m->commit_state != MS_DONE) {
|
|
||||||
server_allow_new_clients( serve );
|
server_allow_new_clients( serve );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -902,9 +896,7 @@ void *mirror_runner(void *serve_params_uncast)
|
|||||||
time_t start_time = time(NULL);
|
time_t start_time = time(NULL);
|
||||||
int connected = mirror_connect( mirror, serve->size );
|
int connected = mirror_connect( mirror, serve->size );
|
||||||
mirror_signal_commit( mirror );
|
mirror_signal_commit( mirror );
|
||||||
if (!connected) {
|
if ( !connected ) { goto abandon_mirror; }
|
||||||
goto abandon_mirror;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* After this point, if we see a failure we need to disconnect
|
/* After this point, if we see a failure we need to disconnect
|
||||||
* and retry everything from mirror_set_state(_, MS_INIT), but
|
* and retry everything from mirror_set_state(_, MS_INIT), but
|
||||||
@@ -940,33 +932,38 @@ void *mirror_runner(void *serve_params_uncast)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct mirror_super *mirror_super_create(const char *filename,
|
struct mirror_super * mirror_super_create(
|
||||||
|
const char * filename,
|
||||||
union mysockaddr * connect_to,
|
union mysockaddr * connect_to,
|
||||||
union mysockaddr * connect_from,
|
union mysockaddr * connect_from,
|
||||||
uint64_t max_Bps,
|
uint64_t max_Bps,
|
||||||
enum mirror_finish_action
|
enum mirror_finish_action action_at_finish,
|
||||||
action_at_finish,
|
|
||||||
struct mbox * state_mbox)
|
struct mbox * state_mbox)
|
||||||
{
|
{
|
||||||
struct mirror_super * super = xmalloc( sizeof( struct mirror_super) );
|
struct mirror_super * super = xmalloc( sizeof( struct mirror_super) );
|
||||||
super->mirror = mirror_create(filename,
|
super->mirror = mirror_create(
|
||||||
|
filename,
|
||||||
connect_to,
|
connect_to,
|
||||||
connect_from,
|
connect_from,
|
||||||
max_Bps,
|
max_Bps,
|
||||||
action_at_finish, mbox_create());
|
action_at_finish,
|
||||||
|
mbox_create() ) ;
|
||||||
super->state_mbox = state_mbox;
|
super->state_mbox = state_mbox;
|
||||||
return super;
|
return super;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Post the current state of the mirror into super->state_mbox.*/
|
/* Post the current state of the mirror into super->state_mbox.*/
|
||||||
void mirror_super_signal_committed(struct mirror_super *super,
|
void mirror_super_signal_committed(
|
||||||
|
struct mirror_super * super ,
|
||||||
enum mirror_state commit_state )
|
enum mirror_state commit_state )
|
||||||
{
|
{
|
||||||
NULLCHECK( super );
|
NULLCHECK( super );
|
||||||
NULLCHECK( super->state_mbox );
|
NULLCHECK( super->state_mbox );
|
||||||
|
|
||||||
mbox_post_mirror_state(super->state_mbox, commit_state);
|
mbox_post_mirror_state(
|
||||||
|
super->state_mbox,
|
||||||
|
commit_state );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1000,7 +997,8 @@ void *mirror_super_runner(void *serve_uncast)
|
|||||||
struct mirror_super * super = serve->mirror_super;
|
struct mirror_super * super = serve->mirror_super;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
FATAL_IF(0 != pthread_create(&mirror->thread,
|
FATAL_IF( 0 != pthread_create(
|
||||||
|
&mirror->thread,
|
||||||
NULL,
|
NULL,
|
||||||
mirror_runner,
|
mirror_runner,
|
||||||
serve),
|
serve),
|
||||||
@@ -1020,7 +1018,9 @@ void *mirror_super_runner(void *serve_uncast)
|
|||||||
should_retry = *commit_state == MS_GO;
|
should_retry = *commit_state == MS_GO;
|
||||||
|
|
||||||
/* Only send this signal the first time */
|
/* Only send this signal the first time */
|
||||||
mirror_super_signal_committed(super, *commit_state);
|
mirror_super_signal_committed(
|
||||||
|
super,
|
||||||
|
*commit_state);
|
||||||
debug("Mirror supervisor committed");
|
debug("Mirror supervisor committed");
|
||||||
}
|
}
|
||||||
/* We only care about the value of the commit signal on
|
/* We only care about the value of the commit signal on
|
||||||
|
@@ -127,13 +127,15 @@ struct mirror_super {
|
|||||||
struct server;
|
struct server;
|
||||||
struct flexnbd;
|
struct flexnbd;
|
||||||
|
|
||||||
struct mirror_super *mirror_super_create(const char *filename,
|
struct mirror_super * mirror_super_create(
|
||||||
|
const char * filename,
|
||||||
union mysockaddr * connect_to,
|
union mysockaddr * connect_to,
|
||||||
union mysockaddr * connect_from,
|
union mysockaddr * connect_from,
|
||||||
uint64_t max_Bps,
|
uint64_t max_Bps,
|
||||||
enum mirror_finish_action
|
enum mirror_finish_action action_at_finish,
|
||||||
action_at_finish,
|
struct mbox * state_mbox
|
||||||
struct mbox *state_mbox);
|
);
|
||||||
void * mirror_super_runner( void * serve_uncast );
|
void * mirror_super_runner( void * serve_uncast );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@ static struct option serve_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char serve_short_options[] = "hl:p:f:s:dk" SOPT_QUIET SOPT_VERBOSE;
|
static char serve_short_options[] = "hl:p:f:s:dk" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char serve_help_text[] =
|
static char serve_help_text[] =
|
||||||
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
|
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
|
||||||
@@ -29,9 +28,10 @@ static char serve_help_text[] =
|
|||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n"
|
||||||
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
|
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
|
||||||
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
|
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
|
||||||
"\t--" OPT_KILLSWITCH
|
"\t--" OPT_KILLSWITCH",-k \tKill the server if a request takes 120 seconds.\n"
|
||||||
",-k \tKill the server if a request takes 120 seconds.\n" SOCK_LINE
|
SOCK_LINE
|
||||||
VERBOSE_LINE QUIET_LINE;
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
|
|
||||||
static struct option listen_options[] = {
|
static struct option listen_options[] = {
|
||||||
@@ -45,7 +45,6 @@ static struct option listen_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char listen_short_options[] = "hl:p:f:s:d" SOPT_QUIET SOPT_VERBOSE;
|
static char listen_short_options[] = "hl:p:f:s:d" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char listen_help_text[] =
|
static char listen_help_text[] =
|
||||||
"Usage: flexnbd " CMD_LISTEN " <options> [<acl_address>*]\n\n"
|
"Usage: flexnbd " CMD_LISTEN " <options> [<acl_address>*]\n\n"
|
||||||
@@ -55,7 +54,9 @@ static char listen_help_text[] =
|
|||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to listen on.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to listen on.\n"
|
||||||
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
|
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
|
||||||
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
|
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
|
||||||
SOCK_LINE VERBOSE_LINE QUIET_LINE;
|
SOCK_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
static struct option read_options[] = {
|
static struct option read_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
@@ -68,7 +69,6 @@ static struct option read_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char read_short_options[] = "hl:p:F:S:b:" SOPT_QUIET SOPT_VERBOSE;
|
static char read_short_options[] = "hl:p:F:S:b:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char read_help_text[] =
|
static char read_help_text[] =
|
||||||
"Usage: flexnbd " CMD_READ " <options>\n\n"
|
"Usage: flexnbd " CMD_READ " <options>\n\n"
|
||||||
@@ -78,7 +78,9 @@ static char read_help_text[] =
|
|||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n"
|
||||||
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n"
|
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n"
|
||||||
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n"
|
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n"
|
||||||
BIND_LINE VERBOSE_LINE QUIET_LINE;
|
BIND_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
|
|
||||||
static struct option *write_options = read_options;
|
static struct option *write_options = read_options;
|
||||||
@@ -91,7 +93,9 @@ static char write_help_text[] =
|
|||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n"
|
||||||
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n"
|
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n"
|
||||||
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n"
|
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n"
|
||||||
BIND_LINE VERBOSE_LINE QUIET_LINE;
|
BIND_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
static struct option acl_options[] = {
|
static struct option acl_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
@@ -100,12 +104,14 @@ static struct option acl_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char acl_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
|
static char acl_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char acl_help_text[] =
|
static char acl_help_text[] =
|
||||||
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
|
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
|
||||||
"Set the access control list for a server with control socket SOCK.\n\n"
|
"Set the access control list for a server with control socket SOCK.\n\n"
|
||||||
HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
|
HELP_LINE
|
||||||
|
SOCK_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
static struct option mirror_speed_options[] = {
|
static struct option mirror_speed_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
@@ -115,12 +121,15 @@ static struct option mirror_speed_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char mirror_speed_short_options[] = "hs:m:" SOPT_QUIET SOPT_VERBOSE;
|
static char mirror_speed_short_options[] = "hs:m:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char mirror_speed_help_text[] =
|
static char mirror_speed_help_text[] =
|
||||||
"Usage: flexnbd " CMD_MIRROR_SPEED " <options>\n\n"
|
"Usage: flexnbd " CMD_MIRROR_SPEED " <options>\n\n"
|
||||||
"Set the maximum speed of a migration from a mirring server listening on SOCK.\n\n"
|
"Set the maximum speed of a migration from a mirring server listening on SOCK.\n\n"
|
||||||
HELP_LINE SOCK_LINE MAX_SPEED_LINE VERBOSE_LINE QUIET_LINE;
|
HELP_LINE
|
||||||
|
SOCK_LINE
|
||||||
|
MAX_SPEED_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
static struct option mirror_options[] = {
|
static struct option mirror_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
@@ -133,7 +142,6 @@ static struct option mirror_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char mirror_short_options[] = "hs:l:p:ub:" SOPT_QUIET SOPT_VERBOSE;
|
static char mirror_short_options[] = "hs:l:p:ub:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char mirror_help_text[] =
|
static char mirror_help_text[] =
|
||||||
"Usage: flexnbd " CMD_MIRROR " <options>\n\n"
|
"Usage: flexnbd " CMD_MIRROR " <options>\n\n"
|
||||||
@@ -143,7 +151,9 @@ static char mirror_help_text[] =
|
|||||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n"
|
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n"
|
||||||
SOCK_LINE
|
SOCK_LINE
|
||||||
"\t--" OPT_UNLINK ",-u\tUnlink the local file when done.\n"
|
"\t--" OPT_UNLINK ",-u\tUnlink the local file when done.\n"
|
||||||
BIND_LINE VERBOSE_LINE QUIET_LINE;
|
BIND_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
static struct option break_options[] = {
|
static struct option break_options[] = {
|
||||||
GETOPT_HELP,
|
GETOPT_HELP,
|
||||||
@@ -152,12 +162,14 @@ static struct option break_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char break_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
|
static char break_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char break_help_text[] =
|
static char break_help_text[] =
|
||||||
"Usage: flexnbd " CMD_BREAK " <options>\n\n"
|
"Usage: flexnbd " CMD_BREAK " <options>\n\n"
|
||||||
"Stop mirroring from the server with control socket SOCK.\n\n"
|
"Stop mirroring from the server with control socket SOCK.\n\n"
|
||||||
HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
|
HELP_LINE
|
||||||
|
SOCK_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
|
|
||||||
static struct option status_options[] = {
|
static struct option status_options[] = {
|
||||||
@@ -167,12 +179,14 @@ static struct option status_options[] = {
|
|||||||
GETOPT_VERBOSE,
|
GETOPT_VERBOSE,
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static char status_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
|
static char status_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char status_help_text[] =
|
static char status_help_text[] =
|
||||||
"Usage: flexnbd " CMD_STATUS " <options>\n\n"
|
"Usage: flexnbd " CMD_STATUS " <options>\n\n"
|
||||||
"Get the status for a server with control socket SOCK.\n\n"
|
"Get the status for a server with control socket SOCK.\n\n"
|
||||||
HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE;
|
HELP_LINE
|
||||||
|
SOCK_LINE
|
||||||
|
VERBOSE_LINE
|
||||||
|
QUIET_LINE;
|
||||||
|
|
||||||
char help_help_text_arr[] =
|
char help_help_text_arr[] =
|
||||||
"Usage: flexnbd <cmd> [cmd options]\n\n"
|
"Usage: flexnbd <cmd> [cmd options]\n\n"
|
||||||
@@ -186,7 +200,8 @@ char help_help_text_arr[] =
|
|||||||
"\tflexnbd mirror-speed\n"
|
"\tflexnbd mirror-speed\n"
|
||||||
"\tflexnbd break\n"
|
"\tflexnbd break\n"
|
||||||
"\tflexnbd status\n"
|
"\tflexnbd status\n"
|
||||||
"\tflexnbd help\n\n" "See flexnbd help <cmd> for further info\n";
|
"\tflexnbd help\n\n"
|
||||||
|
"See flexnbd help <cmd> for further info\n";
|
||||||
/* Slightly odd array/pointer pair to stop the compiler from complaining
|
/* Slightly odd array/pointer pair to stop the compiler from complaining
|
||||||
* about symbol sizes
|
* about symbol sizes
|
||||||
*/
|
*/
|
||||||
@@ -199,8 +214,7 @@ void do_write(struct mode_readwrite_params *params);
|
|||||||
void do_remote_command(char* command, char* mode, int argc, char** argv);
|
void do_remote_command(char* command, char* mode, int argc, char** argv);
|
||||||
|
|
||||||
|
|
||||||
void read_serve_param(int c, char **ip_addr, char **ip_port, char **file,
|
void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char **sock, int *default_deny, int *use_killswitch )
|
||||||
char **sock, int *default_deny, int *use_killswitch)
|
|
||||||
{
|
{
|
||||||
switch(c){
|
switch(c){
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -240,7 +254,9 @@ void read_serve_param(int c, char **ip_addr, char **ip_port, char **file,
|
|||||||
void read_listen_param( int c,
|
void read_listen_param( int c,
|
||||||
char **ip_addr,
|
char **ip_addr,
|
||||||
char **ip_port,
|
char **ip_port,
|
||||||
char **file, char **sock, int *default_deny)
|
char **file,
|
||||||
|
char **sock,
|
||||||
|
int *default_deny )
|
||||||
{
|
{
|
||||||
switch(c){
|
switch(c){
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -273,9 +289,7 @@ void read_listen_param(int c,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_readwrite_param(int c, char **ip_addr, char **ip_port,
|
void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **bind_addr, char **from, char **size, char *err_text )
|
||||||
char **bind_addr, char **from, char **size,
|
|
||||||
char *err_text)
|
|
||||||
{
|
{
|
||||||
switch(c){
|
switch(c){
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -334,7 +348,11 @@ void read_acl_param(int c, char **sock)
|
|||||||
read_sock_param( c, sock, acl_help_text );
|
read_sock_param( c, sock, acl_help_text );
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_mirror_speed_param(int c, char **sock, char **max_speed)
|
void read_mirror_speed_param(
|
||||||
|
int c,
|
||||||
|
char **sock,
|
||||||
|
char **max_speed
|
||||||
|
)
|
||||||
{
|
{
|
||||||
switch( c ) {
|
switch( c ) {
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -359,10 +377,13 @@ void read_mirror_speed_param(int c, char **sock, char **max_speed)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_mirror_param(int c,
|
void read_mirror_param(
|
||||||
|
int c,
|
||||||
char **sock,
|
char **sock,
|
||||||
char **ip_addr,
|
char **ip_addr,
|
||||||
char **ip_port, int *unlink, char **bind_addr)
|
char **ip_port,
|
||||||
|
int *unlink,
|
||||||
|
char **bind_addr )
|
||||||
{
|
{
|
||||||
switch( c ){
|
switch( c ){
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -438,14 +459,10 @@ int mode_serve(int argc, char *argv[])
|
|||||||
struct flexnbd * flexnbd;
|
struct flexnbd * flexnbd;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long(argc, argv, serve_short_options, serve_options,
|
c = getopt_long(argc, argv, serve_short_options, serve_options, NULL);
|
||||||
NULL);
|
if ( c == -1 ) { break; }
|
||||||
if (c == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_serve_param(c, &ip_addr, &ip_port, &file, &sock,
|
read_serve_param( c, &ip_addr, &ip_port, &file, &sock, &default_deny, &use_killswitch );
|
||||||
&default_deny, &use_killswitch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == ip_addr || NULL == ip_port ) {
|
if ( NULL == ip_addr || NULL == ip_port ) {
|
||||||
@@ -456,14 +473,9 @@ int mode_serve(int argc, char *argv[])
|
|||||||
err = 1;
|
err = 1;
|
||||||
fprintf( stderr, "--file is required\n" );
|
fprintf( stderr, "--file is required\n" );
|
||||||
}
|
}
|
||||||
if (err) {
|
if ( err ) { exit_err( serve_help_text ); }
|
||||||
exit_err(serve_help_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
flexnbd =
|
flexnbd = flexnbd_create_serving( ip_addr, ip_port, file, sock, default_deny, argc - optind, argv + optind, MAX_NBD_CLIENTS, use_killswitch );
|
||||||
flexnbd_create_serving(ip_addr, ip_port, file, sock, default_deny,
|
|
||||||
argc - optind, argv + optind,
|
|
||||||
MAX_NBD_CLIENTS, use_killswitch);
|
|
||||||
info( "Serving file %s", file );
|
info( "Serving file %s", file );
|
||||||
success = flexnbd_serve( flexnbd );
|
success = flexnbd_serve( flexnbd );
|
||||||
flexnbd_destroy( flexnbd );
|
flexnbd_destroy( flexnbd );
|
||||||
@@ -487,11 +499,8 @@ int mode_listen(int argc, char *argv[])
|
|||||||
struct flexnbd * flexnbd;
|
struct flexnbd * flexnbd;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long(argc, argv, listen_short_options, listen_options,
|
c = getopt_long(argc, argv, listen_short_options, listen_options, NULL);
|
||||||
NULL);
|
if ( c == -1 ) { break; }
|
||||||
if (c == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_listen_param( c, &ip_addr, &ip_port,
|
read_listen_param( c, &ip_addr, &ip_port,
|
||||||
&file, &sock, &default_deny );
|
&file, &sock, &default_deny );
|
||||||
@@ -505,16 +514,16 @@ int mode_listen(int argc, char *argv[])
|
|||||||
err = 1;
|
err = 1;
|
||||||
fprintf( stderr, "--file is required\n" );
|
fprintf( stderr, "--file is required\n" );
|
||||||
}
|
}
|
||||||
if (err) {
|
if ( err ) { exit_err( listen_help_text ); }
|
||||||
exit_err(listen_help_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
flexnbd = flexnbd_create_listening(ip_addr,
|
flexnbd = flexnbd_create_listening(
|
||||||
|
ip_addr,
|
||||||
ip_port,
|
ip_port,
|
||||||
file,
|
file,
|
||||||
sock,
|
sock,
|
||||||
default_deny,
|
default_deny,
|
||||||
argc - optind, argv + optind);
|
argc - optind,
|
||||||
|
argv + optind);
|
||||||
success = flexnbd_serve( flexnbd );
|
success = flexnbd_serve( flexnbd );
|
||||||
flexnbd_destroy( flexnbd );
|
flexnbd_destroy( flexnbd );
|
||||||
|
|
||||||
@@ -536,25 +545,29 @@ int mode_listen(int argc, char *argv[])
|
|||||||
* char *s_length,
|
* char *s_length,
|
||||||
* char *s_filename )
|
* char *s_filename )
|
||||||
*/
|
*/
|
||||||
void params_readwrite(int write_not_read,
|
void params_readwrite(
|
||||||
|
int write_not_read,
|
||||||
struct mode_readwrite_params* out,
|
struct mode_readwrite_params* out,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_bind_address,
|
char* s_bind_address,
|
||||||
char *s_from, char *s_length_or_filename)
|
char* s_from,
|
||||||
|
char* s_length_or_filename
|
||||||
|
)
|
||||||
{
|
{
|
||||||
FATAL_IF_NULL(s_ip_address, "No IP address supplied");
|
FATAL_IF_NULL(s_ip_address, "No IP address supplied");
|
||||||
FATAL_IF_NULL(s_port, "No port number supplied");
|
FATAL_IF_NULL(s_port, "No port number supplied");
|
||||||
FATAL_IF_NULL(s_from, "No from supplied");
|
FATAL_IF_NULL(s_from, "No from supplied");
|
||||||
FATAL_IF_NULL(s_length_or_filename, "No length supplied");
|
FATAL_IF_NULL(s_length_or_filename, "No length supplied");
|
||||||
|
|
||||||
FATAL_IF_ZERO(parse_ip_to_sockaddr
|
FATAL_IF_ZERO(
|
||||||
(&out->connect_to.generic, s_ip_address),
|
parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address),
|
||||||
"Couldn't parse connection address '%s'", s_ip_address);
|
"Couldn't parse connection address '%s'",
|
||||||
|
s_ip_address
|
||||||
|
);
|
||||||
|
|
||||||
if (s_bind_address != NULL &&
|
if (s_bind_address != NULL &&
|
||||||
parse_ip_to_sockaddr(&out->connect_from.generic,
|
parse_ip_to_sockaddr(&out->connect_from.generic, s_bind_address) == 0) {
|
||||||
s_bind_address) == 0) {
|
|
||||||
fatal("Couldn't parse bind address '%s'", s_bind_address);
|
fatal("Couldn't parse bind address '%s'", s_bind_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,27 +575,30 @@ void params_readwrite(int write_not_read,
|
|||||||
|
|
||||||
long signed_from = atol(s_from);
|
long signed_from = atol(s_from);
|
||||||
FATAL_IF_NEGATIVE( signed_from,
|
FATAL_IF_NEGATIVE( signed_from,
|
||||||
"Can't read from a negative offset %d.",
|
"Can't read from a negative offset %d.", signed_from);
|
||||||
signed_from);
|
|
||||||
out->from = signed_from;
|
out->from = signed_from;
|
||||||
|
|
||||||
if (write_not_read) {
|
if (write_not_read) {
|
||||||
if (s_length_or_filename[0]-48 < 10) {
|
if (s_length_or_filename[0]-48 < 10) {
|
||||||
out->len = atol(s_length_or_filename);
|
out->len = atol(s_length_or_filename);
|
||||||
out->data_fd = 0;
|
out->data_fd = 0;
|
||||||
} else {
|
}
|
||||||
out->data_fd = open(s_length_or_filename, O_RDONLY);
|
else {
|
||||||
|
out->data_fd = open(
|
||||||
|
s_length_or_filename, O_RDONLY);
|
||||||
FATAL_IF_NEGATIVE(out->data_fd,
|
FATAL_IF_NEGATIVE(out->data_fd,
|
||||||
"Couldn't open %s", s_length_or_filename);
|
"Couldn't open %s", s_length_or_filename);
|
||||||
off64_t signed_len = lseek64(out->data_fd, 0, SEEK_END);
|
off64_t signed_len = lseek64(out->data_fd, 0, SEEK_END);
|
||||||
FATAL_IF_NEGATIVE(signed_len,
|
FATAL_IF_NEGATIVE(signed_len,
|
||||||
"Couldn't find length of %s",
|
"Couldn't find length of %s", s_length_or_filename);
|
||||||
s_length_or_filename);
|
|
||||||
out->len = signed_len;
|
out->len = signed_len;
|
||||||
FATAL_IF_NEGATIVE(lseek64(out->data_fd, 0, SEEK_SET),
|
FATAL_IF_NEGATIVE(
|
||||||
"Couldn't rewind %s", s_length_or_filename);
|
lseek64(out->data_fd, 0, SEEK_SET),
|
||||||
|
"Couldn't rewind %s", s_length_or_filename
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
out->len = atol(s_length_or_filename);
|
out->len = atol(s_length_or_filename);
|
||||||
out->data_fd = 1;
|
out->data_fd = 1;
|
||||||
}
|
}
|
||||||
@@ -602,15 +618,11 @@ int mode_read(int argc, char *argv[])
|
|||||||
struct mode_readwrite_params readwrite;
|
struct mode_readwrite_params readwrite;
|
||||||
|
|
||||||
while (1){
|
while (1){
|
||||||
c = getopt_long(argc, argv, read_short_options, read_options,
|
c = getopt_long(argc, argv, read_short_options, read_options, NULL);
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (c == -1) {
|
if ( c == -1 ) { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_readwrite_param(c, &ip_addr, &ip_port, &bind_addr, &from,
|
read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size, read_help_text );
|
||||||
&size, read_help_text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == ip_addr || NULL == ip_port ) {
|
if ( NULL == ip_addr || NULL == ip_port ) {
|
||||||
@@ -621,13 +633,10 @@ int mode_read(int argc, char *argv[])
|
|||||||
err = 1;
|
err = 1;
|
||||||
fprintf( stderr, "both --from and --size are required.\n" );
|
fprintf( stderr, "both --from and --size are required.\n" );
|
||||||
}
|
}
|
||||||
if (err) {
|
if ( err ) { exit_err( read_help_text ); }
|
||||||
exit_err(read_help_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset( &readwrite, 0, sizeof( readwrite ) );
|
memset( &readwrite, 0, sizeof( readwrite ) );
|
||||||
params_readwrite(0, &readwrite, ip_addr, ip_port, bind_addr, from,
|
params_readwrite( 0, &readwrite, ip_addr, ip_port, bind_addr, from, size );
|
||||||
size);
|
|
||||||
do_read( &readwrite );
|
do_read( &readwrite );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -645,14 +654,10 @@ int mode_write(int argc, char *argv[])
|
|||||||
struct mode_readwrite_params readwrite;
|
struct mode_readwrite_params readwrite;
|
||||||
|
|
||||||
while (1){
|
while (1){
|
||||||
c = getopt_long(argc, argv, write_short_options, write_options,
|
c = getopt_long(argc, argv, write_short_options, write_options, NULL);
|
||||||
NULL);
|
if ( c == -1 ) { break; }
|
||||||
if (c == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_readwrite_param(c, &ip_addr, &ip_port, &bind_addr, &from,
|
read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size, write_help_text );
|
||||||
&size, write_help_text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == ip_addr || NULL == ip_port ) {
|
if ( NULL == ip_addr || NULL == ip_port ) {
|
||||||
@@ -663,13 +668,10 @@ int mode_write(int argc, char *argv[])
|
|||||||
err = 1;
|
err = 1;
|
||||||
fprintf( stderr, "both --from and --size are required.\n" );
|
fprintf( stderr, "both --from and --size are required.\n" );
|
||||||
}
|
}
|
||||||
if (err) {
|
if ( err ) { exit_err( write_help_text ); }
|
||||||
exit_err(write_help_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset( &readwrite, 0, sizeof( readwrite ) );
|
memset( &readwrite, 0, sizeof( readwrite ) );
|
||||||
params_readwrite(1, &readwrite, ip_addr, ip_port, bind_addr, from,
|
params_readwrite( 1, &readwrite, ip_addr, ip_port, bind_addr, from, size );
|
||||||
size);
|
|
||||||
do_write( &readwrite );
|
do_write( &readwrite );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -681,9 +683,7 @@ int mode_acl(int argc, char *argv[])
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long( argc, argv, acl_short_options, acl_options, NULL );
|
c = getopt_long( argc, argv, acl_short_options, acl_options, NULL );
|
||||||
if (c == -1) {
|
if ( c == -1 ) { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
read_acl_param( c, &sock );
|
read_acl_param( c, &sock );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,11 +708,8 @@ int mode_mirror_speed(int argc, char *argv[])
|
|||||||
char *speed = NULL;
|
char *speed = NULL;
|
||||||
|
|
||||||
while( 1 ) {
|
while( 1 ) {
|
||||||
c = getopt_long(argc, argv, mirror_speed_short_options,
|
c = getopt_long( argc, argv, mirror_speed_short_options, mirror_speed_options, NULL );
|
||||||
mirror_speed_options, NULL);
|
if ( -1 == c ) { break; }
|
||||||
if (-1 == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
read_mirror_speed_param( c, &sock, &speed );
|
read_mirror_speed_param( c, &sock, &speed );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,15 +739,14 @@ int mode_mirror(int argc, char *argv[])
|
|||||||
remote_argv[2] = "exit";
|
remote_argv[2] = "exit";
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long(argc, argv, mirror_short_options, mirror_options,
|
c = getopt_long( argc, argv, mirror_short_options, mirror_options, NULL);
|
||||||
NULL);
|
if ( -1 == c ) { break; }
|
||||||
if (-1 == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
read_mirror_param( c,
|
read_mirror_param( c,
|
||||||
&sock,
|
&sock,
|
||||||
&remote_argv[0],
|
&remote_argv[0],
|
||||||
&remote_argv[1], &unlink, &remote_argv[3]);
|
&remote_argv[1],
|
||||||
|
&unlink,
|
||||||
|
&remote_argv[3] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL == sock ){
|
if ( NULL == sock ){
|
||||||
@@ -761,16 +757,13 @@ int mode_mirror(int argc, char *argv[])
|
|||||||
fprintf( stderr, "both --addr and --port are required.\n");
|
fprintf( stderr, "both --addr and --port are required.\n");
|
||||||
err = 1;
|
err = 1;
|
||||||
}
|
}
|
||||||
if (err) {
|
if ( err ) { exit_err( mirror_help_text ); }
|
||||||
exit_err(mirror_help_text);
|
if ( unlink ) { remote_argv[2] = "unlink"; }
|
||||||
}
|
|
||||||
if (unlink) {
|
|
||||||
remote_argv[2] = "unlink";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remote_argv[3] == NULL) {
|
if (remote_argv[3] == NULL) {
|
||||||
do_remote_command( "mirror", sock, 3, remote_argv );
|
do_remote_command( "mirror", sock, 3, remote_argv );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
do_remote_command( "mirror", sock, 4, remote_argv );
|
do_remote_command( "mirror", sock, 4, remote_argv );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,11 +777,8 @@ int mode_break(int argc, char *argv[])
|
|||||||
char *sock = NULL;
|
char *sock = NULL;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long(argc, argv, break_short_options, break_options,
|
c = getopt_long( argc, argv, break_short_options, break_options, NULL );
|
||||||
NULL);
|
if ( -1 == c ) { break; }
|
||||||
if (-1 == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
read_break_param( c, &sock );
|
read_break_param( c, &sock );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,11 +798,8 @@ int mode_status(int argc, char *argv[])
|
|||||||
char *sock = NULL;
|
char *sock = NULL;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long(argc, argv, status_short_options, status_options,
|
c = getopt_long( argc, argv, status_short_options, status_options, NULL );
|
||||||
NULL);
|
if ( -1 == c ) { break; }
|
||||||
if (-1 == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
read_status_param( c, &sock );
|
read_status_param( c, &sock );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,9 +836,7 @@ int mode_help(int argc, char *argv[])
|
|||||||
help_text = mirror_help_text;
|
help_text = mirror_help_text;
|
||||||
} else if ( IS_CMD( CMD_STATUS, cmd ) ) {
|
} else if ( IS_CMD( CMD_STATUS, cmd ) ) {
|
||||||
help_text = status_help_text;
|
help_text = status_help_text;
|
||||||
} else {
|
} else { exit_err( help_help_text ); }
|
||||||
exit_err(help_help_text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf( stdout, "%s\n", help_text );
|
fprintf( stdout, "%s\n", help_text );
|
||||||
@@ -863,27 +848,37 @@ void mode(char *mode, int argc, char **argv)
|
|||||||
{
|
{
|
||||||
if ( IS_CMD( CMD_SERVE, mode ) ) {
|
if ( IS_CMD( CMD_SERVE, mode ) ) {
|
||||||
exit( mode_serve( argc, argv ) );
|
exit( mode_serve( argc, argv ) );
|
||||||
} else if (IS_CMD(CMD_LISTEN, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_LISTEN, mode ) ) {
|
||||||
exit( mode_listen( argc, argv ) );
|
exit( mode_listen( argc, argv ) );
|
||||||
} else if (IS_CMD(CMD_READ, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_READ, mode ) ) {
|
||||||
mode_read( argc, argv );
|
mode_read( argc, argv );
|
||||||
} else if (IS_CMD(CMD_WRITE, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_WRITE, mode ) ) {
|
||||||
mode_write( argc, argv );
|
mode_write( argc, argv );
|
||||||
} else if (IS_CMD(CMD_ACL, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_ACL, mode ) ) {
|
||||||
mode_acl( argc, argv );
|
mode_acl( argc, argv );
|
||||||
} else if ( IS_CMD ( CMD_MIRROR_SPEED, mode ) ) {
|
} else if ( IS_CMD ( CMD_MIRROR_SPEED, mode ) ) {
|
||||||
mode_mirror_speed( argc, argv );
|
mode_mirror_speed( argc, argv );
|
||||||
} else if (IS_CMD(CMD_MIRROR, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_MIRROR, mode ) ) {
|
||||||
mode_mirror( argc, argv );
|
mode_mirror( argc, argv );
|
||||||
} else if (IS_CMD(CMD_BREAK, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_BREAK, mode ) ) {
|
||||||
mode_break( argc, argv );
|
mode_break( argc, argv );
|
||||||
} else if (IS_CMD(CMD_STATUS, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_STATUS, mode ) ) {
|
||||||
mode_status( argc, argv );
|
mode_status( argc, argv );
|
||||||
} else if (IS_CMD(CMD_HELP, mode)) {
|
}
|
||||||
|
else if ( IS_CMD( CMD_HELP, mode ) ) {
|
||||||
mode_help( argc-1, argv+1 );
|
mode_help( argc-1, argv+1 );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
mode_help( argc-1, argv+1 );
|
mode_help( argc-1, argv+1 );
|
||||||
exit( 1 );
|
exit( 1 );
|
||||||
}
|
}
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,8 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
struct server *server_create(struct flexnbd *flexnbd,
|
struct server * server_create (
|
||||||
|
struct flexnbd * flexnbd,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
@@ -29,7 +30,8 @@ struct server *server_create(struct flexnbd *flexnbd,
|
|||||||
int acl_entries,
|
int acl_entries,
|
||||||
char** s_acl_entries,
|
char** s_acl_entries,
|
||||||
int max_nbd_clients,
|
int max_nbd_clients,
|
||||||
int use_killswitch, int success)
|
int use_killswitch,
|
||||||
|
int success)
|
||||||
{
|
{
|
||||||
NULLCHECK( flexnbd );
|
NULLCHECK( flexnbd );
|
||||||
struct server * out;
|
struct server * out;
|
||||||
@@ -41,18 +43,19 @@ struct server *server_create(struct flexnbd *flexnbd,
|
|||||||
|
|
||||||
server_allow_new_clients( out );
|
server_allow_new_clients( out );
|
||||||
|
|
||||||
out->nbd_client =
|
out->nbd_client = xmalloc( max_nbd_clients * sizeof( struct client_tbl_entry ) );
|
||||||
xmalloc(max_nbd_clients * sizeof(struct client_tbl_entry));
|
|
||||||
out->tcp_backlog = 10; /* does this need to be settable? */
|
out->tcp_backlog = 10; /* does this need to be settable? */
|
||||||
|
|
||||||
FATAL_IF_NULL(s_ip_address, "No IP address supplied");
|
FATAL_IF_NULL(s_ip_address, "No IP address supplied");
|
||||||
FATAL_IF_NULL(s_port, "No port number supplied");
|
FATAL_IF_NULL(s_port, "No port number supplied");
|
||||||
FATAL_IF_NULL(s_file, "No filename supplied");
|
FATAL_IF_NULL(s_file, "No filename supplied");
|
||||||
NULLCHECK( s_ip_address );
|
NULLCHECK( s_ip_address );
|
||||||
FATAL_IF_ZERO(parse_ip_to_sockaddr
|
FATAL_IF_ZERO(
|
||||||
(&out->bind_to.generic, s_ip_address),
|
parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address),
|
||||||
"Couldn't parse server address '%s' (use 0 if "
|
"Couldn't parse server address '%s' (use 0 if "
|
||||||
"you want to bind to all IPs)", s_ip_address);
|
"you want to bind to all IPs)",
|
||||||
|
s_ip_address
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
out->acl = acl_create( acl_entries, s_acl_entries, default_deny );
|
out->acl = acl_create( acl_entries, s_acl_entries, default_deny );
|
||||||
@@ -107,7 +110,8 @@ void server_unlink(struct server *serve)
|
|||||||
|
|
||||||
FATAL_IF_NEGATIVE( unlink( serve->filename ),
|
FATAL_IF_NEGATIVE( unlink( serve->filename ),
|
||||||
"Failed to unlink %s: %s",
|
"Failed to unlink %s: %s",
|
||||||
serve->filename, strerror(errno));
|
serve->filename,
|
||||||
|
strerror( errno ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +155,7 @@ void server_unlock_start_mirror(struct server *serve)
|
|||||||
{
|
{
|
||||||
debug("Mirror start unlocking");
|
debug("Mirror start unlocking");
|
||||||
|
|
||||||
SERVER_UNLOCK(serve, l_start_mirror,
|
SERVER_UNLOCK( serve, l_start_mirror, "Problem with start mirror unlock" );
|
||||||
"Problem with start mirror unlock");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_start_mirror_locked( struct server * serve )
|
int server_start_mirror_locked( struct server * serve )
|
||||||
@@ -183,9 +186,8 @@ void serve_open_server_socket(struct server *params)
|
|||||||
{
|
{
|
||||||
NULLCHECK( params );
|
NULLCHECK( params );
|
||||||
|
|
||||||
params->server_fd =
|
params->server_fd = socket(params->bind_to.generic.sa_family == AF_INET ?
|
||||||
socket(params->bind_to.generic.sa_family ==
|
PF_INET : PF_INET6, SOCK_STREAM, 0);
|
||||||
AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0);
|
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE( params->server_fd, "Couldn't create server socket" );
|
FATAL_IF_NEGATIVE( params->server_fd, "Couldn't create server socket" );
|
||||||
|
|
||||||
@@ -197,32 +199,35 @@ void serve_open_server_socket(struct server *params)
|
|||||||
* problem. It's also indicative of something odd going on, so
|
* problem. It's also indicative of something odd going on, so
|
||||||
* we barf.
|
* we barf.
|
||||||
*/
|
*/
|
||||||
FATAL_IF_NEGATIVE(sock_set_reuseaddr(params->server_fd, 1),
|
FATAL_IF_NEGATIVE(
|
||||||
"Couldn't set SO_REUSEADDR");
|
sock_set_reuseaddr( params->server_fd, 1 ), "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
|
||||||
* this, again, there's something odd going on which we don't
|
* this, again, there's something odd going on which we don't
|
||||||
* understand.
|
* understand.
|
||||||
*/
|
*/
|
||||||
FATAL_IF_NEGATIVE(sock_set_tcp_nodelay(params->server_fd, 1),
|
FATAL_IF_NEGATIVE(
|
||||||
"Couldn't set TCP_NODELAY");
|
sock_set_tcp_nodelay( params->server_fd, 1 ), "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. */
|
||||||
FATAL_UNLESS_ZERO(sock_try_bind
|
FATAL_UNLESS_ZERO(
|
||||||
(params->server_fd, ¶ms->bind_to.generic),
|
sock_try_bind( params->server_fd, ¶ms->bind_to.generic ),
|
||||||
SHOW_ERRNO( "Failed to bind() socket" )
|
SHOW_ERRNO( "Failed to bind() socket" )
|
||||||
);
|
);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(listen(params->server_fd, params->tcp_backlog),
|
FATAL_IF_NEGATIVE(
|
||||||
"Couldn't listen on server socket");
|
listen(params->server_fd, params->tcp_backlog),
|
||||||
|
"Couldn't listen on server socket"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int tryjoin_client_thread(struct client_tbl_entry *entry,
|
int tryjoin_client_thread( struct client_tbl_entry *entry, int (*joinfunc)(pthread_t, void **) )
|
||||||
int (*joinfunc) (pthread_t, void **))
|
|
||||||
{
|
{
|
||||||
|
|
||||||
NULLCHECK( entry );
|
NULLCHECK( entry );
|
||||||
@@ -234,25 +239,25 @@ int tryjoin_client_thread(struct client_tbl_entry *entry,
|
|||||||
if (entry->thread != 0) {
|
if (entry->thread != 0) {
|
||||||
char s_client_address[128];
|
char s_client_address[128];
|
||||||
|
|
||||||
sockaddr_address_string(&entry->address.generic,
|
sockaddr_address_string( &entry->address.generic, &s_client_address[0], 128 );
|
||||||
&s_client_address[0], 128);
|
|
||||||
|
|
||||||
debug("%s(%p,...)",
|
debug( "%s(%p,...)", joinfunc == pthread_join ? "joining" : "tryjoining", entry->thread );
|
||||||
joinfunc == pthread_join ? "joining" : "tryjoining",
|
|
||||||
entry->thread);
|
|
||||||
int join_errno = joinfunc(entry->thread, &status);
|
int 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 ) {
|
||||||
debug("join_errno was %s, stopped was %d",
|
debug( "join_errno was %s, stopped was %d", strerror( join_errno ), entry->client->stopped );
|
||||||
strerror(join_errno), entry->client->stopped);
|
|
||||||
FATAL_UNLESS( join_errno == EBUSY,
|
FATAL_UNLESS( join_errno == EBUSY,
|
||||||
"Problem with joining thread %p: %s",
|
"Problem with joining thread %p: %s",
|
||||||
entry->thread, strerror(join_errno));
|
entry->thread,
|
||||||
} else if (join_errno == 0) {
|
strerror(join_errno) );
|
||||||
|
}
|
||||||
|
else if ( join_errno == 0 ) {
|
||||||
debug("nbd thread %016x exited (%s) with status %ld",
|
debug("nbd thread %016x exited (%s) with status %ld",
|
||||||
entry->thread, s_client_address, (uintptr_t) status);
|
entry->thread,
|
||||||
|
s_client_address,
|
||||||
|
(uintptr_t)status);
|
||||||
client_destroy( entry->client );
|
client_destroy( entry->client );
|
||||||
entry->client = NULL;
|
entry->client = NULL;
|
||||||
entry->thread = 0;
|
entry->thread = 0;
|
||||||
@@ -282,8 +287,7 @@ int cleanup_client_thread(struct client_tbl_entry *entry)
|
|||||||
return tryjoin_client_thread( entry, pthread_tryjoin_np );
|
return tryjoin_client_thread( entry, pthread_tryjoin_np );
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_client_threads(struct client_tbl_entry *entries,
|
void cleanup_client_threads( struct client_tbl_entry * entries, size_t entries_len )
|
||||||
size_t entries_len)
|
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
for( i = 0; i < entries_len; i++ ) {
|
for( i = 0; i < entries_len; i++ ) {
|
||||||
@@ -329,8 +333,6 @@ int server_count_clients(struct server *params)
|
|||||||
NULLCHECK( params );
|
NULLCHECK( params );
|
||||||
int i, count = 0;
|
int i, count = 0;
|
||||||
|
|
||||||
cleanup_client_threads(params->nbd_client, params->max_nbd_clients);
|
|
||||||
|
|
||||||
for ( i = 0 ; i < params->max_nbd_clients ; i++ ) {
|
for ( i = 0 ; i < params->max_nbd_clients ; i++ ) {
|
||||||
if ( params->nbd_client[i].thread != 0 ) {
|
if ( params->nbd_client[i].thread != 0 ) {
|
||||||
count++;
|
count++;
|
||||||
@@ -345,8 +347,7 @@ int server_count_clients(struct server *params)
|
|||||||
* to the current acl. If params->acl is NULL, the result will be 1,
|
* to the current acl. If params->acl is NULL, the result will be 1,
|
||||||
* otherwise it will be the result of acl_includes().
|
* otherwise it will be the result of acl_includes().
|
||||||
*/
|
*/
|
||||||
int server_acl_accepts(struct server *params,
|
int server_acl_accepts( struct server *params, union mysockaddr * client_address )
|
||||||
union mysockaddr *client_address)
|
|
||||||
{
|
{
|
||||||
NULLCHECK( params );
|
NULLCHECK( params );
|
||||||
NULLCHECK( client_address );
|
NULLCHECK( client_address );
|
||||||
@@ -365,7 +366,8 @@ int server_acl_accepts(struct server *params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int server_should_accept_client(struct server *params,
|
int server_should_accept_client(
|
||||||
|
struct server * params,
|
||||||
union mysockaddr * client_address,
|
union mysockaddr * client_address,
|
||||||
char *s_client_address,
|
char *s_client_address,
|
||||||
size_t s_client_address_len )
|
size_t s_client_address_len )
|
||||||
@@ -374,9 +376,9 @@ int server_should_accept_client(struct server *params,
|
|||||||
NULLCHECK( client_address );
|
NULLCHECK( client_address );
|
||||||
NULLCHECK( s_client_address );
|
NULLCHECK( s_client_address );
|
||||||
|
|
||||||
const char *result =
|
const char* result = sockaddr_address_string(
|
||||||
sockaddr_address_string(&client_address->generic, s_client_address,
|
&client_address->generic, s_client_address, s_client_address_len
|
||||||
s_client_address_len);
|
);
|
||||||
|
|
||||||
if ( NULL == result ) {
|
if ( NULL == result ) {
|
||||||
warn( "Rejecting client %s: Bad client_address", s_client_address );
|
warn( "Rejecting client %s: Bad client_address", s_client_address );
|
||||||
@@ -384,8 +386,7 @@ int server_should_accept_client(struct server *params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( !server_acl_accepts( params, client_address ) ) {
|
if ( !server_acl_accepts( params, client_address ) ) {
|
||||||
warn("Rejecting client %s: Access control error",
|
warn( "Rejecting client %s: Access control error", s_client_address );
|
||||||
s_client_address);
|
|
||||||
debug( "We %s have an acl, and default_deny is %s",
|
debug( "We %s have an acl, and default_deny is %s",
|
||||||
(params->acl ? "do" : "do not"),
|
(params->acl ? "do" : "do not"),
|
||||||
(params->acl->default_deny ? "true" : "false") );
|
(params->acl->default_deny ? "true" : "false") );
|
||||||
@@ -397,11 +398,11 @@ int server_should_accept_client(struct server *params,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int spawn_client_thread(struct client *client_params,
|
int spawn_client_thread(
|
||||||
|
struct client * client_params,
|
||||||
pthread_t *out_thread)
|
pthread_t *out_thread)
|
||||||
{
|
{
|
||||||
int result =
|
int result = pthread_create(out_thread, NULL, client_serve, client_params);
|
||||||
pthread_create(out_thread, NULL, client_serve, client_params);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -411,8 +412,10 @@ int spawn_client_thread(struct client *client_params,
|
|||||||
* to handle it. Rejects the connection if there is an ACL, and the far end's
|
* to handle it. Rejects the connection if there is an ACL, and the far end's
|
||||||
* address doesn't match, or if there are too many clients already connected.
|
* address doesn't match, or if there are too many clients already connected.
|
||||||
*/
|
*/
|
||||||
void accept_nbd_client(struct server *params,
|
void accept_nbd_client(
|
||||||
int client_fd, union mysockaddr *client_address)
|
struct server* params,
|
||||||
|
int client_fd,
|
||||||
|
union mysockaddr* client_address)
|
||||||
{
|
{
|
||||||
NULLCHECK(params);
|
NULLCHECK(params);
|
||||||
NULLCHECK(client_address);
|
NULLCHECK(client_address);
|
||||||
@@ -421,15 +424,11 @@ void accept_nbd_client(struct server *params,
|
|||||||
int slot;
|
int slot;
|
||||||
char s_client_address[64] = {0};
|
char s_client_address[64] = {0};
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(sock_set_keepalive_params
|
FATAL_IF_NEGATIVE( sock_set_keepalive_params( client_fd, CLIENT_KEEPALIVE_TIME, CLIENT_KEEPALIVE_INTVL, CLIENT_KEEPALIVE_PROBES),
|
||||||
(client_fd, CLIENT_KEEPALIVE_TIME,
|
"Error setting keepalive parameters on client socket fd %d", client_fd );
|
||||||
CLIENT_KEEPALIVE_INTVL, CLIENT_KEEPALIVE_PROBES),
|
|
||||||
"Error setting keepalive parameters on client socket fd %d",
|
|
||||||
client_fd);
|
|
||||||
|
|
||||||
|
|
||||||
if (!server_should_accept_client
|
if ( !server_should_accept_client( params, client_address, s_client_address, 64 ) ) {
|
||||||
(params, client_address, s_client_address, 64)) {
|
|
||||||
FATAL_IF_NEGATIVE( close( client_fd ),
|
FATAL_IF_NEGATIVE( close( client_fd ),
|
||||||
"Error closing client socket fd %d", client_fd );
|
"Error closing client socket fd %d", client_fd );
|
||||||
debug("Closed client socket fd %d", client_fd);
|
debug("Closed client socket fd %d", client_fd);
|
||||||
@@ -463,8 +462,7 @@ void accept_nbd_client(struct server *params,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("nbd thread %p started (%s)", params->nbd_client[slot].thread,
|
debug("nbd thread %p started (%s)", params->nbd_client[slot].thread, s_client_address);
|
||||||
s_client_address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -485,12 +483,8 @@ void server_audit_clients(struct server *serve)
|
|||||||
*/
|
*/
|
||||||
for( i = 0; i < serve->max_nbd_clients; i++ ) {
|
for( i = 0; i < serve->max_nbd_clients; i++ ) {
|
||||||
entry = &serve->nbd_client[i];
|
entry = &serve->nbd_client[i];
|
||||||
if (0 == entry->thread) {
|
if ( 0 == entry->thread ) { continue; }
|
||||||
continue;
|
if ( server_acl_accepts( serve, &entry->address ) ) { continue; }
|
||||||
}
|
|
||||||
if (server_acl_accepts(serve, &entry->address)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
client_signal_stop( entry->client );
|
client_signal_stop( entry->client );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -550,9 +544,7 @@ void server_replace_acl(struct server *serve, struct acl *new_acl)
|
|||||||
struct acl * old_acl = serve->acl;
|
struct acl * old_acl = serve->acl;
|
||||||
serve->acl = new_acl;
|
serve->acl = new_acl;
|
||||||
/* We should always have an old_acl, but just in case... */
|
/* We should always have an old_acl, but just in case... */
|
||||||
if (old_acl) {
|
if ( old_acl ) { acl_destroy( old_acl ); }
|
||||||
acl_destroy(old_acl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
server_unlock_acl( serve );
|
server_unlock_acl( serve );
|
||||||
|
|
||||||
@@ -620,13 +612,12 @@ int server_accept(struct server *params)
|
|||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(params->server_fd, &fds);
|
FD_SET(params->server_fd, &fds);
|
||||||
if (0 < signal_fd) {
|
if( 0 < signal_fd ) { FD_SET(signal_fd, &fds); }
|
||||||
FD_SET(signal_fd, &fds);
|
|
||||||
}
|
|
||||||
self_pipe_fd_set( params->close_signal, &fds );
|
self_pipe_fd_set( params->close_signal, &fds );
|
||||||
self_pipe_fd_set( params->acl_updated_signal, &fds );
|
self_pipe_fd_set( params->acl_updated_signal, &fds );
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
|
FATAL_IF_NEGATIVE(
|
||||||
|
sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
|
||||||
SHOW_ERRNO( "select() failed" )
|
SHOW_ERRNO( "select() failed" )
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -639,8 +630,7 @@ int server_accept(struct server *params)
|
|||||||
if ( 0 < signal_fd && FD_ISSET( signal_fd, &fds ) ){
|
if ( 0 < signal_fd && FD_ISSET( signal_fd, &fds ) ){
|
||||||
debug( "Stop signal received." );
|
debug( "Stop signal received." );
|
||||||
server_close_clients( params );
|
server_close_clients( params );
|
||||||
params->success = params->success
|
params->success = params->success && serve_shutdown_is_graceful( params );
|
||||||
&& serve_shutdown_is_graceful(params);
|
|
||||||
should_continue = 0;
|
should_continue = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,8 +641,7 @@ int server_accept(struct server *params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( FD_ISSET( params->server_fd, &fds ) ){
|
if ( FD_ISSET( params->server_fd, &fds ) ){
|
||||||
int client_fd =
|
int client_fd = accept( params->server_fd, &client_address.generic, &socklen );
|
||||||
accept(params->server_fd, &client_address.generic, &socklen);
|
|
||||||
|
|
||||||
if ( params->allow_new_clients ) {
|
if ( params->allow_new_clients ) {
|
||||||
debug("Accepted nbd client socket fd %d", client_fd);
|
debug("Accepted nbd client socket fd %d", client_fd);
|
||||||
@@ -687,7 +676,8 @@ void *build_allocation_map_thread(void *serve_uncast)
|
|||||||
|
|
||||||
if ( build_allocation_map( serve->allocation_map, fd ) ) {
|
if ( build_allocation_map( serve->allocation_map, fd ) ) {
|
||||||
serve->allocation_map_built = 1;
|
serve->allocation_map_built = 1;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
/* We can operate without it, but we can't free it without a race.
|
/* We can operate without it, but we can't free it without a race.
|
||||||
* All that happens if we leave it is that it gradually builds up an
|
* All that happens if we leave it is that it gradually builds up an
|
||||||
* *incomplete* record of writes. Nobody will use it, as
|
* *incomplete* record of writes. Nobody will use it, as
|
||||||
@@ -720,17 +710,9 @@ void serve_init_allocation_map(struct server *params)
|
|||||||
|
|
||||||
FATAL_IF_NEGATIVE(fd, "Couldn't open %s", params->filename );
|
FATAL_IF_NEGATIVE(fd, "Couldn't open %s", params->filename );
|
||||||
size = lseek64( fd, 0, SEEK_END );
|
size = lseek64( fd, 0, SEEK_END );
|
||||||
|
|
||||||
/* If discs are not in multiples of 512, then odd things happen,
|
|
||||||
* resulting in reads/writes past the ends of files.
|
|
||||||
*/
|
|
||||||
if (size != (size & ~0x1ff)) {
|
|
||||||
warn("file does not fit into 512-byte sectors; the end of the file will be ignored.");
|
|
||||||
size &= ~0x1ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
params->size = size;
|
params->size = size;
|
||||||
FATAL_IF_NEGATIVE(size, "Couldn't find size of %s", params->filename);
|
FATAL_IF_NEGATIVE( size, "Couldn't find size of %s",
|
||||||
|
params->filename );
|
||||||
|
|
||||||
params->allocation_map =
|
params->allocation_map =
|
||||||
bitset_alloc( params->size, block_allocation_resolution );
|
bitset_alloc( params->size, block_allocation_resolution );
|
||||||
@@ -756,8 +738,7 @@ void server_allow_new_clients(struct server *serve)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_join_clients(struct server *serve)
|
void server_join_clients( struct server * serve ) {
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
void* status;
|
void* status;
|
||||||
|
|
||||||
@@ -770,8 +751,7 @@ void server_join_clients(struct server *serve)
|
|||||||
if ( 0 == err ) {
|
if ( 0 == err ) {
|
||||||
serve->nbd_client[i].thread = 0;
|
serve->nbd_client[i].thread = 0;
|
||||||
} else {
|
} else {
|
||||||
warn("Error %s (%i) joining thread %p", strerror(err), err,
|
warn( "Error %s (%i) joining thread %p", strerror( err ), err, thread_id );
|
||||||
thread_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -823,9 +803,7 @@ void serve_cleanup(struct server *params,
|
|||||||
|
|
||||||
info("cleaning up");
|
info("cleaning up");
|
||||||
|
|
||||||
if (params->server_fd) {
|
if (params->server_fd){ close(params->server_fd); }
|
||||||
close(params->server_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* need to stop background build if we're killed very early on */
|
/* need to stop background build if we're killed very early on */
|
||||||
pthread_cancel(params->allocation_map_builder_thread);
|
pthread_cancel(params->allocation_map_builder_thread);
|
||||||
@@ -834,18 +812,14 @@ void serve_cleanup(struct server *params,
|
|||||||
int need_mirror_lock;
|
int need_mirror_lock;
|
||||||
need_mirror_lock = !server_start_mirror_locked( params );
|
need_mirror_lock = !server_start_mirror_locked( params );
|
||||||
|
|
||||||
if (need_mirror_lock) {
|
if ( need_mirror_lock ) { server_lock_start_mirror( params ); }
|
||||||
server_lock_start_mirror(params);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
if ( server_is_mirroring( params ) ) {
|
if ( server_is_mirroring( params ) ) {
|
||||||
server_abandon_mirror( params );
|
server_abandon_mirror( params );
|
||||||
}
|
}
|
||||||
server_prevent_mirror_start( params );
|
server_prevent_mirror_start( params );
|
||||||
}
|
}
|
||||||
if (need_mirror_lock) {
|
if ( need_mirror_lock ) { server_unlock_start_mirror( params ); }
|
||||||
server_unlock_start_mirror(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
server_join_clients( params );
|
server_join_clients( params );
|
||||||
|
|
||||||
@@ -890,11 +864,8 @@ uint64_t server_mirror_bytes_remaining(struct server * serve)
|
|||||||
{
|
{
|
||||||
if ( server_is_mirroring( serve ) ) {
|
if ( server_is_mirroring( serve ) ) {
|
||||||
uint64_t bytes_to_xfer =
|
uint64_t bytes_to_xfer =
|
||||||
bitset_stream_queued_bytes(serve->allocation_map,
|
bitset_stream_queued_bytes( serve->allocation_map, BITSET_STREAM_SET ) +
|
||||||
BITSET_STREAM_SET) + (serve->size -
|
( serve->size - serve->mirror->offset );
|
||||||
serve->
|
|
||||||
mirror->
|
|
||||||
offset);
|
|
||||||
|
|
||||||
return bytes_to_xfer;
|
return bytes_to_xfer;
|
||||||
}
|
}
|
||||||
@@ -940,8 +911,10 @@ void server_abandon_mirror(struct server *serve)
|
|||||||
* We can set abandon_signal after mirror_super has checked it, but
|
* We can set abandon_signal after mirror_super has checked it, but
|
||||||
* before the reset. However, mirror_reset doesn't clear abandon_signal
|
* before the reset. However, mirror_reset doesn't clear abandon_signal
|
||||||
* so it'll just terminate early on the next pass. */
|
* so it'll just terminate early on the next pass. */
|
||||||
ERROR_UNLESS(self_pipe_signal(serve->mirror->abandon_signal),
|
ERROR_UNLESS(
|
||||||
"Failed to signal abandon to mirror");
|
self_pipe_signal( serve->mirror->abandon_signal ),
|
||||||
|
"Failed to signal abandon to mirror"
|
||||||
|
);
|
||||||
|
|
||||||
pthread_t tid = serve->mirror_super->thread;
|
pthread_t tid = serve->mirror_super->thread;
|
||||||
pthread_join( tid, NULL );
|
pthread_join( tid, NULL );
|
||||||
@@ -975,9 +948,7 @@ int do_serve(struct server *params, struct self_pipe *open_signal)
|
|||||||
|
|
||||||
/* Only signal that we are open for business once the server
|
/* Only signal that we are open for business once the server
|
||||||
socket is open */
|
socket is open */
|
||||||
if (NULL != open_signal) {
|
if ( NULL != open_signal ) { self_pipe_signal( open_signal ); }
|
||||||
self_pipe_signal(open_signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
serve_init_allocation_map(params);
|
serve_init_allocation_map(params);
|
||||||
serve_accept_loop(params);
|
serve_accept_loop(params);
|
||||||
|
@@ -103,7 +103,8 @@ struct server {
|
|||||||
int success;
|
int success;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct server *server_create(struct flexnbd *flexnbd,
|
struct server * server_create(
|
||||||
|
struct flexnbd * flexnbd,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file,
|
char* s_file,
|
||||||
@@ -111,7 +112,8 @@ struct server *server_create(struct flexnbd *flexnbd,
|
|||||||
int acl_entries,
|
int acl_entries,
|
||||||
char** s_acl_entries,
|
char** s_acl_entries,
|
||||||
int max_nbd_clients,
|
int max_nbd_clients,
|
||||||
int use_killswitch, int success);
|
int use_killswitch,
|
||||||
|
int success );
|
||||||
void server_destroy( struct server * );
|
void server_destroy( struct server * );
|
||||||
int server_is_closed(struct server* serve);
|
int server_is_closed(struct server* serve);
|
||||||
void serve_signal_close( struct server *serve );
|
void serve_signal_close( struct server *serve );
|
||||||
@@ -165,3 +167,4 @@ struct mode_readwrite_params {
|
|||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -21,20 +21,17 @@ struct status *status_create(struct server *serve)
|
|||||||
if ( status->is_mirroring ) {
|
if ( status->is_mirroring ) {
|
||||||
status->migration_duration = monotonic_time_ms();
|
status->migration_duration = monotonic_time_ms();
|
||||||
|
|
||||||
if ((serve->mirror->migration_started) <
|
if ( ( serve->mirror->migration_started ) < status->migration_duration ) {
|
||||||
status->migration_duration) {
|
|
||||||
status->migration_duration -= serve->mirror->migration_started;
|
status->migration_duration -= serve->mirror->migration_started;
|
||||||
} else {
|
} else {
|
||||||
status->migration_duration = 0;
|
status->migration_duration = 0;
|
||||||
}
|
}
|
||||||
status->migration_duration /= 1000;
|
status->migration_duration /= 1000;
|
||||||
status->migration_speed = server_mirror_bps( serve );
|
status->migration_speed = server_mirror_bps( serve );
|
||||||
status->migration_speed_limit =
|
status->migration_speed_limit = serve->mirror->max_bytes_per_second;
|
||||||
serve->mirror->max_bytes_per_second;
|
|
||||||
|
|
||||||
status->migration_seconds_left = server_mirror_eta( serve );
|
status->migration_seconds_left = server_mirror_eta( serve );
|
||||||
status->migration_bytes_left =
|
status->migration_bytes_left = server_mirror_bytes_remaining( serve );
|
||||||
server_mirror_bytes_remaining(serve);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server_unlock_start_mirror( serve );
|
server_unlock_start_mirror( serve );
|
||||||
@@ -80,3 +77,4 @@ void status_destroy(struct status *status)
|
|||||||
NULLCHECK( status );
|
NULLCHECK( status );
|
||||||
free( status );
|
free( status );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -101,3 +101,4 @@ void status_destroy(struct status *);
|
|||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'flexnbd'
|
require 'flexnbd'
|
||||||
require 'file_writer'
|
require 'file_writer'
|
||||||
|
|
||||||
@@ -7,23 +9,18 @@ class Environment
|
|||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@blocksize = 1024
|
@blocksize = 1024
|
||||||
@filename1 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.1"
|
@filename1 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.1"
|
||||||
@filename2 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.2"
|
@filename2 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.2"
|
||||||
@ip = '127.0.0.1'
|
@ip = "127.0.0.1"
|
||||||
@available_ports = [*40_000..41_000] - listening_ports
|
@available_ports = [*40000..41000] - listening_ports
|
||||||
@port1 = @available_ports.shift
|
@port1 = @available_ports.shift
|
||||||
@port2 = @available_ports.shift
|
@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)
|
||||||
|
|
||||||
@fake_pid = nil
|
@fake_pid = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocksize=(b)
|
|
||||||
raise RuntimeError, "Unable to change blocksize after files have been opened" if @file1 or @file2
|
|
||||||
@blocksize = b
|
|
||||||
end
|
|
||||||
|
|
||||||
def prefetch_proxy!
|
def prefetch_proxy!
|
||||||
@nbd1.prefetch_proxy = true
|
@nbd1.prefetch_proxy = true
|
||||||
@nbd2.prefetch_proxy = true
|
@nbd2.prefetch_proxy = true
|
||||||
@@ -32,11 +29,11 @@ class Environment
|
|||||||
def proxy1(port=@port2)
|
def proxy1(port=@port2)
|
||||||
@nbd1.proxy(@ip, port)
|
@nbd1.proxy(@ip, port)
|
||||||
end
|
end
|
||||||
|
|
||||||
def proxy2(port=@port1)
|
def proxy2(port=@port1)
|
||||||
@nbd2.proxy(@ip, port)
|
@nbd2.proxy(@ip, port)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def serve1(*acl)
|
def serve1(*acl)
|
||||||
@nbd1.serve(@filename1, *acl)
|
@nbd1.serve(@filename1, *acl)
|
||||||
end
|
end
|
||||||
@@ -45,6 +42,7 @@ class Environment
|
|||||||
@nbd2.serve(@filename2, *acl)
|
@nbd2.serve(@filename2, *acl)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def listen1( *acl )
|
def listen1( *acl )
|
||||||
@nbd1.listen( @filename1, *(acl.empty? ? @acl1: acl) )
|
@nbd1.listen( @filename1, *(acl.empty? ? @acl1: acl) )
|
||||||
end
|
end
|
||||||
@@ -53,6 +51,7 @@ class Environment
|
|||||||
@nbd2.listen( @filename2, *acl )
|
@nbd2.listen( @filename2, *acl )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def break1
|
def break1
|
||||||
@nbd1.break
|
@nbd1.break
|
||||||
end
|
end
|
||||||
@@ -65,6 +64,7 @@ class Environment
|
|||||||
@nbd2.acl( *acl )
|
@nbd2.acl( *acl )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def status1
|
def status1
|
||||||
@nbd1.status.first
|
@nbd1.status.first
|
||||||
end
|
end
|
||||||
@@ -73,6 +73,8 @@ class Environment
|
|||||||
@nbd2.status.first
|
@nbd2.status.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def mirror12
|
def mirror12
|
||||||
@nbd1.mirror( @nbd2.ip, @nbd2.port )
|
@nbd1.mirror( @nbd2.ip, @nbd2.port )
|
||||||
end
|
end
|
||||||
@@ -85,6 +87,7 @@ class Environment
|
|||||||
@nbd1.mirror_unlink( @nbd2.ip, @nbd2.port, 2 )
|
@nbd1.mirror_unlink( @nbd2.ip, @nbd2.port, 2 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write1( data )
|
def write1( data )
|
||||||
@nbd1.write( 0, data )
|
@nbd1.write( 0, data )
|
||||||
end
|
end
|
||||||
@@ -97,17 +100,20 @@ class Environment
|
|||||||
@file2 = FileWriter.new(@filename2, @blocksize).write(data)
|
@file2 = FileWriter.new(@filename2, @blocksize).write(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def truncate1( size )
|
def truncate1( size )
|
||||||
system "truncate -s #{size} #{@filename1}"
|
system "truncate -s #{size} #{@filename1}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def listening_ports
|
def listening_ports
|
||||||
`netstat -ltn`
|
`netstat -ltn`.
|
||||||
.split("\n")
|
split("\n").
|
||||||
.map { |x| x.split(/\s+/) }[2..-1]
|
map { |x| x.split(/\s+/) }[2..-1].
|
||||||
.map { |l| l[3].split(':')[-1].to_i }
|
map { |l| l[3].split(":")[-1].to_i }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def cleanup
|
def cleanup
|
||||||
if @fake_pid
|
if @fake_pid
|
||||||
begin
|
begin
|
||||||
@@ -116,35 +122,41 @@ class Environment
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@nbd1.can_die(0)
|
@nbd1.can_die(0)
|
||||||
@nbd1.kill
|
@nbd1.kill
|
||||||
@nbd2.kill
|
@nbd2.kill
|
||||||
|
|
||||||
[@filename1, @filename2].each do |f|
|
[@filename1, @filename2].each do |f|
|
||||||
File.unlink(f) if File.exist?(f)
|
File.unlink(f) if File.exists?(f)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def run_fake( name, addr, port, sock=nil )
|
def run_fake( name, addr, port, sock=nil )
|
||||||
fakedir = File.join(File.dirname(__FILE__), 'fakes')
|
fakedir = File.join( File.dirname( __FILE__ ), "fakes" )
|
||||||
fakeglob = File.join(fakedir, name) + '*'
|
fakeglob = File.join( fakedir, name ) + "*"
|
||||||
fake = Dir[fakeglob].sort.find do |fn|
|
fake = Dir[fakeglob].sort.find { |fn|
|
||||||
File.executable?( fn )
|
File.executable?( fn )
|
||||||
end
|
}
|
||||||
|
|
||||||
raise "no fake executable at #{fakeglob}" unless fake
|
raise "no fake executable at #{fakeglob}" unless fake
|
||||||
raise 'no addr' unless addr
|
raise "no addr" unless addr
|
||||||
raise 'no port' unless port
|
raise "no port" unless port
|
||||||
|
|
||||||
@fake_pid = fork do
|
@fake_pid = fork do
|
||||||
exec [fake, addr, port, @nbd1.pid, sock].map(&:to_s).join(' ')
|
exec [fake, addr, port, @nbd1.pid, sock].map{|x| x.to_s}.join(" ")
|
||||||
end
|
end
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def fake_reports_success
|
def fake_reports_success
|
||||||
_,status = Process.waitpid2( @fake_pid )
|
_,status = Process.waitpid2( @fake_pid )
|
||||||
@fake_pid = nil
|
@fake_pid = nil
|
||||||
status.success?
|
status.success?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end # class Environment
|
end # class Environment
|
||||||
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# Open a server, accept a client, then cancel the migration by issuing
|
# Open a server, accept a client, then cancel the migration by issuing
|
||||||
# a break command.
|
# a break command.
|
||||||
|
|
||||||
@@ -11,22 +13,23 @@ client = server.accept
|
|||||||
|
|
||||||
ctrl = UNIXSocket.open( sock )
|
ctrl = UNIXSocket.open( sock )
|
||||||
|
|
||||||
Process.kill('STOP', src_pid.to_i)
|
Process.kill("STOP", src_pid.to_i)
|
||||||
ctrl.write( "break\n" )
|
ctrl.write( "break\n" )
|
||||||
ctrl.close_write
|
ctrl.close_write
|
||||||
client.write_hello
|
client.write_hello
|
||||||
Process.kill('CONT', src_pid.to_i)
|
Process.kill("CONT", src_pid.to_i)
|
||||||
|
|
||||||
raise 'Unexpected control response' unless
|
fail "Unexpected control response" unless
|
||||||
ctrl.read =~ /0: mirror stopped/
|
ctrl.read =~ /0: mirror stopped/
|
||||||
|
|
||||||
client2 = nil
|
client2 = nil
|
||||||
begin
|
begin
|
||||||
client2 = server.accept('Expected timeout')
|
client2 = server.accept( "Expected timeout" )
|
||||||
raise 'Unexpected reconnection'
|
fail "Unexpected reconnection"
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
# expected
|
# expected
|
||||||
end
|
end
|
||||||
client.close
|
client.close
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# Receive a mirror, and disconnect after sending the entrust reply but
|
# Receive a mirror, and disconnect after sending the entrust reply but
|
||||||
# before it can send the disconnect signal.
|
# before it can send the disconnect signal.
|
||||||
#
|
#
|
||||||
@@ -13,7 +15,7 @@ server = FakeDest.new(addr, port)
|
|||||||
client = server.accept
|
client = server.accept
|
||||||
|
|
||||||
client.write_hello
|
client.write_hello
|
||||||
while req = client.read_request; req[:type] == 1
|
while (req = client.read_request; req[:type] == 1)
|
||||||
client.read_data( req[:len] )
|
client.read_data( req[:len] )
|
||||||
client.write_reply( req[:handle] )
|
client.write_reply( req[:handle] )
|
||||||
end
|
end
|
||||||
@@ -24,10 +26,11 @@ client.close
|
|||||||
system "kill -CONT #{src_pid}"
|
system "kill -CONT #{src_pid}"
|
||||||
|
|
||||||
sleep( 0.25 )
|
sleep( 0.25 )
|
||||||
client2 = server.accept('Timed out waiting for a reconnection')
|
client2 = server.accept( "Timed out waiting for a reconnection" )
|
||||||
|
|
||||||
client2.close
|
client2.close
|
||||||
server.close
|
server.close
|
||||||
|
|
||||||
warn 'done'
|
$stderr.puts "done"
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
@@ -11,11 +11,11 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
server = FakeDest.new( addr, port )
|
server = FakeDest.new( addr, port )
|
||||||
client = server.accept('Timed out waiting for a connection')
|
client = server.accept( "Timed out waiting for a connection" )
|
||||||
client.write_hello
|
client.write_hello
|
||||||
client.close
|
client.close
|
||||||
|
|
||||||
new_client = server.accept('Timed out waiting for a reconnection')
|
new_client = server.accept( "Timed out waiting for a reconnection" )
|
||||||
new_client.close
|
new_client.close
|
||||||
|
|
||||||
server.close
|
server.close
|
||||||
|
@@ -12,12 +12,12 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
server = FakeDest.new( addr, port )
|
server = FakeDest.new( addr, port )
|
||||||
client = server.accept('Timed out waiting for a connection')
|
client = server.accept( "Timed out waiting for a connection" )
|
||||||
client.write_hello
|
client.write_hello
|
||||||
client.read_request
|
client.read_request
|
||||||
client.close
|
client.close
|
||||||
|
|
||||||
new_client = server.accept('Timed out waiting for a reconnection')
|
new_client = server.accept( "Timed out waiting for a reconnection" )
|
||||||
new_client.close
|
new_client.close
|
||||||
|
|
||||||
server.close
|
server.close
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# Open a server, accept a client, then we expect a single write
|
# Open a server, accept a client, then we expect a single write
|
||||||
# followed by an entrust. However, we disconnect after the write so
|
# followed by an entrust. However, we disconnect after the write so
|
||||||
# the entrust will fail. We don't expect a reconnection: the sender
|
# the entrust will fail. We don't expect a reconnection: the sender
|
||||||
@@ -15,9 +17,9 @@ client.write_hello
|
|||||||
req = client.read_request
|
req = client.read_request
|
||||||
data = client.read_data( req[:len] )
|
data = client.read_data( req[:len] )
|
||||||
|
|
||||||
Process.kill('STOP', src_pid.to_i)
|
Process.kill("STOP", src_pid.to_i)
|
||||||
client.write_reply( req[:handle], 0 )
|
client.write_reply( req[:handle], 0 )
|
||||||
client.close
|
client.close
|
||||||
Process.kill('CONT', src_pid.to_i)
|
Process.kill("CONT", src_pid.to_i)
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'flexnbd/fake_dest'
|
require 'flexnbd/fake_dest'
|
||||||
include FlexNBD
|
include FlexNBD
|
||||||
|
|
||||||
@@ -10,7 +12,8 @@ client.write_hello
|
|||||||
handle = client.read_request[:handle]
|
handle = client.read_request[:handle]
|
||||||
client.write_error( handle )
|
client.write_error( handle )
|
||||||
|
|
||||||
client2 = server.accept('Timed out waiting for a reconnection')
|
|
||||||
|
client2 = server.accept( "Timed out waiting for a reconnection" )
|
||||||
|
|
||||||
client.close
|
client.close
|
||||||
client2.close
|
client2.close
|
||||||
|
@@ -26,8 +26,8 @@ client.close
|
|||||||
# Invert the sense of the timeout exception, since we *don't* want a
|
# Invert the sense of the timeout exception, since we *don't* want a
|
||||||
# connection attempt
|
# connection attempt
|
||||||
begin
|
begin
|
||||||
server.accept('Expected timeout')
|
server.accept( "Expected timeout" )
|
||||||
raise 'Unexpected reconnection'
|
fail "Unexpected reconnection"
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
# expected
|
# expected
|
||||||
end
|
end
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# Open a socket, say hello, receive a write, then sleep for >
|
# Open a socket, say hello, receive a write, then sleep for >
|
||||||
# MS_REQUEST_LIMIT_SECS seconds. This should tell the source that the
|
# MS_REQUEST_LIMIT_SECS seconds. This should tell the source that the
|
||||||
# write has gone MIA, and we expect a reconnect.
|
# write has gone MIA, and we expect a reconnect.
|
||||||
@@ -13,12 +15,12 @@ client1.write_hello
|
|||||||
client1.read_request
|
client1.read_request
|
||||||
|
|
||||||
t = Thread.start do
|
t = Thread.start do
|
||||||
client2 = server.accept('Timed out waiting for a reconnection',
|
client2 = server.accept( "Timed out waiting for a reconnection",
|
||||||
FlexNBD::MS_REQUEST_LIMIT_SECS + 2 )
|
FlexNBD::MS_REQUEST_LIMIT_SECS + 2 )
|
||||||
client2.close
|
client2.close
|
||||||
end
|
end
|
||||||
|
|
||||||
sleep_time = if ENV.key?('FLEXNBD_MS_REQUEST_LIMIT_SECS')
|
sleep_time = if ENV.has_key?('FLEXNBD_MS_REQUEST_LIMIT_SECS')
|
||||||
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'].to_f
|
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'].to_f
|
||||||
else
|
else
|
||||||
FlexNBD::MS_REQUEST_LIMIT_SECS
|
FlexNBD::MS_REQUEST_LIMIT_SECS
|
||||||
|
@@ -13,15 +13,15 @@ client1 = server.accept
|
|||||||
# We don't expect a reconnection attempt.
|
# We don't expect a reconnection attempt.
|
||||||
t = Thread.new do
|
t = Thread.new do
|
||||||
begin
|
begin
|
||||||
client2 = server.accept('Timed out waiting for a reconnection',
|
client2 = server.accept( "Timed out waiting for a reconnection",
|
||||||
FlexNBD::MS_RETRY_DELAY_SECS + 1 )
|
FlexNBD::MS_RETRY_DELAY_SECS + 1 )
|
||||||
raise 'Unexpected reconnection'
|
fail "Unexpected reconnection"
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
#expected
|
#expected
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
client1.write_hello(magic: :wrong)
|
client1.write_hello( :magic => :wrong )
|
||||||
|
|
||||||
t.join
|
t.join
|
||||||
|
|
||||||
|
@@ -18,21 +18,21 @@ t = Thread.new do
|
|||||||
# so it makes no sense to continue. This means we have to invert the
|
# so it makes no sense to continue. This means we have to invert the
|
||||||
# sense of the exception.
|
# sense of the exception.
|
||||||
begin
|
begin
|
||||||
client2 = server.accept('Timed out waiting for a reconnection',
|
client2 = server.accept( "Timed out waiting for a reconnection",
|
||||||
FlexNBD::MS_RETRY_DELAY_SECS + 1 )
|
FlexNBD::MS_RETRY_DELAY_SECS + 1 )
|
||||||
client2.close
|
client2.close
|
||||||
raise 'Unexpected reconnection.'
|
fail "Unexpected reconnection."
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
client.write_hello(size: :wrong)
|
client.write_hello( :size => :wrong )
|
||||||
|
|
||||||
t.join
|
t.join
|
||||||
|
|
||||||
# Now check that the source closed the first socket (yes, this was an
|
# Now check that the source closed the first socket (yes, this was an
|
||||||
# actual bug)
|
# actual bug)
|
||||||
|
|
||||||
raise "Didn't close socket" unless client.disconnected?
|
fail "Didn't close socket" unless client.disconnected?
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@@ -10,13 +10,15 @@ addr, port = *ARGV
|
|||||||
server = FakeDest.new( addr, port )
|
server = FakeDest.new( addr, port )
|
||||||
server.accept.close
|
server.accept.close
|
||||||
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
server.accept
|
server.accept
|
||||||
raise 'Unexpected reconnection'
|
fail "Unexpected reconnection"
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
# expected
|
# expected
|
||||||
end
|
end
|
||||||
|
|
||||||
server.close
|
server.close
|
||||||
|
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -9,7 +9,7 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, pid = *ARGV
|
addr, port, pid = *ARGV
|
||||||
server = FakeDest.new( addr, port )
|
server = FakeDest.new( addr, port )
|
||||||
client = server.accept('Timed out waiting for a connection')
|
client = server.accept( "Timed out waiting for a connection" )
|
||||||
client.write_hello
|
client.write_hello
|
||||||
|
|
||||||
Process.kill(15, pid.to_i)
|
Process.kill(15, pid.to_i)
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# Accept a connection, write hello, wait for a write request, read the
|
# Accept a connection, write hello, wait for a write request, read the
|
||||||
# data, then write back a reply with a bad magic field. We then
|
# data, then write back a reply with a bad magic field. We then
|
||||||
# expect a reconnect.
|
# expect a reconnect.
|
||||||
@@ -13,7 +15,7 @@ client = server.accept
|
|||||||
client.write_hello
|
client.write_hello
|
||||||
req = client.read_request
|
req = client.read_request
|
||||||
client.read_data( req[:len] )
|
client.read_data( req[:len] )
|
||||||
client.write_reply(req[:handle], 0, magic: :wrong)
|
client.write_reply( req[:handle], 0, :magic => :wrong )
|
||||||
|
|
||||||
client2 = server.accept
|
client2 = server.accept
|
||||||
client.close
|
client.close
|
||||||
|
@@ -11,13 +11,13 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
|
|
||||||
FakeSource.new(addr, port, 'Failed to connect').close
|
FakeSource.new( addr, port, "Failed to connect" ).close
|
||||||
# Sleep to be sure we don't try to connect too soon. That wouldn't
|
# Sleep to be sure we don't try to connect too soon. That wouldn't
|
||||||
# be a problem for the destination, but it would prevent us from
|
# be a problem for the destination, but it would prevent us from
|
||||||
# determining success or failure here in the case where we try to
|
# determining success or failure here in the case where we try to
|
||||||
# reconnect before the destination has tidied up after the first
|
# reconnect before the destination has tidied up after the first
|
||||||
# thread went away.
|
# thread went away.
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
FakeSource.new(addr, port, 'Failed to reconnect').close
|
FakeSource.new( addr, port, "Failed to reconnect" ).close
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@@ -11,10 +11,10 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, srv_pid = *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
|
||||||
client.write_write_request( 0, 8 )
|
client.write_write_request( 0, 8 )
|
||||||
client.write_data('12345678')
|
client.write_data( "12345678" )
|
||||||
|
|
||||||
# Use system "kill" rather than Process.kill because Process.kill
|
# Use system "kill" rather than Process.kill because Process.kill
|
||||||
# doesn't seem to work
|
# doesn't seem to work
|
||||||
@@ -25,11 +25,12 @@ client.close
|
|||||||
|
|
||||||
system "kill -CONT #{srv_pid}"
|
system "kill -CONT #{srv_pid}"
|
||||||
|
|
||||||
|
|
||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
client2 = FakeSource.new(addr, port, 'Expected timeout')
|
client2 = FakeSource.new( addr, port, "Expected timeout" )
|
||||||
raise 'Unexpected reconnection'
|
fail "Unexpected reconnection"
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
# expected
|
# expected
|
||||||
end
|
end
|
||||||
|
@@ -10,10 +10,10 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, srv_pid = *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
|
||||||
client.write_write_request( 0, 8 )
|
client.write_write_request( 0, 8 )
|
||||||
client.write_data('12345678')
|
client.write_data( "12345678" )
|
||||||
|
|
||||||
client.write_entrust_request
|
client.write_entrust_request
|
||||||
client.read_response
|
client.read_response
|
||||||
@@ -21,11 +21,13 @@ client.close
|
|||||||
|
|
||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
client2 = FakeSource.new(addr, port, 'Expected timeout')
|
client2 = FakeSource.new( addr, port, "Expected timeout" )
|
||||||
raise 'Unexpected reconnection'
|
fail "Unexpected reconnection"
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
# expected
|
# expected
|
||||||
end
|
end
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
@@ -12,12 +12,13 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting.')
|
|
||||||
|
client = FakeSource.new( addr, port, "Timed out connecting." )
|
||||||
client.read_hello
|
client.read_hello
|
||||||
client.close
|
client.close
|
||||||
|
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
|
|
||||||
FakeSource.new(addr, port, 'Timed out reconnecting.')
|
FakeSource.new( addr, port, "Timed out reconnecting." )
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# We connect, pause the server, issue a write request, disconnect,
|
# We connect, pause the server, issue a write request, disconnect,
|
||||||
# then cont the server. This ensures that our disconnect happens
|
# then cont the server. This ensures that our disconnect happens
|
||||||
# while the server is trying to read the write data.
|
# while the server is trying to read the write data.
|
||||||
@@ -8,7 +10,7 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, srv_pid = *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
|
||||||
|
|
||||||
system "kill -STOP #{srv_pid}"
|
system "kill -STOP #{srv_pid}"
|
||||||
@@ -22,7 +24,7 @@ system "kill -CONT #{srv_pid}"
|
|||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
|
|
||||||
# ...and can we reconnect?
|
# ...and can we reconnect?
|
||||||
client2 = FakeSource.new(addr, port, 'Timed out connecting')
|
client2 = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
client2.close
|
client2.close
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# We connect, pause the server, issue a write request, send data,
|
# We connect, pause the server, issue a write request, send data,
|
||||||
# disconnect, then cont the server. This ensures that our disconnect
|
# disconnect, then cont the server. This ensures that our disconnect
|
||||||
# happens before the server can try to write the reply.
|
# happens before the server can try to write the reply.
|
||||||
@@ -8,13 +10,13 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, srv_pid = *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
|
||||||
|
|
||||||
system "kill -STOP #{srv_pid}"
|
system "kill -STOP #{srv_pid}"
|
||||||
|
|
||||||
client.write_write_request( 0, 8 )
|
client.write_write_request( 0, 8 )
|
||||||
client.write_data('12345678')
|
client.write_data( "12345678" )
|
||||||
client.close
|
client.close
|
||||||
|
|
||||||
system "kill -CONT #{srv_pid}"
|
system "kill -CONT #{srv_pid}"
|
||||||
@@ -25,7 +27,7 @@ system "kill -CONT #{srv_pid}"
|
|||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
|
|
||||||
# ...and can we reconnect?
|
# ...and can we reconnect?
|
||||||
client2 = FakeSource.new(addr, port, 'Timed out reconnecting')
|
client2 = FakeSource.new( addr, port, "Timed out reconnecting" )
|
||||||
client2.close
|
client2.close
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -8,9 +8,10 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, srv_pid, newaddr, newport = *ARGV
|
addr, port, srv_pid, newaddr, newport = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting')
|
client = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
client.write_read_request( 0, 8 )
|
client.write_read_request( 0, 8 )
|
||||||
client.read_raw( 4 )
|
client.read_raw( 4 )
|
||||||
client.close
|
client.close
|
||||||
|
|
||||||
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -10,9 +10,9 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
|
|
||||||
client1 = FakeSource.new(addr, port, 'Timed out connecting')
|
client1 = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
sleep(0.25)
|
sleep(0.25)
|
||||||
client2 = FakeSource.new(addr, port, 'Timed out connecting a second time')
|
client2 = FakeSource.new( addr, port, "Timed out connecting a second time" )
|
||||||
|
|
||||||
# This is the expected source crashing after connect
|
# This is the expected source crashing after connect
|
||||||
client1.close
|
client1.close
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# We connect from a local address which should be blocked, sleep for a
|
# We connect from a local address which should be blocked, sleep for a
|
||||||
# bit, then try to read from the socket. We should get an instant EOF
|
# bit, then try to read from the socket. We should get an instant EOF
|
||||||
# as we've been cut off by the destination.
|
# as we've been cut off by the destination.
|
||||||
@@ -9,9 +11,10 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting', '127.0.0.6')
|
client = FakeSource.new( addr, port, "Timed out connecting", "127.0.0.6" )
|
||||||
sleep( 0.25 )
|
sleep( 0.25 )
|
||||||
|
|
||||||
rsp = client.disconnected? ? 0 : 1
|
rsp = client.disconnected? ? 0 : 1
|
||||||
client.close
|
client.close
|
||||||
exit(rsp)
|
exit(rsp)
|
||||||
|
|
||||||
|
@@ -7,10 +7,10 @@
|
|||||||
# listening for an incoming migration.
|
# listening for an incoming migration.
|
||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
require 'flexnbd/fake_source'
|
require "flexnbd/fake_source"
|
||||||
include FlexNBD
|
include FlexNBD
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting')
|
client = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
client.read_hello
|
client.read_hello
|
||||||
|
|
||||||
# Now we do two things:
|
# Now we do two things:
|
||||||
@@ -25,7 +25,7 @@ kidpid = fork do
|
|||||||
client.close
|
client.close
|
||||||
new_client = nil
|
new_client = nil
|
||||||
sleep( FlexNBD::CLIENT_MAX_WAIT_SECS + 1 )
|
sleep( FlexNBD::CLIENT_MAX_WAIT_SECS + 1 )
|
||||||
new_client = FakeSource.new(addr, port, 'Timed out reconnecting.')
|
new_client = FakeSource.new( addr, port, "Timed out reconnecting." )
|
||||||
new_client.read_hello
|
new_client.read_hello
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
|
@@ -9,10 +9,10 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, pid = *ARGV
|
addr, port, pid = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting.')
|
client = FakeSource.new( addr, port, "Timed out connecting." )
|
||||||
client.read_hello
|
client.read_hello
|
||||||
|
|
||||||
Process.kill('TERM', pid.to_i)
|
Process.kill( "TERM", pid.to_i )
|
||||||
|
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
client.close
|
client.close
|
||||||
|
@@ -9,9 +9,10 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port, srv_pid, newaddr, newport = *ARGV
|
addr, port, srv_pid, newaddr, newport = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting')
|
client = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
client.send_mirror
|
client.send_mirror()
|
||||||
|
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
|
|
||||||
exit( 0 )
|
exit( 0 )
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
# Connect, read the hello then make a write request with an impossible
|
# Connect, read the hello then make a write request with an impossible
|
||||||
# (from,len) pair. We expect an error response, and not to be
|
# (from,len) pair. We expect an error response, and not to be
|
||||||
# disconnected.
|
# disconnected.
|
||||||
@@ -11,20 +13,20 @@ include FlexNBD
|
|||||||
|
|
||||||
addr, port = *ARGV
|
addr, port = *ARGV
|
||||||
|
|
||||||
client = FakeSource.new(addr, port, 'Timed out connecting')
|
client = FakeSource.new( addr, port, "Timed out connecting" )
|
||||||
hello = client.read_hello
|
hello = client.read_hello
|
||||||
client.write_write_request(hello[:size] + 1, 32, 'myhandle')
|
client.write_write_request( hello[:size]+1, 32, "myhandle" )
|
||||||
client.write_data('1' * 32)
|
client.write_data("1"*32)
|
||||||
response = client.read_response
|
response = client.read_response
|
||||||
|
|
||||||
raise 'Not an error' if response[:error] == 0
|
fail "Not an error" if response[:error] == 0
|
||||||
raise 'Wrong handle' unless response[:handle] == 'myhandle'
|
fail "Wrong handle" unless "myhandle" == response[:handle]
|
||||||
|
|
||||||
client.write_write_request( 0, 32 )
|
client.write_write_request( 0, 32 )
|
||||||
client.write_data('2' * 32)
|
client.write_data( "2"*32 )
|
||||||
success_response = client.read_response
|
success_response = client.read_response
|
||||||
|
|
||||||
raise 'Second write failed' unless success_response[:error] == 0
|
fail "Second write failed" unless success_response[:error] == 0
|
||||||
|
|
||||||
client.close
|
client.close
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@@ -3,13 +3,13 @@
|
|||||||
#
|
#
|
||||||
class FileWriter
|
class FileWriter
|
||||||
def initialize(filename, blocksize)
|
def initialize(filename, blocksize)
|
||||||
@fh = File.open(filename, 'w+')
|
@fh = File.open(filename, "w+")
|
||||||
@blocksize = blocksize
|
@blocksize = blocksize
|
||||||
@pattern = ''
|
@pattern = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
def size
|
def size
|
||||||
@blocksize * @pattern.split('').size
|
@blocksize * @pattern.split("").size
|
||||||
end
|
end
|
||||||
|
|
||||||
# We write in fixed block sizes, given by "blocksize"
|
# We write in fixed block sizes, given by "blocksize"
|
||||||
@@ -20,8 +20,8 @@ class FileWriter
|
|||||||
def write(data)
|
def write(data)
|
||||||
@pattern += data
|
@pattern += data
|
||||||
|
|
||||||
data.split('').each do |code|
|
data.split("").each do |code|
|
||||||
if code == '_'
|
if code == "_"
|
||||||
@fh.seek(@blocksize, IO::SEEK_CUR)
|
@fh.seek(@blocksize, IO::SEEK_CUR)
|
||||||
else
|
else
|
||||||
@fh.write(data(code))
|
@fh.write(data(code))
|
||||||
@@ -31,14 +31,15 @@ class FileWriter
|
|||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Returns what the data ought to be at the given offset and length
|
# Returns what the data ought to be at the given offset and length
|
||||||
#
|
#
|
||||||
def read_original( off, len )
|
def read_original( off, len )
|
||||||
patterns = @pattern.split('')
|
patterns = @pattern.split( "" )
|
||||||
patterns.zip((0...patterns.length).to_a)
|
patterns.zip( (0...patterns.length).to_a ).
|
||||||
.map do |blk, blk_off|
|
map { |blk, blk_off|
|
||||||
data(blk, blk_off)
|
data(blk, blk_off)
|
||||||
end.join[off...(off + len)]
|
}.join[off...(off+len)]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Read what's actually in the file
|
# Read what's actually in the file
|
||||||
@@ -61,14 +62,14 @@ class FileWriter
|
|||||||
|
|
||||||
def data(code, at=@fh.tell)
|
def data(code, at=@fh.tell)
|
||||||
case code
|
case code
|
||||||
when '0', '_'
|
when "0", "_"
|
||||||
"\0" * @blocksize
|
"\0" * @blocksize
|
||||||
when 'X'
|
when "X"
|
||||||
'X' * @blocksize
|
"X" * @blocksize
|
||||||
when 'f'
|
when "f"
|
||||||
r = ''
|
r = ""
|
||||||
(@blocksize/4).times do
|
(@blocksize/4).times do
|
||||||
r += [at].pack('I')
|
r += [at].pack("I")
|
||||||
at += 4
|
at += 4
|
||||||
end
|
end
|
||||||
r
|
r
|
||||||
@@ -76,49 +77,51 @@ class FileWriter
|
|||||||
raise "Unknown character '#{block}'"
|
raise "Unknown character '#{block}'"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if $PROGRAM_NAME == __FILE__
|
if __FILE__==$0
|
||||||
require 'tempfile'
|
require 'tempfile'
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
|
|
||||||
class FileWriterTest < Test::Unit::TestCase
|
class FileWriterTest < Test::Unit::TestCase
|
||||||
def test_read_original_zeros
|
def test_read_original_zeros
|
||||||
Tempfile.open('test_read_original_zeros') do |tempfile|
|
Tempfile.open("test_read_original_zeros") do |tempfile|
|
||||||
tempfile.close
|
tempfile.close
|
||||||
file = FileWriter.new( tempfile.path, 4096 )
|
file = FileWriter.new( tempfile.path, 4096 )
|
||||||
file.write('0')
|
file.write( "0" )
|
||||||
assert_equal file.read( 0, 4096 ), file.read_original( 0, 4096 )
|
assert_equal file.read( 0, 4096 ), file.read_original( 0, 4096 )
|
||||||
assert(file.untouched?(0, 4096), 'Untouched file was touched.')
|
assert( file.untouched?(0,4096) , "Untouched file was touched." )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_read_original_offsets
|
def test_read_original_offsets
|
||||||
Tempfile.open('test_read_original_offsets') do |tempfile|
|
Tempfile.open("test_read_original_offsets") do |tempfile|
|
||||||
tempfile.close
|
tempfile.close
|
||||||
file = FileWriter.new( tempfile.path, 4096 )
|
file = FileWriter.new( tempfile.path, 4096 )
|
||||||
file.write('f')
|
file.write( "f" )
|
||||||
assert_equal file.read( 0, 4096 ), file.read_original( 0, 4096 )
|
assert_equal file.read( 0, 4096 ), file.read_original( 0, 4096 )
|
||||||
assert(file.untouched?(0, 4096), 'Untouched file was touched.')
|
assert( file.untouched?(0,4096) , "Untouched file was touched." )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_file_size
|
def test_file_size
|
||||||
Tempfile.open('test_file_size') do |tempfile|
|
Tempfile.open("test_file_size") do |tempfile|
|
||||||
tempfile.close
|
tempfile.close
|
||||||
file = FileWriter.new( tempfile.path, 4096 )
|
file = FileWriter.new( tempfile.path, 4096 )
|
||||||
file.write('f')
|
file.write( "f" )
|
||||||
assert_equal 4096, File.stat( tempfile.path ).size
|
assert_equal 4096, File.stat( tempfile.path ).size
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_read_original_size
|
def test_read_original_size
|
||||||
Tempfile.open('test_read_original_offsets') do |tempfile|
|
Tempfile.open("test_read_original_offsets") do |tempfile|
|
||||||
tempfile.close
|
tempfile.close
|
||||||
file = FileWriter.new( tempfile.path, 4)
|
file = FileWriter.new( tempfile.path, 4)
|
||||||
file.write('f' * 4)
|
file.write( "f"*4 )
|
||||||
assert_equal 4, file.read_original(0, 4).length
|
assert_equal 4, file.read_original(0, 4).length
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -4,26 +4,28 @@ require 'open3'
|
|||||||
require 'timeout'
|
require 'timeout'
|
||||||
require 'rexml/document'
|
require 'rexml/document'
|
||||||
require 'rexml/streamlistener'
|
require 'rexml/streamlistener'
|
||||||
require 'English'
|
|
||||||
|
|
||||||
Thread.abort_on_exception = true
|
Thread.abort_on_exception = true
|
||||||
|
|
||||||
|
|
||||||
class Executor
|
class Executor
|
||||||
attr_reader :pid
|
attr_reader :pid
|
||||||
|
|
||||||
def run( cmd )
|
def run( cmd )
|
||||||
@pid = fork { exec cmd }
|
@pid = fork do exec cmd end
|
||||||
end
|
end
|
||||||
end # class Executor
|
end # class Executor
|
||||||
|
|
||||||
|
|
||||||
class ValgrindExecutor
|
class ValgrindExecutor
|
||||||
attr_reader :pid
|
attr_reader :pid
|
||||||
|
|
||||||
def run( cmd )
|
def run( cmd )
|
||||||
@pid = fork { exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" }
|
@pid = fork do exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" end
|
||||||
end
|
end
|
||||||
end # class ValgrindExecutor
|
end # class ValgrindExecutor
|
||||||
|
|
||||||
|
|
||||||
class ValgrindKillingExecutor
|
class ValgrindKillingExecutor
|
||||||
attr_reader :pid
|
attr_reader :pid
|
||||||
|
|
||||||
@@ -32,9 +34,9 @@ class ValgrindKillingExecutor
|
|||||||
attr_reader :backtrace
|
attr_reader :backtrace
|
||||||
def initialize
|
def initialize
|
||||||
@backtrace=[]
|
@backtrace=[]
|
||||||
@what = ''
|
@what = ""
|
||||||
@kind = ''
|
@kind = ""
|
||||||
@pid = ''
|
@pid = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_frame
|
def add_frame
|
||||||
@@ -56,8 +58,10 @@ class ValgrindKillingExecutor
|
|||||||
def to_s
|
def to_s
|
||||||
([@what + " (#{@kind}) in #{@pid}"] + @backtrace.map{|h| "#{h[:file]}:#{h[:line]} #{h[:fn]}" }).join("\n")
|
([@what + " (#{@kind}) in #{@pid}"] + @backtrace.map{|h| "#{h[:file]}:#{h[:line]} #{h[:fn]}" }).join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
end # class Error
|
end # class Error
|
||||||
|
|
||||||
|
|
||||||
class ErrorListener
|
class ErrorListener
|
||||||
include REXML::StreamListener
|
include REXML::StreamListener
|
||||||
def initialize( killer )
|
def initialize( killer )
|
||||||
@@ -70,36 +74,39 @@ class ValgrindKillingExecutor
|
|||||||
@text = text
|
@text = text
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_start(tag, _attrs)
|
def tag_start(tag, attrs)
|
||||||
case tag.to_s
|
case tag.to_s
|
||||||
when 'error'
|
when "error"
|
||||||
@found = true
|
@found = true
|
||||||
when 'frame'
|
when "frame"
|
||||||
@error.add_frame
|
@error.add_frame
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_end(tag)
|
def tag_end(tag)
|
||||||
case tag.to_s
|
case tag.to_s
|
||||||
when 'what'
|
when "what"
|
||||||
@error.what = @text if @found
|
@error.what = @text if @found
|
||||||
@text = ''
|
@text = ""
|
||||||
when 'kind'
|
when "kind"
|
||||||
@error.kind = @text if @found
|
@error.kind = @text if @found
|
||||||
when 'file'
|
when "file"
|
||||||
@error.add_file( @text ) if @found
|
@error.add_file( @text ) if @found
|
||||||
when 'fn'
|
when "fn"
|
||||||
@error.add_fn( @text ) if @found
|
@error.add_fn( @text ) if @found
|
||||||
when 'line'
|
when "line"
|
||||||
@error.add_line( @text ) if @found
|
@error.add_line( @text ) if @found
|
||||||
when 'error', 'stack'
|
when "error", "stack"
|
||||||
@killer.call(@error) if @found
|
if @found
|
||||||
when 'pid'
|
@killer.call( @error )
|
||||||
|
end
|
||||||
|
when "pid"
|
||||||
@error.pid=@text
|
@error.pid=@text
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end # class ErrorListener
|
end # class ErrorListener
|
||||||
|
|
||||||
|
|
||||||
class DebugErrorListener < ErrorListener
|
class DebugErrorListener < ErrorListener
|
||||||
def text( txt )
|
def text( txt )
|
||||||
print txt
|
print txt
|
||||||
@@ -117,41 +124,47 @@ class ValgrindKillingExecutor
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@pid = nil
|
@pid = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def run( cmd )
|
def run( cmd )
|
||||||
@io_r, io_w = IO.pipe
|
@io_r, io_w = IO.pipe
|
||||||
@pid = fork { exec("valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd) }
|
@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
|
||||||
|
|
||||||
|
|
||||||
def call( err )
|
def call( err )
|
||||||
warn '*' * 72
|
$stderr.puts "*"*72
|
||||||
warn '* Valgrind error spotted:'
|
$stderr.puts "* Valgrind error spotted:"
|
||||||
warn err.to_s.split("\n").map { |s| " #{s}" }
|
$stderr.puts err.to_s.split("\n").map{|s| " #{s}"}
|
||||||
warn '*' * 72
|
$stderr.puts "*"*72
|
||||||
Process.kill('KILL', @pid)
|
Process.kill( "KILL", @pid )
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def pick_listener
|
def pick_listener
|
||||||
ENV['DEBUG'] ? DebugErrorListener : ErrorListener
|
ENV['DEBUG'] ? DebugErrorListener : ErrorListener
|
||||||
end
|
end
|
||||||
|
|
||||||
def launch_watch_thread(_pid, io_r)
|
def launch_watch_thread(pid, io_r)
|
||||||
Thread.start do
|
Thread.start do
|
||||||
io_source = REXML::IOSource.new( io_r )
|
io_source = REXML::IOSource.new( io_r )
|
||||||
listener = pick_listener.new( self )
|
listener = pick_listener.new( self )
|
||||||
REXML::Document.parse_stream( io_source, listener )
|
REXML::Document.parse_stream( io_source, listener )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end # class ValgrindExecutor
|
end # class ValgrindExecutor
|
||||||
|
|
||||||
|
|
||||||
module FlexNBD
|
module FlexNBD
|
||||||
# Noddy test class to exercise FlexNBD from the outside for testing.
|
# Noddy test class to exercise FlexNBD from the outside for testing.
|
||||||
#
|
#
|
||||||
@@ -176,11 +189,12 @@ module FlexNBD
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def build_debug_opt
|
def build_debug_opt
|
||||||
if @do_debug
|
if @do_debug
|
||||||
'--verbose'
|
"--verbose"
|
||||||
else
|
else
|
||||||
'--quiet'
|
"--quiet"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -195,19 +209,20 @@ module FlexNBD
|
|||||||
@ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}"
|
@ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}"
|
||||||
@ip = ip
|
@ip = ip
|
||||||
@port = port
|
@port = port
|
||||||
@pid = @wait_thread = nil
|
|
||||||
@kill = []
|
@kill = []
|
||||||
@prefetch_proxy = false
|
@prefetch_proxy = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def debug?
|
def debug?
|
||||||
!!@do_debug
|
!!@do_debug
|
||||||
end
|
end
|
||||||
|
|
||||||
def debug( msg )
|
def debug( msg )
|
||||||
warn msg if debug?
|
$stderr.puts msg if debug?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def serve_cmd( file, acl )
|
def serve_cmd( file, acl )
|
||||||
"#{bin} serve "\
|
"#{bin} serve "\
|
||||||
"--addr #{ip} "\
|
"--addr #{ip} "\
|
||||||
@@ -218,6 +233,7 @@ module FlexNBD
|
|||||||
"#{acl.join(' ')}"
|
"#{acl.join(' ')}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def listen_cmd( file, acl )
|
def listen_cmd( file, acl )
|
||||||
"#{bin} listen "\
|
"#{bin} listen "\
|
||||||
"--addr #{ip} "\
|
"--addr #{ip} "\
|
||||||
@@ -234,10 +250,11 @@ module FlexNBD
|
|||||||
"--port #{port} "\
|
"--port #{port} "\
|
||||||
"--conn-addr #{connect_ip} "\
|
"--conn-addr #{connect_ip} "\
|
||||||
"--conn-port #{connect_port} "\
|
"--conn-port #{connect_port} "\
|
||||||
"#{prefetch_proxy ? '--cache ' : ''}"\
|
"#{prefetch_proxy ? "--cache " : ""}"\
|
||||||
"#{@debug}"
|
"#{@debug}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def read_cmd( offset, length )
|
def read_cmd( offset, length )
|
||||||
"#{bin} read "\
|
"#{bin} read "\
|
||||||
"--addr #{ip} "\
|
"--addr #{ip} "\
|
||||||
@@ -247,6 +264,7 @@ module FlexNBD
|
|||||||
"--size #{length}"
|
"--size #{length}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_cmd( offset, data )
|
def write_cmd( offset, data )
|
||||||
"#{bin} write "\
|
"#{bin} write "\
|
||||||
"--addr #{ip} "\
|
"--addr #{ip} "\
|
||||||
@@ -256,6 +274,7 @@ module FlexNBD
|
|||||||
"--size #{data.length}"
|
"--size #{data.length}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def base_mirror_opts( dest_ip, dest_port )
|
def base_mirror_opts( dest_ip, dest_port )
|
||||||
"--addr #{dest_ip} "\
|
"--addr #{dest_ip} "\
|
||||||
"--port #{dest_port} "\
|
"--port #{dest_port} "\
|
||||||
@@ -264,7 +283,7 @@ module FlexNBD
|
|||||||
|
|
||||||
def unlink_mirror_opts( dest_ip, dest_port )
|
def unlink_mirror_opts( dest_ip, dest_port )
|
||||||
"#{base_mirror_opts( dest_ip, dest_port )} "\
|
"#{base_mirror_opts( dest_ip, dest_port )} "\
|
||||||
'--unlink '
|
"--unlink "
|
||||||
end
|
end
|
||||||
|
|
||||||
def base_mirror_cmd( opts )
|
def base_mirror_cmd( opts )
|
||||||
@@ -297,52 +316,46 @@ module FlexNBD
|
|||||||
"#{@bin} acl " \
|
"#{@bin} acl " \
|
||||||
"--sock #{ctrl} "\
|
"--sock #{ctrl} "\
|
||||||
"#{@debug} "\
|
"#{@debug} "\
|
||||||
"#{acl.join ' '}"
|
"#{acl.join " "}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def run_serve_cmd(cmd)
|
def run_serve_cmd(cmd)
|
||||||
File.unlink(ctrl) if File.exist?(ctrl)
|
File.unlink(ctrl) if File.exists?(ctrl)
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
|
|
||||||
@pid = @executor.run( cmd )
|
@pid = @executor.run( cmd )
|
||||||
|
|
||||||
until File.socket?(ctrl)
|
while !File.socket?(ctrl)
|
||||||
pid, status = Process.wait2(@pid, Process::WNOHANG)
|
pid, status = Process.wait2(@pid, Process::WNOHANG)
|
||||||
raise "server did not start (#{cmd})" if pid
|
raise "server did not start (#{cmd})" if pid
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
start_wait_thread( @pid )
|
start_wait_thread( @pid )
|
||||||
at_exit { kill }
|
at_exit { kill }
|
||||||
end
|
end
|
||||||
private :run_serve_cmd
|
private :run_serve_cmd
|
||||||
|
|
||||||
|
|
||||||
def serve( file, *acl)
|
def serve( file, *acl)
|
||||||
cmd = serve_cmd( file, acl )
|
cmd = serve_cmd( file, acl )
|
||||||
run_serve_cmd( cmd )
|
run_serve_cmd( cmd )
|
||||||
sleep(0.2) until File.exist?(ctrl)
|
sleep( 0.2 ) until File.exists?( ctrl )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def listen(file, *acl)
|
def listen(file, *acl)
|
||||||
run_serve_cmd( listen_cmd( file, acl ) )
|
run_serve_cmd( listen_cmd( file, acl ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
def tcp_server_open?
|
def tcp_server_open?
|
||||||
# raises if the other side doesn't accept()
|
# raises if the other side doesn't accept()
|
||||||
sock = begin
|
sock = TCPSocket.new(ip, port) rescue nil
|
||||||
TCPSocket.new(ip, port)
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
success = !!sock
|
success = !!sock
|
||||||
if sock
|
( sock.close rescue nil) if sock
|
||||||
(begin
|
|
||||||
sock.close
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
success
|
success
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -362,25 +375,27 @@ module FlexNBD
|
|||||||
at_exit { kill }
|
at_exit { kill }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def start_wait_thread( pid )
|
def start_wait_thread( pid )
|
||||||
@wait_thread = Thread.start do
|
@wait_thread = Thread.start do
|
||||||
_, status = Process.waitpid2( pid )
|
_, status = Process.waitpid2( pid )
|
||||||
|
|
||||||
if @kill
|
if @kill
|
||||||
if status.signaled?
|
if status.signaled?
|
||||||
raise "flexnbd quit with a bad signal: #{status.inspect}" unless
|
fail "flexnbd quit with a bad signal: #{status.inspect}" unless
|
||||||
@kill.include? status.termsig
|
@kill.include? status.termsig
|
||||||
else
|
else
|
||||||
raise "flexnbd quit with a bad status: #{status.inspect}" unless
|
fail "flexnbd quit with a bad status: #{status.inspect}" unless
|
||||||
@kill.include? status.exitstatus
|
@kill.include? status.exitstatus
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
warn "flexnbd #{self.pid} quit"
|
$stderr.puts "flexnbd #{self.pid} quit"
|
||||||
raise "flexnbd #{self.pid} quit early with status #{status.to_i}"
|
fail "flexnbd #{self.pid} quit early with status #{status.to_i}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def can_die(*status)
|
def can_die(*status)
|
||||||
status = [0] if status.empty?
|
status = [0] if status.empty?
|
||||||
@kill += status
|
@kill += status
|
||||||
@@ -392,7 +407,7 @@ module FlexNBD
|
|||||||
can_die(1)
|
can_die(1)
|
||||||
if @pid
|
if @pid
|
||||||
begin
|
begin
|
||||||
Process.kill('INT', @pid)
|
Process.kill("INT", @pid)
|
||||||
rescue Errno::ESRCH => e
|
rescue Errno::ESRCH => e
|
||||||
# already dead. Presumably this means it went away after a
|
# already dead. Presumably this means it went away after a
|
||||||
# can_die() call.
|
# can_die() call.
|
||||||
@@ -408,7 +423,7 @@ module FlexNBD
|
|||||||
IO.popen(cmd) do |fh|
|
IO.popen(cmd) do |fh|
|
||||||
return fh.read
|
return fh.read
|
||||||
end
|
end
|
||||||
raise IOError, 'NBD read failed' unless $CHILD_STATUS.success?
|
raise IOError.new "NBD read failed" unless $?.success?
|
||||||
out
|
out
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -416,24 +431,27 @@ module FlexNBD
|
|||||||
cmd = write_cmd( offset, data )
|
cmd = write_cmd( offset, data )
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
|
|
||||||
IO.popen(cmd, 'w') do |fh|
|
IO.popen(cmd, "w") do |fh|
|
||||||
fh.write(data)
|
fh.write(data)
|
||||||
end
|
end
|
||||||
raise IOError, 'NBD write failed' unless $CHILD_STATUS.success?
|
raise IOError.new "NBD write failed" unless $?.success?
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def join
|
def join
|
||||||
@wait_thread.join
|
@wait_thread.join
|
||||||
end
|
end
|
||||||
|
|
||||||
def mirror_unchecked(dest_ip, dest_port, _bandwidth = nil, _action = nil, timeout = nil)
|
|
||||||
|
def mirror_unchecked( dest_ip, dest_port, bandwidth=nil, action=nil, timeout=nil )
|
||||||
cmd = mirror_cmd( dest_ip, dest_port)
|
cmd = mirror_cmd( dest_ip, dest_port)
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
|
|
||||||
maybe_timeout( cmd, timeout )
|
maybe_timeout( cmd, timeout )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def mirror_unlink( dest_ip, dest_port, timeout=nil )
|
def mirror_unlink( dest_ip, dest_port, timeout=nil )
|
||||||
cmd = mirror_unlink_cmd( dest_ip, dest_port )
|
cmd = mirror_unlink_cmd( dest_ip, dest_port )
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
@@ -441,11 +459,11 @@ module FlexNBD
|
|||||||
maybe_timeout( cmd, timeout )
|
maybe_timeout( cmd, timeout )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def maybe_timeout(cmd, timeout=nil )
|
def maybe_timeout(cmd, timeout=nil )
|
||||||
stdout = ''
|
stdout, stderr = "",""
|
||||||
stderr = ''
|
|
||||||
stat = nil
|
stat = nil
|
||||||
run = proc do
|
run = Proc.new do
|
||||||
# Ruby 1.9 changed the popen3 api. instead of 3 args, the block
|
# Ruby 1.9 changed the popen3 api. instead of 3 args, the block
|
||||||
# gets 4. Not only that, but it no longer sets $?, so we have to
|
# gets 4. Not only that, but it no longer sets $?, so we have to
|
||||||
# go elsewhere for the process' exit status.
|
# go elsewhere for the process' exit status.
|
||||||
@@ -455,7 +473,7 @@ module FlexNBD
|
|||||||
stderr.replace io_err.read
|
stderr.replace io_err.read
|
||||||
stat = maybe_thr.value if maybe_thr
|
stat = maybe_thr.value if maybe_thr
|
||||||
end
|
end
|
||||||
stat ||= $CHILD_STATUS
|
stat ||= $?
|
||||||
end
|
end
|
||||||
|
|
||||||
if timeout
|
if timeout
|
||||||
@@ -467,13 +485,16 @@ module FlexNBD
|
|||||||
[stdout, stderr, stat]
|
[stdout, stderr, stat]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def mirror(dest_ip, dest_port, bandwidth=nil, action=nil)
|
def mirror(dest_ip, dest_port, bandwidth=nil, action=nil)
|
||||||
stdout, stderr, status = mirror_unchecked( dest_ip, dest_port, bandwidth, action )
|
stdout, stderr, status = mirror_unchecked( dest_ip, dest_port, bandwidth, action )
|
||||||
raise IOError, "Migrate command failed\n" + stderr unless status.success?
|
raise IOError.new( "Migrate command failed\n" + stderr) unless status.success?
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def break(timeout=nil)
|
def break(timeout=nil)
|
||||||
cmd = break_cmd
|
cmd = break_cmd
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
@@ -481,6 +502,7 @@ module FlexNBD
|
|||||||
maybe_timeout( cmd, timeout )
|
maybe_timeout( cmd, timeout )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def acl(*acl)
|
def acl(*acl)
|
||||||
cmd = acl_cmd( *acl )
|
cmd = acl_cmd( *acl )
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
@@ -488,8 +510,9 @@ module FlexNBD
|
|||||||
maybe_timeout( cmd, 2 )
|
maybe_timeout( cmd, 2 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def status( timeout = nil )
|
def status( timeout = nil )
|
||||||
cmd = status_cmd
|
cmd = status_cmd()
|
||||||
debug( cmd )
|
debug( cmd )
|
||||||
|
|
||||||
o,e = maybe_timeout( cmd, timeout )
|
o,e = maybe_timeout( cmd, timeout )
|
||||||
@@ -497,43 +520,50 @@ module FlexNBD
|
|||||||
[parse_status(o), e]
|
[parse_status(o), e]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def launched?
|
def launched?
|
||||||
!!@pid
|
!!@pid
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def paused
|
def paused
|
||||||
Process.kill('STOP', @pid)
|
Process.kill( "STOP", @pid )
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
Process.kill('CONT', @pid)
|
Process.kill( "CONT", @pid )
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
|
protected
|
||||||
def control_command(*args)
|
def control_command(*args)
|
||||||
raise 'Server not running' unless @pid
|
raise "Server not running" unless @pid
|
||||||
args = args.compact
|
args = args.compact
|
||||||
UNIXSocket.open(@ctrl) do |u|
|
UNIXSocket.open(@ctrl) do |u|
|
||||||
u.write(args.join("\n") + "\n")
|
u.write(args.join("\n") + "\n")
|
||||||
code, message = u.readline.split(': ', 2)
|
code, message = u.readline.split(": ", 2)
|
||||||
return [code, message]
|
return [code, message]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def parse_status( status )
|
def parse_status( status )
|
||||||
hsh = {}
|
hsh = {}
|
||||||
|
|
||||||
status.split(' ').each do |part|
|
status.split(" ").each do |part|
|
||||||
next if part.strip.empty?
|
next if part.strip.empty?
|
||||||
a, b = part.split('=')
|
a,b = part.split("=")
|
||||||
b.strip!
|
b.strip!
|
||||||
b = true if b == 'true'
|
b = true if b == "true"
|
||||||
b = false if b == 'false'
|
b = false if b == "false"
|
||||||
|
|
||||||
hsh[a.strip] = b
|
hsh[a.strip] = b
|
||||||
end
|
end
|
||||||
|
|
||||||
hsh
|
hsh
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
module FlexNBD
|
module FlexNBD
|
||||||
|
|
||||||
def self.binary( str )
|
def self.binary( str )
|
||||||
if str.respond_to? :force_encoding
|
if str.respond_to? :force_encoding
|
||||||
str.force_encoding 'ASCII-8BIT'
|
str.force_encoding "ASCII-8BIT"
|
||||||
else
|
else
|
||||||
str
|
str
|
||||||
end
|
end
|
||||||
@@ -10,33 +13,36 @@ module FlexNBD
|
|||||||
# eeevil is his one and only name...
|
# eeevil is his one and only name...
|
||||||
def self.read_constants
|
def self.read_constants
|
||||||
parents = []
|
parents = []
|
||||||
current = File.expand_path('.')
|
current = File.expand_path(".")
|
||||||
while current != '/'
|
while current != "/"
|
||||||
parents << current
|
parents << current
|
||||||
current = File.expand_path(File.join(current, '..'))
|
current = File.expand_path( File.join( current, ".." ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
source_root = parents.find do |dirname|
|
source_root = parents.find do |dirname|
|
||||||
File.directory?(File.join(dirname, 'src'))
|
File.directory?( File.join( dirname, "src" ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
raise 'No source root!' unless source_root
|
fail "No source root!" unless source_root
|
||||||
|
|
||||||
headers = Dir[File.join(source_root, 'src', '{common,proxy,server}', '*.h')]
|
headers = Dir[File.join( source_root, "src", "{common,proxy,server}","*.h" ) ]
|
||||||
|
|
||||||
headers.each do |header_filename|
|
headers.each do |header_filename|
|
||||||
txt_lines = File.readlines( header_filename )
|
txt_lines = File.readlines( header_filename )
|
||||||
txt_lines.each do |line|
|
txt_lines.each do |line|
|
||||||
if line =~ /^#\s*define\s+([A-Z0-9_]+)\s+(\d+)\s*$/
|
if line =~ /^#\s*define\s+([A-Z0-9_]+)\s+(\d+)\s*$/
|
||||||
# Bodge until I can figure out what to do with #ifdefs
|
# Bodge until I can figure out what to do with #ifdefs
|
||||||
const_set(Regexp.last_match(1), Regexp.last_match(2).to_i) unless const_defined?(Regexp.last_match(1))
|
const_set($1, $2.to_i) unless const_defined?( $1 )
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
read_constants
|
end
|
||||||
|
|
||||||
|
read_constants()
|
||||||
|
|
||||||
REQUEST_MAGIC = binary("\x25\x60\x95\x13") unless defined?(REQUEST_MAGIC)
|
REQUEST_MAGIC = binary("\x25\x60\x95\x13") unless defined?(REQUEST_MAGIC)
|
||||||
REPLY_MAGIC = binary("\x67\x44\x66\x98") unless defined?(REPLY_MAGIC)
|
REPLY_MAGIC = binary("\x67\x44\x66\x98") unless defined?(REPLY_MAGIC)
|
||||||
|
|
||||||
end # module FlexNBD
|
end # module FlexNBD
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'socket'
|
require 'socket'
|
||||||
require 'timeout'
|
require 'timeout'
|
||||||
|
|
||||||
@@ -5,13 +7,15 @@ require 'flexnbd/constants'
|
|||||||
|
|
||||||
module FlexNBD
|
module FlexNBD
|
||||||
class FakeDest
|
class FakeDest
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
def initialize( sock )
|
def initialize( sock )
|
||||||
@sock = sock
|
@sock = sock
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_hello( opts = {} )
|
def write_hello( opts = {} )
|
||||||
@sock.write('NBDMAGIC')
|
@sock.write( "NBDMAGIC" )
|
||||||
|
|
||||||
if opts[:magic] == :wrong
|
if opts[:magic] == :wrong
|
||||||
write_rand( @sock, 8 )
|
write_rand( @sock, 8 )
|
||||||
@@ -28,11 +32,13 @@ module FlexNBD
|
|||||||
@sock.write( "\x00" * 128 )
|
@sock.write( "\x00" * 128 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_rand( sock, len )
|
def write_rand( sock, len )
|
||||||
len.times { sock.write(rand(256).chr) }
|
len.times do sock.write( rand(256).chr ) end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_request
|
|
||||||
|
def read_request()
|
||||||
req = @sock.read(28)
|
req = @sock.read(28)
|
||||||
|
|
||||||
magic_s = req[0 ... 4 ]
|
magic_s = req[0 ... 4 ]
|
||||||
@@ -42,11 +48,11 @@ module FlexNBD
|
|||||||
len_s = req[24 ... 28]
|
len_s = req[24 ... 28]
|
||||||
|
|
||||||
{
|
{
|
||||||
magic: magic_s,
|
:magic => magic_s,
|
||||||
type: type_s.unpack('N').first,
|
:type => type_s.unpack("N").first,
|
||||||
handle: handle_s,
|
:handle => handle_s,
|
||||||
from: self.class.parse_be64(from_s),
|
:from => self.class.parse_be64( from_s ),
|
||||||
len: len_s.unpack('N').first
|
:len => len_s.unpack( "N").first
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -55,12 +61,14 @@ module FlexNBD
|
|||||||
end
|
end
|
||||||
|
|
||||||
def disconnected?
|
def disconnected?
|
||||||
|
begin
|
||||||
Timeout.timeout(2) do
|
Timeout.timeout(2) do
|
||||||
@sock.read(1).nil?
|
@sock.read(1) == nil
|
||||||
end
|
end
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def write_reply( handle, err=0, opts={} )
|
def write_reply( handle, err=0, opts={} )
|
||||||
if opts[:magic] == :wrong
|
if opts[:magic] == :wrong
|
||||||
@@ -69,14 +77,16 @@ module FlexNBD
|
|||||||
@sock.write( ::FlexNBD::REPLY_MAGIC )
|
@sock.write( ::FlexNBD::REPLY_MAGIC )
|
||||||
end
|
end
|
||||||
|
|
||||||
@sock.write([err].pack('N'))
|
@sock.write( [err].pack("N") )
|
||||||
@sock.write( handle )
|
@sock.write( handle )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def close
|
def close
|
||||||
@sock.close
|
@sock.close
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def read_data( len )
|
def read_data( len )
|
||||||
@sock.read( len )
|
@sock.read( len )
|
||||||
end
|
end
|
||||||
@@ -85,23 +95,25 @@ module FlexNBD
|
|||||||
@sock.write( len )
|
@sock.write( len )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def self.parse_be64(str)
|
def self.parse_be64(str)
|
||||||
raise "String is the wrong length: 8 bytes expected (#{str.length} received)" unless
|
raise "String is the wrong length: 8 bytes expected (#{str.length} received)" unless
|
||||||
str.length == 8
|
str.length == 8
|
||||||
|
|
||||||
top, bottom = str.unpack('NN')
|
top, bottom = str.unpack("NN")
|
||||||
(top << 32) + bottom
|
(top << 32) + bottom
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def receive_mirror( opts = {} )
|
def receive_mirror( opts = {} )
|
||||||
write_hello
|
write_hello()
|
||||||
loop do
|
loop do
|
||||||
req = read_request
|
req = read_request
|
||||||
case req[:type]
|
case req[:type]
|
||||||
when 1
|
when 1
|
||||||
read_data( req[:len] )
|
read_data( req[:len] )
|
||||||
write_reply( req[:handle] )
|
write_reply( req[:handle] )
|
||||||
when 65_536
|
when 65536
|
||||||
write_reply( req[:handle], opts[:err] == :entrust ? 1 : 0 )
|
write_reply( req[:handle], opts[:err] == :entrust ? 1 : 0 )
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
@@ -117,13 +129,16 @@ module FlexNBD
|
|||||||
raise "Not a disconnect: #{req.inspect}"
|
raise "Not a disconnect: #{req.inspect}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end # class Client
|
end # class Client
|
||||||
|
|
||||||
|
|
||||||
def initialize( addr, port )
|
def initialize( addr, port )
|
||||||
@sock = TCPServer.new( addr, port )
|
@sock = TCPServer.new( addr, port )
|
||||||
end
|
end
|
||||||
|
|
||||||
def accept(err_msg = 'Timed out waiting for a connection', timeout = 5)
|
|
||||||
|
def accept( err_msg = "Timed out waiting for a connection", timeout = 5)
|
||||||
client_sock = nil
|
client_sock = nil
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@@ -131,7 +146,7 @@ module FlexNBD
|
|||||||
client_sock = @sock.accept
|
client_sock = @sock.accept
|
||||||
end
|
end
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
raise Timeout::Error, err_msg
|
raise Timeout::Error.new(err_msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
client_sock
|
client_sock
|
||||||
@@ -139,8 +154,13 @@ module FlexNBD
|
|||||||
Client.new( client_sock )
|
Client.new( client_sock )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def close
|
def close
|
||||||
@sock.close
|
@sock.close
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end # module FakeDest
|
end # module FakeDest
|
||||||
end # module FlexNBD
|
end # module FlexNBD
|
||||||
|
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'socket'
|
require 'socket'
|
||||||
require 'timeout'
|
require "timeout"
|
||||||
require 'flexnbd/constants'
|
require 'flexnbd/constants'
|
||||||
|
|
||||||
module FlexNBD
|
module FlexNBD
|
||||||
class FakeSource
|
class FakeSource
|
||||||
|
|
||||||
def initialize( addr, port, err_msg, source_addr=nil, source_port=0 )
|
def initialize( addr, port, err_msg, source_addr=nil, source_port=0 )
|
||||||
timing_out( 2, err_msg ) do
|
timing_out( 2, err_msg ) do
|
||||||
begin
|
begin
|
||||||
@@ -13,76 +16,75 @@ module FlexNBD
|
|||||||
TCPSocket.new( addr, port )
|
TCPSocket.new( addr, port )
|
||||||
end
|
end
|
||||||
rescue Errno::ECONNREFUSED
|
rescue Errno::ECONNREFUSED
|
||||||
warn 'Connection refused, retrying'
|
$stderr.puts "Connection refused, retrying"
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def close
|
def close
|
||||||
@sock.close
|
@sock.close
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_hello
|
|
||||||
|
def read_hello()
|
||||||
timing_out( ::FlexNBD::MS_HELLO_TIME_SECS,
|
timing_out( ::FlexNBD::MS_HELLO_TIME_SECS,
|
||||||
'Timed out waiting for hello.') do
|
"Timed out waiting for hello." ) do
|
||||||
raise 'No hello.' unless (hello = @sock.read(152)) &&
|
fail "No hello." unless (hello = @sock.read( 152 )) &&
|
||||||
hello.length==152
|
hello.length==152
|
||||||
|
|
||||||
passwd_s = hello[0..7]
|
magic_s = hello[0..7]
|
||||||
magic = hello[8..15].unpack('Q>').first
|
ignore_s= hello[8..15]
|
||||||
size = hello[16..23].unpack('Q>').first
|
size_s = hello[16..23]
|
||||||
flags = hello[24..27].unpack('L>').first
|
|
||||||
reserved = hello[28..-1]
|
|
||||||
|
|
||||||
return { passwd: passwd_s, magic: magic, size: size, flags: flags, reserved: reserved }
|
size_h, size_l = size_s.unpack("NN")
|
||||||
|
size = (size_h << 32) + size_l
|
||||||
|
|
||||||
|
return { :magic => magic_s, :size => size }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_request(type, handle = 'myhandle', from = 0, len = 0, magic = REQUEST_MAGIC, flags = 0)
|
|
||||||
raise 'Bad handle' unless handle.length == 8
|
def send_request( type, handle="myhandle", from=0, len=0, magic=REQUEST_MAGIC )
|
||||||
|
fail "Bad handle" unless handle.length == 8
|
||||||
|
|
||||||
@sock.write( magic )
|
@sock.write( magic )
|
||||||
@sock.write([flags].pack('n'))
|
@sock.write( [type].pack( 'N' ) )
|
||||||
@sock.write([type].pack('n'))
|
|
||||||
@sock.write( handle )
|
@sock.write( handle )
|
||||||
@sock.write( [n64( from )].pack( 'q' ) )
|
@sock.write( [n64( from )].pack( 'q' ) )
|
||||||
@sock.write( [len].pack( 'N' ) )
|
@sock.write( [len].pack( 'N' ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_write_request(from, len, handle = 'myhandle')
|
|
||||||
|
def write_write_request( from, len, handle="myhandle" )
|
||||||
send_request( 1, handle, from, len )
|
send_request( 1, handle, from, len )
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_write_request_with_fua(from, len, handle = 'myhandle')
|
|
||||||
send_request(1, handle, from, len, REQUEST_MAGIC, 1)
|
def write_entrust_request( handle="myhandle" )
|
||||||
|
send_request( 65536, handle )
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_flush_request(handle = 'myhandle')
|
def write_disconnect_request( handle="myhandle" )
|
||||||
send_request(3, handle, 0, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_entrust_request(handle = 'myhandle')
|
|
||||||
send_request(65_536, handle)
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_disconnect_request(handle = 'myhandle')
|
|
||||||
send_request( 2, handle )
|
send_request( 2, handle )
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_read_request(from, len, _handle = 'myhandle')
|
def write_read_request( from, len, handle="myhandle" )
|
||||||
send_request(0, 'myhandle', from, len)
|
send_request( 0, "myhandle", from, len )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write_data( data )
|
def write_data( data )
|
||||||
@sock.write( data )
|
@sock.write( data )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Handy utility
|
# Handy utility
|
||||||
def read( from, len )
|
def read( from, len )
|
||||||
timing_out(2, 'Timed out reading') do
|
timing_out( 2, "Timed out reading" ) do
|
||||||
send_request(0, 'myhandle', from, len)
|
send_request( 0, "myhandle", from, len )
|
||||||
read_raw( len )
|
read_raw( len )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -92,26 +94,19 @@ module FlexNBD
|
|||||||
end
|
end
|
||||||
|
|
||||||
def send_mirror
|
def send_mirror
|
||||||
read_hello
|
read_hello()
|
||||||
write(0, '12345678')
|
write( 0, "12345678" )
|
||||||
read_response
|
read_response()
|
||||||
write_disconnect_request
|
write_disconnect_request()
|
||||||
close
|
close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def write( from, data )
|
def write( from, data )
|
||||||
write_write_request( from, data.length )
|
write_write_request( from, data.length )
|
||||||
write_data( data )
|
write_data( data )
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_with_fua(from, data)
|
|
||||||
write_write_request_with_fua(from, data.length)
|
|
||||||
write_data(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def flush
|
|
||||||
write_flush_request
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_response
|
def read_response
|
||||||
magic = @sock.read(4)
|
magic = @sock.read(4)
|
||||||
@@ -119,26 +114,30 @@ module FlexNBD
|
|||||||
handle = @sock.read(8)
|
handle = @sock.read(8)
|
||||||
|
|
||||||
{
|
{
|
||||||
magic: magic,
|
:magic => magic,
|
||||||
error: error_s.unpack('N').first,
|
:error => error_s.unpack("N").first,
|
||||||
handle: handle
|
:handle => handle
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def disconnected?
|
def disconnected?
|
||||||
result = nil
|
result = nil
|
||||||
Timeout.timeout(2) { result = @sock.read(1).nil? }
|
Timeout.timeout( 2 ) { result = ( @sock.read(1) == nil ) }
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def timing_out( time, msg )
|
def timing_out( time, msg )
|
||||||
|
begin
|
||||||
Timeout.timeout( time ) do
|
Timeout.timeout( time ) do
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
warn msg
|
$stderr.puts msg
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
@@ -155,5 +154,7 @@ module FlexNBD
|
|||||||
((b & 0x000000000000ff00) << 40) |
|
((b & 0x000000000000ff00) << 40) |
|
||||||
((b & 0x00000000000000ff) << 56)
|
((b & 0x00000000000000ff) << 56)
|
||||||
end
|
end
|
||||||
|
|
||||||
end # class FakeSource
|
end # class FakeSource
|
||||||
end # module FlexNBD
|
end # module FlexNBD
|
||||||
|
|
||||||
|
@@ -1,55 +0,0 @@
|
|||||||
require 'tempfile'
|
|
||||||
|
|
||||||
#
|
|
||||||
# LdPreload is a little wrapper for using LD_PRELOAD loggers to pick up system
|
|
||||||
# calls when testing flexnbd.
|
|
||||||
#
|
|
||||||
module LdPreload
|
|
||||||
#
|
|
||||||
# This takes an object name, sets up a temporary log file, whose name is
|
|
||||||
# recorded in the environment as OUTPUT_obj_name, where obj_name is the
|
|
||||||
# name of the preload module to build and load.
|
|
||||||
def with_ld_preload(obj_name)
|
|
||||||
@ld_preload_logs ||= {}
|
|
||||||
flunk 'Can only load a preload module once!' if @ld_preload_logs[obj_name]
|
|
||||||
|
|
||||||
system("make -C ld_preloads/ #{obj_name}.o > /dev/null") ||
|
|
||||||
flunk("Failed to build object #{obj_name}")
|
|
||||||
orig_env = ENV['LD_PRELOAD']
|
|
||||||
ENV['LD_PRELOAD'] = [orig_env, File.expand_path("./ld_preloads/#{obj_name}.o")].compact.join(' ')
|
|
||||||
|
|
||||||
# Open the log, and stick it in a hash
|
|
||||||
@ld_preload_logs[obj_name] = Tempfile.new(obj_name)
|
|
||||||
ENV['OUTPUT_' + obj_name] = @ld_preload_logs[obj_name].path
|
|
||||||
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
if @ld_preload_logs[obj_name]
|
|
||||||
@ld_preload_logs[obj_name].close
|
|
||||||
@ld_preload_logs.delete(obj_name)
|
|
||||||
end
|
|
||||||
ENV['LD_PRELOAD'] = orig_env
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_ld_preload_log(obj_name)
|
|
||||||
lines = []
|
|
||||||
lines << @ld_preload_logs[obj_name].readline.chomp until
|
|
||||||
@ld_preload_logs[obj_name].eof?
|
|
||||||
lines
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# The next to methods assume the log file has one entry per line, and that
|
|
||||||
# each entry is a series of values separated by colons.
|
|
||||||
#
|
|
||||||
def parse_ld_preload_logs(obj_name)
|
|
||||||
read_ld_preload_log(obj_name).map do |l|
|
|
||||||
l.split(':').map { |i| i =~ /^\d+$/ ? i.to_i : i }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_func_call(loglines, args, msg)
|
|
||||||
re = Regexp.new('^' + args.join(':'))
|
|
||||||
assert(loglines.any? { |l| l.match(re) }, msg)
|
|
||||||
end
|
|
||||||
end
|
|
@@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
SRC := $(wildcard *.c)
|
|
||||||
OBJS := $(SRC:%.c=%.o)
|
|
||||||
|
|
||||||
all: $(OBJS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) $(OBJS)
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
gcc -shared -fPIC -ldl -o $@ $<
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
@@ -1,33 +0,0 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
typedef int (*real_msync_t)(void *addr, size_t length, int flags);
|
|
||||||
|
|
||||||
int real_msync(void *addr, size_t length, int flags) {
|
|
||||||
return ((real_msync_t)dlsym(RTLD_NEXT, "msync"))(addr, length, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Noddy LD_PRELOAD wrapper to catch msync calls, and log them to a file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int msync(void *addr, size_t length, int flags) {
|
|
||||||
FILE *fd;
|
|
||||||
char *fn;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = real_msync(addr, length, flags);
|
|
||||||
|
|
||||||
fn = getenv("OUTPUT_msync_logger");
|
|
||||||
if ( fn != NULL ) {
|
|
||||||
fd = fopen(fn,"a");
|
|
||||||
fprintf(fd,"msync:%d:%i:%i:%i\n", addr, length, flags, retval);
|
|
||||||
fclose(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
@@ -1,38 +0,0 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Noddy LD_PRELOAD wrapper to catch setsockopt calls, and log them to a file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef int (*real_setsockopt_t)(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
|
||||||
|
|
||||||
int real_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
|
|
||||||
{
|
|
||||||
return ((real_setsockopt_t)dlsym(RTLD_NEXT, "setsockopt"))(sockfd, level, optname, optval, optlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
|
|
||||||
{
|
|
||||||
FILE *fd;
|
|
||||||
char *fn;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = real_setsockopt(sockfd, level, optname, optval, optlen);
|
|
||||||
fn = getenv("OUTPUT_setsockopt_logger");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only interested in catching non-null 4-byte (integer) values
|
|
||||||
*/
|
|
||||||
if ( fn != NULL && optval != NULL && optlen == 4) {
|
|
||||||
fd = fopen(fn,"a");
|
|
||||||
fprintf(fd,"setsockopt:%i:%i:%i:%i:%i\n", sockfd, level, optname, *(int *)optval, retval);
|
|
||||||
fclose(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
2
tests/acceptance/nbd_scenarios
Executable file → Normal file
2
tests/acceptance/nbd_scenarios
Executable file → Normal file
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/ruby
|
#!/usr/bin/ruby
|
||||||
|
|
||||||
test_files = Dir[File.dirname(__FILE__) + '/test*.rb']
|
test_files = Dir[File.dirname( __FILE__ ) + "/test*.rb"]
|
||||||
for filename in test_files
|
for filename in test_files
|
||||||
require filename
|
require filename
|
||||||
end
|
end
|
||||||
|
@@ -1,12 +1,8 @@
|
|||||||
|
# encoding: utf-8
|
||||||
require 'flexnbd/fake_source'
|
require 'flexnbd/fake_source'
|
||||||
require 'flexnbd/fake_dest'
|
require 'flexnbd/fake_dest'
|
||||||
require 'ld_preload'
|
|
||||||
|
|
||||||
module ProxyTests
|
module ProxyTests
|
||||||
|
|
||||||
include LdPreload
|
|
||||||
|
|
||||||
def b
|
def b
|
||||||
"\xFF".b
|
"\xFF".b
|
||||||
end
|
end
|
||||||
@@ -17,17 +13,14 @@ module ProxyTests
|
|||||||
@env.nbd2.can_die(0)
|
@env.nbd2.can_die(0)
|
||||||
client = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy")
|
client = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy")
|
||||||
begin
|
begin
|
||||||
|
|
||||||
result = client.read_hello
|
result = client.read_hello
|
||||||
assert_equal 'NBDMAGIC', result[:passwd]
|
assert_equal "NBDMAGIC", result[:magic]
|
||||||
assert_equal override_size || @env.file1.size, result[:size]
|
assert_equal override_size || @env.file1.size, result[:size]
|
||||||
|
|
||||||
yield client
|
yield client
|
||||||
ensure
|
ensure
|
||||||
begin
|
client.close rescue nil
|
||||||
client.close
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -39,11 +32,11 @@ module ProxyTests
|
|||||||
with_proxied_client do |client|
|
with_proxied_client do |client|
|
||||||
(0..3).each do |n|
|
(0..3).each do |n|
|
||||||
offset = n * 4096
|
offset = n * 4096
|
||||||
client.write_read_request(offset, 4096, 'myhandle')
|
client.write_read_request(offset, 4096, "myhandle")
|
||||||
rsp = client.read_response
|
rsp = client.read_response
|
||||||
|
|
||||||
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
assert_equal "myhandle", rsp[:handle]
|
||||||
assert_equal 0, rsp[:error]
|
assert_equal 0, rsp[:error]
|
||||||
|
|
||||||
orig_data = @env.file1.read(offset, 4096)
|
orig_data = @env.file1.read(offset, 4096)
|
||||||
@@ -66,7 +59,7 @@ module ProxyTests
|
|||||||
rsp = client.read_response
|
rsp = client.read_response
|
||||||
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
assert_equal "myhandle", rsp[:handle]
|
||||||
assert_equal 0, rsp[:error]
|
assert_equal 0, rsp[:error]
|
||||||
|
|
||||||
data = @env.file1.read(offset, 4096)
|
data = @env.file1.read(offset, 4096)
|
||||||
@@ -76,20 +69,6 @@ module ProxyTests
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_write_request_past_end_of_disc_returns_to_client
|
|
||||||
with_proxied_client do |client|
|
|
||||||
n = 1000
|
|
||||||
offset = n * 4096
|
|
||||||
client.write(offset, b * 4096)
|
|
||||||
rsp = client.read_response
|
|
||||||
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
|
||||||
# NBD protocol say ENOSPC (28) in this situation
|
|
||||||
assert_equal 28, rsp[:error]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def make_fake_server
|
def make_fake_server
|
||||||
server = FlexNBD::FakeDest.new(@env.ip, @env.port1)
|
server = FlexNBD::FakeDest.new(@env.ip, @env.port1)
|
||||||
@server_up = true
|
@server_up = true
|
||||||
@@ -135,23 +114,23 @@ module ProxyTests
|
|||||||
sc2.write_data( b * 4096 )
|
sc2.write_data( b * 4096 )
|
||||||
|
|
||||||
# Check it to make sure it's correct
|
# Check it to make sure it's correct
|
||||||
rsp = Timeout.timeout(15) { client.read_response }
|
rsp = timeout(15) { client.read_response }
|
||||||
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 0, rsp[:error]
|
assert_equal 0, rsp[:error]
|
||||||
assert_equal req1[:handle], rsp[:handle]
|
assert_equal req1[:handle], rsp[:handle]
|
||||||
|
|
||||||
data = client.read_raw( 4096 )
|
data = client.read_raw( 4096 )
|
||||||
assert_equal((b * 4096), data, 'Wrong data returned')
|
assert_equal( (b * 4096), data, "Wrong data returned" )
|
||||||
|
|
||||||
sc2.close
|
sc2.close
|
||||||
server.close
|
server.close
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_write_request_retried_when_upstream_dies_partway
|
def test_write_request_retried_when_upstream_dies_partway
|
||||||
maker = make_fake_server
|
maker = make_fake_server
|
||||||
|
|
||||||
with_ld_preload('setsockopt_logger') do
|
|
||||||
with_proxied_client(4096) do |client|
|
with_proxied_client(4096) do |client|
|
||||||
server, sc1 = maker.value
|
server, sc1 = maker.value
|
||||||
|
|
||||||
@@ -165,11 +144,7 @@ module ProxyTests
|
|||||||
assert_equal 0, req1[:from]
|
assert_equal 0, req1[:from]
|
||||||
assert_equal 4096, req1[:len]
|
assert_equal 4096, req1[:len]
|
||||||
data1 = sc1.read_data( 4096 )
|
data1 = sc1.read_data( 4096 )
|
||||||
assert_equal((b * 4096), data1, 'Data not proxied successfully')
|
assert_equal( ( b * 4096 ), data1, "Data not proxied successfully" )
|
||||||
|
|
||||||
# Read the setsockopt logs, so we can check that TCP_NODELAY is re-set
|
|
||||||
# later
|
|
||||||
read_ld_preload_log('setsockopt_logger')
|
|
||||||
|
|
||||||
# Kill the server again, now we're sure the read request has been sent once
|
# Kill the server again, now we're sure the read request has been sent once
|
||||||
sc1.close
|
sc1.close
|
||||||
@@ -188,53 +163,32 @@ module ProxyTests
|
|||||||
sc2.write_reply( req2[:handle] )
|
sc2.write_reply( req2[:handle] )
|
||||||
|
|
||||||
# Check it to make sure it's correct
|
# Check it to make sure it's correct
|
||||||
rsp = Timeout.timeout(15) { client.read_response }
|
rsp = timeout(15) { client.read_response }
|
||||||
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 0, rsp[:error]
|
assert_equal 0, rsp[:error]
|
||||||
assert_equal req1[:handle], rsp[:handle]
|
assert_equal req1[:handle], rsp[:handle]
|
||||||
|
|
||||||
sc2.close
|
sc2.close
|
||||||
server.close
|
server.close
|
||||||
|
|
||||||
# Check TCP_NODELAY was set on the upstream socket
|
|
||||||
log = read_ld_preload_log('setsockopt_logger')
|
|
||||||
assert_func_call(log,
|
|
||||||
['setsockopt', 3,
|
|
||||||
Socket::SOL_TCP, Socket::TCP_NODELAY, 1, 0],
|
|
||||||
'TCP_NODELAY not set on upstream fd 3')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_only_one_client_can_connect_to_proxy_at_a_time
|
def test_only_one_client_can_connect_to_proxy_at_a_time
|
||||||
with_proxied_client do |_client|
|
with_proxied_client do |client|
|
||||||
|
|
||||||
c2 = nil
|
c2 = nil
|
||||||
assert_raises(Timeout::Error) do
|
assert_raises(Timeout::Error) do
|
||||||
Timeout.timeout(1) do
|
timeout(1) do
|
||||||
c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)")
|
c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)")
|
||||||
c2.read_hello
|
c2.read_hello
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if c2
|
c2.close rescue nil if c2
|
||||||
begin
|
|
||||||
c2.close
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_maximum_write_request_size
|
|
||||||
# Defined in src/common/nbdtypes.h
|
|
||||||
nbd_max_block_size = 32 * 1024 * 1024
|
|
||||||
@env.writefile1('0' * 40 * 1024)
|
|
||||||
with_proxied_client do |client|
|
|
||||||
# This will crash with EPIPE if the proxy dies.
|
|
||||||
client.write(0, b * nbd_max_block_size)
|
|
||||||
rsp = client.read_response
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
|
||||||
assert_equal 0, rsp[:error]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'environment'
|
require 'environment'
|
||||||
|
|
||||||
class TestDestErrorHandling < Test::Unit::TestCase
|
class TestDestErrorHandling < Test::Unit::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@env = Environment.new
|
@env = Environment.new
|
||||||
@env.writefile1('0' * 4)
|
@env.writefile1( "0" * 4 )
|
||||||
@env.listen1
|
@env.listen1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -12,77 +15,89 @@ class TestDestErrorHandling < Test::Unit::TestCase
|
|||||||
@env.cleanup
|
@env.cleanup
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_hello_blocked_by_disconnect_causes_error_not_fatal
|
def test_hello_blocked_by_disconnect_causes_error_not_fatal
|
||||||
run_fake('source/close_after_connect')
|
run_fake( "source/close_after_connect" )
|
||||||
assert_no_control
|
assert_no_control
|
||||||
end
|
end
|
||||||
|
|
||||||
# # This is disabled while CLIENT_MAX_WAIT_SECS is removed
|
=begin
|
||||||
# def test_hello_goes_astray_causes_timeout_error
|
# This is disabled while CLIENT_MAX_WAIT_SECS is removed
|
||||||
# run_fake( "source/hang_after_hello" )
|
def test_hello_goes_astray_causes_timeout_error
|
||||||
# assert_no_control
|
run_fake( "source/hang_after_hello" )
|
||||||
# end
|
assert_no_control
|
||||||
|
end
|
||||||
|
=end
|
||||||
|
|
||||||
def test_sigterm_has_bad_exit_status
|
def test_sigterm_has_bad_exit_status
|
||||||
@env.nbd1.can_die(1)
|
@env.nbd1.can_die(1)
|
||||||
run_fake('source/sigterm_after_hello')
|
run_fake( "source/sigterm_after_hello" )
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_disconnect_after_hello_causes_error_not_fatal
|
def test_disconnect_after_hello_causes_error_not_fatal
|
||||||
run_fake('source/close_after_hello')
|
run_fake( "source/close_after_hello" )
|
||||||
assert_no_control
|
assert_no_control
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_partial_read_causes_error
|
def test_partial_read_causes_error
|
||||||
run_fake('source/close_mid_read')
|
run_fake( "source/close_mid_read" )
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_double_connect_during_hello
|
def test_double_connect_during_hello
|
||||||
run_fake('source/connect_during_hello')
|
run_fake( "source/connect_during_hello" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_acl_rejection
|
def test_acl_rejection
|
||||||
@env.acl1('127.0.0.1')
|
@env.acl1("127.0.0.1")
|
||||||
run_fake('source/connect_from_banned_ip')
|
run_fake( "source/connect_from_banned_ip")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_bad_write
|
def test_bad_write
|
||||||
run_fake('source/write_out_of_range')
|
run_fake( "source/write_out_of_range" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_disconnect_before_write_data_causes_error
|
def test_disconnect_before_write_data_causes_error
|
||||||
run_fake('source/close_after_write')
|
run_fake( "source/close_after_write" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_disconnect_before_write_reply_causes_error
|
def test_disconnect_before_write_reply_causes_error
|
||||||
# Note that this is an odd case: writing the reply doesn't fail.
|
# Note that this is an odd case: writing the reply doesn't fail.
|
||||||
# The test passes because the next attempt by flexnbd to read a
|
# The test passes because the next attempt by flexnbd to read a
|
||||||
# request returns EOF.
|
# request returns EOF.
|
||||||
run_fake('source/close_after_write_data')
|
run_fake( "source/close_after_write_data" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_straight_migration
|
def test_straight_migration
|
||||||
@env.nbd1.can_die(0)
|
@env.nbd1.can_die(0)
|
||||||
run_fake('source/successful_transfer')
|
run_fake( "source/successful_transfer" )
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
|
private
|
||||||
def run_fake( name )
|
def run_fake( name )
|
||||||
@env.run_fake( name, @env.ip, @env.port1 )
|
@env.run_fake( name, @env.ip, @env.port1 )
|
||||||
assert @env.fake_reports_success, "#{name} failed."
|
assert @env.fake_reports_success, "#{name} failed."
|
||||||
end
|
end
|
||||||
|
|
||||||
def status
|
def status
|
||||||
stat, = @env.status1
|
stat, _ = @env.status1
|
||||||
stat
|
stat
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_no_control
|
def assert_no_control
|
||||||
assert !status['has_control'], 'Thought it had control'
|
assert !status['has_control'], "Thought it had control"
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_control
|
def assert_control
|
||||||
assert status['has_control'], "Didn't think it had control"
|
assert status['has_control'], "Didn't think it had control"
|
||||||
end
|
end
|
||||||
|
|
||||||
end # class TestDestErrorHandling
|
end # class TestDestErrorHandling
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'environment'
|
require 'environment'
|
||||||
require 'flexnbd/constants'
|
require 'flexnbd/constants'
|
||||||
@@ -17,18 +19,20 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
@env.cleanup
|
@env.cleanup
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_read1
|
def test_read1
|
||||||
@env.writefile1('f' * 64)
|
@env.writefile1("f"*64)
|
||||||
@env.serve1
|
@env.serve1
|
||||||
|
|
||||||
[0, 12, 63].each do |num|
|
[0, 12, 63].each do |num|
|
||||||
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
bin( @env.nbd1.read(num*@env.blocksize, @env.blocksize) ),
|
bin( @env.nbd1.read(num*@env.blocksize, @env.blocksize) ),
|
||||||
bin( @env.file1.read(num*@env.blocksize, @env.blocksize) )
|
bin( @env.file1.read(num*@env.blocksize, @env.blocksize) )
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
[124, 1200, 10_028, 25_488].each do |num|
|
[124, 1200, 10028, 25488].each do |num|
|
||||||
assert_equal(bin(@env.nbd1.read(num, 4)), bin(@env.file1.read(num, 4)))
|
assert_equal(bin(@env.nbd1.read(num, 4)), bin(@env.file1.read(num, 4)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -36,11 +40,11 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
# Check that we're not
|
# Check that we're not
|
||||||
#
|
#
|
||||||
def test_writeread1
|
def test_writeread1
|
||||||
@env.writefile1('0' * 64)
|
@env.writefile1("0"*64)
|
||||||
@env.serve1
|
@env.serve1
|
||||||
|
|
||||||
[0, 12, 63].each do |num|
|
[0, 12, 63].each do |num|
|
||||||
data = 'X' * @env.blocksize
|
data = "X"*@env.blocksize
|
||||||
@env.nbd1.write(num*@env.blocksize, data)
|
@env.nbd1.write(num*@env.blocksize, data)
|
||||||
assert_equal(data, @env.file1.read(num*@env.blocksize, data.size))
|
assert_equal(data, @env.file1.read(num*@env.blocksize, data.size))
|
||||||
assert_equal(data, @env.nbd1.read(num*@env.blocksize, data.size))
|
assert_equal(data, @env.nbd1.read(num*@env.blocksize, data.size))
|
||||||
@@ -51,11 +55,11 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
# up.
|
# up.
|
||||||
#
|
#
|
||||||
def test_writeread2
|
def test_writeread2
|
||||||
@env.writefile1('0' * 1024)
|
@env.writefile1("0"*1024)
|
||||||
@env.serve1
|
@env.serve1
|
||||||
|
|
||||||
d0 = "\0"*@env.blocksize
|
d0 = "\0"*@env.blocksize
|
||||||
d1 = 'X' * @env.blocksize
|
d1 = "X"*@env.blocksize
|
||||||
(0..63).each do |num|
|
(0..63).each do |num|
|
||||||
@env.nbd1.write(num*@env.blocksize*2, d1)
|
@env.nbd1.write(num*@env.blocksize*2, d1)
|
||||||
end
|
end
|
||||||
@@ -64,18 +68,20 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def setup_to_mirror
|
def setup_to_mirror
|
||||||
@env.writefile1('f' * 4)
|
@env.writefile1( "f"*4 )
|
||||||
@env.serve1
|
@env.serve1
|
||||||
|
|
||||||
@env.writefile2('0' * 4)
|
@env.writefile2( "0"*4 )
|
||||||
@env.listen2
|
@env.listen2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_mirror
|
def test_mirror
|
||||||
@env.nbd1.can_die
|
@env.nbd1.can_die
|
||||||
@env.nbd2.can_die(0)
|
@env.nbd2.can_die(0)
|
||||||
setup_to_mirror
|
setup_to_mirror()
|
||||||
|
|
||||||
stdout, stderr = @env.mirror12
|
stdout, stderr = @env.mirror12
|
||||||
|
|
||||||
@@ -83,15 +89,16 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
@env.nbd2.join
|
@env.nbd2.join
|
||||||
|
|
||||||
assert( File.file?( @env.filename1 ),
|
assert( File.file?( @env.filename1 ),
|
||||||
'The source file was incorrectly deleted')
|
"The source file was incorrectly deleted")
|
||||||
assert_equal(@env.file1.read_original( 0, @env.blocksize ),
|
assert_equal(@env.file1.read_original( 0, @env.blocksize ),
|
||||||
@env.file2.read( 0, @env.blocksize ) )
|
@env.file2.read( 0, @env.blocksize ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_mirror_unlink
|
def test_mirror_unlink
|
||||||
@env.nbd1.can_die(0)
|
@env.nbd1.can_die(0)
|
||||||
@env.nbd2.can_die(0)
|
@env.nbd2.can_die(0)
|
||||||
setup_to_mirror
|
setup_to_mirror()
|
||||||
|
|
||||||
assert File.file?( @env.filename1 )
|
assert File.file?( @env.filename1 )
|
||||||
|
|
||||||
@@ -99,40 +106,45 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
|
|
||||||
assert_no_match( /unrecognized/, stderr )
|
assert_no_match( /unrecognized/, stderr )
|
||||||
|
|
||||||
Timeout.timeout(10) { @env.nbd1.join }
|
|
||||||
|
Timeout.timeout(10) do @env.nbd1.join end
|
||||||
|
|
||||||
assert !File.file?( @env.filename1 )
|
assert !File.file?( @env.filename1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_write_to_high_block
|
def test_write_to_high_block
|
||||||
#
|
#
|
||||||
# This test does not work on 32 bit platforms.
|
# This test does not work on 32 bit platforms.
|
||||||
#
|
#
|
||||||
skip('Not relevant on 32-bit platforms') if ['a'].pack('p').size < 8
|
skip("Not relevant on 32-bit platforms") if ( ["a"].pack("p").size < 8 )
|
||||||
|
|
||||||
# Create a large file, then try to write to somewhere after the 2G boundary
|
# Create a large file, then try to write to somewhere after the 2G boundary
|
||||||
@env.truncate1 '4G'
|
@env.truncate1 "4G"
|
||||||
@env.serve1
|
@env.serve1
|
||||||
|
|
||||||
@env.nbd1.write(2**31 + 2**29, '12345678')
|
@env.nbd1.write( 2**31+2**29, "12345678" )
|
||||||
sleep(1)
|
sleep(1)
|
||||||
assert_equal '12345678', @env.nbd1.read(2**31 + 2**29, 8)
|
assert_equal "12345678", @env.nbd1.read( 2**31+2**29, 8 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_set_acl
|
def test_set_acl
|
||||||
# Just check that we get sane feedback here
|
# Just check that we get sane feedback here
|
||||||
@env.writefile1('f' * 4)
|
@env.writefile1( "f"*4 )
|
||||||
@env.serve1
|
@env.serve1
|
||||||
|
|
||||||
_, stderr = @env.acl1('127.0.0.1')
|
_,stderr = @env.acl1("127.0.0.1")
|
||||||
assert_no_match( /^(F|E):/, stderr )
|
assert_no_match( /^(F|E):/, stderr )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_write_more_than_one_run
|
def test_write_more_than_one_run
|
||||||
one_mb = 2**20
|
one_mb = 2**20
|
||||||
data = "\0" * 256 * one_mb
|
data = "\0" * 256 * one_mb
|
||||||
|
|
||||||
File.open(@env.filename1, 'wb') { |f| f.write('1' * 256 * one_mb) }
|
File.open(@env.filename1, "wb") do |f| f.write( "1" * 256 * one_mb ) end
|
||||||
|
|
||||||
@env.serve1
|
@env.serve1
|
||||||
sleep 5
|
sleep 5
|
||||||
@@ -141,15 +153,17 @@ class TestHappyPath < Test::Unit::TestCase
|
|||||||
@env.nbd1.kill
|
@env.nbd1.kill
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
File.open(@env.filename1, 'rb') do |f|
|
File.open(@env.filename1, "rb") do |f|
|
||||||
while mb = f.read( one_mb )
|
while mb = f.read( one_mb )
|
||||||
unless "\0"*one_mb == mb
|
unless "\0"*one_mb == mb
|
||||||
msg = format("Read non-zeros after offset %x:\n", (i * one_mb))
|
msg = "Read non-zeros after offset %x:\n"%(i * one_mb)
|
||||||
msg += `hexdump #{@env.filename1} | head -n5`
|
msg += `hexdump #{@env.filename1} | head -n5`
|
||||||
raise msg
|
fail msg
|
||||||
end
|
end
|
||||||
i += 1
|
i += 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ require 'test/unit'
|
|||||||
require 'environment'
|
require 'environment'
|
||||||
require 'proxy_tests'
|
require 'proxy_tests'
|
||||||
|
|
||||||
|
|
||||||
class TestPrefetchProxyMode < Test::Unit::TestCase
|
class TestPrefetchProxyMode < Test::Unit::TestCase
|
||||||
include ProxyTests
|
include ProxyTests
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
|
|||||||
super
|
super
|
||||||
@env = Environment.new
|
@env = Environment.new
|
||||||
@env.prefetch_proxy!
|
@env.prefetch_proxy!
|
||||||
@env.writefile1('f' * 16)
|
@env.writefile1( "f" * 16 )
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
@@ -17,3 +18,5 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
|
|||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@ require 'test/unit'
|
|||||||
require 'environment'
|
require 'environment'
|
||||||
require 'proxy_tests'
|
require 'proxy_tests'
|
||||||
|
|
||||||
|
|
||||||
class TestProxyMode < Test::Unit::TestCase
|
class TestProxyMode < Test::Unit::TestCase
|
||||||
include ProxyTests
|
include ProxyTests
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
@env = Environment.new
|
@env = Environment.new
|
||||||
@env.writefile1('f' * 16)
|
@env.writefile1( "f" * 16 )
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
@@ -16,3 +17,4 @@ class TestProxyMode < Test::Unit::TestCase
|
|||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'environment'
|
require 'environment'
|
||||||
require 'flexnbd/fake_source'
|
require 'flexnbd/fake_source'
|
||||||
require 'ld_preload'
|
|
||||||
|
|
||||||
class TestServeMode < Test::Unit::TestCase
|
class TestServeMode < Test::Unit::TestCase
|
||||||
include LdPreload
|
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
@b = "\xFF".b
|
@b = "\xFF".b
|
||||||
@env = Environment.new
|
@env = Environment.new
|
||||||
|
@env.writefile1( "0" )
|
||||||
|
@env.serve1
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
@@ -18,44 +18,35 @@ class TestServeMode < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def connect_to_server
|
def connect_to_server
|
||||||
@env.writefile1('0')
|
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, "Connecting to server failed")
|
||||||
@env.serve1
|
|
||||||
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
|
|
||||||
begin
|
begin
|
||||||
result = client.read_hello
|
result = client.read_hello
|
||||||
assert_equal 'NBDMAGIC', result[:passwd]
|
assert_equal "NBDMAGIC", result[:magic]
|
||||||
assert_equal 0x00420281861253, result[:magic]
|
|
||||||
assert_equal @env.file1.size, result[:size]
|
assert_equal @env.file1.size, result[:size]
|
||||||
# See src/common/nbdtypes.h for the various flags. At the moment we
|
|
||||||
# support HAS_FLAGS (1), SEND_FLUSH (4), SEND_FUA (8)
|
|
||||||
assert_equal (1 | 4 | 8), result[:flags]
|
|
||||||
assert_equal "\x0" * 124, result[:reserved]
|
|
||||||
yield client
|
yield client
|
||||||
ensure
|
ensure
|
||||||
begin
|
client.close rescue nil
|
||||||
client.close
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_bad_request_magic_receives_error_response
|
def test_bad_request_magic_receives_error_response
|
||||||
connect_to_server do |client|
|
connect_to_server do |client|
|
||||||
|
|
||||||
# replace REQUEST_MAGIC with all 0s to make it look bad
|
# replace REQUEST_MAGIC with all 0s to make it look bad
|
||||||
client.send_request(0, 'myhandle', 0, 0, "\x00\x00\x00\x00")
|
client.send_request( 0, "myhandle", 0, 0, "\x00\x00\x00\x00" )
|
||||||
rsp = client.read_response
|
rsp = client.read_response
|
||||||
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
assert_equal "myhandle", rsp[:handle]
|
||||||
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
||||||
|
|
||||||
# The client should be disconnected now
|
# The client should be disconnected now
|
||||||
assert client.disconnected?, 'Server not disconnected'
|
assert client.disconnected?, "Server not disconnected"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_long_write_on_top_of_short_write_is_respected
|
def test_long_write_on_top_of_short_write_is_respected
|
||||||
|
|
||||||
connect_to_server do |client|
|
connect_to_server do |client|
|
||||||
# Start with a file of all-zeroes.
|
# Start with a file of all-zeroes.
|
||||||
client.write( 0, "\x00" * @env.file1.size )
|
client.write( 0, "\x00" * @env.file1.size )
|
||||||
@@ -77,15 +68,15 @@ class TestServeMode < Test::Unit::TestCase
|
|||||||
assert_equal @b * 2, @env.file1.read( 0, 2 )
|
assert_equal @b * 2, @env.file1.read( 0, 2 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_read_request_out_of_bounds_receives_error_response
|
def test_read_request_out_of_bounds_receives_error_response
|
||||||
connect_to_server do |client|
|
connect_to_server do |client|
|
||||||
client.write_read_request( @env.file1.size, 4096 )
|
client.write_read_request( @env.file1.size, 4096 )
|
||||||
rsp = client.read_response
|
rsp = client.read_response
|
||||||
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
assert_equal "myhandle", rsp[:handle]
|
||||||
# NBD protocol suggests ENOSPC (28) is returned
|
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
||||||
assert_equal 28, rsp[:error], 'Server sent incorrect response'
|
|
||||||
|
|
||||||
# Ensure we're not disconnected by sending a request. We don't care about
|
# Ensure we're not disconnected by sending a request. We don't care about
|
||||||
# whether the reply is good or not, here.
|
# whether the reply is good or not, here.
|
||||||
@@ -101,9 +92,8 @@ class TestServeMode < Test::Unit::TestCase
|
|||||||
rsp = client.read_response
|
rsp = client.read_response
|
||||||
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
assert_equal "myhandle", rsp[:handle]
|
||||||
# NBD protocol suggests ENOSPC (28) is returned
|
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
|
||||||
assert_equal 28, rsp[:error], 'Server sent incorrect response'
|
|
||||||
|
|
||||||
# Ensure we're not disconnected by sending a request. We don't care about
|
# Ensure we're not disconnected by sending a request. We don't care about
|
||||||
# whether the reply is good or not, here.
|
# whether the reply is good or not, here.
|
||||||
@@ -111,113 +101,10 @@ class TestServeMode < Test::Unit::TestCase
|
|||||||
rsp = client.read_response
|
rsp = client.read_response
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_unknown_command_receives_error_response
|
|
||||||
connect_to_server do |client|
|
|
||||||
client.send_request(123)
|
|
||||||
rsp = client.read_response
|
|
||||||
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
|
||||||
assert_equal 'myhandle', rsp[:handle]
|
|
||||||
# NBD protocol suggests EINVAL (22) is returned
|
|
||||||
assert_equal 22, rsp[:error], 'Server sent incorrect response'
|
|
||||||
|
|
||||||
# Ensure we're not disconnected by sending a request. We don't care about
|
|
||||||
# whether the reply is good or not, here.
|
|
||||||
client.write(0, "\x00" * @env.file1.size)
|
|
||||||
rsp = client.read_response
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_flush_is_accepted
|
|
||||||
with_ld_preload('msync_logger') do
|
|
||||||
connect_to_server do |client|
|
|
||||||
client.flush
|
|
||||||
rsp = client.read_response
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
|
||||||
assert_equal 0, rsp[:error]
|
|
||||||
end
|
|
||||||
op = parse_ld_preload_logs('msync_logger')
|
|
||||||
assert_equal 1, op.count, 'Only one msync expected'
|
|
||||||
assert_equal @env.blocksize, op.first[2], 'msync length wrong'
|
|
||||||
assert_equal 6, op.first[3], 'msync called with incorrect flags'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_write_with_fua_is_accepted
|
|
||||||
with_ld_preload('msync_logger') do
|
|
||||||
page_size = Integer(`getconf PAGESIZE`)
|
|
||||||
|
|
||||||
@env.blocksize = page_size * 10
|
|
||||||
connect_to_server do |client|
|
|
||||||
# Write somewhere in the third page
|
|
||||||
pos = page_size * 3 + 100
|
|
||||||
client.write_with_fua(pos, "\x00" * 33)
|
|
||||||
rsp = client.read_response
|
|
||||||
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
|
|
||||||
assert_equal 0, rsp[:error]
|
|
||||||
end
|
|
||||||
op = parse_ld_preload_logs('msync_logger')
|
|
||||||
|
|
||||||
assert_equal 1, op.count, 'Only one msync expected'
|
|
||||||
|
|
||||||
# Should be 100 + 33, as we've started writing 100 bytes into a page, for
|
|
||||||
# 33 bytes
|
|
||||||
assert_equal 133, op.first[2], 'msync length wrong'
|
|
||||||
assert_equal 6, op.first[3], 'msync called with incorrect flags'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_odd_size_discs_are_truncated_to_nearest_512
|
|
||||||
# This should get rounded down to 1024
|
|
||||||
@env.blocksize = 1024 + 511
|
|
||||||
@env.writefile1('0')
|
|
||||||
@env.serve1
|
|
||||||
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
|
|
||||||
begin
|
|
||||||
result = client.read_hello
|
|
||||||
assert_equal 'NBDMAGIC', result[:passwd]
|
|
||||||
assert_equal 0x00420281861253, result[:magic]
|
|
||||||
assert_equal 1024, result[:size]
|
|
||||||
client.close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_server_sets_tcpkeepalive
|
|
||||||
with_ld_preload('setsockopt_logger') do
|
|
||||||
connect_to_server(&:close)
|
|
||||||
op = read_ld_preload_log('setsockopt_logger')
|
|
||||||
assert_func_call(op,
|
|
||||||
['setsockopt', '\d+',
|
|
||||||
Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1, 0],
|
|
||||||
'TCP Keepalive not successfully set')
|
|
||||||
assert_func_call(op,
|
|
||||||
['setsockopt', '\d+',
|
|
||||||
Socket::SOL_TCP, Socket::TCP_KEEPIDLE, 30, 0],
|
|
||||||
'TCP Keepalive idle timeout not set to 30s')
|
|
||||||
assert_func_call(op,
|
|
||||||
['setsockopt', '\d+',
|
|
||||||
Socket::SOL_TCP, Socket::TCP_KEEPINTVL, 10, 0],
|
|
||||||
'TCP keepalive retry time not set to 10s')
|
|
||||||
assert_func_call(op,
|
|
||||||
['setsockopt', '\d+',
|
|
||||||
Socket::SOL_TCP, Socket::TCP_KEEPCNT, 3, 0],
|
|
||||||
'TCP keepalive count not set to 3')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_status_returns_correct_client_count
|
|
||||||
@env.writefile1('0')
|
|
||||||
@env.serve1
|
|
||||||
assert_equal('0', @env.status1['num_clients'])
|
|
||||||
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
|
|
||||||
assert_equal('1', @env.status1['num_clients'])
|
|
||||||
client2 = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
|
|
||||||
assert_equal('2', @env.status1['num_clients'])
|
|
||||||
client2.close
|
|
||||||
client.close
|
|
||||||
assert_equal('0', @env.status1['num_clients'])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
@@ -1,105 +1,126 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'environment'
|
require 'environment'
|
||||||
|
|
||||||
|
|
||||||
class TestSourceErrorHandling < Test::Unit::TestCase
|
class TestSourceErrorHandling < Test::Unit::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@old_env = ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS']
|
@old_env = ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS']
|
||||||
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = '4.0'
|
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = "4.0"
|
||||||
|
|
||||||
@env = Environment.new
|
@env = Environment.new
|
||||||
@env.writefile1('f' * 4)
|
@env.writefile1( "f" * 4 )
|
||||||
@env.serve1
|
@env.serve1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
@env.nbd1.can_die(0)
|
@env.nbd1.can_die(0)
|
||||||
@env.cleanup
|
@env.cleanup
|
||||||
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = @old_env
|
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = @old_env
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def expect_term_during_migration
|
def expect_term_during_migration
|
||||||
@env.nbd1.can_die(1,9)
|
@env.nbd1.can_die(1,9)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_failure_to_connect_reported_in_mirror_cmd_response
|
def test_failure_to_connect_reported_in_mirror_cmd_response
|
||||||
stdout, stderr = @env.mirror12_unchecked
|
stdout, stderr = @env.mirror12_unchecked
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
assert_match( /failed to connect/, stderr )
|
assert_match( /failed to connect/, stderr )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_sigterm_after_hello_quits_with_status_of_1
|
def test_sigterm_after_hello_quits_with_status_of_1
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/sigterm_after_hello')
|
run_fake( "dest/sigterm_after_hello" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_destination_hangs_after_connect_reports_error_at_source
|
def test_destination_hangs_after_connect_reports_error_at_source
|
||||||
run_fake('dest/hang_after_connect',
|
run_fake( "dest/hang_after_connect",
|
||||||
err: /Remote server failed to respond/)
|
:err => /Remote server failed to respond/ )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_destination_rejects_connection_reports_error_at_source
|
def test_destination_rejects_connection_reports_error_at_source
|
||||||
run_fake('dest/reject_acl',
|
run_fake( "dest/reject_acl",
|
||||||
err: /Mirror was rejected/)
|
:err => /Mirror was rejected/ )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_size_causes_disconnect
|
def test_wrong_size_causes_disconnect
|
||||||
run_fake('dest/hello_wrong_size',
|
run_fake( "dest/hello_wrong_size",
|
||||||
err: /Remote size does not match local size/)
|
:err => /Remote size does not match local size/ )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_magic_causes_disconnect
|
def test_wrong_magic_causes_disconnect
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/hello_wrong_magic',
|
run_fake( "dest/hello_wrong_magic",
|
||||||
err: /Mirror was rejected/)
|
:err => /Mirror was rejected/ )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_disconnect_after_hello_causes_retry
|
def test_disconnect_after_hello_causes_retry
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/close_after_hello',
|
run_fake( "dest/close_after_hello",
|
||||||
out: /Mirror started/)
|
:out => /Mirror started/ )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_write_times_out_causes_retry
|
def test_write_times_out_causes_retry
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/hang_after_write')
|
run_fake( "dest/hang_after_write" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_rejected_write_causes_retry
|
def test_rejected_write_causes_retry
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/error_on_write')
|
run_fake( "dest/error_on_write" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_disconnect_before_write_reply_causes_retry
|
def test_disconnect_before_write_reply_causes_retry
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/close_after_write')
|
run_fake( "dest/close_after_write" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_bad_write_reply_causes_retry
|
def test_bad_write_reply_causes_retry
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/write_wrong_magic')
|
run_fake( "dest/write_wrong_magic" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_pre_entrust_disconnect_causes_retry
|
def test_pre_entrust_disconnect_causes_retry
|
||||||
expect_term_during_migration
|
expect_term_during_migration
|
||||||
run_fake('dest/close_after_writes')
|
run_fake( "dest/close_after_writes" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_cancel_migration
|
def test_cancel_migration
|
||||||
run_fake('dest/break_after_hello')
|
run_fake( "dest/break_after_hello" )
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
|
private
|
||||||
def run_fake(name, opts = {})
|
def run_fake(name, opts = {})
|
||||||
@env.run_fake( name, @env.ip, @env.port2, @env.nbd1.ctrl )
|
@env.run_fake( name, @env.ip, @env.port2, @env.nbd1.ctrl )
|
||||||
stdout, stderr = @env.mirror12_unchecked
|
stdout, stderr = @env.mirror12_unchecked
|
||||||
assert_success
|
assert_success
|
||||||
assert_match( opts[:err], stderr ) if opts[:err]
|
assert_match( opts[:err], stderr ) if opts[:err]
|
||||||
assert_match( opts[:out], stdout ) if opts[:out]
|
assert_match( opts[:out], stdout ) if opts[:out]
|
||||||
[stdout, stderr]
|
return stdout, stderr
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_success( msg=nil )
|
def assert_success( msg=nil )
|
||||||
assert @env.fake_reports_success, msg || 'Fake failed'
|
assert @env.fake_reports_success, msg || "Fake failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end # class TestSourceErrorHandling
|
end # class TestSourceErrorHandling
|
||||||
|
99
tests/acceptance/test_write_during_migration.rb
Normal file → Executable file
99
tests/acceptance/test_write_during_migration.rb
Normal file → Executable file
@@ -9,98 +9,102 @@ require 'tmpdir'
|
|||||||
Thread.abort_on_exception = true
|
Thread.abort_on_exception = true
|
||||||
|
|
||||||
class TestWriteDuringMigration < Test::Unit::TestCase
|
class TestWriteDuringMigration < Test::Unit::TestCase
|
||||||
def setup
|
|
||||||
@flexnbd = File.expand_path('../../build/flexnbd')
|
|
||||||
|
|
||||||
raise 'No binary!' unless File.executable?(@flexnbd)
|
def setup
|
||||||
|
@flexnbd = File.expand_path("../../build/flexnbd")
|
||||||
|
|
||||||
|
raise "No binary!" unless File.executable?( @flexnbd )
|
||||||
|
|
||||||
|
|
||||||
@size = 20*1024*1024 # 20MB
|
@size = 20*1024*1024 # 20MB
|
||||||
@write_data = 'foo!' * 2048 # 8K write
|
@write_data = "foo!" * 2048 # 8K write
|
||||||
@source_port = 9990
|
@source_port = 9990
|
||||||
@dest_port = 9991
|
@dest_port = 9991
|
||||||
@source_sock = 'src.sock'
|
@source_sock = "src.sock"
|
||||||
@dest_sock = 'dst.sock'
|
@dest_sock = "dst.sock"
|
||||||
@source_file = 'src.file'
|
@source_file = "src.file"
|
||||||
@dest_file = 'dst.file'
|
@dest_file = "dst.file"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
[@dst_proc, @src_proc].each do |pid|
|
[@dst_proc, @src_proc].each do |pid|
|
||||||
next unless pid
|
if pid
|
||||||
begin
|
Process.kill( "KILL", pid ) rescue nil
|
||||||
Process.kill('KILL', pid)
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def debug_arg
|
def debug_arg
|
||||||
ENV['DEBUG'] ? '--verbose' : ''
|
ENV['DEBUG'] ? "--verbose" : ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def launch_servers
|
def launch_servers
|
||||||
@dst_proc = fork do
|
@dst_proc = fork() {
|
||||||
cmd = "#{@flexnbd} listen -l 127.0.0.1 -p #{@dest_port} -f #{@dest_file} -s #{@dest_sock} #{debug_arg}"
|
cmd = "#{@flexnbd} listen -l 127.0.0.1 -p #{@dest_port} -f #{@dest_file} -s #{@dest_sock} #{debug_arg}"
|
||||||
exec cmd
|
exec cmd
|
||||||
end
|
}
|
||||||
|
|
||||||
@src_proc = fork do
|
@src_proc = fork() {
|
||||||
cmd = "#{@flexnbd} serve -l 127.0.0.1 -p #{@source_port} -f #{@source_file} -s #{@source_sock} #{debug_arg}"
|
cmd = "#{@flexnbd} serve -l 127.0.0.1 -p #{@source_port} -f #{@source_file} -s #{@source_sock} #{debug_arg}"
|
||||||
exec cmd
|
exec cmd
|
||||||
end
|
}
|
||||||
begin
|
begin
|
||||||
awaiting = nil
|
awaiting = nil
|
||||||
Timeout.timeout(10) do
|
Timeout.timeout(10) do
|
||||||
awaiting = :source
|
awaiting = :source
|
||||||
sleep 0.1 until File.exist?(@source_sock)
|
sleep 0.1 while !File.exists?( @source_sock )
|
||||||
awaiting = :dest
|
awaiting = :dest
|
||||||
sleep 0.1 until File.exist?(@dest_sock)
|
sleep 0.1 while !File.exists?( @dest_sock )
|
||||||
end
|
end
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
case awaiting
|
case awaiting
|
||||||
when :source
|
when :source
|
||||||
raise "Couldn't get a source socket."
|
fail "Couldn't get a source socket."
|
||||||
when :dest
|
when :dest
|
||||||
raise "Couldn't get a destination socket."
|
fail "Couldn't get a destination socket."
|
||||||
else
|
else
|
||||||
raise "Something went wrong I don't understand."
|
fail "Something went wrong I don't understand."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_files
|
|
||||||
|
def make_files()
|
||||||
FileUtils.touch(@source_file)
|
FileUtils.touch(@source_file)
|
||||||
File.truncate(@source_file, @size)
|
File.truncate(@source_file, @size)
|
||||||
FileUtils.touch(@dest_file)
|
FileUtils.touch(@dest_file)
|
||||||
File.truncate(@dest_file, @size)
|
File.truncate(@dest_file, @size)
|
||||||
|
|
||||||
File.open(@source_file, 'wb') { |f| f.write 'a' * @size }
|
File.open(@source_file, "wb"){|f| f.write "a"*@size }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def start_mirror
|
def start_mirror
|
||||||
UNIXSocket.open(@source_sock) do |sock|
|
UNIXSocket.open(@source_sock) {|sock|
|
||||||
sock.write(['mirror', '127.0.0.1', @dest_port.to_s, 'exit'].join("\x0A") + "\x0A\x0A")
|
sock.write(["mirror", "127.0.0.1", @dest_port.to_s, "exit"].join("\x0A") + "\x0A\x0A")
|
||||||
sock.flush
|
sock.flush
|
||||||
rsp = sock.readline
|
rsp = sock.readline
|
||||||
end
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_for_quit
|
|
||||||
|
def wait_for_quit()
|
||||||
Timeout.timeout( 10 ) do
|
Timeout.timeout( 10 ) do
|
||||||
start_time = Time.now
|
start_time = Time.now
|
||||||
dst_result = Process.waitpid2(@dst_proc)
|
dst_result = Process::waitpid2(@dst_proc)
|
||||||
src_result = Process.waitpid2(@src_proc)
|
src_result = Process::waitpid2(@src_proc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def source_writer
|
def source_writer
|
||||||
client = FlexNBD::FakeSource.new('127.0.0.1', @source_port, 'Timed out connecting')
|
client = FlexNBD::FakeSource.new( "127.0.0.1", @source_port, "Timed out connecting" )
|
||||||
offsets = Range.new(0, (@size - @write_data.size) / 4096 ).to_a
|
offsets = Range.new(0, (@size - @write_data.size) / 4096 ).to_a
|
||||||
loop do
|
loop do
|
||||||
begin
|
begin
|
||||||
client.write(offsets[rand(offsets.size)] * 4096, @write_data)
|
client.write(offsets[rand(offsets.size)] * 4096, @write_data)
|
||||||
rescue StandardError => err
|
rescue => err
|
||||||
# We expect a broken write at some point, so ignore it
|
# We expect a broken write at some point, so ignore it
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -111,8 +115,8 @@ class TestWriteDuringMigration < Test::Unit::TestCase
|
|||||||
# puts `md5sum #{@source_file} #{@dest_file}`
|
# puts `md5sum #{@source_file} #{@dest_file}`
|
||||||
|
|
||||||
# Ensure each block matches
|
# Ensure each block matches
|
||||||
File.open(@source_file, 'r') do |source|
|
File.open(@source_file, "r") do |source|
|
||||||
File.open(@dest_file, 'r') do |dest|
|
File.open(@dest_file, "r") do |dest|
|
||||||
0.upto( @size / 4096 ) do |block_num|
|
0.upto( @size / 4096 ) do |block_num|
|
||||||
s_data = source.read( 4096 )
|
s_data = source.read( 4096 )
|
||||||
d_data = dest.read( 4096 )
|
d_data = dest.read( 4096 )
|
||||||
@@ -127,16 +131,16 @@ class TestWriteDuringMigration < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_write_during_migration
|
def test_write_during_migration
|
||||||
Dir.mktmpdir do |tmpdir|
|
Dir.mktmpdir() do |tmpdir|
|
||||||
Dir.chdir( tmpdir ) do
|
Dir.chdir( tmpdir ) do
|
||||||
make_files
|
make_files()
|
||||||
|
|
||||||
launch_servers
|
launch_servers()
|
||||||
|
|
||||||
src_writer = Thread.new { source_writer }
|
src_writer = Thread.new { source_writer }
|
||||||
|
|
||||||
start_mirror
|
start_mirror()
|
||||||
wait_for_quit
|
wait_for_quit()
|
||||||
src_writer.join
|
src_writer.join
|
||||||
assert_both_sides_identical
|
assert_both_sides_identical
|
||||||
end
|
end
|
||||||
@@ -144,21 +148,24 @@ class TestWriteDuringMigration < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_many_clients_during_migration
|
def test_many_clients_during_migration
|
||||||
Dir.mktmpdir do |tmpdir|
|
Dir.mktmpdir() do |tmpdir|
|
||||||
Dir.chdir( tmpdir ) do
|
Dir.chdir( tmpdir ) do
|
||||||
make_files
|
make_files()
|
||||||
|
|
||||||
launch_servers
|
launch_servers()
|
||||||
|
|
||||||
src_writers_1 = (1..5).collect { Thread.new { source_writer } }
|
src_writers_1 = (1..5).collect { Thread.new { source_writer } }
|
||||||
|
|
||||||
start_mirror
|
start_mirror()
|
||||||
|
|
||||||
src_writers_2 = (1..5).collect { Thread.new { source_writer } }
|
src_writers_2 = (1..5).collect { Thread.new { source_writer } }
|
||||||
|
|
||||||
wait_for_quit
|
wait_for_quit()
|
||||||
(src_writers_1 + src_writers_2).each(&:join)
|
( src_writers_1 + src_writers_2 ).each {|t| t.join }
|
||||||
assert_both_sides_identical
|
assert_both_sides_identical
|
||||||
end
|
end
|
||||||
end end
|
end end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -13,6 +13,7 @@ START_TEST(test_null_acl)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_parses_single_line )
|
START_TEST( test_parses_single_line )
|
||||||
{
|
{
|
||||||
char *lines[] = {"127.0.0.1"};
|
char *lines[] = {"127.0.0.1"};
|
||||||
@@ -36,11 +37,9 @@ START_TEST(test_parses_multiple_lines)
|
|||||||
|
|
||||||
struct ip_and_mask *entry;
|
struct ip_and_mask *entry;
|
||||||
entry = &(*acl->entries)[0];
|
entry = &(*acl->entries)[0];
|
||||||
fail_unless(entry->ip.family == e0.family,
|
fail_unless(entry->ip.family == e0.family, "entry 0 has wrong family!");
|
||||||
"entry 0 has wrong family!");
|
|
||||||
entry = &(*acl->entries)[1];
|
entry = &(*acl->entries)[1];
|
||||||
fail_unless(entry->ip.family == e1.family,
|
fail_unless(entry->ip.family == e1.family, "entry 1 has wrong family!");
|
||||||
"entry 1 has wrong family!");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
@@ -53,6 +52,7 @@ START_TEST(test_destroy_doesnt_crash)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_includes_single_address )
|
START_TEST( test_includes_single_address )
|
||||||
{
|
{
|
||||||
char *lines[] = {"127.0.0.1"};
|
char *lines[] = {"127.0.0.1"};
|
||||||
@@ -109,13 +109,12 @@ START_TEST(test_includes_single_address_when_multiple_entries_exist)
|
|||||||
parse_ip_to_sockaddr( &e0.generic, "127.0.0.1" );
|
parse_ip_to_sockaddr( &e0.generic, "127.0.0.1" );
|
||||||
parse_ip_to_sockaddr( &e1.generic, "::1" );
|
parse_ip_to_sockaddr( &e1.generic, "::1" );
|
||||||
|
|
||||||
fail_unless(acl_includes(acl, &e0),
|
fail_unless( acl_includes( acl, &e0 ), "Included address 0 wasn't covered" );
|
||||||
"Included address 0 wasn't covered");
|
fail_unless( acl_includes( acl, &e1 ), "Included address 1 wasn't covered" );
|
||||||
fail_unless(acl_includes(acl, &e1),
|
|
||||||
"Included address 1 wasn't covered");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_doesnt_include_other_address )
|
START_TEST( test_doesnt_include_other_address )
|
||||||
{
|
{
|
||||||
char *lines[] = {"127.0.0.1"};
|
char *lines[] = {"127.0.0.1"};
|
||||||
@@ -164,6 +163,7 @@ START_TEST(test_default_deny_rejects)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_default_accept_rejects )
|
START_TEST( test_default_accept_rejects )
|
||||||
{
|
{
|
||||||
struct acl * acl = acl_create( 0, NULL, 0 );
|
struct acl * acl = acl_create( 0, NULL, 0 );
|
||||||
@@ -175,6 +175,7 @@ START_TEST(test_default_accept_rejects)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite* acl_suite(void)
|
Suite* acl_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("acl");
|
Suite *s = suite_create("acl");
|
||||||
@@ -188,19 +189,14 @@ Suite * acl_suite(void)
|
|||||||
tcase_add_test(tc_includes, test_parses_multiple_lines);
|
tcase_add_test(tc_includes, test_parses_multiple_lines);
|
||||||
|
|
||||||
tcase_add_test(tc_includes, test_includes_single_address);
|
tcase_add_test(tc_includes, test_includes_single_address);
|
||||||
tcase_add_test(tc_includes,
|
tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv4);
|
||||||
test_includes_single_address_when_netmask_specified_ipv4);
|
tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv6);
|
||||||
tcase_add_test(tc_includes,
|
|
||||||
test_includes_single_address_when_netmask_specified_ipv6);
|
|
||||||
|
|
||||||
tcase_add_test(tc_includes,
|
tcase_add_test(tc_includes, test_includes_single_address_when_multiple_entries_exist);
|
||||||
test_includes_single_address_when_multiple_entries_exist);
|
|
||||||
|
|
||||||
tcase_add_test(tc_includes, test_doesnt_include_other_address);
|
tcase_add_test(tc_includes, test_doesnt_include_other_address);
|
||||||
tcase_add_test(tc_includes,
|
tcase_add_test(tc_includes, test_doesnt_include_other_address_when_netmask_specified);
|
||||||
test_doesnt_include_other_address_when_netmask_specified);
|
tcase_add_test(tc_includes, test_doesnt_include_other_address_when_multiple_entries_exist);
|
||||||
tcase_add_test(tc_includes,
|
|
||||||
test_doesnt_include_other_address_when_multiple_entries_exist);
|
|
||||||
|
|
||||||
tcase_add_test(tc_includes, test_default_deny_rejects);
|
tcase_add_test(tc_includes, test_default_deny_rejects);
|
||||||
tcase_add_test(tc_includes, test_default_accept_rejects);
|
tcase_add_test(tc_includes, test_default_accept_rejects);
|
||||||
@@ -230,3 +226,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -66,18 +66,18 @@ START_TEST(test_bit_ranges)
|
|||||||
|
|
||||||
for (i=0; i<64; i++) {
|
for (i=0; i<64; i++) {
|
||||||
bit_set_range(buffer, i*64, i);
|
bit_set_range(buffer, i*64, i);
|
||||||
fail_unless(longs[i] == (1ULL << i) - 1,
|
fail_unless(
|
||||||
|
longs[i] == (1ULL<<i)-1,
|
||||||
"longs[%ld] = %lx SHOULD BE %lx",
|
"longs[%ld] = %lx SHOULD BE %lx",
|
||||||
i, longs[i], (1ULL << i) - 1);
|
i, longs[i], (1ULL<<i)-1
|
||||||
|
);
|
||||||
|
|
||||||
fail_unless(longs[i + 1] == 0, "bit_set_range overshot at i=%d",
|
fail_unless(longs[i+1] == 0, "bit_set_range overshot at i=%d", i);
|
||||||
i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0; i<64; i++) {
|
for (i=0; i<64; i++) {
|
||||||
bit_clear_range(buffer, i*64, i);
|
bit_clear_range(buffer, i*64, i);
|
||||||
fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d",
|
fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d", i);
|
||||||
i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
@@ -86,8 +86,7 @@ START_TEST(test_bit_runs)
|
|||||||
{
|
{
|
||||||
bitfield_word_t buffer[BIT_WORDS_FOR_SIZE(256)];
|
bitfield_word_t buffer[BIT_WORDS_FOR_SIZE(256)];
|
||||||
int i, ptr=0, runs[] = {
|
int i, ptr=0, runs[] = {
|
||||||
56, 97, 22, 12, 83, 1, 45, 80, 85, 51, 64, 40, 63, 67, 75, 64, 94,
|
56,97,22,12,83,1,45,80,85,51,64,40,63,67,75,64,94,81,79,62
|
||||||
81, 79, 62
|
|
||||||
};
|
};
|
||||||
|
|
||||||
memset(buffer,0,256);
|
memset(buffer,0,256);
|
||||||
@@ -102,8 +101,11 @@ START_TEST(test_bit_runs)
|
|||||||
|
|
||||||
for (i=0; i < 20; i += 1) {
|
for (i=0; i < 20; i += 1) {
|
||||||
int run = bit_run_count(buffer, ptr, 2048-ptr, NULL);
|
int run = bit_run_count(buffer, ptr, 2048-ptr, NULL);
|
||||||
fail_unless(run == runs[i],
|
fail_unless(
|
||||||
"run %d should have been %d, was %d", i, runs[i], run);
|
run == runs[i],
|
||||||
|
"run %d should have been %d, was %d",
|
||||||
|
i, runs[i], run
|
||||||
|
);
|
||||||
ptr += runs[i];
|
ptr += runs[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +145,7 @@ START_TEST(test_bitset)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_bitset_set )
|
START_TEST( test_bitset_set )
|
||||||
{
|
{
|
||||||
struct bitset * map;
|
struct bitset * map;
|
||||||
@@ -173,6 +176,7 @@ START_TEST(test_bitset_set)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_bitset_clear )
|
START_TEST( test_bitset_clear )
|
||||||
{
|
{
|
||||||
struct bitset * map;
|
struct bitset * map;
|
||||||
@@ -439,14 +443,10 @@ START_TEST(test_bitset_stream_queued_bytes)
|
|||||||
bitset_clear_range( map, 0, 2 );
|
bitset_clear_range( map, 0, 2 );
|
||||||
bitset_disable_stream( map );
|
bitset_disable_stream( map );
|
||||||
|
|
||||||
ck_assert_int_eq(64,
|
ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_ON ) );
|
||||||
bitset_stream_queued_bytes(map, BITSET_STREAM_ON));
|
ck_assert_int_eq( 80, bitset_stream_queued_bytes( map, BITSET_STREAM_SET ) );
|
||||||
ck_assert_int_eq(80,
|
ck_assert_int_eq( 82, bitset_stream_queued_bytes( map, BITSET_STREAM_UNSET ) );
|
||||||
bitset_stream_queued_bytes(map, BITSET_STREAM_SET));
|
ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_OFF ) );
|
||||||
ck_assert_int_eq(82,
|
|
||||||
bitset_stream_queued_bytes(map, BITSET_STREAM_UNSET));
|
|
||||||
ck_assert_int_eq(64,
|
|
||||||
bitset_stream_queued_bytes(map, BITSET_STREAM_OFF));
|
|
||||||
bitset_free( map );
|
bitset_free( map );
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
@@ -471,8 +471,7 @@ Suite * bitset_suite(void)
|
|||||||
tcase_add_test(tc_bitset, test_bitset_set_range);
|
tcase_add_test(tc_bitset, test_bitset_set_range);
|
||||||
tcase_add_test(tc_bitset, test_bitset_clear_range);
|
tcase_add_test(tc_bitset, test_bitset_clear_range);
|
||||||
tcase_add_test(tc_bitset, test_bitset_set_range_doesnt_push_to_stream);
|
tcase_add_test(tc_bitset, test_bitset_set_range_doesnt_push_to_stream);
|
||||||
tcase_add_test(tc_bitset,
|
tcase_add_test(tc_bitset, test_bitset_clear_range_doesnt_push_to_stream);
|
||||||
test_bitset_clear_range_doesnt_push_to_stream);
|
|
||||||
suite_add_tcase(s, tc_bitset);
|
suite_add_tcase(s, tc_bitset);
|
||||||
|
|
||||||
|
|
||||||
@@ -498,3 +497,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
struct server fake_server = {0};
|
struct server fake_server = {0};
|
||||||
|
|
||||||
#define FAKE_SERVER &fake_server
|
#define FAKE_SERVER &fake_server
|
||||||
#define FAKE_SOCKET (42)
|
#define FAKE_SOCKET (42)
|
||||||
|
|
||||||
@@ -24,6 +23,7 @@ START_TEST(test_assigns_socket)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_assigns_server )
|
START_TEST( test_assigns_server )
|
||||||
{
|
{
|
||||||
struct client * c;
|
struct client * c;
|
||||||
@@ -37,6 +37,7 @@ START_TEST(test_assigns_server)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_opens_stop_signal )
|
START_TEST( test_opens_stop_signal )
|
||||||
{
|
{
|
||||||
struct client *c = client_create( FAKE_SERVER, FAKE_SOCKET );
|
struct client *c = client_create( FAKE_SERVER, FAKE_SOCKET );
|
||||||
@@ -49,6 +50,7 @@ START_TEST(test_opens_stop_signal)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
int fd_is_closed(int);
|
int fd_is_closed(int);
|
||||||
|
|
||||||
START_TEST( test_closes_stop_signal )
|
START_TEST( test_closes_stop_signal )
|
||||||
@@ -64,6 +66,7 @@ START_TEST(test_closes_stop_signal)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_read_request_quits_on_stop_signal )
|
START_TEST( test_read_request_quits_on_stop_signal )
|
||||||
{
|
{
|
||||||
int fds[2];
|
int fds[2];
|
||||||
@@ -81,6 +84,7 @@ START_TEST(test_read_request_quits_on_stop_signal)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite *client_suite(void)
|
Suite *client_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("client");
|
Suite *s = suite_create("client");
|
||||||
@@ -115,3 +119,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ START_TEST(test_assigns_sock_name)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite *control_suite(void)
|
Suite *control_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("control");
|
Suite *s = suite_create("control");
|
||||||
@@ -38,3 +39,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
START_TEST( test_listening_assigns_sock )
|
START_TEST( test_listening_assigns_sock )
|
||||||
{
|
{
|
||||||
struct flexnbd *flexnbd = flexnbd_create_listening("127.0.0.1",
|
struct flexnbd * flexnbd = flexnbd_create_listening(
|
||||||
|
"127.0.0.1",
|
||||||
"4777",
|
"4777",
|
||||||
"fakefile",
|
"fakefile",
|
||||||
"fakesock",
|
"fakesock",
|
||||||
@@ -16,6 +17,7 @@ START_TEST(test_listening_assigns_sock)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite *flexnbd_suite(void)
|
Suite *flexnbd_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("flexnbd");
|
Suite *s = suite_create("flexnbd");
|
||||||
@@ -39,3 +41,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,23 +12,22 @@ START_TEST(test_mutex_create)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_mutex_lock )
|
START_TEST( test_mutex_lock )
|
||||||
{
|
{
|
||||||
struct flexthread_mutex * ftm = flexthread_mutex_create();
|
struct flexthread_mutex * ftm = flexthread_mutex_create();
|
||||||
|
|
||||||
fail_if(flexthread_mutex_held(ftm),
|
fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held before lock" );
|
||||||
"Flexthread_mutex is held before lock");
|
|
||||||
flexthread_mutex_lock( ftm );
|
flexthread_mutex_lock( ftm );
|
||||||
fail_unless(flexthread_mutex_held(ftm),
|
fail_unless( flexthread_mutex_held( ftm ), "Flexthread_mutex is not held inside lock" );
|
||||||
"Flexthread_mutex is not held inside lock");
|
|
||||||
flexthread_mutex_unlock( ftm );
|
flexthread_mutex_unlock( ftm );
|
||||||
fail_if(flexthread_mutex_held(ftm),
|
fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held after unlock" );
|
||||||
"Flexthread_mutex is held after unlock");
|
|
||||||
|
|
||||||
flexthread_mutex_destroy( ftm );
|
flexthread_mutex_destroy( ftm );
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite* flexthread_suite(void)
|
Suite* flexthread_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("flexthread");
|
Suite *s = suite_create("flexthread");
|
||||||
@@ -60,3 +59,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ START_TEST(test_read_until_newline_returns_line_length_plus_null)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_read_until_newline_inserts_null )
|
START_TEST( test_read_until_newline_inserts_null )
|
||||||
{
|
{
|
||||||
int fds[2];
|
int fds[2];
|
||||||
@@ -33,6 +34,7 @@ START_TEST(test_read_until_newline_inserts_null)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_read_empty_line_inserts_null )
|
START_TEST( test_read_empty_line_inserts_null )
|
||||||
{
|
{
|
||||||
int fds[2];
|
int fds[2];
|
||||||
@@ -48,6 +50,7 @@ START_TEST(test_read_empty_line_inserts_null)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_read_eof_returns_err )
|
START_TEST( test_read_eof_returns_err )
|
||||||
{
|
{
|
||||||
int fds[2];
|
int fds[2];
|
||||||
@@ -62,6 +65,7 @@ START_TEST(test_read_eof_returns_err)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_read_eof_fills_line )
|
START_TEST( test_read_eof_fills_line )
|
||||||
{
|
{
|
||||||
int fds[2];
|
int fds[2];
|
||||||
@@ -78,6 +82,7 @@ START_TEST(test_read_eof_fills_line)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_read_lines_until_blankline )
|
START_TEST( test_read_lines_until_blankline )
|
||||||
{
|
{
|
||||||
char **lines = NULL;
|
char **lines = NULL;
|
||||||
@@ -93,25 +98,21 @@ START_TEST(test_read_lines_until_blankline)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite *ioutil_suite(void)
|
Suite *ioutil_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("ioutil");
|
Suite *s = suite_create("ioutil");
|
||||||
|
|
||||||
TCase *tc_read_until_newline = tcase_create("read_until_newline");
|
TCase *tc_read_until_newline = tcase_create("read_until_newline");
|
||||||
TCase *tc_read_lines_until_blankline =
|
TCase *tc_read_lines_until_blankline = tcase_create("read_lines_until_blankline");
|
||||||
tcase_create("read_lines_until_blankline");
|
|
||||||
|
|
||||||
tcase_add_test(tc_read_until_newline,
|
tcase_add_test(tc_read_until_newline, test_read_until_newline_returns_line_length_plus_null);
|
||||||
test_read_until_newline_returns_line_length_plus_null);
|
tcase_add_test(tc_read_until_newline, test_read_until_newline_inserts_null);
|
||||||
tcase_add_test(tc_read_until_newline,
|
tcase_add_test(tc_read_until_newline, test_read_empty_line_inserts_null);
|
||||||
test_read_until_newline_inserts_null);
|
|
||||||
tcase_add_test(tc_read_until_newline,
|
|
||||||
test_read_empty_line_inserts_null);
|
|
||||||
tcase_add_test(tc_read_until_newline, test_read_eof_returns_err);
|
tcase_add_test(tc_read_until_newline, test_read_eof_returns_err);
|
||||||
tcase_add_test(tc_read_until_newline, test_read_eof_fills_line );
|
tcase_add_test(tc_read_until_newline, test_read_eof_fills_line );
|
||||||
|
|
||||||
tcase_add_test(tc_read_lines_until_blankline,
|
tcase_add_test(tc_read_lines_until_blankline, test_read_lines_until_blankline );
|
||||||
test_read_lines_until_blankline);
|
|
||||||
|
|
||||||
suite_add_tcase(s, tc_read_until_newline);
|
suite_add_tcase(s, tc_read_until_newline);
|
||||||
suite_add_tcase(s, tc_read_lines_until_blankline);
|
suite_add_tcase(s, tc_read_lines_until_blankline);
|
||||||
@@ -130,3 +131,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,11 +14,12 @@ START_TEST(test_allocs_cvar)
|
|||||||
memset( &cond_zero, 'X', sizeof( cond_zero ) );
|
memset( &cond_zero, 'X', sizeof( cond_zero ) );
|
||||||
fail_if( memcmp( &cond_zero, &mbox->filled_cond, sizeof( cond_zero ) ) == 0 ,
|
fail_if( memcmp( &cond_zero, &mbox->filled_cond, sizeof( cond_zero ) ) == 0 ,
|
||||||
"Condition variable not allocated" );
|
"Condition variable not allocated" );
|
||||||
fail_if(memcmp(&cond_zero, &mbox->emptied_cond, sizeof(cond_zero)) ==
|
fail_if( memcmp( &cond_zero, &mbox->emptied_cond, sizeof( cond_zero ) ) == 0 ,
|
||||||
0, "Condition variable not allocated");
|
"Condition variable not allocated" );
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_post_stores_value )
|
START_TEST( test_post_stores_value )
|
||||||
{
|
{
|
||||||
struct mbox * mbox = mbox_create();
|
struct mbox * mbox = mbox_create();
|
||||||
@@ -31,6 +32,7 @@ START_TEST(test_post_stores_value)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
void * mbox_receive_runner( void * mbox_uncast )
|
void * mbox_receive_runner( void * mbox_uncast )
|
||||||
{
|
{
|
||||||
struct mbox * mbox = (struct mbox *)mbox_uncast;
|
struct mbox * mbox = (struct mbox *)mbox_uncast;
|
||||||
@@ -56,12 +58,14 @@ START_TEST(test_receive_blocks_until_post)
|
|||||||
mbox_post( mbox, deadbeef );
|
mbox_post( mbox, deadbeef );
|
||||||
fail_unless( 0 == pthread_join( receiver, &retval ),
|
fail_unless( 0 == pthread_join( receiver, &retval ),
|
||||||
"Failed to join the receiver thread" );
|
"Failed to join the receiver thread" );
|
||||||
fail_unless(retval == deadbeef, "Return value was wrong");
|
fail_unless( retval == deadbeef,
|
||||||
|
"Return value was wrong" );
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite* mbox_suite(void)
|
Suite* mbox_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("mbox");
|
Suite *s = suite_create("mbox");
|
||||||
@@ -97,3 +101,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,13 +13,12 @@ START_TEST(test_init_passwd)
|
|||||||
memset( init_raw.passwd, 0, 8 );
|
memset( init_raw.passwd, 0, 8 );
|
||||||
nbd_h2r_init( &init, &init_raw );
|
nbd_h2r_init( &init, &init_raw );
|
||||||
|
|
||||||
fail_unless(memcmp(init.passwd, INIT_PASSWD, 8) == 0,
|
fail_unless( memcmp( init.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied." );
|
||||||
"The password was not copied.");
|
fail_unless( memcmp( init_raw.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied back." );
|
||||||
fail_unless(memcmp(init_raw.passwd, INIT_PASSWD, 8) == 0,
|
|
||||||
"The password was not copied back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_init_magic)
|
START_TEST(test_init_magic)
|
||||||
{
|
{
|
||||||
struct nbd_init_raw init_raw;
|
struct nbd_init_raw init_raw;
|
||||||
@@ -31,11 +30,11 @@ START_TEST(test_init_magic)
|
|||||||
|
|
||||||
init.magic = 67890;
|
init.magic = 67890;
|
||||||
nbd_h2r_init( &init, &init_raw );
|
nbd_h2r_init( &init, &init_raw );
|
||||||
fail_unless(htobe64(67890) == init_raw.magic,
|
fail_unless( htobe64( 67890 ) == init_raw.magic, "Magic was not converted back." );
|
||||||
"Magic was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_init_size)
|
START_TEST(test_init_size)
|
||||||
{
|
{
|
||||||
struct nbd_init_raw init_raw;
|
struct nbd_init_raw init_raw;
|
||||||
@@ -47,11 +46,11 @@ START_TEST(test_init_size)
|
|||||||
|
|
||||||
init.size = 67890;
|
init.size = 67890;
|
||||||
nbd_h2r_init( &init, &init_raw );
|
nbd_h2r_init( &init, &init_raw );
|
||||||
fail_unless(htobe64(67890) == init_raw.size,
|
fail_unless( htobe64( 67890 ) == init_raw.size, "Size was not converted back." );
|
||||||
"Size was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_request_magic )
|
START_TEST(test_request_magic )
|
||||||
{
|
{
|
||||||
struct nbd_request_raw request_raw;
|
struct nbd_request_raw request_raw;
|
||||||
@@ -59,13 +58,11 @@ START_TEST(test_request_magic)
|
|||||||
|
|
||||||
request_raw.magic = 12345;
|
request_raw.magic = 12345;
|
||||||
nbd_r2h_request( &request_raw, &request );
|
nbd_r2h_request( &request_raw, &request );
|
||||||
fail_unless(be32toh(12345) == request.magic,
|
fail_unless( be32toh( 12345 ) == request.magic, "Magic was not converted." );
|
||||||
"Magic was not converted.");
|
|
||||||
|
|
||||||
request.magic = 67890;
|
request.magic = 67890;
|
||||||
nbd_h2r_request( &request, &request_raw );
|
nbd_h2r_request( &request, &request_raw );
|
||||||
fail_unless(htobe32(67890) == request_raw.magic,
|
fail_unless( htobe32( 67890 ) == request_raw.magic, "Magic was not converted back." );
|
||||||
"Magic was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
@@ -74,33 +71,17 @@ START_TEST(test_request_type)
|
|||||||
struct nbd_request_raw request_raw;
|
struct nbd_request_raw request_raw;
|
||||||
struct nbd_request request;
|
struct nbd_request request;
|
||||||
|
|
||||||
request_raw.type = 123;
|
request_raw.type = 12345;
|
||||||
nbd_r2h_request( &request_raw, &request );
|
nbd_r2h_request( &request_raw, &request );
|
||||||
fail_unless(be16toh(123) == request.type, "Type was not converted.");
|
fail_unless( be32toh( 12345 ) == request.type, "Type was not converted." );
|
||||||
|
|
||||||
request.type = 234;
|
request.type = 67890;
|
||||||
nbd_h2r_request( &request, &request_raw );
|
nbd_h2r_request( &request, &request_raw );
|
||||||
fail_unless(htobe16(234) == request_raw.type,
|
fail_unless( htobe32( 67890 ) == request_raw.type, "Type was not converted back." );
|
||||||
"Type was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
START_TEST(test_request_flags)
|
|
||||||
{
|
|
||||||
struct nbd_request_raw request_raw;
|
|
||||||
struct nbd_request request;
|
|
||||||
|
|
||||||
request_raw.flags = 123;
|
|
||||||
nbd_r2h_request(&request_raw, &request);
|
|
||||||
fail_unless(be16toh(123) == request.flags,
|
|
||||||
"Flags were not converted.");
|
|
||||||
|
|
||||||
request.flags = 234;
|
|
||||||
nbd_h2r_request(&request, &request_raw);
|
|
||||||
fail_unless(htobe16(234) == request_raw.flags,
|
|
||||||
"Flags were not converted back.");
|
|
||||||
}
|
|
||||||
END_TEST
|
|
||||||
|
|
||||||
START_TEST(test_request_handle)
|
START_TEST(test_request_handle)
|
||||||
{
|
{
|
||||||
@@ -113,13 +94,13 @@ START_TEST(test_request_handle)
|
|||||||
request_raw.handle.w = 0;
|
request_raw.handle.w = 0;
|
||||||
nbd_h2r_request( &request, &request_raw );
|
nbd_h2r_request( &request, &request_raw );
|
||||||
|
|
||||||
fail_unless(memcmp(request.handle.b, "MYHANDLE", 8) == 0,
|
fail_unless( memcmp( request.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied." );
|
||||||
"The handle was not copied.");
|
fail_unless( memcmp( request_raw.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied back." );
|
||||||
fail_unless(memcmp(request_raw.handle.b, "MYHANDLE", 8) == 0,
|
|
||||||
"The handle was not copied back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_request_from )
|
START_TEST(test_request_from )
|
||||||
{
|
{
|
||||||
struct nbd_request_raw request_raw;
|
struct nbd_request_raw request_raw;
|
||||||
@@ -131,11 +112,12 @@ START_TEST(test_request_from)
|
|||||||
|
|
||||||
request.from = 67890;
|
request.from = 67890;
|
||||||
nbd_h2r_request( &request, &request_raw );
|
nbd_h2r_request( &request, &request_raw );
|
||||||
fail_unless(htobe64(67890) == request_raw.from,
|
fail_unless( htobe64( 67890 ) == request_raw.from, "From was not converted back." );
|
||||||
"From was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_request_len )
|
START_TEST(test_request_len )
|
||||||
{
|
{
|
||||||
struct nbd_request_raw request_raw;
|
struct nbd_request_raw request_raw;
|
||||||
@@ -147,11 +129,11 @@ START_TEST(test_request_len)
|
|||||||
|
|
||||||
request.len = 67890;
|
request.len = 67890;
|
||||||
nbd_h2r_request( &request, &request_raw );
|
nbd_h2r_request( &request, &request_raw );
|
||||||
fail_unless(htobe32(67890) == request_raw.len,
|
fail_unless( htobe32( 67890 ) == request_raw.len, "Type was not converted back." );
|
||||||
"Type was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_reply_magic )
|
START_TEST(test_reply_magic )
|
||||||
{
|
{
|
||||||
struct nbd_reply_raw reply_raw;
|
struct nbd_reply_raw reply_raw;
|
||||||
@@ -163,11 +145,11 @@ START_TEST(test_reply_magic)
|
|||||||
|
|
||||||
reply.magic = 67890;
|
reply.magic = 67890;
|
||||||
nbd_h2r_reply( &reply, &reply_raw );
|
nbd_h2r_reply( &reply, &reply_raw );
|
||||||
fail_unless(htobe32(67890) == reply_raw.magic,
|
fail_unless( htobe32( 67890 ) == reply_raw.magic, "Magic was not converted back." );
|
||||||
"Magic was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST(test_reply_error )
|
START_TEST(test_reply_error )
|
||||||
{
|
{
|
||||||
struct nbd_reply_raw reply_raw;
|
struct nbd_reply_raw reply_raw;
|
||||||
@@ -179,8 +161,7 @@ START_TEST(test_reply_error)
|
|||||||
|
|
||||||
reply.error = 67890;
|
reply.error = 67890;
|
||||||
nbd_h2r_reply( &reply, &reply_raw );
|
nbd_h2r_reply( &reply, &reply_raw );
|
||||||
fail_unless(htobe32(67890) == reply_raw.error,
|
fail_unless( htobe32( 67890 ) == reply_raw.error, "Error was not converted back." );
|
||||||
"Error was not converted back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
@@ -195,13 +176,12 @@ START_TEST(test_reply_handle)
|
|||||||
reply_raw.handle.w = 0;
|
reply_raw.handle.w = 0;
|
||||||
nbd_h2r_reply( &reply, &reply_raw );
|
nbd_h2r_reply( &reply, &reply_raw );
|
||||||
|
|
||||||
fail_unless(memcmp(reply.handle.b, "MYHANDLE", 8) == 0,
|
fail_unless( memcmp( reply.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied." );
|
||||||
"The handle was not copied.");
|
fail_unless( memcmp( reply_raw.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied back." );
|
||||||
fail_unless(memcmp(reply_raw.handle.b, "MYHANDLE", 8) == 0,
|
|
||||||
"The handle was not copied back.");
|
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_convert_from )
|
START_TEST( test_convert_from )
|
||||||
{
|
{
|
||||||
/* Check that we can correctly pull numbers out of an
|
/* Check that we can correctly pull numbers out of an
|
||||||
@@ -233,7 +213,6 @@ Suite * nbdtypes_suite(void)
|
|||||||
tcase_add_test( tc_init, test_init_size );
|
tcase_add_test( tc_init, test_init_size );
|
||||||
tcase_add_test( tc_request, test_request_magic );
|
tcase_add_test( tc_request, test_request_magic );
|
||||||
tcase_add_test( tc_request, test_request_type );
|
tcase_add_test( tc_request, test_request_type );
|
||||||
tcase_add_test(tc_request, test_request_flags);
|
|
||||||
tcase_add_test( tc_request, test_request_handle );
|
tcase_add_test( tc_request, test_request_handle );
|
||||||
tcase_add_test( tc_request, test_request_from );
|
tcase_add_test( tc_request, test_request_from );
|
||||||
tcase_add_test( tc_request, test_request_len );
|
tcase_add_test( tc_request, test_request_len );
|
||||||
@@ -261,3 +240,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,6 +13,8 @@ START_TEST(test_can_parse_ip_address_twice)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Suite* parse_suite(void)
|
Suite* parse_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("parse");
|
Suite *s = suite_create("parse");
|
||||||
@@ -42,3 +44,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,7 +54,8 @@ void *responder(void *respond_uncast)
|
|||||||
nbd_r2h_request( &request_raw, &resp->received);
|
nbd_r2h_request( &request_raw, &resp->received);
|
||||||
if (resp->do_fail){
|
if (resp->do_fail){
|
||||||
fd_write_reply( sock_fd, wrong_handle, 0 );
|
fd_write_reply( sock_fd, wrong_handle, 0 );
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
fd_write_reply( sock_fd, resp->received.handle.w, 0 );
|
fd_write_reply( sock_fd, resp->received.handle.w, 0 );
|
||||||
}
|
}
|
||||||
write( sock_fd, "12345678", 8 );
|
write( sock_fd, "12345678", 8 );
|
||||||
@@ -65,20 +66,17 @@ void *responder(void *respond_uncast)
|
|||||||
|
|
||||||
struct respond * respond_create( int do_fail )
|
struct respond * respond_create( int do_fail )
|
||||||
{
|
{
|
||||||
struct respond *respond =
|
struct respond * respond = (struct respond *)calloc( 1, sizeof( struct respond ) );
|
||||||
(struct respond *) calloc(1, sizeof(struct respond));
|
|
||||||
socketpair( PF_UNIX, SOCK_STREAM, 0, respond->sock_fds );
|
socketpair( PF_UNIX, SOCK_STREAM, 0, respond->sock_fds );
|
||||||
respond->do_fail = do_fail;
|
respond->do_fail = do_fail;
|
||||||
|
|
||||||
pthread_attr_init( &respond->thread_attr );
|
pthread_attr_init( &respond->thread_attr );
|
||||||
pthread_create(&respond->thread_id, &respond->thread_attr, responder,
|
pthread_create( &respond->thread_id, &respond->thread_attr, responder, respond );
|
||||||
respond);
|
|
||||||
|
|
||||||
return respond;
|
return respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
void respond_destroy(struct respond *respond)
|
void respond_destroy( struct respond * respond ){
|
||||||
{
|
|
||||||
NULLCHECK( respond );
|
NULLCHECK( respond );
|
||||||
|
|
||||||
pthread_join( respond->thread_id, NULL );
|
pthread_join( respond->thread_id, NULL );
|
||||||
@@ -123,6 +121,7 @@ START_TEST(test_rejects_mismatched_handle)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_accepts_matched_handle )
|
START_TEST( test_accepts_matched_handle )
|
||||||
{
|
{
|
||||||
struct respond * respond = respond_create( 0 );
|
struct respond * respond = respond_create( 0 );
|
||||||
@@ -136,6 +135,7 @@ START_TEST(test_accepts_matched_handle)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_disconnect_doesnt_read_reply )
|
START_TEST( test_disconnect_doesnt_read_reply )
|
||||||
{
|
{
|
||||||
struct respond * respond = respond_create( 1 );
|
struct respond * respond = respond_create( 1 );
|
||||||
@@ -146,6 +146,7 @@ START_TEST(test_disconnect_doesnt_read_reply)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite* readwrite_suite(void)
|
Suite* readwrite_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("readwrite");
|
Suite *s = suite_create("readwrite");
|
||||||
@@ -161,8 +162,7 @@ Suite * readwrite_suite(void)
|
|||||||
* because we want to know that the sender won't even try to
|
* because we want to know that the sender won't even try to
|
||||||
* read the response.
|
* read the response.
|
||||||
*/
|
*/
|
||||||
tcase_add_exit_test(tc_disconnect, test_disconnect_doesnt_read_reply,
|
tcase_add_exit_test( tc_disconnect, test_disconnect_doesnt_read_reply,0 );
|
||||||
0);
|
|
||||||
|
|
||||||
suite_add_tcase(s, tc_transfer);
|
suite_add_tcase(s, tc_transfer);
|
||||||
suite_add_tcase(s, tc_disconnect);
|
suite_add_tcase(s, tc_disconnect);
|
||||||
@@ -190,3 +190,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ START_TEST(test_opens_pipe)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
void * signal_thread( void * thing )
|
void * signal_thread( void * thing )
|
||||||
{
|
{
|
||||||
struct self_pipe *sig = (struct self_pipe *)thing;
|
struct self_pipe *sig = (struct self_pipe *)thing;
|
||||||
@@ -62,14 +63,14 @@ START_TEST(test_signals)
|
|||||||
}
|
}
|
||||||
self_pipe_signal_clear( sig );
|
self_pipe_signal_clear( sig );
|
||||||
|
|
||||||
fail_unless(self_pipe_fd_isset(sig, &fds),
|
fail_unless( self_pipe_fd_isset( sig, &fds ), "Signalled pipe was not FD_ISSET." );
|
||||||
"Signalled pipe was not FD_ISSET.");
|
|
||||||
pthread_join( signal_thread_id, NULL );
|
pthread_join( signal_thread_id, NULL );
|
||||||
|
|
||||||
self_pipe_destroy( sig );
|
self_pipe_destroy( sig );
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_clear_returns_immediately )
|
START_TEST( test_clear_returns_immediately )
|
||||||
{
|
{
|
||||||
struct self_pipe *sig;
|
struct self_pipe *sig;
|
||||||
@@ -78,6 +79,7 @@ START_TEST(test_clear_returns_immediately)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_destroy_closes_read_pipe )
|
START_TEST( test_destroy_closes_read_pipe )
|
||||||
{
|
{
|
||||||
struct self_pipe* sig;
|
struct self_pipe* sig;
|
||||||
@@ -114,6 +116,7 @@ START_TEST(test_destroy_closes_read_pipe)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_destroy_closes_write_pipe )
|
START_TEST( test_destroy_closes_write_pipe )
|
||||||
{
|
{
|
||||||
struct self_pipe * sig;
|
struct self_pipe * sig;
|
||||||
@@ -124,8 +127,7 @@ START_TEST(test_destroy_closes_write_pipe)
|
|||||||
orig_write_fd = sig->write_fd;
|
orig_write_fd = sig->write_fd;
|
||||||
self_pipe_destroy( sig );
|
self_pipe_destroy( sig );
|
||||||
|
|
||||||
while ((write_len = write(orig_write_fd, "", 0)) == -1
|
while ( ( write_len = write( orig_write_fd, "", 0 ) ) == -1 && errno == EINTR );
|
||||||
&& errno == EINTR);
|
|
||||||
|
|
||||||
switch( write_len ) {
|
switch( write_len ) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -156,6 +158,8 @@ START_TEST(test_destroy_closes_write_pipe)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Suite *self_pipe_suite(void)
|
Suite *self_pipe_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("self_pipe");
|
Suite *s = suite_create("self_pipe");
|
||||||
@@ -191,3 +195,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,9 +54,7 @@ void setup(void)
|
|||||||
|
|
||||||
void teardown( void )
|
void teardown( void )
|
||||||
{
|
{
|
||||||
if (dummy_file) {
|
if( dummy_file ){ unlink( dummy_file ); }
|
||||||
unlink(dummy_file);
|
|
||||||
}
|
|
||||||
free( dummy_file );
|
free( dummy_file );
|
||||||
dummy_file = NULL;
|
dummy_file = NULL;
|
||||||
}
|
}
|
||||||
@@ -66,9 +64,7 @@ START_TEST(test_replaces_acl)
|
|||||||
{
|
{
|
||||||
struct flexnbd flexnbd;
|
struct flexnbd flexnbd;
|
||||||
flexnbd.signal_fd = -1;
|
flexnbd.signal_fd = -1;
|
||||||
struct server *s =
|
struct server * s = server_create( &flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL, 1, 0, 1 );
|
||||||
server_create(&flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL,
|
|
||||||
1, 0, 1);
|
|
||||||
struct acl * new_acl = acl_create( 0, NULL, 0 );
|
struct acl * new_acl = acl_create( 0, NULL, 0 );
|
||||||
|
|
||||||
server_replace_acl( s, new_acl );
|
server_replace_acl( s, new_acl );
|
||||||
@@ -78,13 +74,12 @@ START_TEST(test_replaces_acl)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_signals_acl_updated )
|
START_TEST( test_signals_acl_updated )
|
||||||
{
|
{
|
||||||
struct flexnbd flexnbd;
|
struct flexnbd flexnbd;
|
||||||
flexnbd.signal_fd = -1;
|
flexnbd.signal_fd = -1;
|
||||||
struct server *s =
|
struct server * s = server_create( &flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL, 1, 0, 1 );
|
||||||
server_create(&flexnbd, "127.0.0.1", "0", dummy_file, 0, 0, NULL,
|
|
||||||
1, 0, 1);
|
|
||||||
struct acl * new_acl = acl_create( 0, NULL, 0 );
|
struct acl * new_acl = acl_create( 0, NULL, 0 );
|
||||||
|
|
||||||
server_replace_acl( s, new_acl );
|
server_replace_acl( s, new_acl );
|
||||||
@@ -95,6 +90,7 @@ START_TEST(test_signals_acl_updated)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
int connect_client( char *addr, int actual_port, char *source_addr )
|
int connect_client( char *addr, int actual_port, char *source_addr )
|
||||||
{
|
{
|
||||||
int client_fd = -1;
|
int client_fd = -1;
|
||||||
@@ -108,15 +104,12 @@ int connect_client(char *addr, int actual_port, char *source_addr)
|
|||||||
memset( &hint, '\0', sizeof( struct addrinfo ) );
|
memset( &hint, '\0', sizeof( struct addrinfo ) );
|
||||||
hint.ai_socktype = SOCK_STREAM;
|
hint.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
myfail_if(getaddrinfo(addr, NULL, &hint, &ailist) != 0,
|
myfail_if( getaddrinfo( addr, NULL, &hint, &ailist ) != 0, "getaddrinfo failed." );
|
||||||
"getaddrinfo failed.");
|
|
||||||
|
|
||||||
int connected = 0;
|
int connected = 0;
|
||||||
for( aip = ailist; aip; aip = aip->ai_next ) {
|
for( aip = ailist; aip; aip = aip->ai_next ) {
|
||||||
((struct sockaddr_in *) aip->ai_addr)->sin_port =
|
((struct sockaddr_in *)aip->ai_addr)->sin_port = htons( actual_port );
|
||||||
htons(actual_port);
|
client_fd = socket( aip->ai_family, aip->ai_socktype, aip->ai_protocol );
|
||||||
client_fd =
|
|
||||||
socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
|
|
||||||
|
|
||||||
if (source_addr) {
|
if (source_addr) {
|
||||||
struct sockaddr src;
|
struct sockaddr src;
|
||||||
@@ -127,9 +120,7 @@ int connect_client(char *addr, int actual_port, char *source_addr)
|
|||||||
bind(client_fd, &src, sizeof(struct sockaddr_in6));
|
bind(client_fd, &src, sizeof(struct sockaddr_in6));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_fd == -1) {
|
if( client_fd == -1) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if( connect( client_fd, aip->ai_addr, aip->ai_addrlen) == 0 ) {
|
if( connect( client_fd, aip->ai_addr, aip->ai_addrlen) == 0 ) {
|
||||||
connected = 1;
|
connected = 1;
|
||||||
break;
|
break;
|
||||||
@@ -157,9 +148,7 @@ START_TEST(test_acl_update_closes_bad_client)
|
|||||||
*/
|
*/
|
||||||
struct flexnbd flexnbd;
|
struct flexnbd flexnbd;
|
||||||
flexnbd.signal_fd = -1;
|
flexnbd.signal_fd = -1;
|
||||||
struct server *s =
|
struct server * s = server_create( &flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL, 1, 0, 1 );
|
||||||
server_create(&flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL,
|
|
||||||
1, 0, 1);
|
|
||||||
struct acl * new_acl = acl_create( 0, NULL, 1 );
|
struct acl * new_acl = acl_create( 0, NULL, 1 );
|
||||||
struct client * c;
|
struct client * c;
|
||||||
struct client_tbl_entry * entry;
|
struct client_tbl_entry * entry;
|
||||||
@@ -198,14 +187,13 @@ START_TEST(test_acl_update_closes_bad_client)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_acl_update_leaves_good_client )
|
START_TEST( test_acl_update_leaves_good_client )
|
||||||
{
|
{
|
||||||
struct flexnbd flexnbd;
|
struct flexnbd flexnbd;
|
||||||
flexnbd.signal_fd = -1;
|
flexnbd.signal_fd = -1;
|
||||||
|
|
||||||
struct server *s =
|
struct server * s = server_create( &flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL, 1, 0, 1 );
|
||||||
server_create(&flexnbd, "127.0.0.7", "0", dummy_file, 0, 0, NULL,
|
|
||||||
1, 0, 1);
|
|
||||||
|
|
||||||
char *lines[] = {"127.0.0.1"};
|
char *lines[] = {"127.0.0.1"};
|
||||||
struct acl * new_acl = acl_create( 1, lines, 1 );
|
struct acl * new_acl = acl_create( 1, lines, 1 );
|
||||||
@@ -242,6 +230,7 @@ START_TEST(test_acl_update_leaves_good_client)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite* serve_suite(void)
|
Suite* serve_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("serve");
|
Suite *s = suite_create("serve");
|
||||||
@@ -252,10 +241,8 @@ Suite * serve_suite(void)
|
|||||||
tcase_add_test(tc_acl_update, test_replaces_acl);
|
tcase_add_test(tc_acl_update, test_replaces_acl);
|
||||||
tcase_add_test(tc_acl_update, test_signals_acl_updated);
|
tcase_add_test(tc_acl_update, test_signals_acl_updated);
|
||||||
|
|
||||||
tcase_add_exit_test(tc_acl_update, test_acl_update_closes_bad_client,
|
tcase_add_exit_test(tc_acl_update, test_acl_update_closes_bad_client, 0);
|
||||||
0);
|
tcase_add_exit_test(tc_acl_update, test_acl_update_leaves_good_client, 0);
|
||||||
tcase_add_exit_test(tc_acl_update, test_acl_update_leaves_good_client,
|
|
||||||
0);
|
|
||||||
|
|
||||||
suite_add_tcase(s, tc_acl_update);
|
suite_add_tcase(s, tc_acl_update);
|
||||||
|
|
||||||
@@ -274,3 +261,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ START_TEST(test_sockaddr_address_string_af_inet_converts_to_string)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_sockaddr_address_string_af_inet6_converts_to_string )
|
START_TEST( test_sockaddr_address_string_af_inet6_converts_to_string )
|
||||||
{
|
{
|
||||||
struct sockaddr_in6 v6_raw;
|
struct sockaddr_in6 v6_raw;
|
||||||
@@ -89,17 +90,12 @@ Suite * sockutil_suite(void)
|
|||||||
{
|
{
|
||||||
Suite *s = suite_create("sockutil");
|
Suite *s = suite_create("sockutil");
|
||||||
|
|
||||||
TCase *tc_sockaddr_address_string =
|
TCase *tc_sockaddr_address_string = tcase_create("sockaddr_address_string");
|
||||||
tcase_create("sockaddr_address_string");
|
|
||||||
|
|
||||||
tcase_add_test(tc_sockaddr_address_string,
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_af_inet_converts_to_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,
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_af_unspec_is_failure);
|
||||||
test_sockaddr_address_string_af_inet6_converts_to_string);
|
tcase_add_test(tc_sockaddr_address_string, test_sockaddr_address_string_doesnt_overflow_short_buffer);
|
||||||
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);
|
suite_add_tcase(s, tc_sockaddr_address_string);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -116,3 +112,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -68,6 +68,7 @@ START_TEST(test_gets_has_control)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_gets_is_mirroring )
|
START_TEST( test_gets_is_mirroring )
|
||||||
{
|
{
|
||||||
struct server * server = mock_server();
|
struct server * server = mock_server();
|
||||||
@@ -104,6 +105,25 @@ START_TEST(test_gets_clients_allowed)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST( test_gets_num_clients )
|
||||||
|
{
|
||||||
|
struct server * server = mock_server();
|
||||||
|
struct status * status = status_create( server );
|
||||||
|
|
||||||
|
fail_if( status->num_clients != 0, "num_clients was wrong" );
|
||||||
|
status_destroy( status );
|
||||||
|
|
||||||
|
server->nbd_client[0].thread = 1;
|
||||||
|
server->nbd_client[1].thread = 1;
|
||||||
|
status = status_create( server );
|
||||||
|
|
||||||
|
fail_unless( status->num_clients == 2, "num_clients was wrong" );
|
||||||
|
status_destroy( status );
|
||||||
|
destroy_mock_server( server );
|
||||||
|
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
START_TEST( test_gets_pid )
|
START_TEST( test_gets_pid )
|
||||||
{
|
{
|
||||||
struct server * server = mock_server();
|
struct server * server = mock_server();
|
||||||
@@ -142,31 +162,34 @@ START_TEST(test_gets_migration_statistics)
|
|||||||
|
|
||||||
struct status * status = status_create( server );
|
struct status * status = status_create( server );
|
||||||
|
|
||||||
fail_unless(0 == status->migration_duration ||
|
fail_unless (
|
||||||
|
0 == status->migration_duration ||
|
||||||
1 == status->migration_duration ||
|
1 == status->migration_duration ||
|
||||||
2 == status->migration_duration,
|
2 == status->migration_duration,
|
||||||
"migration_duration is unreasonable!");
|
"migration_duration is unreasonable!"
|
||||||
|
);
|
||||||
|
|
||||||
fail_unless(16384 / (status->migration_duration + 1) ==
|
fail_unless(
|
||||||
status->migration_speed,
|
16384 / ( status->migration_duration + 1 ) == status->migration_speed,
|
||||||
"migration_speed not calculated correctly");
|
"migration_speed not calculated correctly"
|
||||||
|
);
|
||||||
|
|
||||||
fail_unless(32768 == status->migration_speed_limit,
|
fail_unless( 32768 == status->migration_speed_limit, "migration_speed_limit not read" );
|
||||||
"migration_speed_limit not read");
|
|
||||||
|
|
||||||
// ( size / current_bps ) + 1 happens to be 3 for this test
|
// ( size / current_bps ) + 1 happens to be 3 for this test
|
||||||
fail_unless(3 == status->migration_seconds_left,
|
fail_unless( 3 == status->migration_seconds_left, "migration_seconds_left not gathered" );
|
||||||
"migration_seconds_left not gathered");
|
|
||||||
|
|
||||||
status_destroy( status );
|
status_destroy( status );
|
||||||
destroy_mock_server( server );
|
destroy_mock_server( server );
|
||||||
}
|
}
|
||||||
|
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
#define RENDER_TEST_SETUP \
|
#define RENDER_TEST_SETUP \
|
||||||
struct status status; \
|
struct status status; \
|
||||||
int fds[2]; \
|
int fds[2]; \
|
||||||
pipe( fds );
|
pipe( fds );
|
||||||
|
|
||||||
void fail_unless_rendered( int fd, char *fragment )
|
void fail_unless_rendered( int fd, char *fragment )
|
||||||
{
|
{
|
||||||
char buf[1024] = {0};
|
char buf[1024] = {0};
|
||||||
@@ -199,7 +222,9 @@ void fail_if_rendered(int fd, char *fragment)
|
|||||||
|
|
||||||
START_TEST( test_renders_has_control )
|
START_TEST( test_renders_has_control )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.has_control = 1;
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.has_control = 1;
|
||||||
status_write( &status, fds[1] );
|
status_write( &status, fds[1] );
|
||||||
fail_unless_rendered( fds[0], "has_control=true" );
|
fail_unless_rendered( fds[0], "has_control=true" );
|
||||||
|
|
||||||
@@ -209,9 +234,12 @@ START_TEST(test_renders_has_control)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_renders_is_mirroring )
|
START_TEST( test_renders_is_mirroring )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.is_mirroring = 1;
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.is_mirroring = 1;
|
||||||
status_write( &status, fds[1] );
|
status_write( &status, fds[1] );
|
||||||
fail_unless_rendered( fds[0], "is_mirroring=true" );
|
fail_unless_rendered( fds[0], "is_mirroring=true" );
|
||||||
|
|
||||||
@@ -223,7 +251,9 @@ END_TEST
|
|||||||
|
|
||||||
START_TEST( test_renders_clients_allowed )
|
START_TEST( test_renders_clients_allowed )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.clients_allowed = 1;
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.clients_allowed = 1;
|
||||||
status_write( &status, fds[1] );
|
status_write( &status, fds[1] );
|
||||||
fail_unless_rendered( fds[0], "clients_allowed=true" );
|
fail_unless_rendered( fds[0], "clients_allowed=true" );
|
||||||
|
|
||||||
@@ -235,7 +265,9 @@ END_TEST
|
|||||||
|
|
||||||
START_TEST( test_renders_num_clients )
|
START_TEST( test_renders_num_clients )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.num_clients = 0;
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.num_clients = 0;
|
||||||
status_write( &status, fds[1] );
|
status_write( &status, fds[1] );
|
||||||
fail_unless_rendered( fds[0], "num_clients=0" );
|
fail_unless_rendered( fds[0], "num_clients=0" );
|
||||||
|
|
||||||
@@ -246,9 +278,12 @@ START_TEST(test_renders_num_clients)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
START_TEST( test_renders_pid )
|
START_TEST( test_renders_pid )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.pid = 42;
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.pid = 42;
|
||||||
status_write( &status, fds[1] );
|
status_write( &status, fds[1] );
|
||||||
fail_unless_rendered( fds[0], "pid=42" );
|
fail_unless_rendered( fds[0], "pid=42" );
|
||||||
}
|
}
|
||||||
@@ -256,7 +291,9 @@ END_TEST
|
|||||||
|
|
||||||
START_TEST( test_renders_size )
|
START_TEST( test_renders_size )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.size = ((uint64_t) 1 << 33);
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.size = ( (uint64_t)1 << 33 );
|
||||||
status_write( &status, fds[1] );
|
status_write( &status, fds[1] );
|
||||||
fail_unless_rendered( fds[0], "size=8589934592" );
|
fail_unless_rendered( fds[0], "size=8589934592" );
|
||||||
}
|
}
|
||||||
@@ -264,7 +301,9 @@ END_TEST
|
|||||||
|
|
||||||
START_TEST( test_renders_migration_statistics )
|
START_TEST( test_renders_migration_statistics )
|
||||||
{
|
{
|
||||||
RENDER_TEST_SETUP status.is_mirroring = 0;
|
RENDER_TEST_SETUP
|
||||||
|
|
||||||
|
status.is_mirroring = 0;
|
||||||
status.migration_duration = 8;
|
status.migration_duration = 8;
|
||||||
status.migration_speed = 40000000;
|
status.migration_speed = 40000000;
|
||||||
status.migration_speed_limit = 40000001;
|
status.migration_speed_limit = 40000001;
|
||||||
@@ -307,6 +346,7 @@ START_TEST(test_renders_migration_statistics)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
||||||
Suite *status_suite(void)
|
Suite *status_suite(void)
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("status");
|
Suite *s = suite_create("status");
|
||||||
@@ -317,6 +357,7 @@ Suite * status_suite(void)
|
|||||||
tcase_add_test(tc_create, test_gets_has_control);
|
tcase_add_test(tc_create, test_gets_has_control);
|
||||||
tcase_add_test(tc_create, test_gets_is_mirroring);
|
tcase_add_test(tc_create, test_gets_is_mirroring);
|
||||||
tcase_add_test(tc_create, test_gets_clients_allowed);
|
tcase_add_test(tc_create, test_gets_clients_allowed);
|
||||||
|
tcase_add_test(tc_create, test_gets_num_clients);
|
||||||
tcase_add_test(tc_create, test_gets_pid);
|
tcase_add_test(tc_create, test_gets_pid);
|
||||||
tcase_add_test(tc_create, test_gets_size);
|
tcase_add_test(tc_create, test_gets_size);
|
||||||
tcase_add_test(tc_create, test_gets_migration_statistics);
|
tcase_add_test(tc_create, test_gets_migration_statistics);
|
||||||
@@ -347,3 +388,4 @@ int main(void)
|
|||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
return (number_failed == 0) ? 0 : 1;
|
return (number_failed == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user