Merge branch 'release-to-master' into 'master'

Release to master

See merge request open-source/flexnbd-c!50
This commit is contained in:
James Carter
2018-02-20 11:52:07 +00:00
106 changed files with 8755 additions and 8444 deletions

24
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,24 @@
# 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.

View File

@@ -109,7 +109,6 @@ 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'

28
debian/changelog vendored
View File

@@ -1,3 +1,31 @@
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)
[ Chris Cottam ]
* 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
* Return bytes_left in migration statistics. * Return bytes_left in migration statistics.

View File

@@ -13,219 +13,236 @@
#include "ioutil.h" #include "ioutil.h"
int build_allocation_map(struct bitset * allocation_map, int fd) int build_allocation_map(struct bitset *allocation_map, int fd)
{ {
/* break blocking ioctls down */ /* break blocking ioctls down */
const unsigned long max_length = 100*1024*1024; const unsigned long max_length = 100 * 1024 * 1024;
const unsigned int max_extents = 1000; const unsigned int max_extents = 1000;
unsigned long offset = 0; unsigned long offset = 0;
struct { struct {
struct fiemap fiemap; struct fiemap fiemap;
struct fiemap_extent extents[max_extents]; struct fiemap_extent extents[max_extents];
} fiemap_static; } fiemap_static;
struct fiemap* fiemap = (struct fiemap*) &fiemap_static; struct fiemap *fiemap = (struct fiemap *) &fiemap_static;
memset(&fiemap_static, 0, sizeof(fiemap_static)); memset(&fiemap_static, 0, sizeof(fiemap_static));
for (offset = 0; offset < allocation_map->size; ) { for (offset = 0; offset < allocation_map->size;) {
fiemap->fm_start = offset; fiemap->fm_start = offset;
fiemap->fm_length = max_length; fiemap->fm_length = max_length;
if ( offset + max_length > allocation_map->size ) { if (offset + max_length > allocation_map->size) {
fiemap->fm_length = allocation_map->size-offset; fiemap->fm_length = allocation_map->size - offset;
}
fiemap->fm_flags = FIEMAP_FLAG_SYNC;
fiemap->fm_extent_count = max_extents;
fiemap->fm_mapped_extents = 0;
if ( ioctl( fd, FS_IOC_FIEMAP, fiemap ) < 0 ) {
debug( "Couldn't get fiemap, returning no allocation_map" );
return 0; /* it's up to the caller to free the map */
}
else {
for ( unsigned int i = 0; i < fiemap->fm_mapped_extents; i++ ) {
bitset_set_range( allocation_map,
fiemap->fm_extents[i].fe_logical,
fiemap->fm_extents[i].fe_length );
}
/* must move the offset on, but careful not to jump max_length
* if we've actually hit max_offsets.
*/
if (fiemap->fm_mapped_extents > 0) {
struct fiemap_extent *last = &fiemap->fm_extents[
fiemap->fm_mapped_extents-1
];
offset = last->fe_logical + last->fe_length;
}
else {
offset += fiemap->fm_length;
}
}
} }
info("Successfully built allocation map"); fiemap->fm_flags = FIEMAP_FLAG_SYNC;
return 1; fiemap->fm_extent_count = max_extents;
fiemap->fm_mapped_extents = 0;
if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
debug("Couldn't get fiemap, returning no allocation_map");
return 0; /* it's up to the caller to free the map */
} else {
for (unsigned int i = 0; i < fiemap->fm_mapped_extents; i++) {
bitset_set_range(allocation_map,
fiemap->fm_extents[i].fe_logical,
fiemap->fm_extents[i].fe_length);
}
/* must move the offset on, but careful not to jump max_length
* if we've actually hit max_offsets.
*/
if (fiemap->fm_mapped_extents > 0) {
struct fiemap_extent *last =
&fiemap->fm_extents[fiemap->fm_mapped_extents - 1];
offset = last->fe_logical + last->fe_length;
} else {
offset += fiemap->fm_length;
}
}
}
info("Successfully built allocation map");
return 1;
} }
int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **out_map) int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
void **out_map)
{ {
/* /*
* size and out_size are intentionally of different types. * size and out_size are intentionally of different types.
* lseek64() uses off64_t to signal errors in the sign bit. * lseek64() uses off64_t to signal errors in the sign bit.
* Since we check for these errors before trying to assign to * Since we check for these errors before trying to assign to
* *out_size, we know *out_size can never go negative. * *out_size, we know *out_size can never go negative.
*/ */
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_SYNC ); *out_fd = open(filename, O_RDWR | O_NOATIME);
if (*out_fd < 1) { if (*out_fd < 1) {
warn("open(%s) failed: does it exist?", filename); warn("open(%s) failed: does it exist?", filename);
return *out_fd; return *out_fd;
} }
size = lseek64(*out_fd, 0, SEEK_END); size = lseek64(*out_fd, 0, SEEK_END);
if (size < 0) { if (size < 0) {
warn("lseek64() failed"); warn("lseek64() failed");
return size; return size;
} }
if (out_size) {
*out_size = size;
}
if (out_map) { /* If discs are not in multiples of 512, then odd things happen,
*out_map = mmap64(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, * resulting in reads/writes past the ends of files.
*out_fd, 0); */
if (((long) *out_map) == -1) { if (size != (size & (~0x1ff))) {
warn("mmap64() failed"); warn("file does not fit into 512-byte sectors; the end of the file will be ignored.");
return -1; size &= ~0x1ff;
} }
debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd, *out_map);
}
else {
debug("opened %s size %ld on fd %d", filename, size, *out_fd);
}
return 0; if (out_size) {
*out_size = size;
}
if (out_map) {
*out_map = mmap64(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
*out_fd, 0);
if (((long) *out_map) == -1) {
warn("mmap64() failed");
return -1;
}
debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd,
*out_map);
} else {
debug("opened %s size %ld on fd %d", filename, size, *out_fd);
}
return 0;
} }
int writeloop(int filedes, const void *buffer, size_t size) int writeloop(int filedes, const void *buffer, size_t size)
{ {
size_t written=0; size_t written = 0;
while (written < size) { while (written < size) {
ssize_t result = write(filedes, buffer+written, size-written); ssize_t result = write(filedes, buffer + written, size - written);
if (result == -1) { if (result == -1) {
if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
continue; // busy-wait continue; // busy-wait
} }
return -1; // failure return -1; // failure
}
written += result;
} }
return 0; written += result;
}
return 0;
} }
int readloop(int filedes, void *buffer, size_t size) int readloop(int filedes, void *buffer, size_t size)
{ {
size_t readden=0; size_t readden = 0;
while (readden < size) { while (readden < 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", readden ); warn("end-of-file detected while reading after %i bytes",
return -1; readden);
} return -1;
if ( result == -1 ) {
if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ) {
continue; // busy-wait
}
return -1; // failure
}
readden += result;
} }
return 0;
if (result == -1) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
continue; // busy-wait
}
return -1; // failure
}
readden += result;
}
return 0;
} }
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)
{ {
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("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld", out_fd, in_fd, offset, count-sent, result); debug
("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld",
out_fd, in_fd, offset, count - sent, result);
if (result == -1) { if (result == -1) {
debug( "%s (%i) calling sendfile64()", strerror(errno), errno ); debug("%s (%i) calling sendfile64()", strerror(errno), errno);
return -1; return -1;
}
sent += result;
debug("sent=%ld, count=%ld", sent, count);
} }
debug("exiting sendfileloop"); sent += result;
return 0; debug("sent=%ld, count=%ld", sent, count);
}
debug("exiting sendfileloop");
return 0;
} }
#include <errno.h> #include <errno.h>
ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2) ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
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;
//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 = splice(fd_in, off_in, fd_out, off_out, len, flags); ssize_t result =
if (result < 0) { splice(fd_in, off_in, fd_out, off_out, len, flags);
//debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len); if (result < 0) {
if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK) ) { //debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len);
return spliced; if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK)) {
} return spliced;
else { } else {
return -1; return -1;
} }
} else { } else {
spliced += result; spliced += result;
//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);
}
} }
}
return spliced; return spliced;
} }
int splice_via_pipe_loop(int fd_in, int fd_out, size_t len) int splice_via_pipe_loop(int fd_in, int fd_out, size_t len)
{ {
int pipefd[2]; /* read end, write end */ int pipefd[2]; /* read end, write end */
size_t spliced=0; size_t spliced = 0;
if (pipe(pipefd) == -1) { if (pipe(pipefd) == -1) {
return -1; return -1;
}
while (spliced < len) {
ssize_t run = len - spliced;
ssize_t s2, s1 =
spliceloop(fd_in, NULL, pipefd[1], NULL, run,
SPLICE_F_NONBLOCK);
/*if (run > 65535)
run = 65535; */
if (s1 < 0) {
break;
} }
while (spliced < len) { s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0);
ssize_t run = len-spliced; if (s2 < 0) {
ssize_t s2, s1 = spliceloop(fd_in, NULL, pipefd[1], NULL, run, SPLICE_F_NONBLOCK); break;
/*if (run > 65535)
run = 65535;*/
if (s1 < 0) { break; }
s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0);
if (s2 < 0) { break; }
spliced += s2;
} }
close(pipefd[0]); spliced += s2;
close(pipefd[1]); }
close(pipefd[0]);
close(pipefd[1]);
return spliced < len ? -1 : 0; return spliced < len ? -1 : 0;
} }
/* Reads single bytes from fd until either an EOF or a newline appears. /* Reads single bytes from fd until either an EOF or a newline appears.
@@ -235,117 +252,123 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len)
* Returns the number of read bytes: the length of the line without the * Returns the number of read bytes: the length of the line without the
* newline, plus the trailing null. * newline, plus the trailing null.
*/ */
int read_until_newline(int fd, char* buf, int bufsize) int read_until_newline(int fd, char *buf, int bufsize)
{ {
int cur; int cur;
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) { return -1; } if (result <= 0) {
if (buf[cur] == 10) { return -1;
buf[cur] = '\0';
break;
}
} }
if (buf[cur] == 10) {
buf[cur] = '\0';
break;
}
}
return cur+1; return cur + 1;
} }
int read_lines_until_blankline(int fd, int max_line_length, char ***lines) int read_lines_until_blankline(int fd, int max_line_length, char ***lines)
{ {
int lines_count = 0; int lines_count = 0;
char line[max_line_length+1]; char line[max_line_length + 1];
*lines = NULL; *lines = NULL;
memset(line, 0, max_line_length+1); memset(line, 0, max_line_length + 1);
while (1) { while (1) {
int readden = read_until_newline(fd, line, max_line_length); int readden = read_until_newline(fd, line, max_line_length);
/* readden will be: /* readden will be:
* 1 for an empty line * 1 for an empty line
* -1 for an eof * -1 for an eof
* -1 for a read error * -1 for a read error
*/ */
if (readden <= 1) { return lines_count; } if (readden <= 1) {
*lines = xrealloc(*lines, (lines_count+1) * sizeof(char*)); return lines_count;
(*lines)[lines_count] = strdup(line);
if ((*lines)[lines_count][0] == 0) {
return lines_count;
}
lines_count++;
} }
*lines = xrealloc(*lines, (lines_count + 1) * sizeof(char *));
(*lines)[lines_count] = strdup(line);
if ((*lines)[lines_count][0] == 0) {
return lines_count;
}
lines_count++;
}
} }
int fd_is_closed( int fd_in ) int fd_is_closed(int fd_in)
{ {
int errno_old = errno; int errno_old = errno;
int result = fcntl( fd_in, F_GETFL ) < 0; int result = fcntl(fd_in, F_GETFL) < 0;
errno = errno_old; errno = errno_old;
return result; return result;
} }
static inline int io_errno_permanent(void) static inline int io_errno_permanent(void)
{ {
return ( errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR ); return (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR);
} }
/* Returns -1 if the operation failed, or the number of bytes read if all is /* Returns -1 if the operation failed, or the number of bytes read if all is
* well. Note that 0 bytes may be returned. Unlike read(), this is not an EOF! */ * well. Note that 0 bytes may be returned. Unlike read(), this is not an EOF! */
ssize_t iobuf_read(int fd, struct iobuf *iobuf, size_t default_size ) ssize_t iobuf_read(int fd, struct iobuf * iobuf, size_t default_size)
{ {
size_t left; size_t left;
ssize_t count; ssize_t count;
if ( iobuf->needle == 0 ) { if (iobuf->needle == 0) {
iobuf->size = default_size; iobuf->size = default_size;
} }
left = iobuf->size - iobuf->needle; left = iobuf->size - iobuf->needle;
debug( "Reading %"PRIu32" of %"PRIu32" bytes from fd %i", left, iobuf->size, fd ); debug("Reading %" PRIu32 " of %" PRIu32 " bytes from fd %i", left,
iobuf->size, fd);
count = read( fd, iobuf->buf + iobuf->needle, left ); count = read(fd, iobuf->buf + iobuf->needle, left);
if ( count > 0 ) { if (count > 0) {
iobuf->needle += count; iobuf->needle += count;
debug( "read() returned %"PRIu32" bytes", count ); debug("read() returned %" PRIu32 " bytes", count);
} else if ( count == 0 ) { } else if (count == 0) {
warn( "read() returned EOF on fd %i", fd ); warn("read() returned EOF on fd %i", fd);
errno = 0; errno = 0;
return -1; return -1;
} else if ( count == -1 ) { } else if (count == -1) {
if ( io_errno_permanent() ) { if (io_errno_permanent()) {
warn( SHOW_ERRNO( "read() failed on fd %i", fd ) ); warn(SHOW_ERRNO("read() failed on fd %i", fd));
} else {
debug( SHOW_ERRNO( "read() returned 0 bytes" ) );
count = 0;
}
}
return count;
}
ssize_t iobuf_write( int fd, struct iobuf *iobuf )
{
size_t left = iobuf->size - iobuf->needle;
ssize_t count;
debug( "Writing %"PRIu32" of %"PRIu32" bytes to fd %i", left, iobuf->size, fd );
count = write( fd, iobuf->buf + iobuf->needle, left );
if ( count >= 0 ) {
iobuf->needle += count;
debug( "write() returned %"PRIu32" bytes", count );
} else { } else {
if ( io_errno_permanent() ) { debug(SHOW_ERRNO("read() returned 0 bytes"));
warn( SHOW_ERRNO( "write() failed on fd %i", fd ) ); count = 0;
} else {
debug( SHOW_ERRNO( "write() returned 0 bytes" ) );
count = 0;
}
} }
}
return count; return count;
}
ssize_t iobuf_write(int fd, struct iobuf * iobuf)
{
size_t left = iobuf->size - iobuf->needle;
ssize_t count;
debug("Writing %" PRIu32 " of %" PRIu32 " bytes to fd %i", left,
iobuf->size, fd);
count = write(fd, iobuf->buf + iobuf->needle, left);
if (count >= 0) {
iobuf->needle += count;
debug("write() returned %" PRIu32 " bytes", count);
} else {
if (io_errno_permanent()) {
warn(SHOW_ERRNO("write() failed on fd %i", fd));
} else {
debug(SHOW_ERRNO("write() returned 0 bytes"));
count = 0;
}
}
return count;
} }

View File

@@ -3,16 +3,16 @@
#include <sys/types.h> #include <sys/types.h>
struct iobuf { struct iobuf {
unsigned char *buf; unsigned char *buf;
size_t size; size_t size;
size_t needle; size_t needle;
}; };
ssize_t iobuf_read( int fd, struct iobuf* iobuf, size_t default_size ); ssize_t iobuf_read(int fd, struct iobuf *iobuf, size_t default_size);
ssize_t iobuf_write( int fd, struct iobuf* iobuf ); ssize_t iobuf_write(int fd, struct iobuf *iobuf);
#include "serve.h" #include "serve.h"
struct bitset; /* don't need whole of bitset.h here */ struct bitset; /* don't need whole of bitset.h here */
/** Scan the file opened in ''fd'', set bits in ''allocation_map'' that /** Scan the file opened in ''fd'', set bits in ''allocation_map'' that
* correspond to which blocks are physically allocated on disc (or part- * correspond to which blocks are physically allocated on disc (or part-
@@ -20,7 +20,7 @@ struct bitset; /* don't need whole of bitset.h here */
* than you've asked for, any block or part block will count as "allocated" * than you've asked for, any block or part block will count as "allocated"
* with the corresponding bit set. Returns 1 if successful, 0 otherwise. * with the corresponding bit set. Returns 1 if successful, 0 otherwise.
*/ */
int build_allocation_map(struct bitset * allocation_map, int fd); int build_allocation_map(struct bitset *allocation_map, int fd);
/** Repeat a write() operation that succeeds partially until ''size'' bytes /** Repeat a write() operation that succeeds partially until ''size'' bytes
* are written, or an error is returned, when it returns -1 as usual. * are written, or an error is returned, when it returns -1 as usual.
@@ -35,10 +35,11 @@ int readloop(int filedes, void *buffer, size_t size);
/** Repeat a sendfile() operation that succeeds partially until ''size'' bytes /** Repeat a sendfile() operation that succeeds partially until ''size'' bytes
* are written, or an error is returned, when it returns -1 as usual. * are written, or an error is returned, when it returns -1 as usual.
*/ */
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, loff_t *off_out, size_t len, unsigned int flags2); ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out,
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
@@ -50,7 +51,7 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len);
* until an LF character is received, which is written to the buffer at a zero * until an LF character is received, which is written to the buffer at a zero
* byte. Returns -1 on error, or the number of bytes written to the buffer. * byte. Returns -1 on error, or the number of bytes written to the buffer.
*/ */
int read_until_newline(int fd, char* buf, int bufsize); int read_until_newline(int fd, char *buf, int bufsize);
/** Read a number of lines using read_until_newline, until an empty line is /** Read a number of lines using read_until_newline, until an empty line is
* received (i.e. the sequence LF LF). The data is read from ''fd'' and * received (i.e. the sequence LF LF). The data is read from ''fd'' and
@@ -65,12 +66,12 @@ 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, void **out_map); int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
void **out_map);
/** Check to see whether the given file descriptor is closed. /** Check to see whether the given file descriptor is closed.
*/ */
int fd_is_closed( int fd_in ); int fd_is_closed(int fd_in);
#endif #endif

View File

@@ -2,7 +2,7 @@
#define MODE_H #define MODE_H
void mode(char* mode, int argc, char **argv); void mode(char *mode, int argc, char **argv);
#include <getopt.h> #include <getopt.h>
@@ -68,9 +68,9 @@ void mode(char* mode, int argc, char **argv);
"\t--" OPT_VERBOSE ",-" SOPT_VERBOSE "\t\tOutput debug information.\n" "\t--" OPT_VERBOSE ",-" SOPT_VERBOSE "\t\tOutput debug information.\n"
#ifdef DEBUG #ifdef DEBUG
# define VERBOSE_LOG_LEVEL 0 #define VERBOSE_LOG_LEVEL 0
#else #else
# define VERBOSE_LOG_LEVEL 1 #define VERBOSE_LOG_LEVEL 1
#endif #endif
#define QUIET_LOG_LEVEL 4 #define QUIET_LOG_LEVEL 4
@@ -91,7 +91,6 @@ void mode(char* mode, int argc, char **argv);
#define MAX_SPEED_LINE \ #define MAX_SPEED_LINE \
"\t--" OPT_MAX_SPEED ",-m <bps>\tMaximum speed of the migration, in bytes/sec.\n" "\t--" OPT_MAX_SPEED ",-m <bps>\tMaximum speed of the migration, in bytes/sec.\n"
char * help_help_text; char *help_help_text;
#endif #endif

View File

@@ -8,51 +8,54 @@
* We intentionally ignore the reserved 128 bytes at the end of the * We intentionally ignore the reserved 128 bytes at the end of the
* request, since there's nothing we can do with them. * request, since there's nothing we can do with them.
*/ */
void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to ) void nbd_r2h_init(struct nbd_init_raw *from, struct nbd_init *to)
{ {
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)
{ {
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 = htobe32( from->magic ); to->magic = be32toh(from->magic);
to->type = htobe32( from->type ); to->flags = be16toh(from->flags);
to->handle.w = from->handle.w; to->type = be16toh(from->type);
to->from = htobe64( from->from ); to->handle.w = from->handle.w;
to->len = htobe32( from->len ); to->from = be64toh(from->from);
to->len = be32toh(from->len);
} }
void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to ) void nbd_h2r_request(struct nbd_request *from, struct nbd_request_raw *to)
{ {
to->magic = be32toh( from->magic ); to->magic = htobe32(from->magic);
to->type = be32toh( from->type ); to->flags = htobe16(from->flags);
to->handle.w = from->handle.w; to->type = htobe16(from->type);
to->from = be64toh( from->from ); to->handle.w = from->handle.w;
to->len = be32toh( from->len ); to->from = htobe64(from->from);
to->len = htobe32(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->magic = be32toh(from->magic);
to->error = htobe32( 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 ) void nbd_h2r_reply(struct nbd_reply *from, struct nbd_reply_raw *to)
{ {
to->magic = be32toh( from->magic ); to->magic = htobe32(from->magic);
to->error = be32toh( from->error ); to->error = htobe32(from->error);
to->handle.w = from->handle.w; to->handle.w = from->handle.w;
} }

View File

@@ -7,16 +7,39 @@
#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
/* The top 2 bytes of the type field are overloaded and can contain flags */ /* values for transmission flag field */
#define REQUEST_MASK 0x0000ffff #define FLAG_HAS_FLAGS (1 << 0) /* Flags are there */
#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
/* 1MiB is the de-facto standard for maximum size of header + data */ /* 32 MiB is the maximum qemu will send you:
#define NBD_MAX_SIZE ( 1024 * 1024 ) * https://github.com/qemu/qemu/blob/v2.11.0/include/block/nbd.h#L183
*/
#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 ) )
#define NBD_REPLY_SIZE ( sizeof( struct nbd_reply_raw ) ) #define NBD_REPLY_SIZE ( sizeof( struct nbd_reply_raw ) )
@@ -25,8 +48,8 @@
#include <inttypes.h> #include <inttypes.h>
typedef union nbd_handle_t { typedef union nbd_handle_t {
uint8_t b[8]; uint8_t b[8];
uint64_t w; uint64_t w;
} nbd_handle_t; } nbd_handle_t;
/* The _raw types are the types as they appear on the wire. Non-_raw /* The _raw types are the types as they appear on the wire. Non-_raw
@@ -35,56 +58,57 @@ typedef union nbd_handle_t {
* for converting host to raw. * for converting host to raw.
*/ */
struct nbd_init_raw { struct nbd_init_raw {
char passwd[8]; char passwd[8];
__be64 magic; __be64 magic;
__be64 size; __be64 size;
char reserved[128]; __be32 flags;
char reserved[124];
}; };
struct nbd_request_raw { struct nbd_request_raw {
__be32 magic; __be32 magic;
__be32 type; /* == READ || == WRITE */ __be16 flags;
nbd_handle_t handle; __be16 type; /* == READ || == WRITE || == FLUSH */
__be64 from; nbd_handle_t handle;
__be32 len; __be64 from;
} __attribute__((packed)); __be32 len;
} __attribute__ ((packed));
struct nbd_reply_raw { struct nbd_reply_raw {
__be32 magic; __be32 magic;
__be32 error; /* 0 = ok, else error */ __be32 error; /* 0 = ok, else error */
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;
char reserved[128]; uint32_t flags;
char reserved[124];
}; };
struct nbd_request { struct nbd_request {
uint32_t magic; uint32_t magic;
uint32_t type; /* == READ || == WRITE || == DISCONNECT */ uint16_t flags;
nbd_handle_t handle; uint16_t type; /* == READ || == WRITE || == DISCONNECT || == FLUSH */
uint64_t from; nbd_handle_t handle;
uint32_t len; uint64_t from;
} __attribute__((packed)); uint32_t len;
} __attribute__ ((packed));
struct nbd_reply { struct nbd_reply {
uint32_t magic; uint32_t magic;
uint32_t error; /* 0 = ok, else error */ uint32_t error; /* 0 = ok, else error */
nbd_handle_t handle; /* handle you got from request */ nbd_handle_t handle; /* handle you got from request */
}; };
void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to ); void nbd_r2h_init(struct nbd_init_raw *from, struct nbd_init *to);
void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to ); void nbd_r2h_request(struct nbd_request_raw *from, struct nbd_request *to);
void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to ); void nbd_r2h_reply(struct nbd_reply_raw *from, struct nbd_reply *to);
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);
void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to ); 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

View File

@@ -10,118 +10,116 @@ int atoi(const char *nptr);
) )
/* FIXME: should change this to return negative on error like everything else */ /* FIXME: should change this to return negative on error like everything else */
int parse_ip_to_sockaddr(struct sockaddr* out, char* src) int parse_ip_to_sockaddr(struct sockaddr *out, char *src)
{ {
NULLCHECK( out ); NULLCHECK(out);
NULLCHECK( src ); NULLCHECK(src);
char temp[64]; char temp[64];
struct sockaddr_in *v4 = (struct sockaddr_in *) out; struct sockaddr_in *v4 = (struct sockaddr_in *) out;
struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) out; struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) out;
/* 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] == '[') { i++; } if (src[i] == '[') {
for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++) { i++;
temp[j++] = src[i];
}
temp[j] = 0;
} }
for (; i < 64 && IS_IP_VALID_CHAR(src[i]); i++) {
if (temp[0] == '0' && temp[1] == '\0') { temp[j++] = src[i];
v4->sin_family = AF_INET;
v4->sin_addr.s_addr = INADDR_ANY;
return 1;
} }
temp[j] = 0;
}
if (inet_pton(AF_INET, temp, &v4->sin_addr) == 1) { if (temp[0] == '0' && temp[1] == '\0') {
out->sa_family = AF_INET; v4->sin_family = AF_INET;
return 1; v4->sin_addr.s_addr = INADDR_ANY;
} return 1;
}
if (inet_pton(AF_INET6, temp, &v6->sin6_addr) == 1) { if (inet_pton(AF_INET, temp, &v4->sin_addr) == 1) {
out->sa_family = AF_INET6; out->sa_family = AF_INET;
return 1; return 1;
} }
return 0; if (inet_pton(AF_INET6, temp, &v6->sin6_addr) == 1) {
out->sa_family = AF_INET6;
return 1;
}
return 0;
} }
int parse_to_sockaddr(struct sockaddr* out, char* address) int parse_to_sockaddr(struct sockaddr *out, char *address)
{ {
struct sockaddr_un* un = (struct sockaddr_un*) out; struct sockaddr_un *un = (struct sockaddr_un *) out;
NULLCHECK( address ); NULLCHECK(address);
if ( address[0] == '/' ) { if (address[0] == '/') {
un->sun_family = AF_UNIX; un->sun_family = AF_UNIX;
strncpy( un->sun_path, address, 108 ); /* FIXME: linux only */ strncpy(un->sun_path, address, 108); /* FIXME: linux only */
return 1; return 1;
} }
return parse_ip_to_sockaddr( out, address ); return parse_ip_to_sockaddr(out, address);
} }
int parse_acl(struct ip_and_mask (**out)[], int max, char **entries) int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
{ {
struct ip_and_mask* list; struct ip_and_mask *list;
int i; int i;
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); }
}
for (i = 0; i < max; i++) { for (i = 0; i < max; i++) {
int j; int j;
struct ip_and_mask* outentry = &list[i]; struct ip_and_mask *outentry = &list[i];
# define MAX_MASK_BITS (outentry->ip.family == AF_INET ? 32 : 128) # define MAX_MASK_BITS (outentry->ip.family == AF_INET ? 32 : 128)
if (parse_ip_to_sockaddr(&outentry->ip.generic, entries[i]) == 0) { if (parse_ip_to_sockaddr(&outentry->ip.generic, entries[i]) == 0) {
return i; return i;
} }
for (j=0; entries[i][j] && entries[i][j] != '/'; j++) for (j = 0; entries[i][j] && entries[i][j] != '/'; j++); // increment 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
debug("acl ptr[%d]: %p %d",i, outentry, outentry->mask); debug("acl ptr[%d]: %p %d", i, outentry, outentry->mask);
} }
for (i=0; i < max; i++) { for (i = 0; i < max; i++) {
debug("acl entry %d @ %p has mask %d", i, list[i], list[i].mask); debug("acl entry %d @ %p has mask %d", i, list[i], list[i].mask);
} }
return max; return max;
} }
void parse_port( char *s_port, struct sockaddr_in *out ) void parse_port(char *s_port, struct sockaddr_in *out)
{ {
NULLCHECK( s_port ); NULLCHECK(s_port);
int raw_port; int raw_port;
raw_port = atoi( s_port ); raw_port = atoi(s_port);
if ( raw_port < 0 || raw_port > 65535 ) { if (raw_port < 0 || raw_port > 65535) {
fatal( "Port number must be >= 0 and <= 65535" ); fatal("Port number must be >= 0 and <= 65535");
} }
out->sin_port = htobe16( raw_port ); out->sin_port = htobe16(raw_port);
} }

View File

@@ -8,22 +8,21 @@
#include <unistd.h> #include <unistd.h>
union mysockaddr { union mysockaddr {
unsigned short family; unsigned short family;
struct sockaddr generic; struct sockaddr generic;
struct sockaddr_in v4; struct sockaddr_in v4;
struct sockaddr_in6 v6; struct sockaddr_in6 v6;
struct sockaddr_un un; struct sockaddr_un un;
}; };
struct ip_and_mask { struct ip_and_mask {
union mysockaddr ip; union mysockaddr ip;
int mask; int mask;
}; };
int parse_ip_to_sockaddr(struct sockaddr* out, char* src); int parse_ip_to_sockaddr(struct sockaddr *out, char *src);
int parse_to_sockaddr(struct sockaddr* out, char* src); int parse_to_sockaddr(struct sockaddr *out, char *src);
int parse_acl(struct ip_and_mask (**out)[], int max, char **entries); 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

View File

@@ -8,212 +8,223 @@
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
int socket_connect(struct sockaddr* to, struct sockaddr* from) int socket_connect(struct sockaddr *to, struct sockaddr *from)
{ {
int fd = socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0); int fd =
if( fd < 0 ){ socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM,
warn( "Couldn't create client socket"); 0);
return -1; if (fd < 0) {
} warn("Couldn't create client socket");
return -1;
}
if (NULL != from) { if (NULL != from) {
if ( 0 > bind( fd, from, sizeof(struct sockaddr_in6 ) ) ){ if (0 > bind(fd, from, sizeof(struct sockaddr_in6))) {
warn( SHOW_ERRNO( "bind() to source address failed" ) ); warn(SHOW_ERRNO("bind() to source address failed"));
if ( 0 > close( fd ) ) { /* Non-fatal leak */ if (0 > close(fd)) { /* Non-fatal leak */
warn( SHOW_ERRNO( "Failed to close fd %i", fd ) ); warn(SHOW_ERRNO("Failed to close fd %i", fd));
} }
return -1; return -1;
}
} }
}
if ( 0 > sock_try_connect( fd, to, sizeof( struct sockaddr_in6 ), 15 ) ) { if (0 > sock_try_connect(fd, to, sizeof(struct sockaddr_in6), 15)) {
warn( SHOW_ERRNO( "connect failed" ) ); warn(SHOW_ERRNO("connect failed"));
if ( 0 > close( fd ) ) { /* Non-fatal leak */ if (0 > close(fd)) { /* Non-fatal leak */
warn( SHOW_ERRNO( "Failed to close fd %i", fd ) ); warn(SHOW_ERRNO("Failed to close fd %i", fd));
}
return -1;
} }
return -1;
}
if ( sock_set_tcp_nodelay( fd, 1 ) == -1 ) { if (sock_set_tcp_nodelay(fd, 1) == -1) {
warn( SHOW_ERRNO( "Failed to set TCP_NODELAY" ) ); warn(SHOW_ERRNO("Failed to set TCP_NODELAY"));
} }
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");
goto fail; goto fail;
} }
if ( be64toh( init_raw->magic ) != INIT_MAGIC ) { if (be64toh(init_raw->magic) != INIT_MAGIC) {
warn( "wrong magic (%x)", be64toh( init_raw->magic ) ); warn("wrong magic (%x)", be64toh(init_raw->magic));
goto fail; goto fail;
} }
if ( NULL != out_size ) { if (NULL != out_size) {
*out_size = be64toh( init_raw->size ); *out_size = be64toh(init_raw->size);
} }
return 1; if (NULL != out_flags) {
fail: *out_flags = be32toh(init_raw->flags);
}
return 1;
fail:
return 0;
}
int socket_nbd_read_hello(int fd, uint64_t * out_size,
uint32_t * out_flags)
{
struct nbd_init_raw init_raw;
if (0 > readloop(fd, &init_raw, sizeof(init_raw))) {
warn("Couldn't read init");
return 0; return 0;
}
return nbd_check_hello(&init_raw, out_size, out_flags);
} }
int socket_nbd_read_hello( int fd, uint64_t* out_size ) void nbd_hello_to_buf(struct nbd_init_raw *buf, off64_t out_size,
uint32_t out_flags)
{ {
struct nbd_init_raw init_raw; struct nbd_init init;
memcpy(&init.passwd, INIT_PASSWD, 8);
init.magic = INIT_MAGIC;
init.size = out_size;
init.flags = out_flags;
if ( 0 > readloop( fd, &init_raw, sizeof(init_raw) ) ) { memset(buf, 0, sizeof(struct nbd_init_raw)); // ensure reserved is 0s
warn( "Couldn't read init" ); nbd_h2r_init(&init, buf);
return 0;
}
return nbd_check_hello( &init_raw, out_size ); return;
} }
void nbd_hello_to_buf( struct nbd_init_raw *buf, off64_t out_size ) int socket_nbd_write_hello(int fd, off64_t out_size, uint32_t out_flags)
{ {
struct nbd_init init; struct nbd_init_raw init_raw;
nbd_hello_to_buf(&init_raw, out_size, out_flags);
memcpy( &init.passwd, INIT_PASSWD, 8 ); if (0 > writeloop(fd, &init_raw, sizeof(init_raw))) {
init.magic = INIT_MAGIC; warn(SHOW_ERRNO("failed to write hello to socket"));
init.size = out_size; return 0;
}
memset( buf, 0, sizeof( struct nbd_init_raw ) ); // ensure reserved is 0s return 1;
nbd_h2r_init( &init, buf );
return;
} }
int socket_nbd_write_hello(int fd, off64_t out_size) void fill_request(struct nbd_request_raw *request_raw, uint16_t type,
uint16_t flags, uint64_t from, uint32_t len)
{ {
struct nbd_init_raw init_raw; request_raw->magic = htobe32(REQUEST_MAGIC);
nbd_hello_to_buf( &init_raw, out_size ); request_raw->type = htobe16(type);
request_raw->flags = htobe16(flags);
if ( 0 > writeloop( fd, &init_raw, sizeof( init_raw ) ) ) { request_raw->handle.w =
warn( SHOW_ERRNO( "failed to write hello to socket" ) ); (((uint64_t) rand()) << 32) | ((uint64_t) rand());
return 0; request_raw->from = htobe64(from);
} request_raw->len = htobe32(len);
return 1;
} }
void fill_request(struct nbd_request *request, int type, uint64_t from, uint32_t len) void read_reply(int fd, uint64_t request_raw_handle,
struct nbd_reply *reply)
{ {
request->magic = htobe32(REQUEST_MAGIC); struct nbd_reply_raw reply_raw;
request->type = htobe32(type);
request->handle.w = (((uint64_t)rand()) << 32) | ((uint64_t)rand()); ERROR_IF_NEGATIVE(readloop
request->from = htobe64(from); (fd, &reply_raw, sizeof(struct nbd_reply_raw)),
request->len = htobe32(len); "Couldn't read reply");
nbd_r2h_reply(&reply_raw, reply);
if (reply->magic != REPLY_MAGIC) {
error("Reply magic incorrect (%x)", reply->magic);
}
if (reply->error != 0) {
error("Server replied with error %d", reply->error);
}
if (request_raw_handle != reply_raw.handle.w) {
error("Did not reply with correct handle");
}
} }
void read_reply(int fd, struct nbd_request *request, struct nbd_reply *reply) void wait_for_data(int fd, int timeout_secs)
{ {
struct nbd_reply_raw reply_raw; fd_set fds;
struct timeval tv = { timeout_secs, 0 };
int selected;
ERROR_IF_NEGATIVE(readloop(fd, &reply_raw, sizeof(struct nbd_reply_raw)), FD_ZERO(&fds);
"Couldn't read reply"); FD_SET(fd, &fds);
nbd_r2h_reply( &reply_raw, reply ); selected =
sock_try_select(FD_SETSIZE, &fds, NULL, NULL,
timeout_secs >= 0 ? &tv : NULL);
if (reply->magic != REPLY_MAGIC) { FATAL_IF(-1 == selected, "Select failed");
error("Reply magic incorrect (%x)", reply->magic); ERROR_IF(0 == selected, "Timed out waiting for reply");
}
if (reply->error != 0) {
error("Server replied with error %d", reply->error);
}
if (request->handle.w != reply->handle.w) {
error("Did not reply with correct handle");
}
}
void wait_for_data( int fd, int timeout_secs )
{
fd_set fds;
struct timeval tv = { timeout_secs, 0 };
int selected;
FD_ZERO( &fds );
FD_SET( fd, &fds );
selected = sock_try_select(
FD_SETSIZE, &fds, NULL, NULL, timeout_secs >=0 ? &tv : NULL
);
FATAL_IF( -1 == selected, "Select failed" );
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* out_buf, int timeout_secs) void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd,
void *out_buf, int timeout_secs)
{ {
struct nbd_request request; struct nbd_request_raw request_raw;
struct nbd_reply reply; struct nbd_reply reply;
fill_request(&request, REQUEST_READ, from, len); fill_request(&request_raw, REQUEST_READ, 0, from, len);
FATAL_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)), FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
"Couldn't write request"); "Couldn't write request");
wait_for_data( fd, timeout_secs );
read_reply(fd, &request, &reply);
if (out_buf) { wait_for_data(fd, timeout_secs);
FATAL_IF_NEGATIVE(readloop(fd, out_buf, len), read_reply(fd, request_raw.handle.w, &reply);
"Read failed");
} if (out_buf) {
else { FATAL_IF_NEGATIVE(readloop(fd, out_buf, len), "Read failed");
FATAL_IF_NEGATIVE( } else {
splice_via_pipe_loop(fd, out_fd, len), FATAL_IF_NEGATIVE(splice_via_pipe_loop(fd, out_fd, len),
"Splice failed" "Splice failed");
); }
}
} }
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd, void* in_buf, int timeout_secs) void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd,
void *in_buf, int timeout_secs)
{ {
struct nbd_request request; struct nbd_request_raw request_raw;
struct nbd_reply reply; struct nbd_reply reply;
fill_request(&request, REQUEST_WRITE, from, len); fill_request(&request_raw, REQUEST_WRITE, 0, from, len);
ERROR_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)), ERROR_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
"Couldn't write request"); "Couldn't write request");
if (in_buf) { if (in_buf) {
ERROR_IF_NEGATIVE(writeloop(fd, in_buf, len), ERROR_IF_NEGATIVE(writeloop(fd, in_buf, len), "Write failed");
"Write failed"); } else {
} ERROR_IF_NEGATIVE(splice_via_pipe_loop(in_fd, fd, len),
else { "Splice failed");
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, &reply); read_reply(fd, request_raw.handle.w, &reply);
} }
int socket_nbd_disconnect( int fd ) int socket_nbd_disconnect(int fd)
{ {
int success = 1; int success = 1;
struct nbd_request request; struct nbd_request_raw request_raw;
fill_request( &request, REQUEST_DISCONNECT, 0, 0 ); fill_request(&request_raw, REQUEST_DISCONNECT, 0, 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, sizeof( request ) ), FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)),
"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;\
int success = socket_nbd_read_hello(params->client, &size); \ uint32_t flags;\
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 || \
@@ -229,23 +240,26 @@ int socket_nbd_disconnect( int fd )
}\ }\
} }
void do_read(struct mode_readwrite_params* params) void do_read(struct mode_readwrite_params *params)
{ {
params->client = socket_connect(&params->connect_to.generic, &params->connect_from.generic); params->client =
FATAL_IF_NEGATIVE( params->client, "Couldn't connect." ); socket_connect(&params->connect_to.generic,
CHECK_RANGE("read"); &params->connect_from.generic);
socket_nbd_read(params->client, params->from, params->len, FATAL_IF_NEGATIVE(params->client, "Couldn't connect.");
params->data_fd, NULL, 10); CHECK_RANGE("read");
close(params->client); socket_nbd_read(params->client, params->from, params->len,
params->data_fd, NULL, 10);
close(params->client);
} }
void do_write(struct mode_readwrite_params* params) void do_write(struct mode_readwrite_params *params)
{ {
params->client = socket_connect(&params->connect_to.generic, &params->connect_from.generic); params->client =
FATAL_IF_NEGATIVE( params->client, "Couldn't connect." ); socket_connect(&params->connect_to.generic,
CHECK_RANGE("write"); &params->connect_from.generic);
socket_nbd_write(params->client, params->from, params->len, FATAL_IF_NEGATIVE(params->client, "Couldn't connect.");
params->data_fd, NULL, 10); CHECK_RANGE("write");
close(params->client); socket_nbd_write(params->client, params->from, params->len,
params->data_fd, NULL, 10);
close(params->client);
} }

View File

@@ -6,18 +6,21 @@
#include <sys/socket.h> #include <sys/socket.h>
#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); int socket_nbd_read_hello(int fd, uint64_t * size, uint32_t * flags);
int socket_nbd_write_hello(int fd, uint64_t size); int socket_nbd_write_hello(int fd, uint64_t size, uint32_t flags);
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs); void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd,
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs); void *out_buf, int timeout_secs);
int socket_nbd_disconnect( int fd ); 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);
/* 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,
int nbd_check_hello( struct nbd_init_raw* init_raw, uint64_t* out_size ); uint32_t out_flags);
int nbd_check_hello(struct nbd_init_raw *init_raw, uint64_t * out_size,
uint32_t * out_flags);
#endif #endif

View File

@@ -4,64 +4,62 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/un.h> #include <sys/un.h>
static const int max_response=1024; static const int max_response = 1024;
void print_response( const char * response ) void print_response(const char *response)
{ {
char * response_text; char *response_text;
FILE * out; FILE *out;
int exit_status; int exit_status;
NULLCHECK( response ); NULLCHECK(response);
exit_status = atoi(response); exit_status = atoi(response);
response_text = strchr( response, ':' ); response_text = strchr(response, ':');
FATAL_IF_NULL( response_text, FATAL_IF_NULL(response_text,
"Error parsing server response: '%s'", response ); "Error parsing server response: '%s'", response);
out = exit_status > 0 ? stderr : stdout; out = exit_status > 0 ? stderr : stdout;
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, char** argv) void do_remote_command(char *command, char *socket_name, int argc,
char **argv)
{ {
char newline=10; char newline = 10;
int i; int i;
debug( "connecting to run remote command %s", command ); debug("connecting to run remote command %s", command);
int remote = socket(AF_UNIX, SOCK_STREAM, 0); int remote = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un address; struct sockaddr_un address;
char response[max_response]; char response[max_response];
memset(&address, 0, sizeof(address)); memset(&address, 0, sizeof(address));
FATAL_IF_NEGATIVE(remote, "Couldn't create client socket"); FATAL_IF_NEGATIVE(remote, "Couldn't create client socket");
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( FATAL_IF_NEGATIVE(connect
connect(remote, (struct sockaddr*) &address, sizeof(address)), (remote, (struct sockaddr *) &address,
"Couldn't connect to %s", socket_name sizeof(address)), "Couldn't connect to %s",
); socket_name);
write(remote, command, strlen(command)); write(remote, command, strlen(command));
write(remote, &newline, 1); write(remote, &newline, 1);
for (i=0; i<argc; i++) { for (i = 0; i < argc; i++) {
if ( NULL != argv[i] ) { if (NULL != argv[i]) {
write(remote, argv[i], strlen(argv[i])); write(remote, argv[i], strlen(argv[i]));
}
write(remote, &newline, 1);
} }
write(remote, &newline, 1); write(remote, &newline, 1);
}
write(remote, &newline, 1);
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(read_until_newline(remote, response, max_response),
read_until_newline(remote, response, max_response), "Couldn't read response from %s", socket_name);
"Couldn't read response from %s", socket_name
);
print_response( response ); print_response(response);
exit(atoi(response)); exit(atoi(response));
} }

View File

@@ -24,18 +24,18 @@
#include "util.h" #include "util.h"
#include "self_pipe.h" #include "self_pipe.h"
#define ERR_MSG_PIPE "Couldn't open a pipe for signaling." #define ERR_MSG_PIPE "Couldn't open a pipe for signaling."
#define ERR_MSG_FCNTL "Couldn't set a signalling pipe non-blocking." #define ERR_MSG_FCNTL "Couldn't set a signalling pipe non-blocking."
#define ERR_MSG_WRITE "Couldn't write to a signaling pipe." #define ERR_MSG_WRITE "Couldn't write to a signaling pipe."
#define ERR_MSG_READ "Couldn't read from a signaling pipe." #define ERR_MSG_READ "Couldn't read from a signaling pipe."
void self_pipe_server_error( int err, char *msg ) void self_pipe_server_error(int err, char *msg)
{ {
char errbuf[1024] = {0}; char errbuf[1024] = { 0 };
strerror_r( err, errbuf, 1024 ); strerror_r(err, errbuf, 1024);
fatal( "%s\t%d (%s)", msg, err, errbuf ); fatal("%s\t%d (%s)", msg, err, errbuf);
} }
/** /**
@@ -47,33 +47,36 @@ void self_pipe_server_error( int err, char *msg )
* Remember to call self_pipe_destroy when you're done with the return * Remember to call self_pipe_destroy when you're done with the return
* value. * value.
*/ */
struct self_pipe * self_pipe_create(void) 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 ) { return NULL; }
if ( pipe( fds ) ) { if (NULL == sig) {
free( sig ); return NULL;
self_pipe_server_error( errno, ERR_MSG_PIPE ); }
return NULL;
}
if ( fcntl( fds[0], F_SETFL, O_NONBLOCK ) || fcntl( fds[1], F_SETFL, O_NONBLOCK ) ) { if (pipe(fds)) {
int fcntl_err = errno; free(sig);
while( close( fds[0] ) == -1 && errno == EINTR ); self_pipe_server_error(errno, ERR_MSG_PIPE);
while( close( fds[1] ) == -1 && errno == EINTR ); return NULL;
free( sig ); }
self_pipe_server_error( fcntl_err, ERR_MSG_FCNTL );
return NULL; if (fcntl(fds[0], F_SETFL, O_NONBLOCK)
} || fcntl(fds[1], F_SETFL, O_NONBLOCK)) {
int fcntl_err = errno;
while (close(fds[0]) == -1 && errno == EINTR);
while (close(fds[1]) == -1 && errno == EINTR);
free(sig);
self_pipe_server_error(fcntl_err, ERR_MSG_FCNTL);
sig->read_fd = fds[0]; return NULL;
sig->write_fd = fds[1]; }
return sig; sig->read_fd = fds[0];
sig->write_fd = fds[1];
return sig;
} }
@@ -83,19 +86,19 @@ struct self_pipe * self_pipe_create(void)
* Returns 1 on success. Can fail if weirdness happened to the write fd * Returns 1 on success. Can fail if weirdness happened to the write fd
* of the pipe in the self_pipe struct. * of the pipe in the self_pipe struct.
*/ */
int self_pipe_signal( struct self_pipe * sig ) int self_pipe_signal(struct self_pipe *sig)
{ {
NULLCHECK( sig ); NULLCHECK(sig);
FATAL_IF( 1 == sig->write_fd, "Shouldn't be writing to stdout" ); FATAL_IF(1 == sig->write_fd, "Shouldn't be writing to stdout");
FATAL_IF( 2 == sig->write_fd, "Shouldn't be writing to stderr" ); FATAL_IF(2 == sig->write_fd, "Shouldn't be writing to stderr");
int written = write( sig->write_fd, "X", 1 ); int written = write(sig->write_fd, "X", 1);
if ( written != 1 ) { if (written != 1) {
self_pipe_server_error( errno, ERR_MSG_WRITE ); self_pipe_server_error(errno, ERR_MSG_WRITE);
return 0; return 0;
} }
return 1; return 1;
} }
@@ -106,11 +109,11 @@ int self_pipe_signal( struct self_pipe * sig )
* Returns the number of bytes read, which will be 1 on success and 0 if * Returns the number of bytes read, which will be 1 on success and 0 if
* there was no signal. * there was no signal.
*/ */
int self_pipe_signal_clear( struct self_pipe *sig ) int self_pipe_signal_clear(struct self_pipe *sig)
{ {
char buf[1]; char buf[1];
return 1 == read( sig->read_fd, buf, 1 ); return 1 == read(sig->read_fd, buf, 1);
} }
@@ -118,30 +121,30 @@ int self_pipe_signal_clear( struct self_pipe *sig )
* Close the pipe and free the self_pipe. Do not try to use the * Close the pipe and free the self_pipe. Do not try to use the
* self_pipe struct after calling this, the innards are mush. * self_pipe struct after calling this, the innards are mush.
*/ */
int self_pipe_destroy( struct self_pipe * sig ) int self_pipe_destroy(struct self_pipe *sig)
{ {
NULLCHECK(sig); NULLCHECK(sig);
while( close( sig->read_fd ) == -1 && errno == EINTR ); while (close(sig->read_fd) == -1 && errno == EINTR);
while( close( sig->write_fd ) == -1 && errno == EINTR ); while (close(sig->write_fd) == -1 && errno == EINTR);
/* Just in case anyone *does* try to use this after free, /* Just in case anyone *does* try to use this after free,
* we should set the memory locations to an error value * we should set the memory locations to an error value
*/ */
sig->read_fd = -1; sig->read_fd = -1;
sig->write_fd = -1; sig->write_fd = -1;
free( sig ); free(sig);
return 1; return 1;
} }
int self_pipe_fd_set( struct self_pipe * sig, fd_set * fds) int self_pipe_fd_set(struct self_pipe *sig, fd_set * fds)
{ {
FD_SET( sig->read_fd, fds ); FD_SET(sig->read_fd, fds);
return 1; return 1;
} }
int self_pipe_fd_isset( struct self_pipe * sig, fd_set * fds) int self_pipe_fd_isset(struct self_pipe *sig, fd_set * fds)
{ {
return FD_ISSET( sig->read_fd, fds ); return FD_ISSET(sig->read_fd, fds);
} }

View File

@@ -4,16 +4,16 @@
#include <sys/select.h> #include <sys/select.h>
struct self_pipe { struct self_pipe {
int read_fd; int read_fd;
int write_fd; int write_fd;
}; };
struct self_pipe * self_pipe_create(void); struct self_pipe *self_pipe_create(void);
int self_pipe_signal( struct self_pipe * sig ); int self_pipe_signal(struct self_pipe *sig);
int self_pipe_signal_clear( struct self_pipe *sig ); int self_pipe_signal_clear(struct self_pipe *sig);
int self_pipe_destroy( struct self_pipe * sig ); int self_pipe_destroy(struct self_pipe *sig);
int self_pipe_fd_set( struct self_pipe * sig, fd_set * fds ); int self_pipe_fd_set(struct self_pipe *sig, fd_set * fds);
int self_pipe_fd_isset( struct self_pipe *sig, fd_set *fds ); int self_pipe_fd_isset(struct self_pipe *sig, fd_set * fds);
#endif #endif

View File

@@ -9,248 +9,287 @@
#include "sockutil.h" #include "sockutil.h"
#include "util.h" #include "util.h"
size_t sockaddr_size( const struct sockaddr* sa ) size_t sockaddr_size(const struct sockaddr * sa)
{ {
struct sockaddr_un* un = (struct sockaddr_un*) sa; struct sockaddr_un *un = (struct sockaddr_un *) sa;
size_t ret = 0; size_t ret = 0;
switch( sa->sa_family ) { switch (sa->sa_family) {
case AF_INET: case AF_INET:
ret = sizeof( struct sockaddr_in ); ret = sizeof(struct sockaddr_in);
break; break;
case AF_INET6: case AF_INET6:
ret = sizeof( struct sockaddr_in6 ); ret = sizeof(struct sockaddr_in6);
break; break;
case AF_UNIX: case AF_UNIX:
ret = sizeof( un->sun_family ) + SUN_LEN( un ); ret = sizeof(un->sun_family) + SUN_LEN(un);
break; break;
} }
return ret; return ret;
} }
const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size_t len ) const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
size_t len)
{ {
NULLCHECK( sa ); NULLCHECK(sa);
NULLCHECK( dest ); NULLCHECK(dest);
struct sockaddr_in* in = ( struct sockaddr_in* ) sa; struct sockaddr_in *in = (struct sockaddr_in *) sa;
struct sockaddr_in6* in6 = ( struct sockaddr_in6* ) sa; struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) sa;
struct sockaddr_un* un = ( struct sockaddr_un* ) sa; struct sockaddr_un *un = (struct sockaddr_un *) sa;
unsigned short real_port = ntohs( in->sin_port ); // common to in and in6 unsigned short real_port = ntohs(in->sin_port); // common to in and in6
const char* ret = NULL; const char *ret = NULL;
memset( dest, 0, len ); memset(dest, 0, len);
if ( sa->sa_family == AF_INET ) { if (sa->sa_family == AF_INET) {
ret = inet_ntop( AF_INET, &in->sin_addr, dest, len ); ret = inet_ntop(AF_INET, &in->sin_addr, dest, len);
} else if ( sa->sa_family == AF_INET6 ) { } else if (sa->sa_family == AF_INET6) {
ret = inet_ntop( AF_INET6, &in6->sin6_addr, dest, len ); ret = inet_ntop(AF_INET6, &in6->sin6_addr, dest, len);
} else if ( sa->sa_family == AF_UNIX ) { } else if (sa->sa_family == AF_UNIX) {
ret = strncpy( dest, un->sun_path, SUN_LEN( un ) ); ret = strncpy(dest, un->sun_path, SUN_LEN(un));
} }
if ( ret == NULL ) { if (ret == NULL) {
strncpy( dest, "???", len ); strncpy(dest, "???", len);
} }
if ( NULL != ret && real_port > 0 && sa->sa_family != AF_UNIX ) { if (NULL != ret && real_port > 0 && sa->sa_family != AF_UNIX) {
size_t size = strlen( dest ); size_t size = strlen(dest);
snprintf( dest + size, len - size, " port %d", real_port ); snprintf(dest + size, len - size, " port %d", real_port);
} }
return ret; return ret;
} }
int sock_set_reuseaddr( int fd, int optval ) int sock_set_reuseaddr(int fd, int optval)
{ {
return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) ); return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval));
}
int sock_set_keepalive_params(int fd, int time, int intvl, int probes)
{
if (sock_set_keepalive(fd, 1) ||
sock_set_tcp_keepidle(fd, time) ||
sock_set_tcp_keepintvl(fd, intvl) ||
sock_set_tcp_keepcnt(fd, probes)) {
return -1;
}
return 0;
}
int sock_set_keepalive(int fd, int optval)
{
return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval,
sizeof(optval));
}
int sock_set_tcp_keepidle(int fd, int optval)
{
return setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval,
sizeof(optval));
}
int sock_set_tcp_keepintvl(int fd, int optval)
{
return setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval,
sizeof(optval));
}
int sock_set_tcp_keepcnt(int fd, int optval)
{
return setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &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, sizeof(optval) ); return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optval,
sizeof(optval));
} }
int sock_set_tcp_cork( int fd, int optval ) int sock_set_tcp_cork(int fd, int optval)
{ {
return setsockopt( fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval) ); return setsockopt(fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
} }
int sock_set_nonblock( int fd, int optval ) int sock_set_nonblock(int fd, int optval)
{ {
int flags = fcntl( fd, F_GETFL ); int flags = fcntl(fd, F_GETFL);
if ( flags == -1 ) { if (flags == -1) {
return -1; return -1;
} }
if ( optval ) { if (optval) {
flags = flags | O_NONBLOCK; flags = flags | O_NONBLOCK;
} else {
flags = flags & (~O_NONBLOCK);
}
return fcntl(fd, F_SETFL, flags);
}
int sock_try_bind(int fd, const struct sockaddr *sa)
{
int bind_result;
char s_address[256];
int retry = 10;
sockaddr_address_string(sa, &s_address[0], 256);
do {
bind_result = bind(fd, sa, sockaddr_size(sa));
if (0 == bind_result) {
info("Bound to %s", s_address);
break;
} else { } else {
flags = flags & (~O_NONBLOCK); warn(SHOW_ERRNO("Couldn't bind to %s", s_address));
switch (errno) {
/* bind() can give us EACCES, EADDRINUSE, EADDRNOTAVAIL, EBADF,
* EINVAL, ENOTSOCK, EFAULT, ELOOP, ENAMETOOLONG, ENOENT,
* ENOMEM, ENOTDIR, EROFS
*
* Any of these other than EADDRINUSE & EADDRNOTAVAIL signify
* that there's a logic error somewhere.
*
* EADDRINUSE is fatal: if there's something already where we
* want to be listening, we have no guarantees that any clients
* will cope with it.
*/
case EADDRNOTAVAIL:
retry--;
if (retry) {
debug("retrying");
sleep(1);
}
continue;
case EADDRINUSE:
warn("%s in use, giving up.", s_address);
retry = 0;
break;
default:
warn("giving up");
retry = 0;
}
}
} while (retry);
return bind_result;
}
int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval *timeout)
{
int result;
do {
result = select(nfds, readfds, writefds, exceptfds, timeout);
if (errno != EINTR) {
break;
} }
return fcntl( fd, F_SETFL, flags ); } while (result == -1);
return result;
} }
int sock_try_bind( int fd, const struct sockaddr* sa ) int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
int wait)
{ {
int bind_result; fd_set fds;
char s_address[256]; struct timeval tv = { wait, 0 };
int retry = 10; int result = 0;
sockaddr_address_string( sa, &s_address[0], 256 ); if (sock_set_nonblock(fd, 1) == -1) {
warn(SHOW_ERRNO
("Failed to set socket non-blocking for connect()"));
return connect(fd, to, addrlen);
}
do { FD_ZERO(&fds);
bind_result = bind( fd, sa, sockaddr_size( sa ) ); FD_SET(fd, &fds);
if ( 0 == bind_result ) {
info( "Bound to %s", s_address );
break;
}
else {
warn( SHOW_ERRNO( "Couldn't bind to %s", s_address ) );
switch ( errno ) { do {
/* bind() can give us EACCES, EADDRINUSE, EADDRNOTAVAIL, EBADF, result = connect(fd, to, addrlen);
* EINVAL, ENOTSOCK, EFAULT, ELOOP, ENAMETOOLONG, ENOENT,
* ENOMEM, ENOTDIR, EROFS
*
* Any of these other than EADDRINUSE & EADDRNOTAVAIL signify
* that there's a logic error somewhere.
*
* EADDRINUSE is fatal: if there's something already where we
* want to be listening, we have no guarantees that any clients
* will cope with it.
*/
case EADDRNOTAVAIL:
retry--;
if (retry) {
debug( "retrying" );
sleep( 1 );
}
continue;
case EADDRINUSE:
warn( "%s in use, giving up.", s_address );
retry = 0;
break;
default:
warn( "giving up" );
retry = 0;
}
}
} while ( retry );
return bind_result; if (result == -1) {
} switch (errno) {
case EINPROGRESS:
int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) result = 0;
{ break; /* success */
int result; case EAGAIN:
case EINTR:
do { /* Try connect() again. This only breaks out of the switch,
result = select(nfds, readfds, writefds, exceptfds, timeout); * not the do...while loop. since result == -1, we go again.
if ( errno != EINTR ) { */
break; break;
} default:
warn(SHOW_ERRNO("Failed to connect()"));
} while ( result == -1 );
return result;
}
int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait )
{
fd_set fds;
struct timeval tv = { wait, 0 };
int result = 0;
if ( sock_set_nonblock( fd, 1 ) == -1 ) {
warn( SHOW_ERRNO( "Failed to set socket non-blocking for connect()" ) );
return connect( fd, to, addrlen );
}
FD_ZERO( &fds );
FD_SET( fd, &fds );
do {
result = connect( fd, to, addrlen );
if ( result == -1 ) {
switch( errno ) {
case EINPROGRESS:
result = 0;
break; /* success */
case EAGAIN:
case EINTR:
/* Try connect() again. This only breaks out of the switch,
* not the do...while loop. since result == -1, we go again.
*/
break;
default:
warn( SHOW_ERRNO( "Failed to connect()" ) );
goto out;
}
}
} while ( result == -1 );
if ( -1 == sock_try_select( FD_SETSIZE, NULL, &fds, NULL, &tv) ) {
warn( SHOW_ERRNO( "failed to select() on non-blocking connect" ) );
result = -1;
goto out; goto out;
}
} }
} while (result == -1);
if ( !FD_ISSET( fd, &fds ) ) { if (-1 == sock_try_select(FD_SETSIZE, NULL, &fds, NULL, &tv)) {
result = -1; warn(SHOW_ERRNO("failed to select() on non-blocking connect"));
errno = ETIMEDOUT; result = -1;
goto out; goto out;
} }
int scratch; if (!FD_ISSET(fd, &fds)) {
socklen_t s_size = sizeof( scratch ); result = -1;
if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &scratch, &s_size ) == -1 ) { errno = ETIMEDOUT;
result = -1; goto out;
warn( SHOW_ERRNO( "getsockopt() failed" ) ); }
goto out;
}
if ( scratch == EINPROGRESS ) { int scratch;
scratch = ETIMEDOUT; socklen_t s_size = sizeof(scratch);
} if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &scratch, &s_size) == -1) {
result = -1;
warn(SHOW_ERRNO("getsockopt() failed"));
goto out;
}
result = scratch ? -1 : 0; if (scratch == EINPROGRESS) {
errno = scratch; scratch = ETIMEDOUT;
}
out: result = scratch ? -1 : 0;
if ( sock_set_nonblock( fd, 0 ) == -1 ) { errno = scratch;
warn( SHOW_ERRNO( "Failed to make socket blocking after connect()" ) );
return -1;
}
debug( "sock_try_connect: %i", result ); out:
return result; if (sock_set_nonblock(fd, 0) == -1) {
warn(SHOW_ERRNO("Failed to make socket blocking after connect()"));
return -1;
}
debug("sock_try_connect: %i", result);
return result;
} }
int sock_try_close( int fd ) int sock_try_close(int fd)
{ {
int result; int result;
do { do {
result = close( fd ); result = close(fd);
if ( result == -1 ) { if (result == -1) {
if ( EINTR == errno ) { if (EINTR == errno) {
continue; /* retry EINTR */ continue; /* retry EINTR */
} else { } else {
warn( SHOW_ERRNO( "Failed to close() fd %i", fd ) ); warn(SHOW_ERRNO("Failed to close() fd %i", fd));
break; /* Other errors get reported */ break; /* Other errors get reported */
} }
} }
} while( 0 ); } while (0);
return result; return result;
} }

View File

@@ -7,16 +7,32 @@
#include <sys/select.h> #include <sys/select.h>
/* Returns the size of the sockaddr, or 0 on error */ /* Returns the size of the sockaddr, or 0 on error */
size_t sockaddr_size(const struct sockaddr* sa); 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, size_t len); const char *sockaddr_address_string(const struct sockaddr *sa, char *dest,
size_t len);
/* Configure TCP keepalive on a socket */
int sock_set_keepalive_params(int fd, int time, int intvl, int probes);
/* Set the SOL_KEEPALIVE otion */
int sock_set_keepalive(int fd, int optval);
/* Set the SOL_REUSEADDR otion */ /* Set the SOL_REUSEADDR otion */
int sock_set_reuseaddr(int fd, int optval); int sock_set_reuseaddr(int fd, int optval);
/* Set the tcp_keepidle option */
int sock_set_tcp_keepidle(int fd, int optval);
/* Set the tcp_keepintvl option */
int sock_set_tcp_keepintvl(int fd, int optval);
/* Set the tcp_keepcnt option */
int sock_set_tcp_keepcnt(int fd, int 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);
@@ -26,16 +42,17 @@ int sock_set_tcp_cork(int fd, int optval);
int sock_set_nonblock(int fd, int optval); int sock_set_nonblock(int fd, int optval);
/* Attempt to bind the fd to the sockaddr, retrying common transient failures */ /* Attempt to bind the fd to the sockaddr, retrying common transient failures */
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, fd_set *exceptfds, struct timeval *timeout); int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds,
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 wait ); int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
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

View File

@@ -13,75 +13,79 @@
pthread_key_t cleanup_handler_key; pthread_key_t cleanup_handler_key;
int log_level = 2; int log_level = 2;
char *log_context = "";
void error_init(void) void error_init(void)
{ {
pthread_key_create(&cleanup_handler_key, free); pthread_key_create(&cleanup_handler_key, free);
} }
void error_handler(int fatal) void error_handler(int fatal)
{ {
DECLARE_ERROR_CONTEXT(context); DECLARE_ERROR_CONTEXT(context);
if (context) { if (context) {
longjmp(context->jmp, fatal ? 1 : 2 ); longjmp(context->jmp, fatal ? 1 : 2);
} } else {
else { if (fatal) {
if ( fatal ) { abort(); } abort();
else { pthread_exit((void*) 1); } } else {
pthread_exit((void *) 1);
} }
}
} }
void exit_err( const char *msg ) void exit_err(const char *msg)
{ {
fprintf( stderr, "%s\n", msg ); fprintf(stderr, "%s\n", msg);
exit( 1 ); exit(1);
} }
void mylog(int line_level, const char* format, ...) void mylog(int line_level, const char *format, ...)
{ {
if (line_level < log_level) { return; } if (line_level < log_level) {
return;
}
va_list argptr; va_list argptr;
va_start(argptr, format); va_start(argptr, format);
vfprintf(stderr, format, argptr); vfprintf(stderr, format, argptr);
va_end(argptr); va_end(argptr);
} }
uint64_t monotonic_time_ms() 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( FATAL_IF_NEGATIVE(clock_gettime(CLOCK_MONOTONIC, &ts),
clock_gettime(CLOCK_MONOTONIC, &ts), SHOW_ERRNO("clock_gettime failed")
SHOW_ERRNO( "clock_gettime failed" )
); );
seconds_ms = ts.tv_sec; seconds_ms = ts.tv_sec;
seconds_ms = seconds_ms * 1000; seconds_ms = seconds_ms * 1000;
nanoseconds_ms = ts.tv_nsec; nanoseconds_ms = ts.tv_nsec;
nanoseconds_ms = nanoseconds_ms / 1000000; nanoseconds_ms = nanoseconds_ms / 1000000;
return seconds_ms + nanoseconds_ms; return seconds_ms + nanoseconds_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", ptr ? "realloc" : "malloc", size); FATAL_IF_NULL(p, "couldn't xrealloc %d bytes",
return p; ptr ? "realloc" : "malloc", size);
return p;
} }
void* xmalloc(size_t size) void *xmalloc(size_t size)
{ {
void* p = xrealloc(NULL, size); void *p = xrealloc(NULL, size);
memset(p, 0, size); memset(p, 0, size);
return p; return p;
} }

View File

@@ -10,10 +10,11 @@
#include <unistd.h> #include <unistd.h>
#include <inttypes.h> #include <inttypes.h>
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 */, int /* is fatal? */); typedef void (cleanup_handler) (void * /* context */ ,
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;
@@ -21,16 +22,19 @@ extern int log_level;
/* set up the error globals */ /* set up the error globals */
void error_init(void); void error_init(void);
/* some context for the overall process that appears on each log line */
extern char *log_context;
void exit_err( const char * );
void exit_err(const char *);
/* error_set_handler must be a macro not a function due to setjmp stack rules */ /* error_set_handler must be a macro not a function due to setjmp stack rules */
#include <setjmp.h> #include <setjmp.h>
struct error_handler_context { struct error_handler_context {
jmp_buf jmp; jmp_buf jmp;
cleanup_handler* handler; cleanup_handler *handler;
void* data; void *data;
}; };
#define DECLARE_ERROR_CONTEXT(name) \ #define DECLARE_ERROR_CONTEXT(name) \
@@ -84,7 +88,7 @@ extern pthread_key_t cleanup_handler_key;
void error_handler(int fatal); void error_handler(int fatal);
/* mylog a line at the given level (0 being most verbose) */ /* mylog a line at the given level (0 being most verbose) */
void mylog(int line_level, const char* format, ...); void mylog(int line_level, const char *format, ...);
/* Returns the current time, in milliseconds, from CLOCK_MONOTONIC */ /* Returns the current time, in milliseconds, from CLOCK_MONOTONIC */
uint64_t monotonic_time_ms(void); uint64_t monotonic_time_ms(void);
@@ -92,12 +96,12 @@ uint64_t monotonic_time_ms(void);
#define levstr(i) (i==0?'D':(i==1?'I':(i==2?'W':(i==3?'E':'F')))) #define levstr(i) (i==0?'D':(i==1?'I':(i==2?'W':(i==3?'E':'F'))))
#define myloglev(level, msg, ...) mylog( level, "%"PRIu64":%c:%d %p %s:%d: "msg"\n", monotonic_time_ms(), levstr(level), getpid(),pthread_self(), __FILE__, __LINE__, ##__VA_ARGS__ ) #define myloglev(level, msg, ...) mylog( level, "%"PRIu64":%c:%d %p %s %s:%d: "msg"\n", monotonic_time_ms(), levstr(level), getpid(),pthread_self(), log_context, __FILE__, __LINE__, ##__VA_ARGS__ )
#ifdef DEBUG #ifdef DEBUG
# define debug(msg, ...) myloglev(0, msg, ##__VA_ARGS__) #define debug(msg, ...) myloglev(0, msg, ##__VA_ARGS__)
#else #else
# define debug(msg, ...) /* no-op */ #define debug(msg, ...) /* no-op */
#endif #endif
/* informational message, not expected to be compiled out */ /* informational message, not expected to be compiled out */
@@ -159,4 +163,3 @@ 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

View File

@@ -5,18 +5,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
int main(int argc, char** argv) 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 */
error_init(); error_init();
srand(time(NULL)); srand(time(NULL));
if (argc < 2) { if (argc < 2) {
exit_err( help_help_text ); exit_err(help_help_text);
} }
mode(argv[1], argc-1, argv+1); /* never returns */ mode(argv[1], argc - 1, argv + 1); /* never returns */
return 0; return 0;
} }

View File

@@ -8,164 +8,158 @@
static struct option proxy_options[] = { static struct option proxy_options[] = {
GETOPT_HELP, GETOPT_HELP,
GETOPT_ADDR, GETOPT_ADDR,
GETOPT_PORT, GETOPT_PORT,
GETOPT_CONNECT_ADDR, GETOPT_CONNECT_ADDR,
GETOPT_CONNECT_PORT, GETOPT_CONNECT_PORT,
GETOPT_BIND, GETOPT_BIND,
GETOPT_CACHE, GETOPT_CACHE,
GETOPT_QUIET, GETOPT_QUIET,
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"
"Resiliently proxy an NBD connection between client and server\n" "Resiliently proxy an NBD connection between client and server\n"
"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 ",-p <PORT>\tThe port we will bind to as a proxy, if required.\n" "\t--" OPT_PORT
"\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n" ",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n" "\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
"\t--" OPT_BIND ",-b <ADDR>\tThe address we connect from, as a proxy.\n" "\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n"
"\t--" OPT_CACHE ",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n" "\t--" OPT_BIND
QUIET_LINE ",-b <ADDR>\tThe address we connect from, as a proxy.\n" "\t--"
VERBOSE_LINE; OPT_CACHE
",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n"
QUIET_LINE VERBOSE_LINE;
static char proxy_default_cache_size[] = "4096"; static char proxy_default_cache_size[] = "4096";
void read_proxy_param( void read_proxy_param(int c,
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':
fprintf( stdout, "%s\n", proxy_help_text ); fprintf(stdout, "%s\n", proxy_help_text);
exit( 0 ); exit(0);
case 'l': case 'l':
*downstream_addr = optarg; *downstream_addr = optarg;
break; break;
case 'p': case 'p':
*downstream_port = optarg; *downstream_port = optarg;
break; break;
case 'C': case 'C':
*upstream_addr = optarg; *upstream_addr = optarg;
break; break;
case 'P': case 'P':
*upstream_port = optarg; *upstream_port = optarg;
break; break;
case 'b': case 'b':
*bind_addr = optarg; *bind_addr = optarg;
break; break;
case 'c': case 'c':
*cache_bytes = optarg ? optarg : proxy_default_cache_size; *cache_bytes = optarg ? optarg : proxy_default_cache_size;
break; break;
case 'q': case 'q':
log_level = QUIET_LOG_LEVEL; log_level = QUIET_LOG_LEVEL;
break; break;
case 'v': case 'v':
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err( proxy_help_text ); exit_err(proxy_help_text);
break; break;
} }
} }
struct proxier * proxy = NULL; struct proxier *proxy = NULL;
void my_exit(int signum) void my_exit(int signum)
{ {
info( "Exit signalled (%i)", signum ); info("Exit signalled (%i)", signum);
if ( NULL != proxy ) { if (NULL != proxy) {
proxy_cleanup( proxy ); proxy_cleanup(proxy);
}; };
exit( 0 ); exit(0);
} }
int main( int argc, char *argv[] ) int main(int argc, char *argv[])
{ {
int c; int c;
char *downstream_addr = NULL; char *downstream_addr = NULL;
char *downstream_port = NULL; char *downstream_port = NULL;
char *upstream_addr = NULL; char *upstream_addr = NULL;
char *upstream_port = NULL; char *upstream_port = NULL;
char *bind_addr = NULL; char *bind_addr = NULL;
char *cache_bytes = NULL; char *cache_bytes = NULL;
int success; int success;
sigset_t mask; sigset_t mask;
struct sigaction exit_action; struct sigaction exit_action;
sigemptyset( &mask ); sigemptyset(&mask);
sigaddset( &mask, SIGTERM ); sigaddset(&mask, SIGTERM);
sigaddset( &mask, SIGQUIT ); sigaddset(&mask, SIGQUIT);
sigaddset( &mask, SIGINT ); sigaddset(&mask, SIGINT);
exit_action.sa_handler = my_exit; exit_action.sa_handler = my_exit;
exit_action.sa_mask = mask; exit_action.sa_mask = mask;
exit_action.sa_flags = 0; exit_action.sa_flags = 0;
srand(time(NULL)); srand(time(NULL));
while (1) { while (1) {
c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL ); c = getopt_long(argc, argv, proxy_short_options, proxy_options,
if ( -1 == c ) { break; } NULL);
read_proxy_param( c, if (-1 == c) {
&downstream_addr, break;
&downstream_port,
&upstream_addr,
&upstream_port,
&bind_addr,
&cache_bytes
);
} }
read_proxy_param(c,
&downstream_addr,
&downstream_port,
&upstream_addr,
&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, "both --conn-addr and --conn-port are required.\n" ); fprintf(stderr,
exit_err( proxy_help_text ); "both --conn-addr and --conn-port are required.\n");
} exit_err(proxy_help_text);
}
proxy = proxy_create( proxy = proxy_create(downstream_addr,
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);
sigaction(SIGQUIT, &exit_action, NULL); sigaction(SIGQUIT, &exit_action, NULL);
sigaction(SIGINT, &exit_action, NULL); sigaction(SIGINT, &exit_action, NULL);
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( info("Proxying between %s %s (downstream) and %s %s (upstream)",
"Proxying between %s %s (downstream) and %s %s (upstream)", downstream_addr, downstream_port, upstream_addr,
downstream_addr, downstream_port, upstream_addr, upstream_port 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);
proxy_destroy( proxy ); proxy_destroy(proxy);
return success ? 0 : 1; return success ? 0 : 1;
} }

View File

@@ -2,67 +2,77 @@
#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);
out->buffer = xmalloc( size_bytes );
NULLCHECK( out->buffer );
out->size = size_bytes; out->buffer = xmalloc(size_bytes);
out->is_full = 0; NULLCHECK(out->buffer);
out->from = 0;
out->len = 0;
return out; out->size = size_bytes;
out->is_full = 0;
out->from = 0;
out->len = 0;
return out;
} }
void prefetch_destroy( struct prefetch *prefetch ) { void prefetch_destroy(struct prefetch *prefetch)
if( prefetch ) { {
free( prefetch->buffer ); if (prefetch) {
free( prefetch ); free(prefetch->buffer);
} free(prefetch);
}
} }
size_t prefetch_size( struct prefetch *prefetch){ size_t prefetch_size(struct prefetch *prefetch)
if ( prefetch ) { {
return prefetch->size; if (prefetch) {
} else { return prefetch->size;
return 0; } else {
} return 0;
}
} }
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 ) { {
prefetch->is_full = val; if (prefetch) {
} prefetch->is_full = val;
}
} }
int prefetch_is_full( struct prefetch *prefetch ){ int prefetch_is_full(struct prefetch *prefetch)
if( prefetch ) { {
return prefetch->is_full; if (prefetch) {
} else { return prefetch->is_full;
return 0; } else {
} return 0;
}
} }
int prefetch_contains( struct prefetch *prefetch, uint64_t from, uint32_t len ){ int prefetch_contains(struct prefetch *prefetch, uint64_t from,
NULLCHECK( prefetch ); uint32_t len)
return from >= prefetch->from && {
from + len <= prefetch->from + prefetch->len; NULLCHECK(prefetch);
return from >= prefetch->from &&
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 ); {
return prefetch->buffer + (from - prefetch->from); NULLCHECK(prefetch);
return prefetch->buffer + (from - prefetch->from);
} }

View File

@@ -7,27 +7,28 @@
#define PREFETCH_BUFSIZE 4096 #define PREFETCH_BUFSIZE 4096
struct prefetch { struct prefetch {
/* True if there is data in the buffer. */ /* True if there is data in the buffer. */
int is_full; int is_full;
/* The start point of the current content of buffer */ /* The start point of the current content of buffer */
uint64_t from; uint64_t from;
/* The length of the current content of buffer */ /* The length of the current content of buffer */
uint32_t len; uint32_t len;
/* The total size of the buffer, in bytes. */ /* The total size of the buffer, in bytes. */
size_t size; size_t size;
char *buffer; char *buffer;
}; };
struct prefetch* prefetch_create( size_t size_bytes ); struct prefetch *prefetch_create(size_t size_bytes);
void prefetch_destroy( struct prefetch *prefetch ); void prefetch_destroy(struct prefetch *prefetch);
size_t prefetch_size( struct prefetch *); size_t prefetch_size(struct prefetch *);
void prefetch_set_is_empty( struct prefetch *prefetch ); 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, uint32_t len ); int prefetch_contains(struct prefetch *prefetch, uint64_t from,
char *prefetch_offset( struct prefetch *prefetch, uint64_t from ); uint32_t len);
char *prefetch_offset(struct prefetch *prefetch, uint64_t from);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
#include "self_pipe.h" #include "self_pipe.h"
#ifdef PREFETCH #ifdef PREFETCH
#include "prefetch.h" #include "prefetch.h"
#endif #endif
/** UPSTREAM_TIMEOUT /** UPSTREAM_TIMEOUT
@@ -21,77 +21,77 @@
struct proxier { struct proxier {
/** address/port to bind to */ /** address/port to bind to */
union mysockaddr listen_on; union mysockaddr listen_on;
/** address/port to connect to */ /** address/port to connect to */
union mysockaddr connect_to; union mysockaddr connect_to;
/** address to bind to when making outgoing connections */ /** address to bind to when making outgoing connections */
union mysockaddr connect_from; union mysockaddr connect_from;
int bind; /* Set to true if we should use it */ int bind; /* Set to true if we should use it */
/* The socket we listen() on and accept() against */ /* The socket we listen() on and accept() against */
int listen_fd; int listen_fd;
/* The socket returned by accept() that we receive requests from and send /* The socket returned by accept() that we receive requests from and send
* responses to * responses to
*/ */
int downstream_fd; int downstream_fd;
/* The socket returned by connect() that we send requests to and receive /* The socket returned by connect() that we send requests to and receive
* responses from * responses from
*/ */
int upstream_fd; int upstream_fd;
/* 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;
/* We transform the raw request header into here */ /* These are the transmission flags sent as part of the handshake */
struct nbd_request req_hdr; uint32_t upstream_flags;
/* We transform the raw request header into here */
struct nbd_request req_hdr;
/* We transform the raw reply header into here */ /* We transform the raw reply header into here */
struct nbd_reply rsp_hdr; struct nbd_reply rsp_hdr;
/* Used for our non-blocking negotiation with upstream. TODO: maybe use /* Used for our non-blocking negotiation with upstream. TODO: maybe use
* for downstream as well ( we currently overload rsp ) */ * for downstream as well ( we currently overload rsp ) */
struct iobuf init; struct iobuf init;
/* The current NBD request from downstream */ /* The current NBD request from downstream */
struct iobuf req; struct iobuf req;
/* The current NBD reply from upstream */ /* The current NBD reply from upstream */
struct iobuf rsp; struct iobuf rsp;
/* It's starting to feel like we need an object for a single proxy session. /* It's starting to feel like we need an object for a single proxy session.
* These two track how many requests we've sent so far, and whether the * These two track how many requests we've sent so far, and whether the
* NBD_INIT code has been sent to the client yet. * NBD_INIT code has been sent to the client yet.
*/ */
uint64_t req_count; uint64_t req_count;
int hello_sent; int hello_sent;
/** These are only used if we pass --cache on the command line */ /** These are only used if we pass --cache on the command line */
/* While the in-flight request has been munged by prefetch, these two are /* While the in-flight request has been munged by prefetch, these two are
* set to true, and the original length of the request, respectively */ * set to true, and the original length of the request, respectively */
int is_prefetch_req; int is_prefetch_req;
uint32_t prefetch_req_orig_len; uint32_t prefetch_req_orig_len;
/* And here, we actually store the prefetched data once it's returned */ /* And here, we actually store the prefetched data once it's returned */
struct prefetch *prefetch; struct prefetch *prefetch;
/** */ /** */
}; };
struct proxier* proxy_create( struct proxier *proxy_create(char *s_downstream_address,
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, int do_proxy(struct proxier *proxy);
char* s_cache_bytes); void proxy_cleanup(struct proxier *proxy);
int do_proxy( struct proxier* proxy ); void proxy_destroy(struct proxier *proxy);
void proxy_cleanup( struct proxier* proxy );
void proxy_destroy( struct proxier* proxy );
#endif #endif

View File

@@ -6,103 +6,104 @@
#include "acl.h" #include "acl.h"
struct acl * acl_create( int len, char ** lines, int default_deny ) struct acl *acl_create(int len, char **lines, int default_deny)
{ {
struct acl * acl; struct acl *acl;
acl = (struct acl *)xmalloc( sizeof( struct acl ) ); acl = (struct acl *) xmalloc(sizeof(struct acl));
acl->len = parse_acl( &acl->entries, len, lines ); acl->len = parse_acl(&acl->entries, len, lines);
acl->default_deny = default_deny; acl->default_deny = default_deny;
return acl; return acl;
} }
static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 }; 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, struct ip_and_mask (*list)[], union mysockaddr* test) static int is_included_in_acl(int list_length,
struct ip_and_mask (*list)[],
union mysockaddr *test)
{ {
NULLCHECK( test ); NULLCHECK(test);
int i; int i;
for (i=0; i < list_length; i++) { for (i = 0; i < list_length; i++) {
struct ip_and_mask *entry = &(*list)[i]; struct ip_and_mask *entry = &(*list)[i];
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, entry->ip.family); debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family,
entry->ip.family);
if (test->generic.sa_family != entry->ip.family) { if (test->generic.sa_family != entry->ip.family) {
continue; continue;
}
if (test->generic.sa_family == AF_INET) {
debug("it's an AF_INET");
raw_address1 = (unsigned char*) &test->v4.sin_addr;
raw_address2 = (unsigned char*) &entry->ip.v4.sin_addr;
}
else if (test->generic.sa_family == AF_INET6) {
debug("it's an AF_INET6");
raw_address1 = (unsigned char*) &test->v6.sin6_addr;
raw_address2 = (unsigned char*) &entry->ip.v6.sin6_addr;
}
else {
fatal( "Can't check an ACL for this address type." );
}
debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%02x, c2=%02x", testbits, raw_address1[0], raw_address2[0]);
if (testbits >= 8) {
if (raw_address1[0] != raw_address2[0]) { goto no_match; }
}
else {
if ((raw_address1[0] & testmasks[testbits%8]) !=
(raw_address2[0] & testmasks[testbits%8]) ) {
goto no_match;
}
}
raw_address1++;
raw_address2++;
}
return 1;
no_match: ;
debug("no match");
} }
return 0; if (test->generic.sa_family == AF_INET) {
} debug("it's an AF_INET");
raw_address1 = (unsigned char *) &test->v4.sin_addr;
int acl_includes( struct acl * acl, union mysockaddr * addr ) raw_address2 = (unsigned char *) &entry->ip.v4.sin_addr;
{ } else if (test->generic.sa_family == AF_INET6) {
NULLCHECK( acl ); debug("it's an AF_INET6");
raw_address1 = (unsigned char *) &test->v6.sin6_addr;
if ( 0 == acl->len ) { raw_address2 = (unsigned char *) &entry->ip.v6.sin6_addr;
return !( acl->default_deny ); } else {
fatal("Can't check an ACL for this address type.");
} }
else {
return is_included_in_acl( acl->len, acl->entries, addr ); debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%02x, c2=%02x", testbits,
raw_address1[0], raw_address2[0]);
if (testbits >= 8) {
if (raw_address1[0] != raw_address2[0]) {
goto no_match;
}
} else {
if ((raw_address1[0] & testmasks[testbits % 8]) !=
(raw_address2[0] & testmasks[testbits % 8])) {
goto no_match;
}
}
raw_address1++;
raw_address2++;
} }
return 1;
no_match:;
debug("no match");
}
return 0;
} }
int acl_default_deny( struct acl * acl ) int acl_includes(struct acl *acl, union mysockaddr *addr)
{ {
NULLCHECK( acl ); NULLCHECK(acl);
return acl->default_deny;
if (0 == acl->len) {
return !(acl->default_deny);
} else {
return is_included_in_acl(acl->len, acl->entries, addr);
}
} }
void acl_destroy( struct acl * acl ) int acl_default_deny(struct acl *acl)
{ {
free( acl->entries ); NULLCHECK(acl);
acl->len = 0; return acl->default_deny;
acl->entries = NULL;
free( acl );
} }
void acl_destroy(struct acl *acl)
{
free(acl->entries);
acl->len = 0;
acl->entries = NULL;
free(acl);
}

View File

@@ -4,9 +4,9 @@
#include "parse.h" #include "parse.h"
struct acl { struct acl {
int len; int len;
int default_deny; int default_deny;
struct ip_and_mask (*entries)[]; struct ip_and_mask (*entries)[];
}; };
/** Allocate a new acl structure, parsing the given lines to sockaddr /** Allocate a new acl structure, parsing the given lines to sockaddr
@@ -17,21 +17,21 @@ struct acl {
* default_deny controls the behaviour of an empty list: if true, all * default_deny controls the behaviour of an empty list: if true, all
* requests will be denied. If true, all requests will be accepted. * requests will be denied. If true, all requests will be accepted.
*/ */
struct acl * acl_create( int len, char **lines, int default_deny ); struct acl *acl_create(int len, char **lines, int default_deny);
/** Check to see whether an address is allowed by an acl. /** Check to see whether an address is allowed by an acl.
* See acl_create for how the default_deny setting affects this. * See acl_create for how the default_deny setting affects this.
*/ */
int acl_includes( struct acl *, union mysockaddr *); int acl_includes(struct acl *, union mysockaddr *);
/** Get the default_deny status */ /** Get the default_deny status */
int acl_default_deny( struct acl * ); int acl_default_deny(struct acl *);
/** Free the acl structure and the internal acl entries table. /** Free the acl structure and the internal acl entries table.
*/ */
void acl_destroy( struct acl * ); void acl_destroy(struct acl *);
#endif #endif

View File

@@ -12,8 +12,8 @@
* poking at the bits directly without using these * poking at the bits directly without using these
* accessors/macros * accessors/macros
*/ */
typedef uint64_t bitfield_word_t; typedef uint64_t bitfield_word_t;
typedef bitfield_word_t * bitfield_p; typedef bitfield_word_t *bitfield_p;
#define BITFIELD_WORD_SIZE sizeof(bitfield_word_t) #define BITFIELD_WORD_SIZE sizeof(bitfield_word_t)
#define BITS_PER_WORD (BITFIELD_WORD_SIZE * 8) #define BITS_PER_WORD (BITFIELD_WORD_SIZE * 8)
@@ -30,65 +30,78 @@ 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)
{ {
for ( ; (from % BITS_PER_WORD) != 0 && len > 0 ; len-- ) { for (; (from % BITS_PER_WORD) != 0 && len > 0; len--) {
bit_set( b, from++ ); bit_set(b, from++);
} }
if (len >= BITS_PER_WORD) { if (len >= BITS_PER_WORD) {
memset(&BIT_WORD(b, from), 0xff, len / 8 ); memset(&BIT_WORD(b, from), 0xff, len / 8);
from += len; from += len;
len = len % BITS_PER_WORD; len = len % BITS_PER_WORD;
from -= len; from -= len;
} }
for ( ; len > 0 ; len-- ) { for (; len > 0; 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, uint64_t len) static inline void bit_clear_range(bitfield_p b, uint64_t from,
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++);
} }
if (len >= BITS_PER_WORD) { if (len >= BITS_PER_WORD) {
memset(&BIT_WORD(b, from), 0, len / 8 ); memset(&BIT_WORD(b, from), 0, len / 8);
from += len; from += len;
len = len % BITS_PER_WORD; len = len % BITS_PER_WORD;
from -= len; from -= len;
} }
for ( ; len > 0 ; len-- ) { for (; len > 0; len--) {
bit_clear( b, from++ ); bit_clear(b, from++);
} }
} }
/** Counts the number of contiguous bits in array ''b'', starting at ''from'' /** Counts the number of contiguous bits in array ''b'', starting at ''from''
@@ -96,52 +109,54 @@ static inline void bit_clear_range(bitfield_p b, uint64_t from, uint64_t len)
* 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, uint64_t len, int *run_is_set) { static inline uint64_t bit_run_count(bitfield_p b, uint64_t from,
uint64_t count = 0; uint64_t len, int *run_is_set)
int first_value = bit_get(b, from); {
bitfield_word_t word_match = first_value ? -1 : 0; uint64_t count = 0;
int first_value = bit_get(b, from);
bitfield_word_t word_match = first_value ? -1 : 0;
if ( run_is_set != NULL ) { if (run_is_set != NULL) {
*run_is_set = first_value; *run_is_set = first_value;
}
for (; ((from + count) % BITS_PER_WORD) != 0 && len > 0; len--) {
if (bit_has_value(b, from + count, first_value)) {
count++;
} else {
return count;
} }
}
for ( ; ((from + count) % BITS_PER_WORD) != 0 && len > 0; len--) { for (; len >= BITS_PER_WORD; len -= BITS_PER_WORD) {
if (bit_has_value(b, from + count, first_value)) { if (BIT_WORD(b, from + count) == word_match) {
count++; count += BITS_PER_WORD;
} else { } else {
return count; break;
}
} }
}
for ( ; len >= BITS_PER_WORD ; len -= BITS_PER_WORD ) { for (; len > 0; len--) {
if (BIT_WORD(b, from + count) == word_match) { if (bit_has_value(b, from + count, first_value)) {
count += BITS_PER_WORD; count++;
} else {
break;
}
} }
}
for ( ; len > 0; len-- ) { return count;
if ( bit_has_value(b, from + count, first_value) ) {
count++;
}
}
return count;
} }
enum bitset_stream_events { enum bitset_stream_events {
BITSET_STREAM_UNSET = 0, BITSET_STREAM_UNSET = 0,
BITSET_STREAM_SET = 1, BITSET_STREAM_SET = 1,
BITSET_STREAM_ON = 2, BITSET_STREAM_ON = 2,
BITSET_STREAM_OFF = 3 BITSET_STREAM_OFF = 3
}; };
#define BITSET_STREAM_EVENTS_ENUM_SIZE 4 #define BITSET_STREAM_EVENTS_ENUM_SIZE 4
struct bitset_stream_entry { struct bitset_stream_entry {
enum bitset_stream_events event; enum bitset_stream_events event;
uint64_t from; uint64_t from;
uint64_t len; uint64_t len;
}; };
/** Limit the stream size to 1MB for now. /** Limit the stream size to 1MB for now.
@@ -152,14 +167,14 @@ struct bitset_stream_entry {
#define BITSET_STREAM_SIZE ( ( 1024 * 1024 ) / sizeof( struct bitset_stream_entry ) ) #define BITSET_STREAM_SIZE ( ( 1024 * 1024 ) / sizeof( struct bitset_stream_entry ) )
struct bitset_stream { struct bitset_stream {
struct bitset_stream_entry entries[BITSET_STREAM_SIZE]; struct bitset_stream_entry entries[BITSET_STREAM_SIZE];
int in; int in;
int out; int out;
int size; int size;
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t cond_not_full; pthread_cond_t cond_not_full;
pthread_cond_t cond_not_empty; pthread_cond_t cond_not_empty;
uint64_t queued_bytes[BITSET_STREAM_EVENTS_ENUM_SIZE]; uint64_t queued_bytes[BITSET_STREAM_EVENTS_ENUM_SIZE];
}; };
@@ -169,47 +184,49 @@ struct bitset_stream {
* written reliably by multiple threads. * written reliably by multiple threads.
*/ */
struct bitset { struct bitset {
pthread_mutex_t lock; pthread_mutex_t lock;
uint64_t size; uint64_t size;
int resolution; int resolution;
struct bitset_stream *stream; struct bitset_stream *stream;
int stream_enabled; int stream_enabled;
bitfield_word_t bits[]; bitfield_word_t bits[];
}; };
/** Allocate a bitset for a file of the given size, and chunks of the /** Allocate a bitset for a file of the given size, and chunks of the
* given resolution. * given resolution.
*/ */
static inline struct bitset *bitset_alloc( uint64_t size, int resolution ) 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 - 1 ) / resolution)) * sizeof( bitfield_word_t ); BIT_WORDS_FOR_SIZE(((size + resolution -
struct bitset *bitset = xmalloc(sizeof( struct bitset ) + ( bitfield_size / 8 ) ); 1) / resolution)) * sizeof(bitfield_word_t);
struct bitset *bitset =
xmalloc(sizeof(struct bitset) + (bitfield_size / 8));
bitset->size = size; bitset->size = size;
bitset->resolution = resolution; bitset->resolution = resolution;
/* don't actually need to call pthread_mutex_destroy '*/ /* don't actually need to call pthread_mutex_destroy ' */
pthread_mutex_init(&bitset->lock, NULL); pthread_mutex_init(&bitset->lock, NULL);
bitset->stream = xmalloc( sizeof( struct bitset_stream ) ); bitset->stream = xmalloc(sizeof(struct bitset_stream));
pthread_mutex_init( &bitset->stream->mutex, NULL ); pthread_mutex_init(&bitset->stream->mutex, NULL);
/* Technically don't need to call pthread_cond_destroy either */ /* Technically don't need to call pthread_cond_destroy either */
pthread_cond_init( &bitset->stream->cond_not_full, NULL ); pthread_cond_init(&bitset->stream->cond_not_full, NULL);
pthread_cond_init( &bitset->stream->cond_not_empty, NULL ); pthread_cond_init(&bitset->stream->cond_not_empty, NULL);
return bitset; return bitset;
} }
static inline void bitset_free( struct bitset * set ) static inline void bitset_free(struct bitset *set)
{ {
/* TODO: free our mutex... */ /* TODO: free our mutex... */
free( set->stream ); free(set->stream);
set->stream = NULL; set->stream = NULL;
free( set ); free(set);
} }
#define INT_FIRST_AND_LAST \ #define INT_FIRST_AND_LAST \
@@ -224,215 +241,201 @@ 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( static inline void bitset_stream_enqueue(struct bitset *set,
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;
pthread_mutex_lock( &stream->mutex ); pthread_mutex_lock(&stream->mutex);
while ( stream->size == BITSET_STREAM_SIZE ) { while (stream->size == BITSET_STREAM_SIZE) {
pthread_cond_wait( &stream->cond_not_full, &stream->mutex ); pthread_cond_wait(&stream->cond_not_full, &stream->mutex);
} }
stream->entries[stream->in].event = event; stream->entries[stream->in].event = event;
stream->entries[stream->in].from = from; stream->entries[stream->in].from = from;
stream->entries[stream->in].len = len; stream->entries[stream->in].len = len;
stream->queued_bytes[event] += len; stream->queued_bytes[event] += len;
stream->size++; stream->size++;
stream->in++; stream->in++;
stream->in %= BITSET_STREAM_SIZE; stream->in %= BITSET_STREAM_SIZE;
pthread_mutex_unlock( & stream->mutex ); pthread_mutex_unlock(&stream->mutex);
pthread_cond_signal( &stream->cond_not_empty ); pthread_cond_signal(&stream->cond_not_empty);
return; return;
} }
static inline void bitset_stream_dequeue( static inline void bitset_stream_dequeue(struct bitset *set,
struct bitset * set, struct bitset_stream_entry *out)
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;
pthread_mutex_lock( &stream->mutex ); pthread_mutex_lock(&stream->mutex);
while ( stream->size == 0 ) { while (stream->size == 0) {
pthread_cond_wait( &stream->cond_not_empty, &stream->mutex ); pthread_cond_wait(&stream->cond_not_empty, &stream->mutex);
} }
dequeued = &stream->entries[stream->out]; dequeued = &stream->entries[stream->out];
if ( out != NULL ) { if (out != NULL) {
out->event = dequeued->event; out->event = dequeued->event;
out->from = dequeued->from; out->from = dequeued->from;
out->len = dequeued->len; out->len = dequeued->len;
} }
stream->queued_bytes[dequeued->event] -= dequeued->len; stream->queued_bytes[dequeued->event] -= dequeued->len;
stream->size--; stream->size--;
stream->out++; stream->out++;
stream->out %= BITSET_STREAM_SIZE; stream->out %= BITSET_STREAM_SIZE;
pthread_mutex_unlock( &stream->mutex ); pthread_mutex_unlock(&stream->mutex);
pthread_cond_signal( &stream->cond_not_full ); pthread_cond_signal(&stream->cond_not_full);
return; return;
} }
static inline size_t bitset_stream_size( struct bitset * set ) static inline size_t bitset_stream_size(struct bitset *set)
{ {
size_t size; size_t size;
pthread_mutex_lock( &set->stream->mutex ); pthread_mutex_lock(&set->stream->mutex);
size = set->stream->size; size = set->stream->size;
pthread_mutex_unlock( &set->stream->mutex ); pthread_mutex_unlock(&set->stream->mutex);
return size; return size;
} }
static inline uint64_t bitset_stream_queued_bytes( static inline uint64_t bitset_stream_queued_bytes(struct bitset *set,
struct bitset * set, enum bitset_stream_events
enum bitset_stream_events event event)
)
{ {
uint64_t total; uint64_t total;
pthread_mutex_lock( &set->stream->mutex ); pthread_mutex_lock(&set->stream->mutex);
total = set->stream->queued_bytes[event]; total = set->stream->queued_bytes[event];
pthread_mutex_unlock( &set->stream->mutex ); pthread_mutex_unlock(&set->stream->mutex);
return total; return total;
} }
static inline void bitset_enable_stream( struct bitset * set ) static inline void bitset_enable_stream(struct bitset *set)
{ {
BITSET_LOCK; BITSET_LOCK;
set->stream_enabled = 1; set->stream_enabled = 1;
bitset_stream_enqueue( set, BITSET_STREAM_ON, 0, set->size ); bitset_stream_enqueue(set, BITSET_STREAM_ON, 0, set->size);
BITSET_UNLOCK; BITSET_UNLOCK;
} }
static inline void bitset_disable_stream( struct bitset * set ) static inline void bitset_disable_stream(struct bitset *set)
{ {
BITSET_LOCK; BITSET_LOCK;
bitset_stream_enqueue( set, BITSET_STREAM_OFF, 0, set->size ); bitset_stream_enqueue(set, BITSET_STREAM_OFF, 0, set->size);
set->stream_enabled = 0; set->stream_enabled = 0;
BITSET_UNLOCK; BITSET_UNLOCK;
} }
/** 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( static inline void bitset_set_range(struct bitset *set,
struct bitset * set, uint64_t from, uint64_t len)
uint64_t from,
uint64_t len)
{ {
INT_FIRST_AND_LAST; INT_FIRST_AND_LAST;
BITSET_LOCK; BITSET_LOCK;
bit_set_range(set->bits, first, bitlen); bit_set_range(set->bits, first, bitlen);
if ( set->stream_enabled ) { if (set->stream_enabled) {
bitset_stream_enqueue( set, BITSET_STREAM_SET, from, len ); bitset_stream_enqueue(set, BITSET_STREAM_SET, from, len);
} }
BITSET_UNLOCK; BITSET_UNLOCK;
} }
/** Set every bit in the bitset. */ /** Set every bit in the bitset. */
static inline void bitset_set( struct bitset * set ) static inline void bitset_set(struct bitset *set)
{ {
bitset_set_range(set, 0, set->size); bitset_set_range(set, 0, set->size);
} }
/** 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( static inline void bitset_clear_range(struct bitset *set,
struct bitset * set, uint64_t from, uint64_t len)
uint64_t from,
uint64_t len)
{ {
INT_FIRST_AND_LAST; INT_FIRST_AND_LAST;
BITSET_LOCK; BITSET_LOCK;
bit_clear_range(set->bits, first, bitlen); bit_clear_range(set->bits, first, bitlen);
if ( set->stream_enabled ) { if (set->stream_enabled) {
bitset_stream_enqueue( set, BITSET_STREAM_UNSET, from, len ); bitset_stream_enqueue(set, BITSET_STREAM_UNSET, from, len);
} }
BITSET_UNLOCK; BITSET_UNLOCK;
} }
/** Clear every bit in the bitset. */ /** Clear every bit in the bitset. */
static inline void bitset_clear( struct bitset * set ) static inline void bitset_clear(struct bitset *set)
{ {
bitset_clear_range(set, 0, set->size); bitset_clear_range(set, 0, set->size);
} }
/** 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( static inline uint64_t bitset_run_count_ex(struct bitset *set,
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;
/* Clip our requests to the end of the bitset, avoiding uint underflow. */ /* Clip our requests to the end of the bitset, avoiding uint underflow. */
if ( from > set->size ) { if (from > set->size) {
return 0; return 0;
} }
len = ( len + from ) > set->size ? ( set->size - from ) : len; len = (len + from) > set->size ? (set->size - from) : len;
INT_FIRST_AND_LAST; INT_FIRST_AND_LAST;
BITSET_LOCK; BITSET_LOCK;
run = bit_run_count(set->bits, first, bitlen, run_is_set) * set->resolution; run =
run -= (from % set->resolution); bit_run_count(set->bits, first, bitlen,
BITSET_UNLOCK; run_is_set) * set->resolution;
run -= (from % set->resolution);
BITSET_UNLOCK;
return run; return run;
} }
/** 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( static inline uint64_t bitset_run_count(struct bitset *set,
struct bitset * set, uint64_t from, uint64_t len)
uint64_t from,
uint64_t len)
{ {
return bitset_run_count_ex( set, from, len, NULL ); return bitset_run_count_ex(set, from, len, NULL);
} }
/** Tests whether the bit field is clear for the given file offset. /** Tests whether the bit field is clear for the given file offset.
*/ */
static inline int bitset_is_clear_at( struct bitset * set, uint64_t at ) static inline int bitset_is_clear_at(struct bitset *set, uint64_t at)
{ {
return bit_is_clear(set->bits, at/set->resolution); return bit_is_clear(set->bits, at / set->resolution);
} }
/** Tests whether the bit field is set for the given file offset. /** Tests whether the bit field is set for the given file offset.
*/ */
static inline int bitset_is_set_at( struct bitset * set, uint64_t at ) static inline int bitset_is_set_at(struct bitset *set, uint64_t at)
{ {
return bit_is_set(set->bits, at/set->resolution); return bit_is_set(set->bits, at / set->resolution);
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#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.
@@ -18,39 +19,40 @@
struct client { struct client {
/* When we call pthread_join, if the thread is already dead /* When we call pthread_join, if the thread is already dead
* we can get an ESRCH. Since we have no other way to tell * we can get an ESRCH. Since we have no other way to tell
* if that ESRCH is from a dead thread or a thread that never * if that ESRCH is from a dead thread or a thread that never
* existed, we use a `stopped` flag to indicate a thread which * existed, we use a `stopped` flag to indicate a thread which
* did exist, but went away. Only check this after a * did exist, but went away. Only check this after a
* pthread_join call. * pthread_join call.
*/ */
int stopped; int stopped;
int socket; int socket;
int fileno; int fileno;
char* mapped; char *mapped;
struct self_pipe * stop_signal; uint64_t mapped_size;
struct server* serve; /* FIXME: remove above duplication */ struct self_pipe *stop_signal;
/* Have we seen a REQUEST_DISCONNECT message? */ struct server *serve; /* FIXME: remove above duplication */
int disconnect;
/* kill the whole server if a request has been outstanding too long, /* Have we seen a REQUEST_DISCONNECT message? */
* assuming use_killswitch is set in serve int disconnect;
*/
timer_t killswitch; /* kill the whole server if a request has been outstanding too long,
* assuming use_killswitch is set in serve
*/
timer_t killswitch;
}; };
void client_killswitch_hit(int signal, siginfo_t *info, void *ptr); void client_killswitch_hit(int signal, siginfo_t * info, void *ptr);
void* client_serve(void* client_uncast); void *client_serve(void *client_uncast);
struct client * client_create( struct server * serve, int socket ); struct client *client_create(struct server *serve, int socket);
void client_destroy( struct client * client ); void client_destroy(struct client *client);
void client_signal_stop( struct client * client ); void client_signal_stop(struct client *client);
#endif #endif

View File

@@ -44,590 +44,570 @@
#include <unistd.h> #include <unistd.h>
struct control * control_create( struct control *control_create(struct flexnbd *flexnbd, const char *csn)
struct flexnbd * flexnbd,
const char * csn)
{ {
struct control * control = xmalloc( sizeof( struct control ) ); struct control *control = xmalloc(sizeof(struct control));
NULLCHECK( csn ); NULLCHECK(csn);
control->flexnbd = flexnbd; control->flexnbd = flexnbd;
control->socket_name = csn; control->socket_name = csn;
control->open_signal = self_pipe_create(); control->open_signal = self_pipe_create();
control->close_signal = self_pipe_create(); control->close_signal = self_pipe_create();
control->mirror_state_mbox = mbox_create(); control->mirror_state_mbox = mbox_create();
return control; return control;
} }
void control_signal_close( struct control * control) void control_signal_close(struct control *control)
{ {
NULLCHECK( control ); NULLCHECK(control);
self_pipe_signal( control->close_signal ); self_pipe_signal(control->close_signal);
} }
void control_destroy( struct control * control ) void control_destroy(struct control *control)
{ {
NULLCHECK( control ); NULLCHECK(control);
mbox_destroy( control->mirror_state_mbox ); mbox_destroy(control->mirror_state_mbox);
self_pipe_destroy( control->close_signal ); self_pipe_destroy(control->close_signal);
self_pipe_destroy( control->open_signal ); self_pipe_destroy(control->open_signal);
free( control ); free(control);
} }
struct control_client * control_client_create( struct control_client *control_client_create(struct flexnbd *flexnbd,
struct flexnbd * flexnbd, int client_fd,
int client_fd , struct mbox *state_mbox)
struct mbox * state_mbox )
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
struct control_client * control_client = struct control_client *control_client =
xmalloc( sizeof( struct control_client ) ); xmalloc(sizeof(struct control_client));
control_client->socket = client_fd; control_client->socket = client_fd;
control_client->flexnbd = flexnbd; control_client->flexnbd = flexnbd;
control_client->mirror_state_mbox = state_mbox; control_client->mirror_state_mbox = state_mbox;
return control_client; return control_client;
} }
void control_client_destroy( struct control_client * client ) void control_client_destroy(struct control_client *client)
{ {
NULLCHECK( client ); NULLCHECK(client);
free( client ); free(client);
} }
void control_respond(struct control_client * client); void control_respond(struct control_client *client);
void control_handle_client( struct control * control, int client_fd ) 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_client_create(control->flexnbd,
control->flexnbd, client_fd,
client_fd , control->mirror_state_mbox);
control->mirror_state_mbox);
/* We intentionally don't spawn a thread for the client here. /* We intentionally don't spawn a thread for the client here.
* This is to avoid having more than one thread potentially * This is to avoid having more than one thread potentially
* waiting on the migration commit status. * waiting on the migration commit status.
*/ */
control_respond( control_client ); control_respond(control_client);
} }
void control_accept_client( struct control * control ) void control_accept_client(struct control *control)
{ {
int client_fd; int client_fd;
union mysockaddr client_address; union mysockaddr client_address;
socklen_t addrlen = sizeof( union mysockaddr ); socklen_t addrlen = sizeof(union mysockaddr);
client_fd = accept( control->control_fd, &client_address.generic, &addrlen ); client_fd =
FATAL_IF( -1 == client_fd, "control accept failed" ); accept(control->control_fd, &client_address.generic, &addrlen);
FATAL_IF(-1 == client_fd, "control accept failed");
control_handle_client( control, client_fd ); control_handle_client(control, client_fd);
} }
int control_accept( struct control * control ) int control_accept(struct control *control)
{ {
NULLCHECK( control ); NULLCHECK(control);
fd_set fds; fd_set fds;
FD_ZERO( &fds ); FD_ZERO(&fds);
FD_SET( control->control_fd, &fds ); FD_SET(control->control_fd, &fds);
self_pipe_fd_set( control->close_signal, &fds ); self_pipe_fd_set(control->close_signal, &fds);
debug("Control thread selecting"); debug("Control thread selecting");
FATAL_UNLESS( 0 < select( FD_SETSIZE, &fds, NULL, NULL, NULL ), FATAL_UNLESS(0 < select(FD_SETSIZE, &fds, NULL, NULL, NULL),
"Control select failed." ); "Control select failed.");
if ( self_pipe_fd_isset( control->close_signal, &fds ) ){ if (self_pipe_fd_isset(control->close_signal, &fds)) {
return 0; return 0;
} }
if ( FD_ISSET( control->control_fd, &fds ) ) { if (FD_ISSET(control->control_fd, &fds)) {
control_accept_client( control ); control_accept_client(control);
} }
return 1; return 1;
} }
void control_accept_loop( struct control * control ) void control_accept_loop(struct control *control)
{ {
while( control_accept( control ) ); while (control_accept(control));
} }
int open_control_socket( const char * socket_name ) int open_control_socket(const char *socket_name)
{ {
struct sockaddr_un bind_address; struct sockaddr_un bind_address;
int control_fd; int control_fd;
if (!socket_name) { if (!socket_name) {
fatal( "Tried to open a control socket without a socket name" ); fatal("Tried to open a control socket without a socket name");
} }
control_fd = socket(AF_UNIX, SOCK_STREAM, 0); control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
FATAL_IF_NEGATIVE(control_fd , FATAL_IF_NEGATIVE(control_fd, "Couldn't create control socket");
"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, sizeof(bind_address.sun_path)-1); strncpy(bind_address.sun_path, socket_name,
sizeof(bind_address.sun_path) - 1);
//unlink(socket_name); /* ignore failure */ //unlink(socket_name); /* ignore failure */
FATAL_IF_NEGATIVE( FATAL_IF_NEGATIVE(bind
bind(control_fd , &bind_address, sizeof(bind_address)), (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( FATAL_IF_NEGATIVE(listen(control_fd, 5),
listen(control_fd , 5), "Couldn't listen on control socket");
"Couldn't listen on control socket" return control_fd;
);
return control_fd;
} }
void control_listen(struct control* control) void control_listen(struct control *control)
{ {
NULLCHECK( control ); NULLCHECK(control);
control->control_fd = open_control_socket( control->socket_name ); control->control_fd = open_control_socket(control->socket_name);
} }
void control_wait_for_open_signal( struct control * control ) void control_wait_for_open_signal(struct control *control)
{ {
fd_set fds; fd_set fds;
FD_ZERO( &fds ); FD_ZERO(&fds);
self_pipe_fd_set( control->open_signal, &fds ); self_pipe_fd_set(control->open_signal, &fds);
FATAL_IF_NEGATIVE( select( FD_SETSIZE, &fds, NULL, NULL, NULL ), FATAL_IF_NEGATIVE(select(FD_SETSIZE, &fds, NULL, NULL, NULL),
"select() failed" ); "select() failed");
self_pipe_signal_clear( control->open_signal ); self_pipe_signal_clear(control->open_signal);
} }
void control_serve( struct control * control ) void control_serve(struct control *control)
{ {
NULLCHECK( control ); NULLCHECK(control);
control_wait_for_open_signal( control ); control_wait_for_open_signal(control);
control_listen( control ); control_listen(control);
while( control_accept( control ) ); while (control_accept(control));
} }
void control_cleanup( void control_cleanup(struct control *control,
struct control * control, int fatal __attribute__ ((unused)))
int fatal __attribute__((unused)) )
{ {
NULLCHECK( control ); NULLCHECK(control);
unlink( control->socket_name ); unlink(control->socket_name);
close( control->control_fd ); close(control->control_fd);
} }
void * control_runner( void * control_uncast ) void *control_runner(void *control_uncast)
{ {
debug("Control thread"); debug("Control thread");
NULLCHECK( control_uncast ); NULLCHECK(control_uncast);
struct control * control = (struct control *)control_uncast; struct control *control = (struct control *) control_uncast;
error_set_handler( (cleanup_handler*)control_cleanup, control ); error_set_handler((cleanup_handler *) control_cleanup, control);
control_serve( control ); control_serve(control);
control_cleanup( control, 0 ); control_cleanup(control, 0);
pthread_exit( NULL ); pthread_exit(NULL);
} }
#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, int client_fd ) void control_write_mirror_response(enum mirror_state mirror_state,
int client_fd)
{ {
switch (mirror_state) { switch (mirror_state) {
case MS_INIT: case MS_INIT:
case MS_UNKNOWN: case MS_UNKNOWN:
write_socket( "1: Mirror failed to initialise" ); write_socket("1: Mirror failed to initialise");
fatal( "Impossible mirror state: %d", mirror_state ); fatal("Impossible mirror state: %d", mirror_state);
case MS_FAIL_CONNECT: case MS_FAIL_CONNECT:
write_socket( "1: Mirror failed to connect"); write_socket("1: Mirror failed to connect");
break; break;
case MS_FAIL_REJECTED: case MS_FAIL_REJECTED:
write_socket( "1: Mirror was rejected" ); write_socket("1: Mirror was rejected");
break; break;
case MS_FAIL_NO_HELLO: case MS_FAIL_NO_HELLO:
write_socket( "1: Remote server failed to respond"); write_socket("1: Remote server failed to respond");
break; break;
case MS_FAIL_SIZE_MISMATCH: case MS_FAIL_SIZE_MISMATCH:
write_socket( "1: Remote size does not match local size" ); write_socket("1: Remote size does not match local size");
break; break;
case MS_ABANDONED: case MS_ABANDONED:
write_socket( "1: Mirroring abandoned" ); write_socket("1: Mirroring abandoned");
break; break;
case MS_GO: case MS_GO:
case MS_DONE: /* Yes, I know we know better, but it's simpler this way */ case MS_DONE: /* Yes, I know we know better, but it's simpler this way */
write_socket( "0: Mirror started" ); write_socket("0: Mirror started");
break; break;
default: default:
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( enum mirror_state control_client_mirror_wait(struct control_client *client)
struct control_client* client)
{ {
NULLCHECK( client ); NULLCHECK(client);
NULLCHECK( client->mirror_state_mbox ); NULLCHECK(client->mirror_state_mbox);
struct mbox * mbox = client->mirror_state_mbox; struct mbox *mbox = client->mirror_state_mbox;
enum mirror_state mirror_state; enum mirror_state mirror_state;
enum mirror_state * contents; enum mirror_state *contents;
contents = (enum mirror_state*)mbox_receive( mbox ); contents = (enum mirror_state *) mbox_receive(mbox);
NULLCHECK( contents ); NULLCHECK(contents);
mirror_state = *contents; mirror_state = *contents;
free( contents ); free(contents);
return mirror_state; return mirror_state;
} }
#define write_socket(msg) write(client->socket, (msg "\n"), strlen((msg))+1) #define write_socket(msg) write(client->socket, (msg "\n"), strlen((msg))+1)
/** Command parser to start mirror process from socket input */ /** Command parser to start mirror process from socket input */
int control_mirror(struct control_client* client, int linesc, char** lines) int control_mirror(struct control_client *client, int linesc, char **lines)
{ {
NULLCHECK( client ); NULLCHECK(client);
struct flexnbd * flexnbd = client->flexnbd; struct flexnbd *flexnbd = client->flexnbd;
union mysockaddr *connect_to = xmalloc( sizeof( union mysockaddr ) ); union mysockaddr *connect_to = xmalloc(sizeof(union mysockaddr));
union mysockaddr *connect_from = NULL; union mysockaddr *connect_from = NULL;
uint64_t max_Bps = UINT64_MAX; uint64_t max_Bps = UINT64_MAX;
int action_at_finish; int action_at_finish;
int raw_port; int raw_port;
if (linesc < 2) { if (linesc < 2) {
write_socket("1: mirror takes at least two parameters"); write_socket("1: mirror takes at least two parameters");
return -1; return -1;
}
if (parse_ip_to_sockaddr(&connect_to->generic, lines[0]) == 0) {
write_socket("1: bad IP address");
return -1;
}
raw_port = atoi(lines[1]);
if (raw_port < 0 || raw_port > 65535) {
write_socket("1: bad IP port number");
return -1;
}
connect_to->v4.sin_port = htobe16(raw_port);
action_at_finish = ACTION_EXIT;
if (linesc > 2) {
if (strcmp("exit", lines[2]) == 0) {
action_at_finish = ACTION_EXIT;
} else if (strcmp("unlink", lines[2]) == 0) {
action_at_finish = ACTION_UNLINK;
} else if (strcmp("nothing", lines[2]) == 0) {
action_at_finish = ACTION_NOTHING;
} else {
write_socket("1: action must be 'exit' or 'nothing'");
return -1;
}
}
if (linesc > 3) {
connect_from = xmalloc(sizeof(union mysockaddr));
if (parse_ip_to_sockaddr(&connect_from->generic, lines[3]) == 0) {
write_socket("1: bad bind address");
return -1;
}
}
if (linesc > 4) {
errno = 0;
max_Bps = strtoull(lines[4], NULL, 10);
if (errno == ERANGE) {
write_socket("1: max_bps out of range");
return -1;
} else if (errno != 0) {
write_socket("1: max_bps couldn't be parsed");
return -1;
}
}
if (linesc > 5) {
write_socket("1: unrecognised parameters to mirror");
return -1;
}
struct server *serve = flexnbd_server(flexnbd);
server_lock_start_mirror(serve);
{
if (server_mirror_can_start(serve)) {
serve->mirror_super = mirror_super_create(serve->filename,
connect_to,
connect_from,
max_Bps,
action_at_finish,
client->
mirror_state_mbox);
serve->mirror = serve->mirror_super->mirror;
server_prevent_mirror_start(serve);
} else {
if (serve->mirror_super) {
warn("Tried to start a second mirror run");
write_socket("1: mirror already running");
} else {
warn("Cannot start mirroring, shutting down");
write_socket("1: shutting down");
}
} }
if (parse_ip_to_sockaddr(&connect_to->generic, lines[0]) == 0) { }
write_socket("1: bad IP address"); server_unlock_start_mirror(serve);
return -1;
}
raw_port = atoi(lines[1]); /* Do this outside the lock to minimise the length of time the
if (raw_port < 0 || raw_port > 65535) { * sighandler can block the serve thread
write_socket("1: bad IP port number"); */
return -1; if (serve->mirror_super) {
} FATAL_IF(0 != pthread_create(&serve->mirror_super->thread,
connect_to->v4.sin_port = htobe16(raw_port); NULL,
mirror_super_runner,
serve),
"Failed to create mirror thread");
action_at_finish = ACTION_EXIT; debug("Control thread mirror super waiting");
if (linesc > 2) { enum mirror_state state = control_client_mirror_wait(client);
if (strcmp("exit", lines[2]) == 0) { debug("Control thread writing response");
action_at_finish = ACTION_EXIT; control_write_mirror_response(state, client->socket);
} }
else if (strcmp( "unlink", lines[2]) == 0 ) {
action_at_finish = ACTION_UNLINK;
}
else if (strcmp("nothing", lines[2]) == 0) {
action_at_finish = ACTION_NOTHING;
}
else {
write_socket("1: action must be 'exit' or 'nothing'");
return -1;
}
}
if (linesc > 3) { debug("Control thread going away.");
connect_from = xmalloc( sizeof( union mysockaddr ) );
if (parse_ip_to_sockaddr(&connect_from->generic, lines[3]) == 0) {
write_socket("1: bad bind address");
return -1;
}
}
if (linesc > 4) { return 0;
errno = 0;
max_Bps = strtoull( lines[4], NULL, 10 );
if ( errno == ERANGE ) {
write_socket( "1: max_bps out of range" );
return -1;
} else if ( errno != 0 ) {
write_socket( "1: max_bps couldn't be parsed" );
return -1;
}
}
if (linesc > 5) {
write_socket("1: unrecognised parameters to mirror");
return -1;
}
struct server * serve = flexnbd_server(flexnbd);
server_lock_start_mirror( serve );
{
if ( server_mirror_can_start( serve ) ) {
serve->mirror_super = mirror_super_create(
serve->filename,
connect_to,
connect_from,
max_Bps ,
action_at_finish,
client->mirror_state_mbox );
serve->mirror = serve->mirror_super->mirror;
server_prevent_mirror_start( serve );
} else {
if ( serve->mirror_super ) {
warn( "Tried to start a second mirror run" );
write_socket( "1: mirror already running" );
} else {
warn( "Cannot start mirroring, shutting down" );
write_socket( "1: shutting down" );
}
}
}
server_unlock_start_mirror( serve );
/* Do this outside the lock to minimise the length of time the
* sighandler can block the serve thread
*/
if ( serve->mirror_super ) {
FATAL_IF( 0 != pthread_create(
&serve->mirror_super->thread,
NULL,
mirror_super_runner,
serve
),
"Failed to create mirror thread"
);
debug("Control thread mirror super waiting");
enum mirror_state state =
control_client_mirror_wait( client );
debug("Control thread writing response");
control_write_mirror_response( state, client->socket );
}
debug( "Control thread going away." );
return 0;
} }
int control_mirror_max_bps( struct control_client* client, int linesc, char** lines ) int control_mirror_max_bps(struct control_client *client, int linesc,
char **lines)
{ {
NULLCHECK( client ); NULLCHECK(client);
NULLCHECK( client->flexnbd ); NULLCHECK(client->flexnbd);
struct server* serve = flexnbd_server( client->flexnbd ); struct server *serve = flexnbd_server(client->flexnbd);
uint64_t max_Bps; uint64_t max_Bps;
if ( !serve->mirror_super ) { if (!serve->mirror_super) {
write_socket( "1: Not currently mirroring" ); write_socket("1: Not currently mirroring");
return -1; return -1;
} }
if ( linesc != 1 ) { if (linesc != 1) {
write_socket( "1: Bad format" ); write_socket("1: Bad format");
return -1; return -1;
} }
errno = 0; errno = 0;
max_Bps = strtoull( lines[0], NULL, 10 ); max_Bps = strtoull(lines[0], NULL, 10);
if ( errno == ERANGE ) { if (errno == ERANGE) {
write_socket( "1: max_bps out of range" ); write_socket("1: max_bps out of range");
return -1; return -1;
} else if ( errno != 0 ) { } else if (errno != 0) {
write_socket( "1: max_bps couldn't be parsed" ); write_socket("1: max_bps couldn't be parsed");
return -1; return -1;
} }
serve->mirror->max_bytes_per_second = max_Bps; serve->mirror->max_bytes_per_second = max_Bps;
write_socket( "0: updated" ); write_socket("0: updated");
return 0; return 0;
} }
#undef write_socket #undef write_socket
/** Command parser to alter access control list from socket input */ /** Command parser to alter access control list from socket input */
int control_acl(struct control_client* client, int linesc, char** lines) int control_acl(struct control_client *client, int linesc, char **lines)
{ {
NULLCHECK( client ); NULLCHECK(client);
NULLCHECK( client->flexnbd ); NULLCHECK(client->flexnbd);
struct flexnbd * flexnbd = client->flexnbd; struct flexnbd *flexnbd = client->flexnbd;
int default_deny = flexnbd_default_deny( flexnbd ); int default_deny = flexnbd_default_deny(flexnbd);
struct acl * new_acl = acl_create( linesc, lines, default_deny ); struct acl *new_acl = acl_create(linesc, lines, default_deny);
if (new_acl->len != linesc) { if (new_acl->len != linesc) {
warn("Bad ACL spec: %s", lines[new_acl->len] ); warn("Bad ACL spec: %s", lines[new_acl->len]);
write(client->socket, "1: bad spec: ", 13); write(client->socket, "1: bad spec: ", 13);
write(client->socket, lines[new_acl->len], write(client->socket, lines[new_acl->len],
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); }
}
return 0; return 0;
} }
int control_break( int control_break(struct control_client *client,
struct control_client* client, int linesc __attribute__ ((unused)),
int linesc __attribute__ ((unused)), char **lines __attribute__ ((unused))
char** lines __attribute__((unused)) )
)
{ {
NULLCHECK( client ); NULLCHECK(client);
NULLCHECK( client->flexnbd ); NULLCHECK(client->flexnbd);
int result = 0; int result = 0;
struct flexnbd* flexnbd = client->flexnbd; struct flexnbd *flexnbd = client->flexnbd;
struct server * serve = flexnbd_server( flexnbd ); struct server *serve = flexnbd_server(flexnbd);
server_lock_start_mirror( serve ); server_lock_start_mirror(serve);
{ {
if ( server_is_mirroring( serve ) ) { if (server_is_mirroring(serve)) {
info( "Signaling to abandon mirror" ); info("Signaling to abandon mirror");
server_abandon_mirror( serve ); server_abandon_mirror(serve);
debug( "Abandon signaled" ); debug("Abandon signaled");
if ( server_is_closed( serve ) ) { if (server_is_closed(serve)) {
info( "Mirror completed while canceling" ); info("Mirror completed while canceling");
write( client->socket, write(client->socket, "1: mirror completed\n", 20);
"1: mirror completed\n", 20 ); } else {
} info("Mirror successfully stopped.");
else { write(client->socket, "0: mirror stopped\n", 18);
info( "Mirror successfully stopped." ); result = 1;
write( client->socket, }
"0: mirror stopped\n", 18 );
result = 1;
}
} else { } else {
warn( "Not mirroring." ); warn("Not mirroring.");
write( client->socket, "1: not mirroring\n", 17 ); write(client->socket, "1: not mirroring\n", 17);
}
} }
server_unlock_start_mirror( serve ); }
server_unlock_start_mirror(serve);
return result; return result;
} }
/** FIXME: add some useful statistics */ /** FIXME: add some useful statistics */
int control_status( int control_status(struct control_client *client,
struct control_client* client, int linesc __attribute__ ((unused)),
int linesc __attribute__ ((unused)), char **lines __attribute__ ((unused))
char** lines __attribute__((unused)) )
)
{ {
NULLCHECK( client ); NULLCHECK(client);
NULLCHECK( client->flexnbd ); NULLCHECK(client->flexnbd);
struct status * status = flexnbd_status_create( client->flexnbd ); struct status *status = flexnbd_status_create(client->flexnbd);
write( client->socket, "0: ", 3 ); write(client->socket, "0: ", 3);
status_write( status, client->socket ); status_write(status, client->socket);
status_destroy( status ); status_destroy(status);
return 0; return 0;
} }
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) { close(client->socket); } if (client->socket) {
close(client->socket);
}
/* This is wrongness */ /* This is wrongness */
if ( server_acl_locked( client->flexnbd->serve ) ) { server_unlock_acl( client->flexnbd->serve ); } if (server_acl_locked(client->flexnbd->serve)) {
server_unlock_acl(client->flexnbd->serve);
}
control_client_destroy( client ); control_client_destroy(client);
} }
/** Master command parser for control socket connections, delegates quickly */ /** Master command parser for control socket connections, delegates quickly */
void control_respond(struct control_client * client) void control_respond(struct control_client *client)
{ {
char **lines = NULL; char **lines = NULL;
error_set_handler((cleanup_handler*) control_client_cleanup, client); error_set_handler((cleanup_handler *) control_client_cleanup, 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) {
info("acl command received");
if (control_acl(client, linesc - 1, lines + 1) < 0) {
debug("acl command failed");
} }
else if (strcmp(lines[0], "acl") == 0) { } else if (strcmp(lines[0], "mirror") == 0) {
info("acl command received" ); info("mirror command received");
if (control_acl(client, linesc-1, lines+1) < 0) { if (control_mirror(client, linesc - 1, lines + 1) < 0) {
debug("acl command failed"); debug("mirror command failed");
}
} }
else if (strcmp(lines[0], "mirror") == 0) { } else if (strcmp(lines[0], "break") == 0) {
info("mirror command received" ); info("break command received");
if (control_mirror(client, linesc-1, lines+1) < 0) { if (control_break(client, linesc - 1, lines + 1) < 0) {
debug("mirror command failed"); debug("break command failed");
}
} }
else if (strcmp(lines[0], "break") == 0) { } else if (strcmp(lines[0], "status") == 0) {
info( "break command received" ); info("status command received");
if ( control_break( client, linesc-1, lines+1) < 0) { if (control_status(client, linesc - 1, lines + 1) < 0) {
debug( "break command failed" ); debug("status command failed");
}
} }
else if (strcmp(lines[0], "status") == 0) { } else if (strcmp(lines[0], "mirror_max_bps") == 0) {
info("status command received" ); info("mirror_max_bps command received");
if (control_status(client, linesc-1, lines+1) < 0) { if (control_mirror_max_bps(client, linesc - 1, lines + 1) < 0) {
debug("status command failed"); debug("mirror_max_bps command failed");
}
} else if ( strcmp( lines[0], "mirror_max_bps" ) == 0 ) {
info( "mirror_max_bps command received" );
if( control_mirror_max_bps( client, linesc-1, lines+1 ) < 0 ) {
debug( "mirror_max_bps command failed" );
}
}
else {
write(client->socket, "10: unknown command\n", 23);
} }
} else {
write(client->socket, "10: unknown command\n", 23);
}
for (i=0; i<linesc; i++) { for (i = 0; i < linesc; i++) {
free(lines[i]); free(lines[i]);
} }
free(lines); free(lines);
control_client_cleanup(client, 0); control_client_cleanup(client, 0);
debug("control command handled" ); debug("control command handled");
} }

View File

@@ -13,47 +13,46 @@ struct server;
#include "mbox.h" #include "mbox.h"
struct control { struct control {
struct flexnbd * flexnbd; struct flexnbd *flexnbd;
int control_fd; int control_fd;
const char * socket_name; const char *socket_name;
pthread_t thread; pthread_t thread;
struct self_pipe * open_signal; struct self_pipe *open_signal;
struct self_pipe * close_signal; struct self_pipe *close_signal;
/* This is owned by the control object, and used by a /* This is owned by the control object, and used by a
* mirror_super to communicate the state of a mirror attempt as * mirror_super to communicate the state of a mirror attempt as
* early as feasible. It can't be owned by the mirror_super * early as feasible. It can't be owned by the mirror_super
* object because the mirror_super object can be freed at any * object because the mirror_super object can be freed at any
* time (including while the control_client is waiting on it), * time (including while the control_client is waiting on it),
* whereas the control object lasts for the lifetime of the * whereas the control object lasts for the lifetime of the
* process (and we can only have a mirror thread if the control * process (and we can only have a mirror thread if the control
* thread has started it). * thread has started it).
*/ */
struct mbox * mirror_state_mbox; struct mbox *mirror_state_mbox;
}; };
struct control_client{ struct control_client {
int socket; int socket;
struct flexnbd * flexnbd; struct flexnbd *flexnbd;
/* Passed in on creation. We know it's all right to do this /* Passed in on creation. We know it's all right to do this
* because we know there's only ever one control_client. * because we know there's only ever one control_client.
*/ */
struct mbox * mirror_state_mbox; struct mbox *mirror_state_mbox;
}; };
struct control * control_create( struct control *control_create(struct flexnbd *,
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, union mysockaddr* client_address); void accept_control_connection(struct server *params, int client_fd,
void serve_open_control_socket(struct server* params); union mysockaddr *client_address);
void serve_open_control_socket(struct server *params);
#endif #endif

View File

@@ -43,223 +43,206 @@
int flexnbd_build_signal_fd(void) int flexnbd_build_signal_fd(void)
{ {
sigset_t mask; sigset_t mask;
int sfd; int sfd;
sigemptyset( &mask ); sigemptyset(&mask);
sigaddset( &mask, SIGTERM ); sigaddset(&mask, SIGTERM);
sigaddset( &mask, SIGQUIT ); sigaddset(&mask, SIGQUIT);
sigaddset( &mask, SIGINT ); sigaddset(&mask, SIGINT);
FATAL_UNLESS( 0 == pthread_sigmask( SIG_BLOCK, &mask, NULL ), FATAL_UNLESS(0 == pthread_sigmask(SIG_BLOCK, &mask, NULL),
"Signal blocking failed" ); "Signal blocking failed");
sfd = signalfd( -1, &mask, 0 ); sfd = signalfd(-1, &mask, 0);
FATAL_IF( -1 == sfd, "Failed to get a signal fd" ); FATAL_IF(-1 == sfd, "Failed to get a signal fd");
return sfd; return sfd;
} }
void flexnbd_create_shared( void flexnbd_create_shared(struct flexnbd *flexnbd,
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 = flexnbd->control = control_create(flexnbd, s_ctrl_sock);
control_create( flexnbd, s_ctrl_sock ); } else {
} flexnbd->control = NULL;
else { }
flexnbd->control = NULL;
}
flexnbd->signal_fd = flexnbd_build_signal_fd(); flexnbd->signal_fd = flexnbd_build_signal_fd();
} }
struct flexnbd * flexnbd_create_serving( struct flexnbd *flexnbd_create_serving(char *s_ip_address,
char* s_ip_address, char *s_port,
char* s_port, char *s_file,
char* s_file, char *s_ctrl_sock,
char* s_ctrl_sock, int default_deny,
int default_deny, 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 use_killswitch)
{ {
struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) ); struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd));
flexnbd->serve = server_create( flexnbd->serve = server_create(flexnbd,
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, flexnbd_create_shared(flexnbd, s_ctrl_sock);
use_killswitch,
1);
flexnbd_create_shared( flexnbd, s_ctrl_sock );
// Beats installing one handler per client instance // Beats installing one handler per client instance
if ( use_killswitch ) { if (use_killswitch) {
struct sigaction act = { struct sigaction act = {
.sa_sigaction = client_killswitch_hit, .sa_sigaction = client_killswitch_hit,
.sa_flags = SA_RESTART | SA_SIGINFO .sa_flags = SA_RESTART | SA_SIGINFO
}; };
FATAL_UNLESS( FATAL_UNLESS(0 == sigaction(CLIENT_KILLSWITCH_SIGNAL, &act, NULL),
0 == sigaction( CLIENT_KILLSWITCH_SIGNAL, &act, NULL ), "Installing client killswitch signal failed");
"Installing client killswitch signal failed" }
);
}
return flexnbd; return flexnbd;
} }
struct flexnbd * flexnbd_create_listening( struct flexnbd *flexnbd_create_listening(char *s_ip_address,
char* s_ip_address, char *s_port,
char* s_port, char *s_file,
char* s_file, char *s_ctrl_sock,
char* s_ctrl_sock, int default_deny,
int default_deny, int acl_entries,
int acl_entries, 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->serve = server_create(flexnbd,
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, flexnbd_create_shared(flexnbd, s_ctrl_sock);
s_acl_entries,
1, 0, 0);
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
// for a very long time. // for a very long time.
return flexnbd; return flexnbd;
} }
void flexnbd_spawn_control(struct flexnbd * flexnbd ) void flexnbd_spawn_control(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
NULLCHECK( flexnbd->control ); NULLCHECK(flexnbd->control);
pthread_t * control_thread = &flexnbd->control->thread; pthread_t *control_thread = &flexnbd->control->thread;
FATAL_UNLESS( 0 == pthread_create( FATAL_UNLESS(0 == pthread_create(control_thread,
control_thread, NULL,
NULL, control_runner,
control_runner, flexnbd->control),
flexnbd->control ), "Couldn't create the control thread");
"Couldn't create the control thread" );
} }
void flexnbd_stop_control( struct flexnbd * flexnbd ) void flexnbd_stop_control(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
NULLCHECK( flexnbd->control ); NULLCHECK(flexnbd->control);
control_signal_close( flexnbd->control ); control_signal_close(flexnbd->control);
pthread_t tid = flexnbd->control->thread; pthread_t tid = flexnbd->control->thread;
FATAL_UNLESS( 0 == pthread_join( tid, NULL ), FATAL_UNLESS(0 == pthread_join(tid, NULL),
"Failed joining the control thread" ); "Failed joining the control thread");
debug( "Control thread %p pthread_join returned", tid ); debug("Control thread %p pthread_join returned", tid);
} }
int flexnbd_signal_fd( struct flexnbd * flexnbd ) int flexnbd_signal_fd(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
return flexnbd->signal_fd; return flexnbd->signal_fd;
} }
void flexnbd_destroy( struct flexnbd * flexnbd ) void flexnbd_destroy(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
if ( flexnbd->control ) { if (flexnbd->control) {
control_destroy( flexnbd->control ); control_destroy(flexnbd->control);
} }
close( flexnbd->signal_fd ); close(flexnbd->signal_fd);
free( flexnbd ); free(flexnbd);
} }
struct server * flexnbd_server( struct flexnbd * flexnbd ) struct server *flexnbd_server(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
return flexnbd->serve; return flexnbd->serve;
} }
void flexnbd_replace_acl( struct flexnbd * flexnbd, struct acl * acl ) void flexnbd_replace_acl(struct flexnbd *flexnbd, struct acl *acl)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
server_replace_acl( flexnbd_server(flexnbd), acl ); server_replace_acl(flexnbd_server(flexnbd), acl);
} }
struct status * flexnbd_status_create( struct flexnbd * flexnbd ) struct status *flexnbd_status_create(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
struct status * status; struct status *status;
status = status_create( flexnbd_server( flexnbd ) ); status = status_create(flexnbd_server(flexnbd));
return status; return status;
} }
void flexnbd_set_server( struct flexnbd * flexnbd, struct server * serve ) void flexnbd_set_server(struct flexnbd *flexnbd, struct server *serve)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
flexnbd->serve = serve; flexnbd->serve = serve;
} }
/* Get the default_deny of the current server object. */ /* Get the default_deny of the current server object. */
int flexnbd_default_deny( struct flexnbd * flexnbd ) int flexnbd_default_deny(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
return server_default_deny( flexnbd->serve ); return server_default_deny(flexnbd->serve);
} }
void make_writable( const char * filename ) 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", "Couldn't chmod %s: %s", filename, strerror(errno));
filename,
strerror( errno ) );
} }
int flexnbd_serve( struct flexnbd * flexnbd ) int flexnbd_serve(struct flexnbd *flexnbd)
{ {
NULLCHECK( flexnbd ); NULLCHECK(flexnbd);
int success; int success;
struct self_pipe * open_signal = NULL; struct self_pipe *open_signal = NULL;
if ( flexnbd->control ){ if (flexnbd->control) {
debug( "Spawning control thread" ); debug("Spawning control thread");
flexnbd_spawn_control( flexnbd ); flexnbd_spawn_control(flexnbd);
open_signal = flexnbd->control->open_signal; open_signal = flexnbd->control->open_signal;
} }
success = do_serve( flexnbd->serve, open_signal ); success = do_serve(flexnbd->serve, open_signal);
debug("do_serve success is %d", success ); debug("do_serve success is %d", success);
if ( flexnbd->control ) { if (flexnbd->control) {
debug( "Stopping control thread" ); debug("Stopping control thread");
flexnbd_stop_control( flexnbd ); flexnbd_stop_control(flexnbd);
debug("Control thread stopped"); debug("Control thread stopped");
} }
return success; return success;
} }

View File

@@ -13,54 +13,51 @@
/* Carries the "globals". */ /* Carries the "globals". */
struct flexnbd { struct flexnbd {
/* Our serve pointer should never be dereferenced outside a /* Our serve pointer should never be dereferenced outside a
* flexnbd_switch_lock/unlock pair. * flexnbd_switch_lock/unlock pair.
*/ */
struct server * serve; struct server *serve;
/* We only have a control object if a control socket name was /* We only have a control object if a control socket name was
* passed on the command line. * passed on the command line.
*/ */
struct control * control; struct control *control;
/* File descriptor for a signalfd(2) signal stream. */ /* File descriptor for a signalfd(2) signal stream. */
int signal_fd; int signal_fd;
}; };
struct flexnbd * flexnbd_create(void); struct flexnbd *flexnbd_create(void);
struct flexnbd * flexnbd_create_serving( struct flexnbd *flexnbd_create_serving(char *s_ip_address,
char* s_ip_address, char *s_port,
char* s_port, char *s_file,
char* s_file, char *s_ctrl_sock,
char* s_ctrl_sock, int default_deny,
int default_deny, 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 use_killswitch);
struct flexnbd * flexnbd_create_listening( struct flexnbd *flexnbd_create_listening(char *s_ip_address,
char* s_ip_address, char *s_port,
char* s_port, char *s_file,
char* s_file, char *s_ctrl_sock,
char* s_ctrl_sock, int default_deny,
int default_deny, int acl_entries,
int acl_entries, char **s_acl_entries);
char** s_acl_entries );
void flexnbd_destroy( struct flexnbd * ); void flexnbd_destroy(struct flexnbd *);
enum mirror_state; enum mirror_state;
enum mirror_state flexnbd_get_mirror_state( struct flexnbd * ); enum mirror_state flexnbd_get_mirror_state(struct flexnbd *);
int flexnbd_default_deny( struct flexnbd * ); int flexnbd_default_deny(struct flexnbd *);
void flexnbd_set_server( struct flexnbd * flexnbd, struct server * serve ); void flexnbd_set_server(struct flexnbd *flexnbd, struct server *serve);
int flexnbd_signal_fd( struct flexnbd * flexnbd ); int flexnbd_signal_fd(struct flexnbd *flexnbd);
int flexnbd_serve( struct flexnbd * flexnbd ); int flexnbd_serve(struct flexnbd *flexnbd);
int flexnbd_proxy( struct flexnbd * flexnbd ); int flexnbd_proxy(struct flexnbd *flexnbd);
struct server * flexnbd_server( struct flexnbd * flexnbd ); 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

View File

@@ -4,72 +4,70 @@
#include <pthread.h> #include <pthread.h>
struct flexthread_mutex * flexthread_mutex_create(void) struct flexthread_mutex *flexthread_mutex_create(void)
{ {
struct flexthread_mutex * ftm = struct flexthread_mutex *ftm =
xmalloc( sizeof( struct flexthread_mutex ) ); xmalloc(sizeof(struct flexthread_mutex));
FATAL_UNLESS( 0 == pthread_mutex_init( &ftm->mutex, NULL ), FATAL_UNLESS(0 == pthread_mutex_init(&ftm->mutex, NULL),
"Mutex initialisation failed" ); "Mutex initialisation failed");
return ftm; return ftm;
} }
void flexthread_mutex_destroy( struct flexthread_mutex * ftm ) void flexthread_mutex_destroy(struct flexthread_mutex *ftm)
{ {
NULLCHECK( ftm ); NULLCHECK(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!" ); }
}
FATAL_UNLESS( 0 == pthread_mutex_destroy( &ftm->mutex ), FATAL_UNLESS(0 == pthread_mutex_destroy(&ftm->mutex),
"Mutex destroy failed" ); "Mutex destroy failed");
free( ftm ); free(ftm);
} }
int flexthread_mutex_lock( struct flexthread_mutex * ftm ) int flexthread_mutex_lock(struct flexthread_mutex *ftm)
{ {
NULLCHECK( ftm ); NULLCHECK(ftm);
int failure = pthread_mutex_lock( &ftm->mutex ); int failure = pthread_mutex_lock(&ftm->mutex);
if ( 0 == failure ) { if (0 == failure) {
ftm->holder = pthread_self(); ftm->holder = pthread_self();
} }
return failure; return failure;
} }
int flexthread_mutex_unlock( struct flexthread_mutex * ftm ) int flexthread_mutex_unlock(struct flexthread_mutex *ftm)
{ {
NULLCHECK( ftm ); NULLCHECK(ftm);
pthread_t orig = ftm->holder; pthread_t orig = ftm->holder;
ftm->holder = (pthread_t)NULL; ftm->holder = (pthread_t) NULL;
int failure = pthread_mutex_unlock( &ftm->mutex ); int failure = pthread_mutex_unlock(&ftm->mutex);
if ( 0 != failure ) { if (0 != failure) {
ftm->holder = orig; ftm->holder = orig;
} }
return failure; return failure;
} }
int flexthread_mutex_held( struct flexthread_mutex * ftm ) int flexthread_mutex_held(struct flexthread_mutex *ftm)
{ {
NULLCHECK( ftm ); NULLCHECK(ftm);
return pthread_self() == ftm->holder; return pthread_self() == ftm->holder;
} }

View File

@@ -15,15 +15,15 @@
*/ */
struct flexthread_mutex { struct flexthread_mutex {
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_t holder; pthread_t holder;
}; };
struct flexthread_mutex * flexthread_mutex_create(void); struct flexthread_mutex *flexthread_mutex_create(void);
void flexthread_mutex_destroy( struct flexthread_mutex * ); void flexthread_mutex_destroy(struct flexthread_mutex *);
int flexthread_mutex_lock( struct flexthread_mutex * ); int flexthread_mutex_lock(struct flexthread_mutex *);
int flexthread_mutex_unlock( struct flexthread_mutex * ); int flexthread_mutex_unlock(struct flexthread_mutex *);
int flexthread_mutex_held( struct flexthread_mutex * ); int flexthread_mutex_held(struct flexthread_mutex *);
#endif #endif

View File

@@ -3,75 +3,75 @@
#include <pthread.h> #include <pthread.h>
struct mbox * mbox_create( void ) struct mbox *mbox_create(void)
{ {
struct mbox * mbox = xmalloc( sizeof( struct mbox ) ); struct mbox *mbox = xmalloc(sizeof(struct mbox));
FATAL_UNLESS( 0 == pthread_cond_init( &mbox->filled_cond, NULL ), FATAL_UNLESS(0 == pthread_cond_init(&mbox->filled_cond, NULL),
"Failed to initialise a condition variable" ); "Failed to initialise a condition variable");
FATAL_UNLESS( 0 == pthread_cond_init( &mbox->emptied_cond, NULL ), FATAL_UNLESS(0 == pthread_cond_init(&mbox->emptied_cond, NULL),
"Failed to initialise a condition variable" ); "Failed to initialise a condition variable");
FATAL_UNLESS( 0 == pthread_mutex_init( &mbox->mutex, NULL ), FATAL_UNLESS(0 == pthread_mutex_init(&mbox->mutex, NULL),
"Failed to initialise a mutex" ); "Failed to initialise a mutex");
return mbox; return mbox;
} }
void mbox_post( struct mbox * mbox, void * contents ) void mbox_post(struct mbox *mbox, void *contents)
{ {
pthread_mutex_lock( &mbox->mutex ); pthread_mutex_lock(&mbox->mutex);
{ {
if (mbox->full){ if (mbox->full) {
pthread_cond_wait( &mbox->emptied_cond, &mbox->mutex ); pthread_cond_wait(&mbox->emptied_cond, &mbox->mutex);
}
mbox->contents = contents;
mbox->full = 1;
while( 0 != pthread_cond_signal( &mbox->filled_cond ) );
} }
pthread_mutex_unlock( &mbox->mutex ); mbox->contents = contents;
mbox->full = 1;
while (0 != pthread_cond_signal(&mbox->filled_cond));
}
pthread_mutex_unlock(&mbox->mutex);
} }
void * mbox_contents( struct mbox * mbox ) void *mbox_contents(struct mbox *mbox)
{ {
return mbox->contents; return mbox->contents;
} }
int mbox_is_full( struct mbox * mbox ) int mbox_is_full(struct mbox *mbox)
{ {
return mbox->full; return mbox->full;
} }
void * mbox_receive( struct mbox * mbox ) void *mbox_receive(struct mbox *mbox)
{ {
NULLCHECK( mbox ); NULLCHECK(mbox);
void * result; void *result;
pthread_mutex_lock( &mbox->mutex ); pthread_mutex_lock(&mbox->mutex);
{ {
if ( !mbox->full ) { if (!mbox->full) {
pthread_cond_wait( &mbox->filled_cond, &mbox->mutex ); pthread_cond_wait(&mbox->filled_cond, &mbox->mutex);
}
mbox->full = 0;
result = mbox->contents;
mbox->contents = NULL;
while( 0 != pthread_cond_signal( &mbox->emptied_cond));
} }
pthread_mutex_unlock( &mbox->mutex ); mbox->full = 0;
result = mbox->contents;
mbox->contents = NULL;
return result; while (0 != pthread_cond_signal(&mbox->emptied_cond));
}
pthread_mutex_unlock(&mbox->mutex);
return result;
} }
void mbox_destroy( struct mbox * mbox ) void mbox_destroy(struct mbox *mbox)
{ {
NULLCHECK( mbox ); NULLCHECK(mbox);
while( 0 != pthread_cond_destroy( &mbox->emptied_cond ) ); while (0 != pthread_cond_destroy(&mbox->emptied_cond));
while( 0 != pthread_cond_destroy( &mbox->filled_cond ) ); while (0 != pthread_cond_destroy(&mbox->filled_cond));
while( 0 != pthread_mutex_destroy( &mbox->mutex ) ); while (0 != pthread_mutex_destroy(&mbox->mutex));
free( mbox ); free(mbox);
} }

View File

@@ -14,42 +14,42 @@
struct mbox { struct mbox {
void * contents; void *contents;
/** Marker to tell us if there's content in the box. /** Marker to tell us if there's content in the box.
* Keeping this separate allows us to use NULL for the contents. * Keeping this separate allows us to use NULL for the contents.
*/ */
int full; int full;
/** This gets signaled by mbox_post, and waited on by /** This gets signaled by mbox_post, and waited on by
* mbox_receive */ * mbox_receive */
pthread_cond_t filled_cond; pthread_cond_t filled_cond;
/** This is signaled by mbox_receive, and waited on by mbox_post */ /** This is signaled by mbox_receive, and waited on by mbox_post */
pthread_cond_t emptied_cond; pthread_cond_t emptied_cond;
pthread_mutex_t mutex; pthread_mutex_t mutex;
}; };
/* Create an mbox. */ /* Create an mbox. */
struct mbox * mbox_create(void); struct mbox *mbox_create(void);
/* Put something in the mbox, blocking if it's already full. /* Put something in the mbox, blocking if it's already full.
* That something can be NULL if you want. * That something can be NULL if you want.
*/ */
void mbox_post( struct mbox *, void *); void mbox_post(struct mbox *, void *);
/* See what's in the mbox. This isn't thread-safe. */ /* See what's in the mbox. This isn't thread-safe. */
void * mbox_contents( struct mbox *); void *mbox_contents(struct mbox *);
/* See if anything has been put into the mbox. This isn't thread-safe. /* See if anything has been put into the mbox. This isn't thread-safe.
* */ * */
int mbox_is_full( struct mbox *); int mbox_is_full(struct mbox *);
/* Get the contents from the mbox, blocking if there's nothing there. */ /* Get the contents from the mbox, blocking if there's nothing there. */
void * mbox_receive( struct mbox *); void *mbox_receive(struct mbox *);
/* Free the mbox and destroy the associated pthread bits. */ /* Free the mbox and destroy the associated pthread bits. */
void mbox_destroy( struct mbox *); void mbox_destroy(struct mbox *);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -58,65 +58,65 @@ enum mirror_state;
#define MS_REQUEST_LIMIT_SECS_F 60.0 #define MS_REQUEST_LIMIT_SECS_F 60.0
enum mirror_finish_action { enum mirror_finish_action {
ACTION_EXIT, ACTION_EXIT,
ACTION_UNLINK, ACTION_UNLINK,
ACTION_NOTHING ACTION_NOTHING
}; };
enum mirror_state { enum mirror_state {
MS_UNKNOWN, MS_UNKNOWN,
MS_INIT, MS_INIT,
MS_GO, MS_GO,
MS_ABANDONED, MS_ABANDONED,
MS_DONE, MS_DONE,
MS_FAIL_CONNECT, MS_FAIL_CONNECT,
MS_FAIL_REJECTED, MS_FAIL_REJECTED,
MS_FAIL_NO_HELLO, MS_FAIL_NO_HELLO,
MS_FAIL_SIZE_MISMATCH MS_FAIL_SIZE_MISMATCH
}; };
struct mirror { struct mirror {
pthread_t thread; pthread_t thread;
/* Signal to this then join the thread if you want to abandon mirroring */ /* Signal to this then join the thread if you want to abandon mirroring */
struct self_pipe * abandon_signal; struct self_pipe *abandon_signal;
union mysockaddr * connect_to; union mysockaddr *connect_to;
union mysockaddr * connect_from; union mysockaddr *connect_from;
int client; int client;
const char * filename; const char *filename;
/* Limiter, used to restrict migration speed Only dirty bytes (those going /* Limiter, used to restrict migration speed Only dirty bytes (those going
* over the network) are considered */ * over the network) are considered */
uint64_t max_bytes_per_second; uint64_t max_bytes_per_second;
enum mirror_finish_action action_at_finish; enum mirror_finish_action action_at_finish;
char *mapped; char *mapped;
/* We need to send every byte at least once; we do so by */ /* We need to send every byte at least once; we do so by */
uint64_t offset; uint64_t offset;
enum mirror_state commit_state; enum mirror_state commit_state;
/* commit_signal is sent immediately after attempting to connect /* commit_signal is sent immediately after attempting to connect
* and checking the remote size, whether successful or not. * and checking the remote size, whether successful or not.
*/ */
struct mbox * commit_signal; struct mbox *commit_signal;
/* The time (from monotonic_time_ms()) the migration was started. Can be /* The time (from monotonic_time_ms()) the migration was started. Can be
* used to calculate bps, etc. */ * used to calculate bps, etc. */
uint64_t migration_started; uint64_t migration_started;
/* Running count of all bytes we've transferred */ /* Running count of all bytes we've transferred */
uint64_t all_dirty; uint64_t all_dirty;
}; };
struct mirror_super { struct mirror_super {
struct mirror * mirror; struct mirror *mirror;
pthread_t thread; pthread_t thread;
struct mbox * state_mbox; struct mbox *state_mbox;
}; };
@@ -127,15 +127,13 @@ struct mirror_super {
struct server; struct server;
struct flexnbd; struct flexnbd;
struct mirror_super * mirror_super_create( struct mirror_super *mirror_super_create(const char *filename,
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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,165 +3,165 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> /* for sig_atomic_t */ #include <signal.h> /* for sig_atomic_t */
#include "flexnbd.h" #include "flexnbd.h"
#include "parse.h" #include "parse.h"
#include "acl.h" #include "acl.h"
static const int block_allocation_resolution = 4096;//128<<10; static const int block_allocation_resolution = 4096; //128<<10;
struct client_tbl_entry { struct client_tbl_entry {
pthread_t thread; pthread_t thread;
union mysockaddr address; union mysockaddr address;
struct client * client; struct client *client;
}; };
#define MAX_NBD_CLIENTS 16 #define MAX_NBD_CLIENTS 16
#define CLIENT_KEEPALIVE_TIME 30
#define CLIENT_KEEPALIVE_INTVL 10
#define CLIENT_KEEPALIVE_PROBES 3
struct server { struct server {
/* The flexnbd wrapper this server is attached to */ /* The flexnbd wrapper this server is attached to */
struct flexnbd * flexnbd; struct flexnbd *flexnbd;
/** address/port to bind to */ /** address/port to bind to */
union mysockaddr bind_to; union mysockaddr bind_to;
/** (static) file name to serve */ /** (static) file name to serve */
char* filename; char *filename;
/** TCP backlog for listen() */ /** TCP backlog for listen() */
int tcp_backlog; int tcp_backlog;
/** (static) file name of UNIX control socket (or NULL if none) */ /** (static) file name of UNIX control socket (or NULL if none) */
char* control_socket_name; char *control_socket_name;
/** size of file */ /** size of file */
uint64_t size; uint64_t size;
/** to interrupt accept loop and clients, write() to close_signal[1] */ /** to interrupt accept loop and clients, write() to close_signal[1] */
struct self_pipe * close_signal; struct self_pipe *close_signal;
/** access control list */ /** access control list */
struct acl * acl; struct acl *acl;
/** acl_updated_signal will be signalled after the acl struct /** acl_updated_signal will be signalled after the acl struct
* has been replaced * has been replaced
*/ */
struct self_pipe * acl_updated_signal; struct self_pipe *acl_updated_signal;
/* Claimed around any updates to the ACL. */ /* Claimed around any updates to the ACL. */
struct flexthread_mutex * l_acl; struct flexthread_mutex *l_acl;
/* Claimed around starting a mirror so that it doesn't race with /* Claimed around starting a mirror so that it doesn't race with
* shutting down on a SIGTERM. */ * shutting down on a SIGTERM. */
struct flexthread_mutex * l_start_mirror; struct flexthread_mutex *l_start_mirror;
struct mirror* mirror; struct mirror *mirror;
struct mirror_super * mirror_super; struct mirror_super *mirror_super;
/* This is used to stop the mirror from starting after we /* This is used to stop the mirror from starting after we
* receive a SIGTERM */ * receive a SIGTERM */
int mirror_can_start; int mirror_can_start;
int server_fd; int server_fd;
int control_fd; int control_fd;
/* the allocation_map keeps track of which blocks in the backing file /* the allocation_map keeps track of which blocks in the backing file
* have been allocated, or part-allocated on disc, with unallocated * have been allocated, or part-allocated on disc, with unallocated
* blocks presumed to contain zeroes (i.e. represented as sparse files * blocks presumed to contain zeroes (i.e. represented as sparse files
* by the filesystem). We can use this information when receiving * by the filesystem). We can use this information when receiving
* incoming writes, and avoid writing zeroes to unallocated sections * incoming writes, and avoid writing zeroes to unallocated sections
* of the file which would needlessly increase disc usage. This * of the file which would needlessly increase disc usage. This
* bitmap will start at all-zeroes for an empty file, and tend towards * bitmap will start at all-zeroes for an empty file, and tend towards
* all-ones as the file is written to (i.e. we assume that allocated * all-ones as the file is written to (i.e. we assume that allocated
* blocks can never become unallocated again, as is the case with ext3 * blocks can never become unallocated again, as is the case with ext3
* at least). * at least).
*/ */
struct bitset * allocation_map; struct bitset *allocation_map;
/* when starting up, this thread builds the allocation_map */ /* when starting up, this thread builds the allocation_map */
pthread_t allocation_map_builder_thread; pthread_t allocation_map_builder_thread;
/* when the thread has finished, it sets this to 1 */ /* when the thread has finished, it sets this to 1 */
volatile sig_atomic_t allocation_map_built; volatile sig_atomic_t allocation_map_built;
volatile sig_atomic_t allocation_map_not_built; volatile sig_atomic_t allocation_map_not_built;
int max_nbd_clients; int max_nbd_clients;
struct client_tbl_entry *nbd_client; struct client_tbl_entry *nbd_client;
/** Should clients use the killswitch? */ /** Should clients use the killswitch? */
int use_killswitch; int use_killswitch;
/** If this isn't set, newly accepted clients will be closed immediately */ /** If this isn't set, newly accepted clients will be closed immediately */
int allow_new_clients; int allow_new_clients;
/* Marker for whether this server has control over the data in /* Marker for whether this server has control over the data in
* the file, or if we're waiting to receive it from an inbound * the file, or if we're waiting to receive it from an inbound
* migration which hasn't yet finished. * migration which hasn't yet finished.
* *
* It's the value which controls the exit status of a serve or * It's the value which controls the exit status of a serve or
* listen process. * listen process.
*/ */
int success; int success;
}; };
struct server * server_create( struct server *server_create(struct flexnbd *flexnbd,
struct flexnbd * flexnbd, char *s_ip_address,
char* s_ip_address, char *s_port,
char* s_port, char *s_file,
char* s_file, int default_deny,
int default_deny, 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, void server_destroy(struct server *);
int success ); int server_is_closed(struct server *serve);
void server_destroy( struct server * ); void serve_signal_close(struct server *serve);
int server_is_closed(struct server* serve); void serve_wait_for_close(struct server *serve);
void serve_signal_close( struct server *serve ); void server_replace_acl(struct server *serve, struct acl *acl);
void serve_wait_for_close( struct server * serve ); void server_control_arrived(struct server *serve);
void server_replace_acl( struct server *serve, struct acl * acl); int server_is_in_control(struct server *serve);
void server_control_arrived( struct server *serve ); int server_default_deny(struct server *serve);
int server_is_in_control( struct server *serve ); int server_acl_locked(struct server *serve);
int server_default_deny( struct server * serve ); void server_lock_acl(struct server *serve);
int server_acl_locked( struct server * serve ); void server_unlock_acl(struct server *serve);
void server_lock_acl( struct server *serve ); void server_lock_start_mirror(struct server *serve);
void server_unlock_acl( struct server *serve ); void server_unlock_start_mirror(struct server *serve);
void server_lock_start_mirror( struct server *serve ); int server_is_mirroring(struct server *serve);
void server_unlock_start_mirror( struct server *serve );
int server_is_mirroring( struct server * serve );
uint64_t server_mirror_bytes_remaining( struct server * serve ); uint64_t server_mirror_bytes_remaining(struct server *serve);
uint64_t server_mirror_eta( struct server * serve ); uint64_t server_mirror_eta(struct server *serve);
uint64_t server_mirror_bps( struct server * serve ); uint64_t server_mirror_bps(struct server *serve);
void server_abandon_mirror( struct server * serve ); void server_abandon_mirror(struct server *serve);
void server_prevent_mirror_start( struct server *serve ); void server_prevent_mirror_start(struct server *serve);
void server_allow_mirror_start( struct server *serve ); void server_allow_mirror_start(struct server *serve);
int server_mirror_can_start( struct server *serve ); int server_mirror_can_start(struct server *serve);
/* These three functions are used by mirror around the final pass, to close /* These three functions are used by mirror around the final pass, to close
* existing clients and prevent new ones from being around * existing clients and prevent new ones from being around
*/ */
void server_forbid_new_clients( struct server *serve ); void server_forbid_new_clients(struct server *serve);
void server_close_clients( struct server *serve ); void server_close_clients(struct server *serve);
void server_join_clients( struct server *serve ); void server_join_clients(struct server *serve);
void server_allow_new_clients( struct server *serve ); void server_allow_new_clients(struct server *serve);
/* Returns a count (ish) of the number of currently-running client threads */ /* Returns a count (ish) of the number of currently-running client threads */
int server_count_clients( struct server *params ); int server_count_clients(struct server *params);
void server_unlink( struct server * serve ); void server_unlink(struct server *serve);
int do_serve( struct server *, struct self_pipe * ); int do_serve(struct server *, struct self_pipe *);
struct mode_readwrite_params { struct mode_readwrite_params {
union mysockaddr connect_to; union mysockaddr connect_to;
union mysockaddr connect_from; union mysockaddr connect_from;
uint64_t from; uint64_t from;
uint32_t len; uint32_t len;
int data_fd; int data_fd;
int client; int client;
}; };
#endif #endif

View File

@@ -2,41 +2,44 @@
#include "serve.h" #include "serve.h"
#include "util.h" #include "util.h"
struct status * status_create( struct server * serve ) struct status *status_create(struct server *serve)
{ {
NULLCHECK( serve ); NULLCHECK(serve);
struct status * status; struct status *status;
status = xmalloc( sizeof( struct status ) ); status = xmalloc(sizeof(struct status));
status->pid = getpid(); status->pid = getpid();
status->size = serve->size; status->size = serve->size;
status->has_control = serve->success; status->has_control = serve->success;
status->clients_allowed = serve->allow_new_clients; status->clients_allowed = serve->allow_new_clients;
status->num_clients = server_count_clients( serve ); status->num_clients = server_count_clients(serve);
server_lock_start_mirror( serve ); server_lock_start_mirror(serve);
status->is_mirroring = NULL != serve->mirror; status->is_mirroring = NULL != serve->mirror;
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 ) < status->migration_duration ) { if ((serve->mirror->migration_started) <
status->migration_duration -= serve->mirror->migration_started; status->migration_duration) {
} else { status->migration_duration -= serve->mirror->migration_started;
status->migration_duration = 0; } else {
} status->migration_duration = 0;
status->migration_duration /= 1000;
status->migration_speed = server_mirror_bps( serve );
status->migration_speed_limit = serve->mirror->max_bytes_per_second;
status->migration_seconds_left = server_mirror_eta( serve );
status->migration_bytes_left = server_mirror_bytes_remaining( serve );
} }
status->migration_duration /= 1000;
status->migration_speed = server_mirror_bps(serve);
status->migration_speed_limit =
serve->mirror->max_bytes_per_second;
server_unlock_start_mirror( serve ); status->migration_seconds_left = server_mirror_eta(serve);
status->migration_bytes_left =
server_mirror_bytes_remaining(serve);
}
return status; server_unlock_start_mirror(serve);
return status;
} }
@@ -48,33 +51,32 @@ struct status * status_create( struct server * serve )
#define PRINT_UINT64( var ) \ #define PRINT_UINT64( var ) \
do{dprintf( fd, #var "=%"PRIu64" ", status->var );}while(0) do{dprintf( fd, #var "=%"PRIu64" ", status->var );}while(0)
int status_write( struct status * status, int fd ) int status_write(struct status *status, int fd)
{ {
PRINT_INT( pid ); PRINT_INT(pid);
PRINT_UINT64( size ); PRINT_UINT64(size);
PRINT_BOOL( is_mirroring ); PRINT_BOOL(is_mirroring);
PRINT_BOOL( clients_allowed ); PRINT_BOOL(clients_allowed);
PRINT_INT( num_clients ); PRINT_INT(num_clients);
PRINT_BOOL( has_control ); PRINT_BOOL(has_control);
if ( status->is_mirroring ) { if (status->is_mirroring) {
PRINT_UINT64( migration_speed ); PRINT_UINT64(migration_speed);
PRINT_UINT64( migration_duration ); PRINT_UINT64(migration_duration);
PRINT_UINT64( migration_seconds_left ); PRINT_UINT64(migration_seconds_left);
PRINT_UINT64( migration_bytes_left ); PRINT_UINT64(migration_bytes_left);
if ( status->migration_speed_limit < UINT64_MAX ) { if (status->migration_speed_limit < UINT64_MAX) {
PRINT_UINT64( migration_speed_limit ); PRINT_UINT64(migration_speed_limit);
}; };
} }
dprintf(fd, "\n"); dprintf(fd, "\n");
return 1; return 1;
} }
void status_destroy( struct status * status ) void status_destroy(struct status *status)
{ {
NULLCHECK( status ); NULLCHECK(status);
free( status ); free(status);
} }

View File

@@ -75,30 +75,29 @@
#include <unistd.h> #include <unistd.h>
struct status { struct status {
pid_t pid; pid_t pid;
uint64_t size; uint64_t size;
int has_control; int has_control;
int clients_allowed; int clients_allowed;
int num_clients; int num_clients;
int is_mirroring; int is_mirroring;
uint64_t migration_duration; uint64_t migration_duration;
uint64_t migration_speed; uint64_t migration_speed;
uint64_t migration_speed_limit; uint64_t migration_speed_limit;
uint64_t migration_seconds_left; uint64_t migration_seconds_left;
uint64_t migration_bytes_left; uint64_t migration_bytes_left;
}; };
/** Create a status object for the given server. */ /** Create a status object for the given server. */
struct status * status_create( struct server * ); struct status *status_create(struct server *);
/** Output the given status object to the given file descriptot */ /** Output the given status object to the given file descriptot */
int status_write( struct status *, int fd ); int status_write(struct status *, int fd);
/** Free the status object */ /** Free the status object */
void status_destroy( struct status * ); void status_destroy(struct status *);
#endif #endif

View File

@@ -1,39 +1,42 @@
# encoding: utf-8
require 'flexnbd' require 'flexnbd'
require 'file_writer' require 'file_writer'
class Environment class Environment
attr_reader( :blocksize, :filename1, :filename2, :ip, attr_reader(:blocksize, :filename1, :filename2, :ip,
:port1, :port2, :nbd1, :nbd2, :file1, :file2 ) :port1, :port2, :nbd1, :nbd2, :file1, :file2)
def initialize def initialize
@blocksize = 1024 @blocksize = 1024
@filename1 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.1" @filename1 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.1"
@filename2 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.2" @filename2 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.2"
@ip = "127.0.0.1" @ip = '127.0.0.1'
@available_ports = [*40000..41000] - listening_ports @available_ports = [*40_000..41_000] - 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
end end
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
@@ -42,29 +45,26 @@ 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
def listen2( *acl ) def listen2(*acl)
@nbd2.listen( @filename2, *acl ) @nbd2.listen(@filename2, *acl)
end end
def break1 def break1
@nbd1.break @nbd1.break
end end
def acl1( *acl ) def acl1(*acl)
@nbd1.acl( *acl ) @nbd1.acl(*acl)
end end
def acl2( *acl ) def acl2(*acl)
@nbd2.acl( *acl ) @nbd2.acl(*acl)
end end
def status1 def status1
@nbd1.status.first @nbd1.status.first
end end
@@ -73,23 +73,20 @@ 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
def mirror12_unchecked def mirror12_unchecked
@nbd1.mirror_unchecked( @nbd2.ip, @nbd2.port, nil, nil, 10 ) @nbd1.mirror_unchecked(@nbd2.ip, @nbd2.port, nil, nil, 10)
end end
def mirror12_unlink def mirror12_unlink
@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
def writefile1(data) def writefile1(data)
@@ -100,63 +97,54 @@ 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
Process.waitpid2( @fake_pid ) Process.waitpid2(@fake_pid)
rescue Errno::ESRCH rescue Errno::ESRCH
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.exists?(f) File.unlink(f) if File.exist?(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{|x| x.to_s}.join(" ") exec [fake, addr, port, @nbd1.pid, sock].map(&: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

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -8,28 +6,27 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port, src_pid, sock = *ARGV addr, port, src_pid, sock = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client = server.accept 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)
fail "Unexpected control response" unless raise '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')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
client.close client.close
exit(0) exit(0)

View File

@@ -1,6 +1,4 @@
#!/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.
# #
@@ -11,26 +9,25 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port, src_pid = *ARGV addr, port, src_pid = *ARGV
server = FakeDest.new( addr, port ) 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
system "kill -STOP #{src_pid}" system "kill -STOP #{src_pid}"
client.write_reply( req[:handle] ) client.write_reply(req[:handle])
client.close 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
$stderr.puts "done" warn 'done'
exit(0) exit(0)

View File

@@ -10,12 +10,12 @@ require 'flexnbd/fake_dest'
include FlexNBD 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

View File

@@ -11,13 +11,13 @@ require 'flexnbd/fake_dest'
include FlexNBD 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

View File

@@ -1,6 +1,4 @@
#!/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
@@ -10,16 +8,16 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port, src_pid = *ARGV addr, port, src_pid = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client = server.accept client = server.accept
client.write_hello 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)

View File

@@ -1,19 +1,16 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
require 'flexnbd/fake_dest' require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client = server.accept client = server.accept
client.write_hello 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

View File

@@ -14,8 +14,8 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client = server.accept( "Client didn't make a connection" ) client = server.accept("Client didn't make a connection")
# Sleep for one second past the timeout (a bit of slop in case ruby # Sleep for one second past the timeout (a bit of slop in case ruby
# doesn't launch things quickly) # doesn't launch things quickly)
@@ -26,10 +26,10 @@ 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')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
server.close server.close

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -9,24 +7,24 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client1 = server.accept( server ) client1 = server.accept(server)
client1.write_hello 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.has_key?('FLEXNBD_MS_REQUEST_LIMIT_SECS') sleep_time = if ENV.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
end end
sleep( sleep_time + 2.0 ) sleep(sleep_time + 2.0)
client1.close client1.close
t.join t.join

View File

@@ -7,21 +7,21 @@ include FlexNBD
Thread.abort_on_exception Thread.abort_on_exception
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client1 = server.accept 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)
fail "Unexpected reconnection" raise '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

View File

@@ -9,7 +9,7 @@ include FlexNBD
Thread.abort_on_exception = true Thread.abort_on_exception = true
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client = server.accept client = server.accept
t = Thread.new do t = Thread.new do
@@ -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
fail "Unexpected reconnection." raise '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)
fail "Didn't close socket" unless client.disconnected? raise "Didn't close socket" unless client.disconnected?
exit 0 exit 0

View File

@@ -7,18 +7,16 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port = *ARGV 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
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
server.close server.close
exit(0) exit(0)

View File

@@ -8,8 +8,8 @@ require 'flexnbd/fake_dest'
include FlexNBD 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)

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -9,13 +7,13 @@ require 'flexnbd/fake_dest'
include FlexNBD include FlexNBD
addr, port = *ARGV addr, port = *ARGV
server = FakeDest.new( addr, port ) server = FakeDest.new(addr, port)
client = server.accept 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

View File

@@ -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

View File

@@ -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,12 +25,11 @@ 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')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end

View File

@@ -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,13 +21,11 @@ client.close
sleep(0.25) sleep(0.25)
begin begin
client2 = FakeSource.new( addr, port, "Expected timeout" ) client2 = FakeSource.new(addr, port, 'Expected timeout')
fail "Unexpected reconnection" raise 'Unexpected reconnection'
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
exit(0) exit(0)

View File

@@ -12,13 +12,12 @@ 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)

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -10,11 +8,11 @@ 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.close client.close
system "kill -CONT #{srv_pid}" system "kill -CONT #{srv_pid}"
@@ -24,7 +22,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)

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -10,13 +8,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}"
@@ -27,7 +25,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)

View File

@@ -8,10 +8,9 @@ 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)

View File

@@ -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

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -11,10 +9,9 @@ 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)

View File

@@ -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:
@@ -24,16 +24,16 @@ client.read_hello
kidpid = fork do 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
# Sleep for longer than the child, to give the flexnbd process a bit # Sleep for longer than the child, to give the flexnbd process a bit
# of slop # of slop
sleep( FlexNBD::CLIENT_MAX_WAIT_SECS + 3 ) sleep(FlexNBD::CLIENT_MAX_WAIT_SECS + 3)
client.close client.close
_,status = Process.waitpid2( kidpid ) _, status = Process.waitpid2(kidpid)
exit status.exitstatus exit status.exitstatus

View File

@@ -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

View File

@@ -9,10 +9,9 @@ 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 )

View File

@@ -1,6 +1,4 @@
#!/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.
@@ -13,20 +11,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
fail "Not an error" if response[:error] == 0 raise 'Not an error' if response[:error] == 0
fail "Wrong handle" unless "myhandle" == response[:handle] raise 'Wrong handle' unless response[:handle] == 'myhandle'
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
fail "Second write failed" unless success_response[:error] == 0 raise 'Second write failed' unless success_response[:error] == 0
client.close client.close
exit(0) exit(0)

View File

@@ -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,15 +31,14 @@ 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 { |blk, blk_off| .map do |blk, blk_off|
data(blk, blk_off) data(blk, blk_off)
}.join[off...(off+len)] end.join[off...(off + len)]
end end
# Read what's actually in the file # Read what's actually in the file
@@ -60,68 +59,66 @@ class FileWriter
protected protected
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
else else
raise "Unknown character '#{block}'" raise "Unknown character '#{block}'"
end end
end end
end end
if __FILE__==$0 if $PROGRAM_NAME == __FILE__
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

View File

@@ -4,28 +4,26 @@ 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 do exec cmd end @pid = fork { exec cmd }
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 do exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" end @pid = fork { exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" }
end end
end # class ValgrindExecutor end # class ValgrindExecutor
class ValgrindKillingExecutor class ValgrindKillingExecutor
attr_reader :pid attr_reader :pid
@@ -33,10 +31,10 @@ class ValgrindKillingExecutor
attr_accessor :what, :kind, :pid attr_accessor :what, :kind, :pid
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,115 +54,104 @@ class ValgrindKillingExecutor
end end
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)
@killer = killer @killer = killer
@error = Error.new @error = Error.new
@found = false @found = false
end end
def text( text ) def text(text)
@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'
if @found @killer.call(@error) if @found
@killer.call( @error ) when 'pid'
end @error.pid = @text
when "pid"
@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
super( txt ) super(txt)
end end
def tag_start( tag, attrs ) def tag_start(tag, attrs)
print "<#{tag}>" print "<#{tag}>"
super( tag, attrs ) super(tag, attrs)
end end
def tag_end( tag ) def tag_end(tag)
print "</#{tag}>" print "</#{tag}>"
super( tag ) super(tag)
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 do exec( "valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd ) end @pid = fork { exec("valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd) }
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.
# #
@@ -173,7 +160,7 @@ module FlexNBD
class << self class << self
def counter def counter
Dir['tmp/*'].select{|f| File.file?(f)}.length + 1 Dir['tmp/*'].select { |f| File.file?(f) }.length + 1
end end
end end
@@ -189,19 +176,18 @@ 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
attr_accessor :prefetch_proxy attr_accessor :prefetch_proxy
def initialize( bin, ip, port ) def initialize(bin, ip, port)
@bin = bin @bin = bin
@do_debug = ENV['DEBUG'] @do_debug = ENV['DEBUG']
@debug = build_debug_opt @debug = build_debug_opt
raise "#{bin} not executable" unless File.executable?(bin) raise "#{bin} not executable" unless File.executable?(bin)
@@ -209,21 +195,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)
$stderr.puts msg if debug? warn msg if debug?
end end
def serve_cmd(file, acl)
def serve_cmd( file, acl )
"#{bin} serve "\ "#{bin} serve "\
"--addr #{ip} "\ "--addr #{ip} "\
"--port #{port} "\ "--port #{port} "\
@@ -233,8 +218,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} "\
"--port #{port} "\ "--port #{port} "\
@@ -244,18 +228,17 @@ module FlexNBD
"#{acl.join(' ')}" "#{acl.join(' ')}"
end end
def proxy_cmd( connect_ip, connect_port ) def proxy_cmd(connect_ip, connect_port)
"#{bin}-proxy "\ "#{bin}-proxy "\
"--addr #{ip} "\ "--addr #{ip} "\
"--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} "\
"--port #{port} "\ "--port #{port} "\
@@ -264,8 +247,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} "\
"--port #{port} "\ "--port #{port} "\
@@ -274,30 +256,29 @@ 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} "\
"--sock #{ctrl} "\ "--sock #{ctrl} "\
end end
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)
"#{@bin} mirror "\ "#{@bin} mirror "\
"#{opts} "\ "#{opts} "\
"#{@debug}" "#{@debug}"
end end
def mirror_cmd(dest_ip, dest_port) def mirror_cmd(dest_ip, dest_port)
base_mirror_cmd( base_mirror_opts( dest_ip, dest_port ) ) base_mirror_cmd(base_mirror_opts(dest_ip, dest_port))
end end
def mirror_unlink_cmd( dest_ip, dest_port ) def mirror_unlink_cmd(dest_ip, dest_port)
base_mirror_cmd( unlink_mirror_opts( dest_ip, dest_port ) ) base_mirror_cmd(unlink_mirror_opts(dest_ip, dest_port))
end end
def break_cmd def break_cmd
@@ -312,58 +293,64 @@ module FlexNBD
"#{@debug}" "#{@debug}"
end end
def acl_cmd( *acl ) def acl_cmd(*acl)
"#{@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.exists?(ctrl) File.unlink(ctrl) if File.exist?(ctrl)
debug( cmd ) debug(cmd)
@pid = @executor.run( cmd ) @pid = @executor.run(cmd)
while !File.socket?(ctrl) until 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 = TCPSocket.new(ip, port) rescue nil sock = begin
TCPSocket.new(ip, port)
rescue StandardError
nil
end
success = !!sock success = !!sock
( sock.close rescue nil) if sock if sock
(begin
sock.close
rescue StandardError
nil
end)
end
success success
end end
def proxy( connect_ip, connect_port ) def proxy(connect_ip, connect_port)
cmd = proxy_cmd( connect_ip, connect_port ) cmd = proxy_cmd(connect_ip, connect_port)
debug( cmd ) debug(cmd)
@pid = @executor.run( cmd ) @pid = @executor.run(cmd)
until tcp_server_open? until tcp_server_open?
pid, status = Process.wait2(@pid, Process::WNOHANG) pid, status = Process.wait2(@pid, Process::WNOHANG)
@@ -371,31 +358,29 @@ module FlexNBD
sleep 0.1 sleep 0.1
end end
start_wait_thread( @pid ) start_wait_thread(@pid)
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?
fail "flexnbd quit with a bad signal: #{status.inspect}" unless raise "flexnbd quit with a bad signal: #{status.inspect}" unless
@kill.include? status.termsig @kill.include? status.termsig
else else
fail "flexnbd quit with a bad status: #{status.inspect}" unless raise "flexnbd quit with a bad status: #{status.inspect}" unless
@kill.include? status.exitstatus @kill.include? status.exitstatus
end end
else else
$stderr.puts "flexnbd #{self.pid} quit" warn "flexnbd #{self.pid} quit"
fail "flexnbd #{self.pid} quit early with status #{status.to_i}" raise "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
@@ -407,7 +392,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.
@@ -417,63 +402,60 @@ module FlexNBD
end end
def read(offset, length) def read(offset, length)
cmd = read_cmd( offset, length ) cmd = read_cmd(offset, length)
debug( cmd ) debug(cmd)
IO.popen(cmd) do |fh| IO.popen(cmd) do |fh|
return fh.read return fh.read
end end
raise IOError.new "NBD read failed" unless $?.success? raise IOError, 'NBD read failed' unless $CHILD_STATUS.success?
out out
end end
def write(offset, data) def write(offset, data)
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.new "NBD write failed" unless $?.success? raise IOError, 'NBD write failed' unless $CHILD_STATUS.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)
cmd = mirror_cmd(dest_ip, dest_port)
debug(cmd)
def mirror_unchecked( dest_ip, dest_port, bandwidth=nil, action=nil, timeout=nil ) maybe_timeout(cmd, timeout)
cmd = mirror_cmd( dest_ip, dest_port)
debug( cmd )
maybe_timeout( cmd, timeout )
end end
def mirror_unlink(dest_ip, dest_port, timeout = nil)
cmd = mirror_unlink_cmd(dest_ip, dest_port)
debug(cmd)
def mirror_unlink( dest_ip, dest_port, timeout=nil ) maybe_timeout(cmd, timeout)
cmd = mirror_unlink_cmd( dest_ip, dest_port )
debug( cmd )
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.new do run = proc 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.
Open3.popen3( cmd ) do |io_in, io_out, io_err, maybe_thr| Open3.popen3(cmd) do |io_in, io_out, io_err, maybe_thr|
io_in.close io_in.close
stdout.replace io_out.read stdout.replace io_out.read
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 ||= $? stat ||= $CHILD_STATUS
end end
if timeout if timeout
@@ -485,85 +467,73 @@ 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)
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)
maybe_timeout( cmd, 2 ) maybe_timeout(cmd, 2)
end end
def status(timeout = nil)
cmd = status_cmd
debug(cmd)
def status( timeout = nil ) o, e = maybe_timeout(cmd, timeout)
cmd = status_cmd()
debug( cmd )
o,e = maybe_timeout( cmd, timeout )
[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

View File

@@ -1,10 +1,7 @@
# 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
@@ -13,36 +10,33 @@ 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
fail "No source root!" unless source_root raise '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($1, $2.to_i) unless const_defined?( $1 ) const_set(Regexp.last_match(1), Regexp.last_match(2).to_i) unless const_defined?(Regexp.last_match(1))
end end
end end
end end
end end
read_constants() 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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
require 'socket' require 'socket'
require 'timeout' require 'timeout'
@@ -7,114 +5,104 @@ 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)
else else
@sock.write( "\x00\x00\x42\x02\x81\x86\x12\x53" ) @sock.write("\x00\x00\x42\x02\x81\x86\x12\x53")
end end
if opts[:size] == :wrong if opts[:size] == :wrong
write_rand( @sock, 8 ) write_rand(@sock, 8)
else else
@sock.write( "\x00\x00\x00\x00\x00\x00\x10\x00" ) @sock.write("\x00\x00\x00\x00\x00\x00\x10\x00")
end end
@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]
type_s = req[4 ... 8 ] type_s = req[4...8]
handle_s = req[8 ... 16] handle_s = req[8...16]
from_s = req[16 ... 24] from_s = req[16...24]
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
def write_error( handle ) def write_error(handle)
write_reply( handle, 1 ) write_reply(handle, 1)
end end
def disconnected? def disconnected?
begin Timeout.timeout(2) do
Timeout.timeout(2) do @sock.read(1).nil?
@sock.read(1) == nil
end
rescue Timeout::Error
return false
end end
rescue Timeout::Error
return false
end end
def write_reply( handle, err=0, opts={} ) def write_reply(handle, err = 0, opts = {})
if opts[:magic] == :wrong if opts[:magic] == :wrong
write_rand( @sock, 4 ) write_rand(@sock, 4)
else else
@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
def write_data( len ) def write_data(len)
@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 65536 when 65_536
write_reply( req[:handle], opts[:err] == :entrust ? 1 : 0 ) write_reply(req[:handle], opts[:err] == :entrust ? 1 : 0)
break break
else else
raise "Unexpected request: #{req.inspect}" raise "Unexpected request: #{req.inspect}"
@@ -129,16 +117,13 @@ 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
@@ -146,21 +131,16 @@ module FlexNBD
client_sock = @sock.accept client_sock = @sock.accept
end end
rescue Timeout::Error rescue Timeout::Error
raise Timeout::Error.new(err_msg) raise Timeout::Error, err_msg
end end
client_sock client_sock
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

View File

@@ -1,112 +1,117 @@
# 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
@sock = if source_addr @sock = if source_addr
TCPSocket.new( addr, port, source_addr, source_port ) TCPSocket.new(addr, port, source_addr, source_port)
else else
TCPSocket.new( addr, port ) TCPSocket.new(addr, port)
end end
rescue Errno::ECONNREFUSED rescue Errno::ECONNREFUSED
$stderr.puts "Connection refused, retrying" warn '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
timing_out(::FlexNBD::MS_HELLO_TIME_SECS,
'Timed out waiting for hello.') do
raise 'No hello.' unless (hello = @sock.read(152)) &&
hello.length == 152
def read_hello() passwd_s = hello[0..7]
timing_out( ::FlexNBD::MS_HELLO_TIME_SECS, magic = hello[8..15].unpack('Q>').first
"Timed out waiting for hello." ) do size = hello[16..23].unpack('Q>').first
fail "No hello." unless (hello = @sock.read( 152 )) && flags = hello[24..27].unpack('L>').first
hello.length==152 reserved = hello[28..-1]
magic_s = hello[0..7] return { passwd: passwd_s, magic: magic, size: size, flags: flags, reserved: reserved }
ignore_s= hello[8..15]
size_s = hello[16..23]
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 ) @sock.write(magic)
fail "Bad handle" unless handle.length == 8 @sock.write([flags].pack('n'))
@sock.write([type].pack('n'))
@sock.write( magic ) @sock.write(handle)
@sock.write( [type].pack( 'N' ) ) @sock.write([n64(from)].pack('q'))
@sock.write( handle ) @sock.write([len].pack('N'))
@sock.write( [n64( from )].pack( 'q' ) )
@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')
def write_entrust_request( handle="myhandle" ) send_request(1, handle, from, len, REQUEST_MAGIC, 1)
send_request( 65536, handle )
end end
def write_disconnect_request( handle="myhandle" ) def write_flush_request(handle = 'myhandle')
send_request( 2, handle ) send_request(3, handle, 0, 0)
end end
def write_read_request( from, len, handle="myhandle" ) def write_entrust_request(handle = 'myhandle')
send_request( 0, "myhandle", from, len ) send_request(65_536, handle)
end end
def write_disconnect_request(handle = 'myhandle')
def write_data( data ) send_request(2, handle)
@sock.write( data )
end end
def write_read_request(from, len, _handle = 'myhandle')
send_request(0, 'myhandle', from, len)
end
def write_data(data)
@sock.write(data)
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
def read_raw( len ) def read_raw(len)
@sock.read( len ) @sock.read(len)
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)
@@ -114,29 +119,25 @@ 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 ) Timeout.timeout(time) do
begin yield
Timeout.timeout( time ) do
yield
end
rescue Timeout::Error
$stderr.puts msg
exit 1
end end
rescue Timeout::Error
warn msg
exit 1
end end
private private
@@ -146,15 +147,13 @@ module FlexNBD
# ) # )
def n64(b) def n64(b)
((b & 0xff00000000000000) >> 56) | ((b & 0xff00000000000000) >> 56) |
((b & 0x00ff000000000000) >> 40) | ((b & 0x00ff000000000000) >> 40) |
((b & 0x0000ff0000000000) >> 24) | ((b & 0x0000ff0000000000) >> 24) |
((b & 0x000000ff00000000) >> 8) | ((b & 0x000000ff00000000) >> 8) |
((b & 0x00000000ff000000) << 8) | ((b & 0x00000000ff000000) << 8) |
((b & 0x0000000000ff0000) << 24) | ((b & 0x0000000000ff0000) << 24) |
((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

View File

@@ -0,0 +1,55 @@
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

View File

@@ -0,0 +1,13 @@
SRC := $(wildcard *.c)
OBJS := $(SRC:%.c=%.o)
all: $(OBJS)
clean:
$(RM) $(OBJS)
%.o: %.c
gcc -shared -fPIC -ldl -o $@ $<
.PHONY: all clean

View File

@@ -0,0 +1,33 @@
#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;
}

View File

@@ -0,0 +1,38 @@
#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 Normal file → Executable file
View 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

View File

@@ -1,26 +1,33 @@
# 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
def with_proxied_client( override_size = nil ) def with_proxied_client(override_size = nil)
@env.serve1 unless @server_up @env.serve1 unless @server_up
@env.proxy2 unless @proxy_up @env.proxy2 unless @proxy_up
@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[:magic] assert_equal 'NBDMAGIC', result[:passwd]
assert_equal override_size || @env.file1.size, result[:size] assert_equal override_size || @env.file1.size, result[:size]
yield client yield client
ensure ensure
client.close rescue nil begin
client.close
rescue StandardError
nil
end
end end
end end
@@ -32,11 +39,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)
@@ -45,8 +52,8 @@ module ProxyTests
assert_equal 4096, orig_data.size assert_equal 4096, orig_data.size
assert_equal 4096, data.size assert_equal 4096, data.size
assert_equal( orig_data, data, assert_equal(orig_data, data,
"Returned data does not match on request #{n+1}" ) "Returned data does not match on request #{n + 1}")
end end
end end
end end
@@ -59,16 +66,30 @@ 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)
assert_equal( ( b * 4096 ), data, "Data not written correctly (offset is #{n})" ) assert_equal((b * 4096), data, "Data not written correctly (offset is #{n})")
end end
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
@@ -78,7 +99,7 @@ module ProxyTests
sc = server.accept # just tell the supervisor we're up sc = server.accept # just tell the supervisor we're up
sc.write_hello sc.write_hello
[ server, sc ] [server, sc]
end end
end end
@@ -89,7 +110,7 @@ module ProxyTests
server, sc1 = maker.value server, sc1 = maker.value
# Send the read request to the proxy # Send the read request to the proxy
client.write_read_request( 0, 4096 ) client.write_read_request(0, 4096)
# ensure we're given the read request # ensure we're given the read request
req1 = sc1.read_request req1 = sc1.read_request
@@ -110,85 +131,110 @@ module ProxyTests
assert_equal req1, req2 assert_equal req1, req2
# The reply should be proxied back to the client. # The reply should be proxied back to the client.
sc2.write_reply( req2[:handle] ) sc2.write_reply(req2[:handle])
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(15) { client.read_response } rsp = Timeout.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_proxied_client(4096) do |client| with_ld_preload('setsockopt_logger') do
server, sc1 = maker.value with_proxied_client(4096) do |client|
server, sc1 = maker.value
# Send the read request to the proxy # Send the read request to the proxy
client.write( 0, ( b * 4096 ) ) client.write(0, (b * 4096))
# ensure we're given the read request # ensure we're given the read request
req1 = sc1.read_request req1 = sc1.read_request
assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic] assert_equal ::FlexNBD::REQUEST_MAGIC, req1[:magic]
assert_equal ::FlexNBD::REQUEST_WRITE, req1[:type] assert_equal ::FlexNBD::REQUEST_WRITE, req1[:type]
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')
# Kill the server again, now we're sure the read request has been sent once # Read the setsockopt logs, so we can check that TCP_NODELAY is re-set
sc1.close # later
read_ld_preload_log('setsockopt_logger')
# We expect the proxy to reconnect without our client doing anything. # Kill the server again, now we're sure the read request has been sent once
sc2 = server.accept sc1.close
sc2.write_hello
# And once reconnected, it should resend an identical request. # We expect the proxy to reconnect without our client doing anything.
req2 = sc2.read_request sc2 = server.accept
assert_equal req1, req2 sc2.write_hello
data2 = sc2.read_data( 4096 )
assert_equal data1, data2
# The reply should be proxied back to the client. # And once reconnected, it should resend an identical request.
sc2.write_reply( req2[:handle] ) req2 = sc2.read_request
assert_equal req1, req2
data2 = sc2.read_data(4096)
assert_equal data1, data2
# Check it to make sure it's correct # The reply should be proxied back to the client.
rsp = timeout(15) { client.read_response } sc2.write_reply(req2[:handle])
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error]
assert_equal req1[:handle], rsp[:handle]
sc2.close # Check it to make sure it's correct
server.close rsp = Timeout.timeout(15) { client.read_response }
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error]
assert_equal req1[:handle], rsp[:handle]
sc2.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(1) do Timeout.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
c2.close rescue nil if c2 if c2
begin
c2.close
rescue StandardError
nil
end
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

View File

@@ -1,13 +1,10 @@
# 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
@@ -15,89 +12,77 @@ 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
=begin # # This is disabled while CLIENT_MAX_WAIT_SECS is removed
# This is disabled while CLIENT_MAX_WAIT_SECS is removed # def test_hello_goes_astray_causes_timeout_error
def test_hello_goes_astray_causes_timeout_error # run_fake( "source/hang_after_hello" )
run_fake( "source/hang_after_hello" ) # assert_no_control
assert_no_control # end
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 )
@env.run_fake( name, @env.ip, @env.port1 ) def run_fake(name)
@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

View File

@@ -1,5 +1,3 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
require 'flexnbd/constants' require 'flexnbd/constants'
@@ -19,20 +17,18 @@ 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, 10028, 25488].each do |num| [124, 1200, 10_028, 25_488].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
@@ -40,14 +36,14 @@ 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))
end end
end end
@@ -55,115 +51,105 @@ 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
(0..63).each do |num| (0..63).each do |num|
assert_equal(d0, @env.nbd1.read(((2*num)+1)*@env.blocksize, d0.size)) assert_equal(d0, @env.nbd1.read(((2 * num) + 1) * @env.blocksize, d0.size))
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
@env.nbd1.join @env.nbd1.join
@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)
stdout, stderr = @env.mirror12_unlink stdout, stderr = @env.mirror12_unlink
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") do |f| f.write( "1" * 256 * one_mb ) end File.open(@env.filename1, 'wb') { |f| f.write('1' * 256 * one_mb) }
@env.serve1 @env.serve1
sleep 5 sleep 5
@env.write1( data ) @env.write1(data)
@env.nbd1.can_die(0) @env.nbd1.can_die(0)
@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 = "Read non-zeros after offset %x:\n"%(i * one_mb) msg = format("Read non-zeros after offset %x:\n", (i * one_mb))
msg += `hexdump #{@env.filename1} | head -n5` msg += `hexdump #{@env.filename1} | head -n5`
fail msg raise msg
end end
i += 1 i += 1
end end
end end
end end
end end

View File

@@ -2,7 +2,6 @@ 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
@@ -10,7 +9,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
@@ -18,5 +17,3 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
super super
end end
end end

View File

@@ -2,14 +2,13 @@ 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
@@ -17,4 +16,3 @@ class TestProxyMode < Test::Unit::TestCase
super super
end end
end end

View File

@@ -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,69 +18,78 @@ class TestServeMode < Test::Unit::TestCase
end end
def connect_to_server def connect_to_server
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, "Connecting to server failed") @env.writefile1('0')
@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[:magic] assert_equal 'NBDMAGIC', result[:passwd]
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
client.close rescue nil begin
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)
rsp = client.read_response rsp = 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]
client.write( 0, @b ) client.write(0, @b)
rsp = client.read_response rsp = 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]
client.write( 0, @b * 2 ) client.write(0, @b * 2)
rsp = client.read_response rsp = 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]
end end
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]
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}" # NBD protocol suggests ENOSPC (28) is returned
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.
client.write_read_request( 0, 4096 ) client.write_read_request(0, 4096)
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
end end
@@ -88,23 +97,127 @@ class TestServeMode < Test::Unit::TestCase
def test_write_request_out_of_bounds_receives_error_response def test_write_request_out_of_bounds_receives_error_response
connect_to_server do |client| connect_to_server do |client|
client.write( @env.file1.size, "\x00" * 4096 ) client.write(@env.file1.size, "\x00" * 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]
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}" # NBD protocol suggests ENOSPC (28) is returned
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.
client.write( 0, "\x00" * @env.file1.size ) client.write(0, "\x00" * @env.file1.size)
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
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 end

View File

@@ -1,126 +1,105 @@
# 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]
return stdout, stderr [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

121
tests/acceptance/test_write_during_migration.rb Executable file → Normal file
View File

@@ -9,102 +9,98 @@ require 'tmpdir'
Thread.abort_on_exception = true Thread.abort_on_exception = true
class TestWriteDuringMigration < Test::Unit::TestCase class TestWriteDuringMigration < Test::Unit::TestCase
def setup def setup
@flexnbd = File.expand_path("../../build/flexnbd") @flexnbd = File.expand_path('../../build/flexnbd')
raise "No binary!" unless File.executable?( @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|
if pid next unless pid
Process.kill( "KILL", pid ) rescue nil begin
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() { @dst_proc = fork do
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() { @src_proc = fork do
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 while !File.exists?( @source_sock ) sleep 0.1 until File.exist?(@source_sock)
awaiting = :dest awaiting = :dest
sleep 0.1 while !File.exists?( @dest_sock ) sleep 0.1 until File.exist?(@dest_sock)
end end
rescue Timeout::Error rescue Timeout::Error
case awaiting case awaiting
when :source when :source
fail "Couldn't get a source socket." raise "Couldn't get a source socket."
when :dest when :dest
fail "Couldn't get a destination socket." raise "Couldn't get a destination socket."
else else
fail "Something went wrong I don't understand." raise "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) {|sock| UNIXSocket.open(@source_sock) do |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 => err rescue StandardError => 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
@@ -115,32 +111,32 @@ 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)
assert s_data == d_data, "Block #{block_num} mismatch!" assert s_data == d_data, "Block #{block_num} mismatch!"
source.seek( 4096, IO::SEEK_CUR ) source.seek(4096, IO::SEEK_CUR)
dest.seek( 4096, IO::SEEK_CUR ) dest.seek(4096, IO::SEEK_CUR)
end end
end end
end end
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
@@ -148,24 +144,21 @@ 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 {|t| t.join } (src_writers_1 + src_writers_2).each(&:join)
assert_both_sides_identical assert_both_sides_identical
end end
end end end end
end end

View File

@@ -4,226 +4,229 @@
#include "acl.h" #include "acl.h"
#include "util.h" #include "util.h"
START_TEST( test_null_acl ) START_TEST(test_null_acl)
{ {
struct acl *acl = acl_create( 0,NULL, 0 ); struct acl *acl = acl_create(0, NULL, 0);
fail_if( NULL == acl, "No acl alloced." ); fail_if(NULL == acl, "No acl alloced.");
fail_unless( 0 == acl->len, "Incorrect length" ); fail_unless(0 == acl->len, "Incorrect length");
} }
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" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
fail_unless( 1 == acl->len, "Incorrect length." ); fail_unless(1 == acl->len, "Incorrect length.");
fail_if( NULL == acl->entries, "No entries present." ); fail_if(NULL == acl->entries, "No entries present.");
} }
END_TEST END_TEST
START_TEST( test_parses_multiple_lines ) START_TEST(test_parses_multiple_lines)
{ {
char *lines[] = {"127.0.0.1", "::1"}; char *lines[] = { "127.0.0.1", "::1" };
struct acl * acl = acl_create( 2, lines, 0 ); struct acl *acl = acl_create(2, lines, 0);
union mysockaddr e0, e1; union mysockaddr e0, e1;
parse_ip_to_sockaddr( &e0.generic, lines[0] ); parse_ip_to_sockaddr(&e0.generic, lines[0]);
parse_ip_to_sockaddr( &e1.generic, lines[1] ); parse_ip_to_sockaddr(&e1.generic, lines[1]);
fail_unless( acl->len == 2, "Multiple lines not parsed" ); fail_unless(acl->len == 2, "Multiple lines not parsed");
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, "entry 0 has wrong family!"); fail_unless(entry->ip.family == e0.family,
entry = &(*acl->entries)[1]; "entry 0 has wrong family!");
fail_unless(entry->ip.family == e1.family, "entry 1 has wrong family!"); entry = &(*acl->entries)[1];
fail_unless(entry->ip.family == e1.family,
"entry 1 has wrong family!");
} }
END_TEST END_TEST
START_TEST( test_destroy_doesnt_crash ) START_TEST(test_destroy_doesnt_crash)
{ {
char *lines[] = {"127.0.0.1"}; char *lines[] = { "127.0.0.1" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
acl_destroy( acl ); acl_destroy(acl);
} }
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" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "127.0.0.1" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.1");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
} }
END_TEST END_TEST
START_TEST( test_includes_single_address_when_netmask_specified_ipv4 ) START_TEST(test_includes_single_address_when_netmask_specified_ipv4)
{ {
char *lines[] = {"127.0.0.1/24"}; char *lines[] = { "127.0.0.1/24" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "127.0.0.0" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.0");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
parse_ip_to_sockaddr( &x.generic, "127.0.0.1" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.1");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
parse_ip_to_sockaddr( &x.generic, "127.0.0.255" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.255");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
} }
END_TEST END_TEST
START_TEST( test_includes_single_address_when_netmask_specified_ipv6 ) START_TEST(test_includes_single_address_when_netmask_specified_ipv6)
{ {
char *lines[] = {"fe80::/10"}; char *lines[] = { "fe80::/10" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "fe80::1" ); parse_ip_to_sockaddr(&x.generic, "fe80::1");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
parse_ip_to_sockaddr( &x.generic, "fe80::2" ); parse_ip_to_sockaddr(&x.generic, "fe80::2");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
parse_ip_to_sockaddr( &x.generic, "fe80:ffff:ffff::ffff" ); parse_ip_to_sockaddr(&x.generic, "fe80:ffff:ffff::ffff");
fail_unless( acl_includes( acl, &x ), "Included address wasn't covered" ); fail_unless(acl_includes(acl, &x), "Included address wasn't covered");
} }
END_TEST END_TEST
START_TEST( test_includes_single_address_when_multiple_entries_exist ) START_TEST(test_includes_single_address_when_multiple_entries_exist)
{ {
char *lines[] = {"127.0.0.1", "::1"}; char *lines[] = { "127.0.0.1", "::1" };
struct acl * acl = acl_create( 2, lines, 0 ); struct acl *acl = acl_create(2, lines, 0);
union mysockaddr e0; union mysockaddr e0;
union mysockaddr e1; union mysockaddr e1;
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 ), "Included address 0 wasn't covered" ); fail_unless(acl_includes(acl, &e0),
fail_unless( acl_includes( acl, &e1 ), "Included address 1 wasn't covered" ); "Included address 0 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" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "127.0.0.2" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.2");
fail_if( acl_includes( acl, &x ), "Excluded address was covered." ); fail_if(acl_includes(acl, &x), "Excluded address was covered.");
} }
END_TEST END_TEST
START_TEST( test_doesnt_include_other_address_when_netmask_specified ) START_TEST(test_doesnt_include_other_address_when_netmask_specified)
{ {
char *lines[] = {"127.0.0.1/32"}; char *lines[] = { "127.0.0.1/32" };
struct acl * acl = acl_create( 1, lines, 0 ); struct acl *acl = acl_create(1, lines, 0);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "127.0.0.2" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.2");
fail_if( acl_includes( acl, &x ), "Excluded address was covered." ); fail_if(acl_includes(acl, &x), "Excluded address was covered.");
} }
END_TEST END_TEST
START_TEST( test_doesnt_include_other_address_when_multiple_entries_exist ) START_TEST(test_doesnt_include_other_address_when_multiple_entries_exist)
{ {
char *lines[] = {"127.0.0.1", "::1"}; char *lines[] = { "127.0.0.1", "::1" };
struct acl * acl = acl_create( 2, lines, 0 ); struct acl *acl = acl_create(2, lines, 0);
union mysockaddr e0; union mysockaddr e0;
union mysockaddr e1; union mysockaddr e1;
parse_ip_to_sockaddr( &e0.generic, "127.0.0.2" ); parse_ip_to_sockaddr(&e0.generic, "127.0.0.2");
parse_ip_to_sockaddr( &e1.generic, "::2" ); parse_ip_to_sockaddr(&e1.generic, "::2");
fail_if( acl_includes( acl, &e0 ), "Excluded address 0 was covered." ); fail_if(acl_includes(acl, &e0), "Excluded address 0 was covered.");
fail_if( acl_includes( acl, &e1 ), "Excluded address 1 was covered." ); fail_if(acl_includes(acl, &e1), "Excluded address 1 was covered.");
} }
END_TEST END_TEST
START_TEST( test_default_deny_rejects ) START_TEST(test_default_deny_rejects)
{ {
struct acl * acl = acl_create( 0, NULL, 1 ); struct acl *acl = acl_create(0, NULL, 1);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "127.0.0.1" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.1");
fail_if( acl_includes( acl, &x ), "Default deny accepted." ); fail_if(acl_includes(acl, &x), "Default deny accepted.");
} }
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);
union mysockaddr x; union mysockaddr x;
parse_ip_to_sockaddr( &x.generic, "127.0.0.1" ); parse_ip_to_sockaddr(&x.generic, "127.0.0.1");
fail_unless( acl_includes( acl, &x ), "Default accept rejected." ); fail_unless(acl_includes(acl, &x), "Default accept rejected.");
} }
END_TEST END_TEST
Suite * acl_suite(void)
Suite* acl_suite(void)
{ {
Suite *s = suite_create("acl"); Suite *s = suite_create("acl");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
TCase *tc_includes = tcase_create("includes"); TCase *tc_includes = tcase_create("includes");
TCase *tc_destroy = tcase_create("destroy"); TCase *tc_destroy = tcase_create("destroy");
tcase_add_test(tc_create, test_null_acl); tcase_add_test(tc_create, test_null_acl);
tcase_add_test(tc_create, test_parses_single_line); tcase_add_test(tc_create, test_parses_single_line);
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, test_includes_single_address_when_netmask_specified_ipv4); tcase_add_test(tc_includes,
tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv6); 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_multiple_entries_exist); tcase_add_test(tc_includes,
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, test_doesnt_include_other_address_when_netmask_specified); tcase_add_test(tc_includes,
tcase_add_test(tc_includes, test_doesnt_include_other_address_when_multiple_entries_exist); 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_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);
tcase_add_test(tc_destroy, test_destroy_doesnt_crash); tcase_add_test(tc_destroy, test_destroy_doesnt_crash);
suite_add_tcase(s, tc_create); suite_add_tcase(s, tc_create);
suite_add_tcase(s, tc_includes); suite_add_tcase(s, tc_includes);
suite_add_tcase(s, tc_destroy); suite_add_tcase(s, tc_destroy);
return s; return s;
} }
int main(void) int main(void)
{ {
#ifdef DEBUG #ifdef DEBUG
log_level = 0; log_level = 0;
#else #else
log_level = 2; log_level = 2;
#endif #endif
int number_failed; int number_failed;
Suite *s = acl_suite(); Suite *s = acl_suite();
SRunner *sr = srunner_create(s); SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL); srunner_run_all(sr, CK_NORMAL);
log_level = 0; log_level = 0;
number_failed = srunner_ntests_failed(sr); number_failed = srunner_ntests_failed(sr);
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -9,492 +9,492 @@
START_TEST(test_bit_set) START_TEST(test_bit_set)
{ {
uint64_t num = 0; uint64_t num = 0;
bitfield_p bits = (bitfield_p) &num; bitfield_p bits = (bitfield_p) & num;
#define TEST_BIT_SET(bit, newvalue) \ #define TEST_BIT_SET(bit, newvalue) \
bit_set(bits, (bit)); \ bit_set(bits, (bit)); \
fail_unless(num == (newvalue), "num was %x instead of %x", num, (newvalue)); fail_unless(num == (newvalue), "num was %x instead of %x", num, (newvalue));
TEST_BIT_SET(0, 1); TEST_BIT_SET(0, 1);
TEST_BIT_SET(1, 3); TEST_BIT_SET(1, 3);
TEST_BIT_SET(2, 7); TEST_BIT_SET(2, 7);
TEST_BIT_SET(7, 0x87); TEST_BIT_SET(7, 0x87);
TEST_BIT_SET(63, 0x8000000000000087); TEST_BIT_SET(63, 0x8000000000000087);
} }
END_TEST END_TEST
START_TEST(test_bit_clear) START_TEST(test_bit_clear)
{ {
uint64_t num = 0xffffffffffffffff; uint64_t num = 0xffffffffffffffff;
bitfield_p bits = (bitfield_p) &num; bitfield_p bits = (bitfield_p) & num;
#define TEST_BIT_CLEAR(bit, newvalue) \ #define TEST_BIT_CLEAR(bit, newvalue) \
bit_clear(bits, (bit)); \ bit_clear(bits, (bit)); \
fail_unless(num == (newvalue), "num was %x instead of %x", num, (newvalue)); fail_unless(num == (newvalue), "num was %x instead of %x", num, (newvalue));
TEST_BIT_CLEAR(0, 0xfffffffffffffffe); TEST_BIT_CLEAR(0, 0xfffffffffffffffe);
TEST_BIT_CLEAR(1, 0xfffffffffffffffc); TEST_BIT_CLEAR(1, 0xfffffffffffffffc);
TEST_BIT_CLEAR(2, 0xfffffffffffffff8); TEST_BIT_CLEAR(2, 0xfffffffffffffff8);
TEST_BIT_CLEAR(7, 0xffffffffffffff78); TEST_BIT_CLEAR(7, 0xffffffffffffff78);
TEST_BIT_CLEAR(63,0x7fffffffffffff78); TEST_BIT_CLEAR(63, 0x7fffffffffffff78);
} }
END_TEST END_TEST
START_TEST(test_bit_tests) START_TEST(test_bit_tests)
{ {
uint64_t num = 0x5555555555555555; uint64_t num = 0x5555555555555555;
bitfield_p bits = (bitfield_p) &num; bitfield_p bits = (bitfield_p) & num;
fail_unless(bit_has_value(bits, 0, 1), "bit_has_value malfunction"); fail_unless(bit_has_value(bits, 0, 1), "bit_has_value malfunction");
fail_unless(bit_has_value(bits, 1, 0), "bit_has_value malfunction"); fail_unless(bit_has_value(bits, 1, 0), "bit_has_value malfunction");
fail_unless(bit_has_value(bits, 63, 0), "bit_has_value malfunction"); fail_unless(bit_has_value(bits, 63, 0), "bit_has_value malfunction");
fail_unless(bit_is_set(bits, 0), "bit_is_set malfunction"); fail_unless(bit_is_set(bits, 0), "bit_is_set malfunction");
fail_unless(bit_is_clear(bits, 1), "bit_is_clear malfunction"); fail_unless(bit_is_clear(bits, 1), "bit_is_clear malfunction");
fail_unless(bit_is_set(bits, 62), "bit_is_set malfunction"); fail_unless(bit_is_set(bits, 62), "bit_is_set malfunction");
fail_unless(bit_is_clear(bits, 63), "bit_is_clear malfunction"); fail_unless(bit_is_clear(bits, 63), "bit_is_clear malfunction");
} }
END_TEST END_TEST
START_TEST(test_bit_ranges) START_TEST(test_bit_ranges)
{ {
bitfield_word_t buffer[BIT_WORDS_FOR_SIZE(4160)]; bitfield_word_t buffer[BIT_WORDS_FOR_SIZE(4160)];
uint64_t *longs = (uint64_t *) buffer; uint64_t *longs = (uint64_t *) buffer;
uint64_t i; uint64_t i;
memset(buffer, 0, 4160); memset(buffer, 0, 4160);
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( fail_unless(longs[i] == (1ULL << i) - 1,
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", i); fail_unless(longs[i + 1] == 0, "bit_set_range overshot at i=%d",
} 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", i); fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d",
} i);
}
} }
END_TEST END_TEST
START_TEST(test_bit_runs) 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,81,79,62 56, 97, 22, 12, 83, 1, 45, 80, 85, 51, 64, 40, 63, 67, 75, 64, 94,
}; 81, 79, 62
};
memset(buffer,0,256); memset(buffer, 0, 256);
for (i=0; i < 20; i += 2) { for (i = 0; i < 20; i += 2) {
ptr += runs[i]; ptr += runs[i];
bit_set_range(buffer, ptr, runs[i+1]); bit_set_range(buffer, ptr, runs[i + 1]);
ptr += runs[i+1]; ptr += runs[i + 1];
} }
ptr = 0; ptr = 0;
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( fail_unless(run == runs[i],
run == runs[i], "run %d should have been %d, was %d", i, runs[i], run);
"run %d should have been %d, was %d", ptr += runs[i];
i, runs[i], run }
);
ptr += runs[i];
}
} }
END_TEST END_TEST
START_TEST(test_bitset) START_TEST(test_bitset)
{ {
struct bitset * map; struct bitset *map;
uint64_t *num; uint64_t *num;
map = bitset_alloc(6400, 100); map = bitset_alloc(6400, 100);
num = (uint64_t*) map->bits; num = (uint64_t *) map->bits;
bitset_set_range(map,0,50); bitset_set_range(map, 0, 50);
ck_assert_int_eq(1, *num); ck_assert_int_eq(1, *num);
bitset_set_range(map,99,1); bitset_set_range(map, 99, 1);
ck_assert_int_eq(1, *num); ck_assert_int_eq(1, *num);
bitset_set_range(map,100,1); bitset_set_range(map, 100, 1);
ck_assert_int_eq(3, *num); ck_assert_int_eq(3, *num);
bitset_set_range(map,0,800); bitset_set_range(map, 0, 800);
ck_assert_int_eq(255, *num); ck_assert_int_eq(255, *num);
bitset_set_range(map,1499,2); bitset_set_range(map, 1499, 2);
ck_assert_int_eq(0xc0ff, *num); ck_assert_int_eq(0xc0ff, *num);
bitset_clear_range(map,1499,2); bitset_clear_range(map, 1499, 2);
ck_assert_int_eq(255, *num); ck_assert_int_eq(255, *num);
*num = 0; *num = 0;
bitset_set_range(map, 1499, 2); bitset_set_range(map, 1499, 2);
bitset_clear_range(map, 1300, 200); bitset_clear_range(map, 1300, 200);
ck_assert_int_eq(0x8000, *num); ck_assert_int_eq(0x8000, *num);
*num = 0; *num = 0;
bitset_set_range(map, 0, 6400); bitset_set_range(map, 0, 6400);
ck_assert_int_eq(0xffffffffffffffff, *num); ck_assert_int_eq(0xffffffffffffffff, *num);
bitset_clear_range(map, 3200, 400); bitset_clear_range(map, 3200, 400);
ck_assert_int_eq(0xfffffff0ffffffff, *num); ck_assert_int_eq(0xfffffff0ffffffff, *num);
} }
END_TEST END_TEST
START_TEST(test_bitset_set)
START_TEST( test_bitset_set )
{ {
struct bitset * map; struct bitset *map;
uint64_t run; uint64_t run;
map = bitset_alloc(64, 1); map = bitset_alloc(64, 1);
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
bitset_set( map ); bitset_set(map);
assert_bitset_is( map, 0xffffffffffffffff ); assert_bitset_is(map, 0xffffffffffffffff);
bitset_free( map ); bitset_free(map);
map = bitset_alloc( 6400, 100 ); map = bitset_alloc(6400, 100);
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
bitset_set( map ); bitset_set(map);
assert_bitset_is( map, 0xffffffffffffffff ); assert_bitset_is(map, 0xffffffffffffffff);
bitset_free( map ); bitset_free(map);
// Now do something large and representative // Now do something large and representative
map = bitset_alloc( 53687091200, 4096 ); map = bitset_alloc(53687091200, 4096);
bitset_set( map ); bitset_set(map);
run = bitset_run_count( map, 0, 53687091200 ); run = bitset_run_count(map, 0, 53687091200);
ck_assert_int_eq( run, 53687091200 ); ck_assert_int_eq(run, 53687091200);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_clear)
START_TEST( test_bitset_clear )
{ {
struct bitset * map; struct bitset *map;
uint64_t *num; uint64_t *num;
uint64_t run; uint64_t run;
map = bitset_alloc(64, 1); map = bitset_alloc(64, 1);
num = (uint64_t*) map->bits; num = (uint64_t *) map->bits;
ck_assert_int_eq( 0x0000000000000000, *num ); ck_assert_int_eq(0x0000000000000000, *num);
bitset_set( map ); bitset_set(map);
bitset_clear( map ); bitset_clear(map);
ck_assert_int_eq( 0x0000000000000000, *num ); ck_assert_int_eq(0x0000000000000000, *num);
bitset_free( map ); bitset_free(map);
// Now do something large and representative // Now do something large and representative
map = bitset_alloc( 53687091200, 4096 ); map = bitset_alloc(53687091200, 4096);
bitset_set( map ); bitset_set(map);
bitset_clear( map ); bitset_clear(map);
run = bitset_run_count( map, 0, 53687091200 ); run = bitset_run_count(map, 0, 53687091200);
ck_assert_int_eq( run, 53687091200 ); ck_assert_int_eq(run, 53687091200);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST( test_bitset_set_range ) START_TEST(test_bitset_set_range)
{ {
struct bitset* map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
bitset_set_range( map, 8, 8 ); bitset_set_range(map, 8, 8);
assert_bitset_is( map, 0x000000000000ff00 ); assert_bitset_is(map, 0x000000000000ff00);
bitset_clear( map ); bitset_clear(map);
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
bitset_set_range( map, 0, 0 ); bitset_set_range(map, 0, 0);
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST( test_bitset_clear_range ) START_TEST(test_bitset_clear_range)
{ {
struct bitset* map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
bitset_set( map ); bitset_set(map);
assert_bitset_is( map, 0xffffffffffffffff ); assert_bitset_is(map, 0xffffffffffffffff);
bitset_clear_range( map, 8, 8 ); bitset_clear_range(map, 8, 8);
assert_bitset_is( map, 0xffffffffffff00ff ); assert_bitset_is(map, 0xffffffffffff00ff);
bitset_set( map ); bitset_set(map);
assert_bitset_is( map, 0xffffffffffffffff ); assert_bitset_is(map, 0xffffffffffffffff);
bitset_clear_range( map, 0, 0 ); bitset_clear_range(map, 0, 0);
assert_bitset_is( map, 0xffffffffffffffff ); assert_bitset_is(map, 0xffffffffffffffff);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST( test_bitset_run_count ) START_TEST(test_bitset_run_count)
{ {
struct bitset* map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
uint64_t run; uint64_t run;
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
run = bitset_run_count( map, 0, 64 ); run = bitset_run_count(map, 0, 64);
ck_assert_int_eq( 64, run ); ck_assert_int_eq(64, run);
bitset_set_range( map, 0, 32 ); bitset_set_range(map, 0, 32);
assert_bitset_is( map, 0x00000000ffffffff ); assert_bitset_is(map, 0x00000000ffffffff);
run = bitset_run_count( map, 0, 64 ); run = bitset_run_count(map, 0, 64);
ck_assert_int_eq( 32, run ); ck_assert_int_eq(32, run);
run = bitset_run_count( map, 0, 16 ); run = bitset_run_count(map, 0, 16);
ck_assert_int_eq( 16, run ); ck_assert_int_eq(16, run);
run = bitset_run_count( map, 16, 64 ); run = bitset_run_count(map, 16, 64);
ck_assert_int_eq( 16, run ); ck_assert_int_eq(16, run);
run = bitset_run_count( map, 31, 64 ); run = bitset_run_count(map, 31, 64);
ck_assert_int_eq( 1, run ); ck_assert_int_eq(1, run);
run = bitset_run_count( map, 32, 64 ); run = bitset_run_count(map, 32, 64);
ck_assert_int_eq( 32, run ); ck_assert_int_eq(32, run);
run = bitset_run_count( map, 32, 32 ); run = bitset_run_count(map, 32, 32);
ck_assert_int_eq( 32, run ); ck_assert_int_eq(32, run);
run = bitset_run_count( map, 32, 16 ); run = bitset_run_count(map, 32, 16);
ck_assert_int_eq( 16, run ); ck_assert_int_eq(16, run);
bitset_free( map ); bitset_free(map);
map = bitset_alloc( 6400, 100 ); map = bitset_alloc(6400, 100);
assert_bitset_is( map, 0x0000000000000000 ); assert_bitset_is(map, 0x0000000000000000);
run = bitset_run_count( map, 0, 6400 ); run = bitset_run_count(map, 0, 6400);
ck_assert_int_eq( 6400, run ); ck_assert_int_eq(6400, run);
bitset_set_range( map, 0, 3200 ); bitset_set_range(map, 0, 3200);
run = bitset_run_count( map, 0, 6400 ); run = bitset_run_count(map, 0, 6400);
ck_assert_int_eq( 3200, run ); ck_assert_int_eq(3200, run);
run = bitset_run_count( map, 1, 6400 ); run = bitset_run_count(map, 1, 6400);
ck_assert_int_eq( 3199, run ); ck_assert_int_eq(3199, run);
run = bitset_run_count( map, 3200, 6400 ); run = bitset_run_count(map, 3200, 6400);
ck_assert_int_eq( 3200, run ); ck_assert_int_eq(3200, run);
run = bitset_run_count( map, 6500, 6400 ); run = bitset_run_count(map, 6500, 6400);
ck_assert_int_eq( 0, run ); ck_assert_int_eq(0, run);
bitset_free( map ); bitset_free(map);
// Now do something large and representative // Now do something large and representative
map = bitset_alloc( 53687091200, 4096 ); map = bitset_alloc(53687091200, 4096);
bitset_set( map ); bitset_set(map);
run = bitset_run_count( map, 0, 53687091200 ); run = bitset_run_count(map, 0, 53687091200);
ck_assert_int_eq( run, 53687091200 ); ck_assert_int_eq(run, 53687091200);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST( test_bitset_set_range_doesnt_push_to_stream ) START_TEST(test_bitset_set_range_doesnt_push_to_stream)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
bitset_set_range( map, 0, 64 ); bitset_set_range(map, 0, 64);
ck_assert_int_eq( 0, bitset_stream_size( map ) ); ck_assert_int_eq(0, bitset_stream_size(map));
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST( test_bitset_clear_range_doesnt_push_to_stream ) START_TEST(test_bitset_clear_range_doesnt_push_to_stream)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
bitset_clear_range( map, 0, 64 ); bitset_clear_range(map, 0, 64);
ck_assert_int_eq( 0, bitset_stream_size( map ) ); ck_assert_int_eq(0, bitset_stream_size(map));
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_enable_stream) START_TEST(test_bitset_enable_stream)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
struct bitset_stream_entry result; struct bitset_stream_entry result;
memset( &result, 0, sizeof( result ) ); memset(&result, 0, sizeof(result));
bitset_enable_stream( map ); bitset_enable_stream(map);
ck_assert_int_eq( 1, map->stream_enabled ); ck_assert_int_eq(1, map->stream_enabled);
bitset_stream_dequeue( map, &result ); bitset_stream_dequeue(map, &result);
ck_assert_int_eq( BITSET_STREAM_ON, result.event ); ck_assert_int_eq(BITSET_STREAM_ON, result.event);
ck_assert_int_eq( 0, result.from ); ck_assert_int_eq(0, result.from);
ck_assert_int_eq( 64, result.len ); ck_assert_int_eq(64, result.len);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_disable_stream) START_TEST(test_bitset_disable_stream)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
struct bitset_stream_entry result; struct bitset_stream_entry result;
memset( &result, 0, sizeof( result ) ); memset(&result, 0, sizeof(result));
bitset_enable_stream( map ); bitset_enable_stream(map);
bitset_disable_stream( map ); bitset_disable_stream(map);
ck_assert_int_eq( 0, map->stream_enabled ); ck_assert_int_eq(0, map->stream_enabled);
ck_assert_int_eq( 2, bitset_stream_size( map ) ); ck_assert_int_eq(2, bitset_stream_size(map));
bitset_stream_dequeue( map, NULL ); // ON bitset_stream_dequeue(map, NULL); // ON
bitset_stream_dequeue( map, &result ); // OFF bitset_stream_dequeue(map, &result); // OFF
ck_assert_int_eq( BITSET_STREAM_OFF, result.event ); ck_assert_int_eq(BITSET_STREAM_OFF, result.event);
ck_assert_int_eq( 0, result.from ); ck_assert_int_eq(0, result.from);
ck_assert_int_eq( 64, result.len ); ck_assert_int_eq(64, result.len);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_stream_with_set_range) START_TEST(test_bitset_stream_with_set_range)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
struct bitset_stream_entry result; struct bitset_stream_entry result;
memset( &result, 0, sizeof( result ) ); memset(&result, 0, sizeof(result));
bitset_enable_stream( map ); bitset_enable_stream(map);
bitset_set_range( map, 0, 32 ); bitset_set_range(map, 0, 32);
ck_assert_int_eq( 2, bitset_stream_size( map ) ); ck_assert_int_eq(2, bitset_stream_size(map));
bitset_stream_dequeue( map, NULL ); // ON bitset_stream_dequeue(map, NULL); // ON
bitset_stream_dequeue( map, &result ); // SET bitset_stream_dequeue(map, &result); // SET
ck_assert_int_eq( BITSET_STREAM_SET, result.event ); ck_assert_int_eq(BITSET_STREAM_SET, result.event);
ck_assert_int_eq( 0, result.from ); ck_assert_int_eq(0, result.from);
ck_assert_int_eq( 32, result.len ); ck_assert_int_eq(32, result.len);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_stream_with_clear_range) START_TEST(test_bitset_stream_with_clear_range)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
struct bitset_stream_entry result; struct bitset_stream_entry result;
memset( &result, 0, sizeof( result ) ); memset(&result, 0, sizeof(result));
bitset_enable_stream( map ); bitset_enable_stream(map);
bitset_clear_range( map, 0, 32 ); bitset_clear_range(map, 0, 32);
ck_assert_int_eq( 2, bitset_stream_size( map ) ); ck_assert_int_eq(2, bitset_stream_size(map));
bitset_stream_dequeue( map, NULL ); // ON bitset_stream_dequeue(map, NULL); // ON
bitset_stream_dequeue( map, &result ); // UNSET bitset_stream_dequeue(map, &result); // UNSET
ck_assert_int_eq( BITSET_STREAM_UNSET, result.event ); ck_assert_int_eq(BITSET_STREAM_UNSET, result.event);
ck_assert_int_eq( 0, result.from ); ck_assert_int_eq(0, result.from);
ck_assert_int_eq( 32, result.len ); ck_assert_int_eq(32, result.len);
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_stream_size) START_TEST(test_bitset_stream_size)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
bitset_enable_stream( map ); bitset_enable_stream(map);
bitset_set_range( map, 0, 32 ); bitset_set_range(map, 0, 32);
bitset_set_range( map, 16, 32 ); bitset_set_range(map, 16, 32);
bitset_set_range( map, 7, 16 ); bitset_set_range(map, 7, 16);
bitset_clear_range( map, 0, 32 ); bitset_clear_range(map, 0, 32);
bitset_clear_range( map, 16, 32 ); bitset_clear_range(map, 16, 32);
bitset_clear_range( map, 48, 16 ); bitset_clear_range(map, 48, 16);
bitset_disable_stream( map ); bitset_disable_stream(map);
ck_assert_int_eq( 8, bitset_stream_size( map ) ); ck_assert_int_eq(8, bitset_stream_size(map));
bitset_free( map ); bitset_free(map);
} }
END_TEST END_TEST
START_TEST(test_bitset_stream_queued_bytes) START_TEST(test_bitset_stream_queued_bytes)
{ {
struct bitset *map = bitset_alloc( 64, 1 ); struct bitset *map = bitset_alloc(64, 1);
bitset_enable_stream( map ); bitset_enable_stream(map);
bitset_set_range( map, 0, 32 ); bitset_set_range(map, 0, 32);
bitset_set_range( map, 16, 32 ); bitset_set_range(map, 16, 32);
bitset_set_range( map, 7, 16 ); bitset_set_range(map, 7, 16);
bitset_clear_range( map, 0, 32 ); bitset_clear_range(map, 0, 32);
bitset_clear_range( map, 16, 32 ); bitset_clear_range(map, 16, 32);
bitset_clear_range( map, 48, 16 ); bitset_clear_range(map, 48, 16);
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, bitset_stream_queued_bytes( map, BITSET_STREAM_ON ) ); ck_assert_int_eq(64,
ck_assert_int_eq( 80, bitset_stream_queued_bytes( map, BITSET_STREAM_SET ) ); bitset_stream_queued_bytes(map, BITSET_STREAM_ON));
ck_assert_int_eq( 82, bitset_stream_queued_bytes( map, BITSET_STREAM_UNSET ) ); ck_assert_int_eq(80,
ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_OFF ) ); bitset_stream_queued_bytes(map, BITSET_STREAM_SET));
bitset_free( map ); 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);
} }
END_TEST END_TEST
Suite* bitset_suite(void) Suite * bitset_suite(void)
{ {
Suite *s = suite_create("bitset"); Suite *s = suite_create("bitset");
TCase *tc_bit = tcase_create("bit"); TCase *tc_bit = tcase_create("bit");
tcase_add_test(tc_bit, test_bit_set); tcase_add_test(tc_bit, test_bit_set);
tcase_add_test(tc_bit, test_bit_clear); tcase_add_test(tc_bit, test_bit_clear);
tcase_add_test(tc_bit, test_bit_tests); tcase_add_test(tc_bit, test_bit_tests);
tcase_add_test(tc_bit, test_bit_ranges); tcase_add_test(tc_bit, test_bit_ranges);
tcase_add_test(tc_bit, test_bit_runs); tcase_add_test(tc_bit, test_bit_runs);
suite_add_tcase(s, tc_bit); suite_add_tcase(s, tc_bit);
TCase *tc_bitset = tcase_create("bitset"); TCase *tc_bitset = tcase_create("bitset");
tcase_add_test(tc_bitset, test_bitset); tcase_add_test(tc_bitset, test_bitset);
tcase_add_test(tc_bitset, test_bitset_set); tcase_add_test(tc_bitset, test_bitset_set);
tcase_add_test(tc_bitset, test_bitset_clear); tcase_add_test(tc_bitset, test_bitset_clear);
tcase_add_test(tc_bitset, test_bitset_run_count); tcase_add_test(tc_bitset, test_bitset_run_count);
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, test_bitset_clear_range_doesnt_push_to_stream); tcase_add_test(tc_bitset,
suite_add_tcase(s, tc_bitset); test_bitset_clear_range_doesnt_push_to_stream);
suite_add_tcase(s, tc_bitset);
TCase *tc_bitset_stream = tcase_create("bitset_stream"); TCase *tc_bitset_stream = tcase_create("bitset_stream");
tcase_add_test(tc_bitset_stream, test_bitset_enable_stream); tcase_add_test(tc_bitset_stream, test_bitset_enable_stream);
tcase_add_test(tc_bitset_stream, test_bitset_disable_stream); tcase_add_test(tc_bitset_stream, test_bitset_disable_stream);
tcase_add_test(tc_bitset_stream, test_bitset_stream_with_set_range); tcase_add_test(tc_bitset_stream, test_bitset_stream_with_set_range);
tcase_add_test(tc_bitset_stream, test_bitset_stream_with_clear_range); tcase_add_test(tc_bitset_stream, test_bitset_stream_with_clear_range);
tcase_add_test(tc_bitset_stream, test_bitset_stream_size); tcase_add_test(tc_bitset_stream, test_bitset_stream_size);
tcase_add_test(tc_bitset_stream, test_bitset_stream_queued_bytes); tcase_add_test(tc_bitset_stream, test_bitset_stream_queued_bytes);
suite_add_tcase(s, tc_bitset_stream); suite_add_tcase(s, tc_bitset_stream);
return s; return s;
} }
int main(void) int main(void)
{ {
int number_failed; int number_failed;
Suite *s = bitset_suite(); Suite *s = bitset_suite();
SRunner *sr = srunner_create(s); SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL); srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr); number_failed = srunner_ntests_failed(sr);
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -9,114 +9,109 @@
#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)
START_TEST( test_assigns_socket ) START_TEST(test_assigns_socket)
{ {
struct client * c; struct client *c;
c = client_create( FAKE_SERVER, FAKE_SOCKET ); c = client_create(FAKE_SERVER, FAKE_SOCKET);
fail_unless( 42 == c->socket, "Socket wasn't assigned." ); fail_unless(42 == c->socket, "Socket wasn't assigned.");
} }
END_TEST END_TEST
START_TEST(test_assigns_server)
START_TEST( test_assigns_server )
{ {
struct client * c; struct client *c;
/* can't predict the storage size so we can't allocate one on /* can't predict the storage size so we can't allocate one on
* the stack * the stack
*/ */
c = client_create( FAKE_SERVER, FAKE_SOCKET ); c = client_create(FAKE_SERVER, FAKE_SOCKET);
fail_unless( FAKE_SERVER == c->serve, "Serve wasn't assigned." ); fail_unless(FAKE_SERVER == c->serve, "Serve wasn't assigned.");
} }
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);
client_signal_stop( c ); client_signal_stop(c);
fail_unless( 1 == self_pipe_signal_clear( c->stop_signal ), fail_unless(1 == self_pipe_signal_clear(c->stop_signal),
"No signal was sent." ); "No signal was sent.");
} }
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)
{ {
struct client *c = client_create( FAKE_SERVER, FAKE_SOCKET ); struct client *c = client_create(FAKE_SERVER, FAKE_SOCKET);
int read_fd = c->stop_signal->read_fd; int read_fd = c->stop_signal->read_fd;
int write_fd = c->stop_signal->write_fd; int write_fd = c->stop_signal->write_fd;
client_destroy( c ); client_destroy(c);
fail_unless( fd_is_closed( read_fd ), "Stop signal wasn't destroyed." ); fail_unless(fd_is_closed(read_fd), "Stop signal wasn't destroyed.");
fail_unless( fd_is_closed( write_fd ), "Stop signal wasn't destroyed." ); fail_unless(fd_is_closed(write_fd), "Stop signal wasn't destroyed.");
} }
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];
struct nbd_request nbdr; struct nbd_request nbdr;
pipe( fds ); pipe(fds);
struct client *c = client_create( FAKE_SERVER, fds[0] ); struct client *c = client_create(FAKE_SERVER, fds[0]);
client_signal_stop( c );
int client_serve_request( struct client *); client_signal_stop(c);
fail_unless( 1 == client_serve_request( c ), "Didn't quit on stop." );
close( fds[0] ); int client_serve_request(struct client *);
close( fds[1] ); fail_unless(1 == client_serve_request(c), "Didn't quit on stop.");
close(fds[0]);
close(fds[1]);
} }
END_TEST END_TEST
Suite * client_suite(void)
Suite *client_suite(void)
{ {
Suite *s = suite_create("client"); Suite *s = suite_create("client");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
TCase *tc_signal = tcase_create("signal"); TCase *tc_signal = tcase_create("signal");
TCase *tc_destroy = tcase_create("destroy"); TCase *tc_destroy = tcase_create("destroy");
tcase_add_test(tc_create, test_assigns_socket); tcase_add_test(tc_create, test_assigns_socket);
tcase_add_test(tc_create, test_assigns_server); tcase_add_test(tc_create, test_assigns_server);
tcase_add_test(tc_signal, test_opens_stop_signal); tcase_add_test(tc_signal, test_opens_stop_signal);
tcase_add_test(tc_signal, test_read_request_quits_on_stop_signal); tcase_add_test(tc_signal, test_read_request_quits_on_stop_signal);
tcase_add_test( tc_destroy, test_closes_stop_signal ); tcase_add_test(tc_destroy, test_closes_stop_signal);
suite_add_tcase(s, tc_create); suite_add_tcase(s, tc_create);
suite_add_tcase(s, tc_signal); suite_add_tcase(s, tc_signal);
suite_add_tcase(s, tc_destroy); suite_add_tcase(s, tc_destroy);
return s; return s;
} }
int main(void) int main(void)
{ {
int number_failed; int number_failed;
Suite *s = client_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}
Suite *s = client_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}

View File

@@ -4,39 +4,37 @@
#include <check.h> #include <check.h>
START_TEST( test_assigns_sock_name ) START_TEST(test_assigns_sock_name)
{ {
struct flexnbd flexnbd = {0}; struct flexnbd flexnbd = { 0 };
char csn[] = "foobar"; char csn[] = "foobar";
struct control * control = control_create(&flexnbd, csn ); struct control *control = control_create(&flexnbd, csn);
fail_unless( csn == control->socket_name, "Socket name not assigned" ); fail_unless(csn == control->socket_name, "Socket name not assigned");
} }
END_TEST END_TEST
Suite * control_suite(void)
Suite *control_suite(void)
{ {
Suite *s = suite_create("control"); Suite *s = suite_create("control");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
tcase_add_test(tc_create, test_assigns_sock_name); tcase_add_test(tc_create, test_assigns_sock_name);
suite_add_tcase( s, tc_create ); suite_add_tcase(s, tc_create);
return s; return s;
} }
int main(void) int main(void)
{ {
int number_failed; int number_failed;
Suite *s = control_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}
Suite *s = control_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}

View File

@@ -3,42 +3,39 @@
#include <check.h> #include <check.h>
START_TEST( test_listening_assigns_sock ) START_TEST(test_listening_assigns_sock)
{ {
struct flexnbd * flexnbd = flexnbd_create_listening( struct flexnbd *flexnbd = flexnbd_create_listening("127.0.0.1",
"127.0.0.1", "4777",
"4777", "fakefile",
"fakefile", "fakesock",
"fakesock", 0,
0, 0,
0, NULL);
NULL ); fail_if(NULL == flexnbd->control->socket_name, "No socket was copied");
fail_if( NULL == flexnbd->control->socket_name, "No socket was copied" );
} }
END_TEST END_TEST
Suite * flexnbd_suite(void)
Suite *flexnbd_suite(void)
{ {
Suite *s = suite_create("flexnbd"); Suite *s = suite_create("flexnbd");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
tcase_add_test(tc_create, test_listening_assigns_sock); tcase_add_test(tc_create, test_listening_assigns_sock);
suite_add_tcase( s, tc_create ); suite_add_tcase(s, tc_create);
return s; return s;
} }
int main(void) int main(void)
{ {
int number_failed; int number_failed;
Suite *s = flexnbd_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}
Suite *s = flexnbd_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}

View File

@@ -4,59 +4,59 @@
#include <check.h> #include <check.h>
START_TEST( test_mutex_create ) START_TEST(test_mutex_create)
{ {
struct flexthread_mutex * ftm = flexthread_mutex_create(); struct flexthread_mutex *ftm = flexthread_mutex_create();
NULLCHECK( ftm ); NULLCHECK(ftm);
flexthread_mutex_destroy( ftm ); flexthread_mutex_destroy(ftm);
} }
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 ), "Flexthread_mutex is held before lock" ); fail_if(flexthread_mutex_held(ftm),
flexthread_mutex_lock( ftm ); "Flexthread_mutex is held before lock");
fail_unless( flexthread_mutex_held( ftm ), "Flexthread_mutex is not held inside lock" ); flexthread_mutex_lock(ftm);
flexthread_mutex_unlock( ftm ); fail_unless(flexthread_mutex_held(ftm),
fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held after unlock" ); "Flexthread_mutex is not held inside lock");
flexthread_mutex_unlock(ftm);
fail_if(flexthread_mutex_held(ftm),
"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");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
TCase *tc_destroy = tcase_create("destroy"); TCase *tc_destroy = tcase_create("destroy");
tcase_add_test( tc_create, test_mutex_create ); tcase_add_test(tc_create, test_mutex_create);
tcase_add_test( tc_create, test_mutex_lock ); tcase_add_test(tc_create, test_mutex_lock);
suite_add_tcase(s, tc_create); suite_add_tcase(s, tc_create);
suite_add_tcase(s, tc_destroy); suite_add_tcase(s, tc_destroy);
return s; return s;
} }
int main(void) int main(void)
{ {
#ifdef DEBUG #ifdef DEBUG
log_level = 0; log_level = 0;
#else #else
log_level = 2; log_level = 2;
#endif #endif
int number_failed; int number_failed;
Suite *s = flexthread_suite(); Suite *s = flexthread_suite();
SRunner *sr = srunner_create(s); SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL); srunner_run_all(sr, CK_NORMAL);
log_level = 0; log_level = 0;
number_failed = srunner_ntests_failed(sr); number_failed = srunner_ntests_failed(sr);
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -2,133 +2,131 @@
#include <check.h> #include <check.h>
START_TEST( test_read_until_newline_returns_line_length_plus_null ) START_TEST(test_read_until_newline_returns_line_length_plus_null)
{ {
int fds[2]; int fds[2];
int nread; int nread;
char buf[5] = {0}; char buf[5] = { 0 };
pipe(fds); pipe(fds);
write( fds[1], "1234\n", 5 ); write(fds[1], "1234\n", 5);
nread = read_until_newline( fds[0], buf, 5 );
ck_assert_int_eq( 5, nread ); nread = read_until_newline(fds[0], buf, 5);
ck_assert_int_eq(5, nread);
} }
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];
int nread; int nread;
char buf[5] = {0}; char buf[5] = { 0 };
pipe(fds); pipe(fds);
write( fds[1], "1234\n", 5 ); write(fds[1], "1234\n", 5);
nread = read_until_newline( fds[0], buf, 5 );
ck_assert_int_eq( '\0', buf[4] ); nread = read_until_newline(fds[0], buf, 5);
ck_assert_int_eq('\0', buf[4]);
} }
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];
int nread; int nread;
char buf[5] = {0}; char buf[5] = { 0 };
pipe(fds); pipe(fds);
write( fds[1], "\n", 1 ); write(fds[1], "\n", 1);
nread = read_until_newline( fds[0], buf, 1 ); nread = read_until_newline(fds[0], buf, 1);
ck_assert_int_eq( '\0', buf[0] ); ck_assert_int_eq('\0', buf[0]);
ck_assert_int_eq( 1, nread ); ck_assert_int_eq(1, nread);
} }
END_TEST END_TEST
START_TEST(test_read_eof_returns_err)
START_TEST( test_read_eof_returns_err )
{ {
int fds[2]; int fds[2];
int nread; int nread;
char buf[5] = {0}; char buf[5] = { 0 };
pipe( fds ); pipe(fds);
close( fds[1] ); close(fds[1]);
nread = read_until_newline( fds[0], buf, 5 ); nread = read_until_newline(fds[0], buf, 5);
ck_assert_int_eq( -1, nread ); ck_assert_int_eq(-1, nread);
} }
END_TEST END_TEST
START_TEST(test_read_eof_fills_line)
START_TEST( test_read_eof_fills_line )
{ {
int fds[2]; int fds[2];
int nread; int nread;
char buf[5] = {0}; char buf[5] = { 0 };
pipe(fds); pipe(fds);
write( fds[1], "1234", 4 ); write(fds[1], "1234", 4);
close( fds[1] ); close(fds[1]);
nread = read_until_newline( fds[0], buf, 5 ); nread = read_until_newline(fds[0], buf, 5);
ck_assert_int_eq( -1, nread ); ck_assert_int_eq(-1, nread);
ck_assert_int_eq( '4', buf[3] ); ck_assert_int_eq('4', buf[3]);
} }
END_TEST END_TEST
START_TEST(test_read_lines_until_blankline)
START_TEST( test_read_lines_until_blankline )
{ {
char **lines = NULL; char **lines = NULL;
int fds[2]; int fds[2];
int nlines; int nlines;
pipe( fds ); pipe(fds);
write( fds[1], "a\nb\nc\n\n", 7 ); write(fds[1], "a\nb\nc\n\n", 7);
nlines = read_lines_until_blankline( fds[0], 256, &lines ); nlines = read_lines_until_blankline(fds[0], 256, &lines);
ck_assert_int_eq( 3, nlines ); ck_assert_int_eq(3, nlines);
} }
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_create("read_lines_until_blankline"); TCase *tc_read_lines_until_blankline =
tcase_create("read_lines_until_blankline");
tcase_add_test(tc_read_until_newline, test_read_until_newline_returns_line_length_plus_null); tcase_add_test(tc_read_until_newline,
tcase_add_test(tc_read_until_newline, test_read_until_newline_inserts_null); test_read_until_newline_returns_line_length_plus_null);
tcase_add_test(tc_read_until_newline, test_read_empty_line_inserts_null); tcase_add_test(tc_read_until_newline,
tcase_add_test(tc_read_until_newline, test_read_eof_returns_err); test_read_until_newline_inserts_null);
tcase_add_test(tc_read_until_newline, test_read_eof_fills_line ); 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_fills_line);
tcase_add_test(tc_read_lines_until_blankline, test_read_lines_until_blankline ); tcase_add_test(tc_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);
return s; return s;
} }
int main(void) int main(void)
{ {
int number_failed; int number_failed;
Suite *s = ioutil_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}
Suite *s = ioutil_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}

View File

@@ -4,83 +4,79 @@
#include <pthread.h> #include <pthread.h>
#include <check.h> #include <check.h>
START_TEST( test_allocs_cvar ) START_TEST(test_allocs_cvar)
{ {
struct mbox * mbox = mbox_create(); struct mbox *mbox = mbox_create();
fail_if( NULL == mbox, "Nothing allocated" ); fail_if(NULL == mbox, "Nothing allocated");
pthread_cond_t cond_zero; pthread_cond_t cond_zero;
/* A freshly inited pthread_cond_t is set to {0} */ /* A freshly inited pthread_cond_t is set to {0} */
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 ) ) == 0 , fail_if(memcmp(&cond_zero, &mbox->emptied_cond, sizeof(cond_zero)) ==
"Condition variable not allocated" ); 0, "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();
void * deadbeef = (void *)0xDEADBEEF;
mbox_post( mbox, deadbeef );
fail_unless( deadbeef == mbox_contents( mbox ), void *deadbeef = (void *) 0xDEADBEEF;
"Contents were not posted" ); mbox_post(mbox, deadbeef);
fail_unless(deadbeef == mbox_contents(mbox),
"Contents were not posted");
} }
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;
void * contents = NULL; void *contents = NULL;
contents = mbox_receive( mbox ); contents = mbox_receive(mbox);
return contents; return contents;
} }
START_TEST( test_receive_blocks_until_post ) START_TEST(test_receive_blocks_until_post)
{ {
struct mbox * mbox = mbox_create(); struct mbox *mbox = mbox_create();
pthread_t receiver; pthread_t receiver;
pthread_create( &receiver, NULL, mbox_receive_runner, mbox ); pthread_create(&receiver, NULL, mbox_receive_runner, mbox);
void * deadbeef = (void *)0xDEADBEEF;
void * retval =NULL;
usleep(10000);
fail_unless( EBUSY == pthread_tryjoin_np( receiver, &retval ),
"Receiver thread wasn't blocked");
mbox_post( mbox, deadbeef ); void *deadbeef = (void *) 0xDEADBEEF;
fail_unless( 0 == pthread_join( receiver, &retval ), void *retval = NULL;
"Failed to join the receiver thread" ); usleep(10000);
fail_unless( retval == deadbeef, fail_unless(EBUSY == pthread_tryjoin_np(receiver, &retval),
"Return value was wrong" ); "Receiver thread wasn't blocked");
mbox_post(mbox, deadbeef);
fail_unless(0 == pthread_join(receiver, &retval),
"Failed to join the receiver thread");
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");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
TCase *tc_post = tcase_create("post"); TCase *tc_post = tcase_create("post");
tcase_add_test(tc_create, test_allocs_cvar); tcase_add_test(tc_create, test_allocs_cvar);
tcase_add_test( tc_post, test_post_stores_value ); tcase_add_test(tc_post, test_post_stores_value);
tcase_add_test( tc_post, test_receive_blocks_until_post); tcase_add_test(tc_post, test_receive_blocks_until_post);
suite_add_tcase(s, tc_create); suite_add_tcase(s, tc_create);
suite_add_tcase(s, tc_post); suite_add_tcase(s, tc_post);
return s; return s;
} }
@@ -88,17 +84,16 @@ Suite* mbox_suite(void)
int main(void) int main(void)
{ {
#ifdef DEBUG #ifdef DEBUG
log_level = 0; log_level = 0;
#else #else
log_level = 2; log_level = 2;
#endif #endif
int number_failed; int number_failed;
Suite *s = mbox_suite(); Suite *s = mbox_suite();
SRunner *sr = srunner_create(s); SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL); srunner_run_all(sr, CK_NORMAL);
log_level = 0; log_level = 0;
number_failed = srunner_ntests_failed(sr); number_failed = srunner_ntests_failed(sr);
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -4,240 +4,260 @@
START_TEST(test_init_passwd) START_TEST(test_init_passwd)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
struct nbd_init init; struct nbd_init init;
memcpy( init_raw.passwd, INIT_PASSWD, 8 ); memcpy(init_raw.passwd, INIT_PASSWD, 8);
nbd_r2h_init( &init_raw, &init ); nbd_r2h_init(&init_raw, &init);
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, "The password was not copied." ); fail_unless(memcmp(init.passwd, INIT_PASSWD, 8) == 0,
fail_unless( memcmp( init_raw.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied back." ); "The password was not copied.");
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;
struct nbd_init init; struct nbd_init init;
init_raw.magic = 12345; init_raw.magic = 12345;
nbd_r2h_init( &init_raw, &init ); nbd_r2h_init(&init_raw, &init);
fail_unless( be64toh( 12345 ) == init.magic, "Magic was not converted." ); fail_unless(be64toh(12345) == init.magic, "Magic was not converted.");
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, "Magic was not converted back." ); fail_unless(htobe64(67890) == init_raw.magic,
"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;
struct nbd_init init; struct nbd_init init;
init_raw.size = 12345; init_raw.size = 12345;
nbd_r2h_init( &init_raw, &init ); nbd_r2h_init(&init_raw, &init);
fail_unless( be64toh( 12345 ) == init.size, "Size was not converted." ); fail_unless(be64toh(12345) == init.size, "Size was not converted.");
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, "Size was not converted back." ); fail_unless(htobe64(67890) == init_raw.size,
"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;
struct nbd_request request; struct nbd_request request;
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, "Magic was not converted." ); fail_unless(be32toh(12345) == request.magic,
"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, "Magic was not converted back." ); fail_unless(htobe32(67890) == request_raw.magic,
"Magic was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_request_type ) 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 = 12345; request_raw.type = 123;
nbd_r2h_request( &request_raw, &request ); nbd_r2h_request(&request_raw, &request);
fail_unless( be32toh( 12345 ) == request.type, "Type was not converted." ); fail_unless(be16toh(123) == request.type, "Type was not converted.");
request.type = 67890; request.type = 234;
nbd_h2r_request( &request, &request_raw ); nbd_h2r_request(&request, &request_raw);
fail_unless( htobe32( 67890 ) == request_raw.type, "Type was not converted back." ); fail_unless(htobe16(234) == request_raw.type,
"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)
{ {
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
struct nbd_request request; struct nbd_request request;
memcpy( request_raw.handle.b, "MYHANDLE", 8 ); memcpy(request_raw.handle.b, "MYHANDLE", 8);
nbd_r2h_request( &request_raw, &request ); nbd_r2h_request(&request_raw, &request);
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, "The handle was not copied." ); fail_unless(memcmp(request.handle.b, "MYHANDLE", 8) == 0,
fail_unless( memcmp( request_raw.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied back." ); "The handle was not copied.");
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;
struct nbd_request request; struct nbd_request request;
request_raw.from = 12345; request_raw.from = 12345;
nbd_r2h_request( &request_raw, &request ); nbd_r2h_request(&request_raw, &request);
fail_unless( be64toh( 12345 ) == request.from, "From was not converted." ); fail_unless(be64toh(12345) == request.from, "From was not converted.");
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, "From was not converted back." ); fail_unless(htobe64(67890) == request_raw.from,
"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;
struct nbd_request request; struct nbd_request request;
request_raw.len = 12345; request_raw.len = 12345;
nbd_r2h_request( &request_raw, &request ); nbd_r2h_request(&request_raw, &request);
fail_unless( be32toh( 12345 ) == request.len, "Type was not converted." ); fail_unless(be32toh(12345) == request.len, "Type was not converted.");
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, "Type was not converted back." ); fail_unless(htobe32(67890) == request_raw.len,
"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;
struct nbd_reply reply; struct nbd_reply reply;
reply_raw.magic = 12345; reply_raw.magic = 12345;
nbd_r2h_reply( &reply_raw, &reply ); nbd_r2h_reply(&reply_raw, &reply);
fail_unless( be32toh( 12345 ) == reply.magic, "Magic was not converted." ); fail_unless(be32toh(12345) == reply.magic, "Magic was not converted.");
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, "Magic was not converted back." ); fail_unless(htobe32(67890) == reply_raw.magic,
"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;
struct nbd_reply reply; struct nbd_reply reply;
reply_raw.error = 12345; reply_raw.error = 12345;
nbd_r2h_reply( &reply_raw, &reply ); nbd_r2h_reply(&reply_raw, &reply);
fail_unless( be32toh( 12345 ) == reply.error, "Error was not converted." ); fail_unless(be32toh(12345) == reply.error, "Error was not converted.");
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, "Error was not converted back." ); fail_unless(htobe32(67890) == reply_raw.error,
"Error was not converted back.");
} }
END_TEST END_TEST
START_TEST(test_reply_handle) START_TEST(test_reply_handle)
{ {
struct nbd_reply_raw reply_raw; struct nbd_reply_raw reply_raw;
struct nbd_reply reply; struct nbd_reply reply;
memcpy( reply_raw.handle.b, "MYHANDLE", 8 ); memcpy(reply_raw.handle.b, "MYHANDLE", 8);
nbd_r2h_reply( &reply_raw, &reply ); nbd_r2h_reply(&reply_raw, &reply);
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, "The handle was not copied." ); fail_unless(memcmp(reply.handle.b, "MYHANDLE", 8) == 0,
fail_unless( memcmp( reply_raw.handle.b, "MYHANDLE", 8 ) == 0, "The handle was not copied back." ); "The handle was not copied.");
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
* nbd_request_raw */ * nbd_request_raw */
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
struct nbd_request request; struct nbd_request request;
uint64_t target = 0x8000000000000000; uint64_t target = 0x8000000000000000;
/* this is stored big-endian */ /* this is stored big-endian */
request_raw.from = htobe64(target); request_raw.from = htobe64(target);
/* We expect this to convert big-endian to the host format */ /* We expect this to convert big-endian to the host format */
nbd_r2h_request( &request_raw, &request ); nbd_r2h_request(&request_raw, &request);
fail_unless( target == request.from, "from was wrong" ); fail_unless(target == request.from, "from was wrong");
} }
END_TEST END_TEST
Suite *nbdtypes_suite(void) Suite * nbdtypes_suite(void)
{ {
Suite *s = suite_create( "nbdtypes" ); Suite *s = suite_create("nbdtypes");
TCase *tc_init = tcase_create( "nbd_init" ); TCase *tc_init = tcase_create("nbd_init");
TCase *tc_request = tcase_create( "nbd_request" ); TCase *tc_request = tcase_create("nbd_request");
TCase *tc_reply = tcase_create( "nbd_reply" ); TCase *tc_reply = tcase_create("nbd_reply");
tcase_add_test( tc_init, test_init_passwd ); tcase_add_test(tc_init, test_init_passwd);
tcase_add_test( tc_init, test_init_magic ); tcase_add_test(tc_init, test_init_magic);
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_handle ); tcase_add_test(tc_request, test_request_flags);
tcase_add_test( tc_request, test_request_from ); tcase_add_test(tc_request, test_request_handle);
tcase_add_test( tc_request, test_request_len ); tcase_add_test(tc_request, test_request_from);
tcase_add_test( tc_request, test_convert_from ); tcase_add_test(tc_request, test_request_len);
tcase_add_test( tc_reply, test_reply_magic ); tcase_add_test(tc_request, test_convert_from);
tcase_add_test( tc_reply, test_reply_error ); tcase_add_test(tc_reply, test_reply_magic);
tcase_add_test( tc_reply, test_reply_handle ); tcase_add_test(tc_reply, test_reply_error);
tcase_add_test(tc_reply, test_reply_handle);
suite_add_tcase( s, tc_init ); suite_add_tcase(s, tc_init);
suite_add_tcase( s, tc_request ); suite_add_tcase(s, tc_request);
suite_add_tcase( s, tc_reply ); suite_add_tcase(s, tc_reply);
return s; return s;
} }
int main(void) int main(void)
{ {
int number_failed; int number_failed;
Suite *s = nbdtypes_suite(); Suite *s = nbdtypes_suite();
SRunner *sr = srunner_create(s); SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL); srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr); number_failed = srunner_ntests_failed(sr);
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -3,45 +3,42 @@
#include <check.h> #include <check.h>
START_TEST( test_can_parse_ip_address_twice ) START_TEST(test_can_parse_ip_address_twice)
{ {
char ip_address[] = "127.0.0.1"; char ip_address[] = "127.0.0.1";
struct sockaddr saddr; struct sockaddr saddr;
parse_ip_to_sockaddr( &saddr, ip_address ); parse_ip_to_sockaddr(&saddr, ip_address);
parse_ip_to_sockaddr( &saddr, ip_address ); parse_ip_to_sockaddr(&saddr, ip_address);
} }
END_TEST END_TEST
Suite * parse_suite(void)
Suite* parse_suite(void)
{ {
Suite *s = suite_create("parse"); Suite *s = suite_create("parse");
TCase *tc_create = tcase_create("ip_to_sockaddr"); TCase *tc_create = tcase_create("ip_to_sockaddr");
tcase_add_test(tc_create, test_can_parse_ip_address_twice); tcase_add_test(tc_create, test_can_parse_ip_address_twice);
suite_add_tcase(s, tc_create); suite_add_tcase(s, tc_create);
return s; return s;
} }
#ifdef DEBUG #ifdef DEBUG
# define LOG_LEVEL 0 #define LOG_LEVEL 0
#else #else
# define LOG_LEVEL 2 #define LOG_LEVEL 2
#endif #endif
int main(void) int main(void)
{ {
log_level = LOG_LEVEL; log_level = LOG_LEVEL;
int number_failed; int number_failed;
Suite *s = parse_suite(); Suite *s = parse_suite();
SRunner *sr = srunner_create(s); SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL); srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr); number_failed = srunner_ntests_failed(sr);
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