6 Commits

Author SHA1 Message Date
Patrick J Cherry
e547696d0d Merge branch 'dev-michel' of gitlab.bytemark.co.uk:open-source/flexnbd-c into dev-michel 2017-07-14 17:05:17 +01:00
Michel Pollet
c19901cf10 mbox: Simplified
Removed the existing mbox that is used to pass transactions from one
thread to the other, use a non-locking FIFO and a simple semaphore
instead.

Removed all notion of void* from the FIFO system, use the now generic
FIFO. Also started to rework the mirror.c code to use typedefs for
structs enums etc.

DO NOT USE. check_mbox is borken and needs changing. Currently
'functional' otherwise like this, but requires more testing.

Signed-off-by: Michel Pollet <buserror@gmail.com>
2016-11-02 12:22:19 +00:00
Michel Pollet
781a91fe3d fifo: Add fifo_declare.h
My own implementation, used in countless projects

Signed-off-by: Michel Pollet <buserror@gmail.com>
2016-11-02 12:21:55 +00:00
Michel Pollet
90e8b13df5 fifo: Split the bitset.h
And fix all dependencies

Signed-off-by: Michel Pollet <buserror@gmail.com>
2016-11-02 12:21:55 +00:00
Michel Pollet
9ab1af8dff tools: semtest: new tool
Piece of code I used to validate that socketpair is a lot quicker than
pthread_semaphores.

Signed-off-by: Michel Pollet <buserror@gmail.com>
2016-11-02 12:21:55 +00:00
Michel Pollet
c265d7fe3f tools: holemap: new tool
Tool to list how a file is 'sparse' and calculate how 'sparse' it
/could/ be.

Signed-off-by: Michel Pollet <buserror@gmail.com>
2016-11-02 12:21:55 +00:00
111 changed files with 9235 additions and 8964 deletions

View File

@@ -1,24 +0,0 @@
# Contribution guide
The code is formatted using the K&R style of "indent".
```
indent -kr <files go here>
```
The C unit tests have also been indented in the same way, but manually adjsted
such that the functions follow the normal libcheck layout.
```c
START_TEST( ... ) {
}
END TEST
```
Indent tends to mangle the `END_TEST` macro, so that will need adjusting if
`indent` is run over the test files again.

View File

@@ -109,6 +109,7 @@ install:
clean: clean:
rm -rf build/* rm -rf build/*
.PHONY: clean objs check_objs all server proxy check_bins check doc build test acceptance .PHONY: clean objs check_objs all server proxy check_bins check doc build test acceptance
# Include extra dependencies at the end, NOT before 'all' # Include extra dependencies at the end, NOT before 'all'

40
debian/changelog vendored
View File

@@ -1,43 +1,3 @@
flexnbd (0.2.1) UNRELEASED; urgency=medium
* Force a msync after every write, ignoring FUA flag, or lack thereof.
-- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 24 Apr 2018 10:27:12 +0100
flexnbd (0.2.0) stable; urgency=medium
[ James Carter ]
* Set TCP keepalive on sockets so broken connections are reaped (#33, !33,
!36)
* Add a context to logs to make debugging problems easier (#34, !34)
[ 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
* Return bytes_left in migration statistics.
-- Chris Elsworth <chris.elsworth@bytemark.co.uk> Fri, 14 Jul 2017 17:00:38 +0100
flexnbd (0.1.6) stable; urgency=medium flexnbd (0.1.6) stable; urgency=medium
* Remove lots of per-cpu compiler flags, notably march=native. * Remove lots of per-cpu compiler flags, notably march=native.

189
src/common/fifo_declare.h Normal file
View File

@@ -0,0 +1,189 @@
/*
fido_declare.h
Copyright (C) 2003-2012 Michel Pollet <buserror@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* FIFO helpers, aka circular buffers
*
* these macros define accessories for FIFOs of any name, type and
* any (power of two) size
*/
#ifndef __FIFO_DECLARE__
#define __FIFO_DECLARE__
#ifdef __cplusplus
extern "C" {
#endif
/*
doing a :
DECLARE_FIFO(uint8_t, myfifo, 128);
will declare :
enum : myfifo_overflow_f
type : myfifo_t
functions:
// write a byte into the fifo, return 1 if there was room, 0 if there wasn't
int myfifo_write(myfifo_t *c, uint8_t b);
// reads a byte from the fifo, return 0 if empty. Use myfifo_isempty() to check beforehand
uint8_t myfifo_read(myfifo_t *c);
int myfifo_isfull(myfifo_t *c);
int myfifo_isempty(myfifo_t *c);
// returns number of items to read now
uint16_t myfifo_get_read_size(myfifo_t *c);
// read item at offset o from read cursor, no cursor advance
uint8_t myfifo_read_at(myfifo_t *c, uint16_t o);
// write b at offset o compared to current write cursor, no cursor advance
void myfifo_write_at(myfifo_t *c, uint16_t o, uint8_t b);
In your .c you need to 'implement' the fifo:
DEFINE_FIFO(uint8_t, myfifo)
To use the fifo, you must declare at least one :
myfifo_t fifo = FIFO_NULL;
while (!myfifo_isfull(&fifo))
myfifo_write(&fifo, 0xaa);
....
while (!myfifo_isempty(&fifo))
b = myfifo_read(&fifo);
*/
#include <stdint.h>
#if __AVR__
#define FIFO_CURSOR_TYPE uint8_t
#define FIFO_BOOL_TYPE char
#define FIFO_INLINE
#define FIFO_SYNC
#endif
#ifndef FIFO_CURSOR_TYPE
#define FIFO_CURSOR_TYPE uint16_t
#endif
#ifndef FIFO_BOOL_TYPE
#define FIFO_BOOL_TYPE int
#endif
#ifndef FIFO_INLINE
#define FIFO_INLINE inline
#endif
/* We should not need volatile */
#ifndef FIFO_VOLATILE
#define FIFO_VOLATILE
#endif
#ifndef FIFO_SYNC
#define FIFO_SYNC __sync_synchronize()
#endif
#ifndef FIFO_ZERO_INIT
#define FIFO_ZERO_INIT {0}
#endif
#define FIFO_NULL { FIFO_ZERO_INIT, 0, 0, 0 }
/* New compilers don't like unused static functions. However,
* we do like 'static inlines' for these small accessors,
* so we mark them as 'unused'. It stops it complaining */
#ifdef __GNUC__
#define FIFO_DECL static __attribute__ ((unused))
#else
#define FIFO_DECL static
#endif
#define DECLARE_FIFO(__type, __name, __size) \
enum { __name##_overflow_f = (1 << 0) }; \
enum { __name##_fifo_size = (__size) }; \
typedef struct __name##_t { \
__type buffer[__name##_fifo_size]; \
FIFO_VOLATILE FIFO_CURSOR_TYPE read; \
FIFO_VOLATILE FIFO_CURSOR_TYPE write; \
FIFO_VOLATILE uint8_t flags; \
} __name##_t
#define DEFINE_FIFO(__type, __name) \
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\
{\
FIFO_CURSOR_TYPE now = c->write;\
FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\
if (c->read != next) { \
c->buffer[now] = b;\
FIFO_SYNC; \
c->write = next;\
return 1;\
}\
return 0;\
}\
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\
{\
FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\
return c->read == next;\
}\
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\
{\
return c->read == c->write;\
}\
FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\
{\
__type res = FIFO_ZERO_INIT; \
FIFO_CURSOR_TYPE read = c->read;\
if (read == c->write)\
return res;\
res = c->buffer[read];\
FIFO_SYNC; \
c->read = (read + 1) & (__name##_fifo_size-1);\
return res;\
}\
FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\
{\
return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\
}\
FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\
{\
return (__name##_fifo_size-1) - __name##_get_read_size(c);\
}\
FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
{\
FIFO_SYNC; \
c->read = (c->read + o) & (__name##_fifo_size-1);\
}\
FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\
{\
return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\
}\
FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\
{\
c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\
}\
FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
{\
FIFO_SYNC; \
c->write = (c->write + o) & (__name##_fifo_size-1);\
}\
FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\
{\
FIFO_SYNC; \
c->read = c->write = c->flags = 0;\
}\
struct __name##_t
#ifdef __cplusplus
};
#endif
#endif

View File

@@ -9,14 +9,14 @@
#include <fcntl.h> #include <fcntl.h>
#include "util.h" #include "util.h"
#include "bitset.h" #include "bitstream.h"
#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;
@@ -25,31 +25,32 @@ int build_allocation_map(struct bitset *allocation_map, int fd)
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_flags = FIEMAP_FLAG_SYNC;
fiemap->fm_extent_count = max_extents; fiemap->fm_extent_count = max_extents;
fiemap->fm_mapped_extents = 0; fiemap->fm_mapped_extents = 0;
if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) { if ( ioctl( fd, FS_IOC_FIEMAP, fiemap ) < 0 ) {
debug("Couldn't get fiemap, returning no allocation_map"); debug( "Couldn't get fiemap, returning no allocation_map" );
return 0; /* it's up to the caller to free the map */ return 0; /* it's up to the caller to free the map */
} else { }
for (unsigned int i = 0; i < fiemap->fm_mapped_extents; i++) { else {
bitset_set_range(allocation_map, 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_logical,
fiemap->fm_extents[i].fe_length); fiemap->fm_extents[i].fe_length );
} }
@@ -57,10 +58,12 @@ int build_allocation_map(struct bitset *allocation_map, int fd)
* if we've actually hit max_offsets. * if we've actually hit max_offsets.
*/ */
if (fiemap->fm_mapped_extents > 0) { if (fiemap->fm_mapped_extents > 0) {
struct fiemap_extent *last = struct fiemap_extent *last = &fiemap->fm_extents[
&fiemap->fm_extents[fiemap->fm_mapped_extents - 1]; fiemap->fm_mapped_extents-1
];
offset = last->fe_logical + last->fe_length; offset = last->fe_logical + last->fe_length;
} else { }
else {
offset += fiemap->fm_length; offset += fiemap->fm_length;
} }
} }
@@ -71,8 +74,7 @@ int build_allocation_map(struct bitset *allocation_map, int fd)
} }
int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size, int open_and_mmap(const char* filename, int* out_fd, uint64_t *out_size, void **out_map)
void **out_map)
{ {
/* /*
* size and out_size are intentionally of different types. * size and out_size are intentionally of different types.
@@ -83,7 +85,7 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
off64_t size; off64_t size;
/* O_DIRECT should not be used with mmap() */ /* O_DIRECT should not be used with mmap() */
*out_fd = open(filename, O_RDWR | O_NOATIME); *out_fd = open(filename, O_RDWR | O_SYNC );
if (*out_fd < 1) { if (*out_fd < 1) {
warn("open(%s) failed: does it exist?", filename); warn("open(%s) failed: does it exist?", filename);
@@ -95,29 +97,20 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
warn("lseek64() failed"); warn("lseek64() failed");
return size; return size;
} }
/* If discs are not in multiples of 512, then odd things happen,
* resulting in reads/writes past the ends of files.
*/
if (size != (size & (~0x1ff))) {
warn("file does not fit into 512-byte sectors; the end of the file will be ignored.");
size &= ~0x1ff;
}
if (out_size) { if (out_size) {
*out_size = size; *out_size = size;
} }
if (out_map) { if (out_map) {
*out_map = mmap64(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *out_map = mmap64(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED,
*out_fd, 0); *out_fd, 0);
if (((long) *out_map) == -1) { if (((long) *out_map) == -1) {
warn("mmap64() failed"); warn("mmap64() failed");
return -1; return -1;
} }
debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd, debug("opened %s size %ld on fd %d @ %p", filename, size, *out_fd, *out_map);
*out_map); }
} else { else {
debug("opened %s size %ld on fd %d", filename, size, *out_fd); debug("opened %s size %ld on fd %d", filename, size, *out_fd);
} }
@@ -127,11 +120,11 @@ int open_and_mmap(const char *filename, int *out_fd, uint64_t * out_size,
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
@@ -143,16 +136,17 @@ int writeloop(int filedes, const void *buffer, size_t size)
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 );
return -1; return -1;
} }
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
@@ -162,17 +156,15 @@ int readloop(int filedes, void *buffer, size_t size)
return 0; 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 debug("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld", out_fd, in_fd, offset, count-sent, result);
("sendfile64(out_fd=%d, in_fd=%d, offset=%p, count-sent=%ld) = %ld",
out_fd, in_fd, offset, count - sent, result);
if (result == -1) { if (result == -1) {
debug("%s (%i) calling sendfile64()", strerror(errno), errno); debug( "%s (%i) calling sendfile64()", strerror(errno), errno );
return -1; return -1;
} }
sent += result; sent += result;
@@ -183,22 +175,21 @@ int sendfileloop(int out_fd, int in_fd, off64_t * offset, size_t count)
} }
#include <errno.h> #include <errno.h>
ssize_t spliceloop(int fd_in, loff_t * off_in, int fd_out, ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2)
loff_t * off_out, size_t len, unsigned int flags2)
{ {
const unsigned int flags = SPLICE_F_MORE | SPLICE_F_MOVE | flags2; const unsigned int flags = SPLICE_F_MORE|SPLICE_F_MOVE|flags2;
size_t spliced = 0; size_t spliced=0;
//debug("spliceloop(%d, %ld, %d, %ld, %ld)", fd_in, off_in ? *off_in : 0, fd_out, off_out ? *off_out : 0, len); //debug("spliceloop(%d, %ld, %d, %ld, %ld)", fd_in, off_in ? *off_in : 0, fd_out, off_out ? *off_out : 0, len);
while (spliced < len) { while (spliced < len) {
ssize_t result = ssize_t result = splice(fd_in, off_in, fd_out, off_out, len, flags);
splice(fd_in, off_in, fd_out, off_out, len, flags);
if (result < 0) { if (result < 0) {
//debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len); //debug("result=%ld (%s), spliced=%ld, len=%ld", result, strerror(errno), spliced, len);
if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK)) { if (errno == EAGAIN && (flags & SPLICE_F_NONBLOCK) ) {
return spliced; return spliced;
} else { }
else {
return -1; return -1;
} }
} else { } else {
@@ -214,26 +205,21 @@ 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) { while (spliced < len) {
ssize_t run = len - spliced; ssize_t run = len-spliced;
ssize_t s2, s1 = spliceloop(fd_in, NULL, pipefd[1], NULL, run, ssize_t s2, s1 = spliceloop(fd_in, NULL, pipefd[1], NULL, run, SPLICE_F_NONBLOCK);
SPLICE_F_NONBLOCK);
/*if (run > 65535) /*if (run > 65535)
run = 65535; */ run = 65535;*/
if (s1 < 0) { if (s1 < 0) { break; }
break;
}
s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0); s2 = spliceloop(pipefd[0], NULL, fd_out, NULL, s1, 0);
if (s2 < 0) { if (s2 < 0) { break; }
break;
}
spliced += s2; spliced += s2;
} }
close(pipefd[0]); close(pipefd[0]);
@@ -249,31 +235,29 @@ 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) { if (result <= 0) { return -1; }
return -1;
}
if (buf[cur] == 10) { if (buf[cur] == 10) {
buf[cur] = '\0'; buf[cur] = '\0';
break; 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);
@@ -282,10 +266,8 @@ int read_lines_until_blankline(int fd, int max_line_length, char ***lines)
* -1 for an eof * -1 for an eof
* -1 for a read error * -1 for a read error
*/ */
if (readden <= 1) { if (readden <= 1) { return lines_count; }
return lines_count; *lines = xrealloc(*lines, (lines_count+1) * sizeof(char*));
}
*lines = xrealloc(*lines, (lines_count + 1) * sizeof(char *));
(*lines)[lines_count] = strdup(line); (*lines)[lines_count] = strdup(line);
if ((*lines)[lines_count][0] == 0) { if ((*lines)[lines_count][0] == 0) {
return lines_count; return lines_count;
@@ -295,10 +277,10 @@ int read_lines_until_blankline(int fd, int max_line_length, char ***lines)
} }
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;
} }
@@ -306,39 +288,38 @@ int fd_is_closed(int fd_in)
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, debug( "Reading %"PRIu32" of %"PRIu32" bytes from fd %i", left, iobuf->size, fd );
iobuf->size, fd);
count = read(fd, iobuf->buf + iobuf->needle, left); count = read( fd, iobuf->buf + iobuf->needle, left );
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 { } else {
debug(SHOW_ERRNO("read() returned 0 bytes")); debug( SHOW_ERRNO( "read() returned 0 bytes" ) );
count = 0; count = 0;
} }
} }
@@ -346,23 +327,22 @@ ssize_t iobuf_read(int fd, struct iobuf * iobuf, size_t default_size)
return count; return count;
} }
ssize_t iobuf_write(int fd, struct iobuf * iobuf) ssize_t iobuf_write( int fd, struct iobuf *iobuf )
{ {
size_t left = iobuf->size - iobuf->needle; size_t left = iobuf->size - iobuf->needle;
ssize_t count; ssize_t count;
debug("Writing %" PRIu32 " of %" PRIu32 " bytes to fd %i", left, debug( "Writing %"PRIu32" of %"PRIu32" bytes to fd %i", left, iobuf->size, fd );
iobuf->size, fd); count = write( fd, iobuf->buf + iobuf->needle, left );
count = write(fd, iobuf->buf + iobuf->needle, left);
if (count >= 0) { if ( count >= 0 ) {
iobuf->needle += count; iobuf->needle += count;
debug("write() returned %" PRIu32 " bytes", count); debug( "write() returned %"PRIu32" bytes", count );
} else { } else {
if (io_errno_permanent()) { if ( io_errno_permanent() ) {
warn(SHOW_ERRNO("write() failed on fd %i", fd)); warn( SHOW_ERRNO( "write() failed on fd %i", fd ) );
} else { } else {
debug(SHOW_ERRNO("write() returned 0 bytes")); debug( SHOW_ERRNO( "write() returned 0 bytes" ) );
count = 0; count = 0;
} }
} }

View File

@@ -8,8 +8,8 @@ struct iobuf {
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 */
@@ -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,11 +35,10 @@ 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, ssize_t spliceloop(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags2);
loff_t * off_out, size_t len, unsigned int flags2);
/** Copy ''len'' bytes from ''fd_in'' to ''fd_out'' by creating a temporary /** Copy ''len'' bytes from ''fd_in'' to ''fd_out'' by creating a temporary
* pipe and using the Linux splice call repeatedly until it has transferred * pipe and using the Linux splice call repeatedly until it has transferred
@@ -51,7 +50,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
@@ -66,12 +65,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, int open_and_mmap( const char* filename, int* out_fd, uint64_t* out_size, void **out_map);
void **out_map);
/** Check to see whether the given file descriptor is closed. /** Check to see whether the given file descriptor is closed.
*/ */
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,6 +91,7 @@ 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,54 +8,51 @@
* 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 = be32toh(from->magic); to->magic = htobe32( from->magic );
to->flags = be16toh(from->flags); to->type = htobe32( from->type );
to->type = be16toh(from->type);
to->handle.w = from->handle.w; to->handle.w = from->handle.w;
to->from = be64toh(from->from); to->from = htobe64( from->from );
to->len = be32toh(from->len); to->len = htobe32( 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 = htobe32(from->magic); to->magic = be32toh( from->magic );
to->flags = htobe16(from->flags); to->type = be32toh( from->type );
to->type = htobe16(from->type);
to->handle.w = from->handle.w; to->handle.w = from->handle.w;
to->from = htobe64(from->from); to->from = be64toh( from->from );
to->len = htobe32(from->len); to->len = be32toh( from->len );
} }
void nbd_r2h_reply(struct nbd_reply_raw *from, struct nbd_reply *to) void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to )
{ {
to->magic = 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;
} }
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 = 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;
} }

View File

@@ -7,39 +7,16 @@
#define INIT_MAGIC 0x0000420281861253 #define INIT_MAGIC 0x0000420281861253
#define REQUEST_MAGIC 0x25609513 #define REQUEST_MAGIC 0x25609513
#define REPLY_MAGIC 0x67446698 #define REPLY_MAGIC 0x67446698
#define REQUEST_READ 0 #define REQUEST_READ 0
#define REQUEST_WRITE 1 #define REQUEST_WRITE 1
#define REQUEST_DISCONNECT 2 #define REQUEST_DISCONNECT 2
#define REQUEST_FLUSH 3
/* values for transmission flag field */ /* The top 2 bytes of the type field are overloaded and can contain flags */
#define FLAG_HAS_FLAGS (1 << 0) /* Flags are there */ #define REQUEST_MASK 0x0000ffff
#define FLAG_SEND_FLUSH (1 << 2) /* Send FLUSH */
#define FLAG_SEND_FUA (1 << 3) /* Send FUA (Force Unit Access) */
/* values for command flag field */
#define CMD_FLAG_FUA (1 << 0)
#if 0
/* Not yet implemented by flexnbd */
#define REQUEST_TRIM 4
#define REQUEST_WRITE_ZEROES 6
#define FLAG_READ_ONLY (1 << 1) /* Device is read-only */
#define FLAG_ROTATIONAL (1 << 4) /* Use elevator algorithm - rotational media */
#define FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */
#define FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send NBD_CMD_WRITE_ZEROES */
#define FLAG_CAN_MULTI_CONN (1 << 8) /* multiple connections are okay */
#define CMD_FLAG_NO_HOLE (1 << 1)
#endif
/* 32 MiB is the maximum qemu will send you: /* 1MiB is the de-facto standard for maximum size of header + data */
* https://github.com/qemu/qemu/blob/v2.11.0/include/block/nbd.h#L183 #define NBD_MAX_SIZE ( 1024 * 1024 )
*/
#define NBD_MAX_SIZE ( 32 * 1024 * 1024 )
#define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) ) #define NBD_REQUEST_SIZE ( sizeof( struct nbd_request_raw ) )
#define NBD_REPLY_SIZE ( sizeof( struct nbd_reply_raw ) ) #define NBD_REPLY_SIZE ( sizeof( struct nbd_reply_raw ) )
@@ -61,18 +38,16 @@ struct nbd_init_raw {
char passwd[8]; char passwd[8];
__be64 magic; __be64 magic;
__be64 size; __be64 size;
__be32 flags; char reserved[128];
char reserved[124];
}; };
struct nbd_request_raw { struct nbd_request_raw {
__be32 magic; __be32 magic;
__be16 flags; __be32 type; /* == READ || == WRITE */
__be16 type; /* == READ || == WRITE || == FLUSH */
nbd_handle_t handle; nbd_handle_t handle;
__be64 from; __be64 from;
__be32 len; __be32 len;
} __attribute__ ((packed)); } __attribute__((packed));
struct nbd_reply_raw { struct nbd_reply_raw {
__be32 magic; __be32 magic;
@@ -80,22 +55,22 @@ struct nbd_reply_raw {
nbd_handle_t handle; /* handle you got from request */ nbd_handle_t handle; /* handle you got from request */
}; };
struct nbd_init { struct nbd_init {
char passwd[8]; char passwd[8];
uint64_t magic; uint64_t magic;
uint64_t size; uint64_t size;
uint32_t flags; char reserved[128];
char reserved[124];
}; };
struct nbd_request { struct nbd_request {
uint32_t magic; uint32_t magic;
uint16_t flags; uint32_t type; /* == READ || == WRITE || == DISCONNECT */
uint16_t type; /* == READ || == WRITE || == DISCONNECT || == FLUSH */
nbd_handle_t handle; nbd_handle_t handle;
uint64_t from; uint64_t from;
uint32_t len; uint32_t len;
} __attribute__ ((packed)); } __attribute__((packed));
struct nbd_reply { struct nbd_reply {
uint32_t magic; uint32_t magic;
@@ -103,12 +78,13 @@ struct nbd_reply {
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,10 +10,10 @@ 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;
@@ -21,11 +21,9 @@ int parse_ip_to_sockaddr(struct sockaddr *out, char *src)
/* allow user to start with [ and end with any other invalid char */ /* allow user to start with [ and end with any other invalid char */
{ {
int i = 0, j = 0; int i=0, j=0;
if (src[i] == '[') { if (src[i] == '[') { i++; }
i++; for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++) {
}
for (; i < 64 && IS_IP_VALID_CHAR(src[i]); i++) {
temp[j++] = src[i]; temp[j++] = src[i];
} }
temp[j] = 0; temp[j] = 0;
@@ -51,59 +49,62 @@ int parse_ip_to_sockaddr(struct sockaddr *out, char *src)
} }
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++); // increment j! for (j=0; entries[i][j] && entries[i][j] != '/'; j++)
; // increment j!
if (entries[i][j] == '/') { if (entries[i][j] == '/') {
outentry->mask = atoi(entries[i] + j + 1); outentry->mask = atoi(entries[i]+j+1);
if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) { if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) {
return i; return i;
} }
} else { }
else {
outentry->mask = MAX_MASK_BITS; outentry->mask = MAX_MASK_BITS;
} }
# undef MAX_MASK_BITS # undef MAX_MASK_BITS
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);
} }
@@ -111,15 +112,16 @@ 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 )
{ {
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

@@ -20,9 +20,10 @@ struct ip_and_mask {
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,131 +8,116 @@
#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 = int fd = socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0);
socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, if( fd < 0 ){
0); warn( "Couldn't create client socket");
if (fd < 0) {
warn("Couldn't create client socket");
return -1; 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 );
}
if (NULL != out_flags) {
*out_flags = be32toh(init_raw->flags);
} }
return 1; return 1;
fail: fail:
return 0; return 0;
} }
int socket_nbd_read_hello(int fd, uint64_t * out_size, int socket_nbd_read_hello( int fd, uint64_t* out_size )
uint32_t * out_flags)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
if (0 > readloop(fd, &init_raw, sizeof(init_raw))) { if ( 0 > readloop( fd, &init_raw, sizeof(init_raw) ) ) {
warn(SHOW_ERRNO("Couldn't read init")); warn( "Couldn't read init" );
return 0; return 0;
} }
return nbd_check_hello(&init_raw, out_size, out_flags); return nbd_check_hello( &init_raw, out_size );
} }
void nbd_hello_to_buf(struct nbd_init_raw *buf, off64_t out_size, void nbd_hello_to_buf( struct nbd_init_raw *buf, off64_t out_size )
uint32_t out_flags)
{ {
struct nbd_init init; struct nbd_init init;
memcpy(&init.passwd, INIT_PASSWD, 8); memcpy( &init.passwd, INIT_PASSWD, 8 );
init.magic = INIT_MAGIC; init.magic = INIT_MAGIC;
init.size = out_size; init.size = out_size;
init.flags = out_flags;
memset(buf, 0, sizeof(struct nbd_init_raw)); // ensure reserved is 0s memset( buf, 0, sizeof( struct nbd_init_raw ) ); // ensure reserved is 0s
nbd_h2r_init(&init, buf); nbd_h2r_init( &init, buf );
return; return;
} }
int socket_nbd_write_hello(int fd, off64_t out_size, uint32_t out_flags) int socket_nbd_write_hello(int fd, off64_t out_size)
{ {
struct nbd_init_raw init_raw; struct nbd_init_raw init_raw;
nbd_hello_to_buf(&init_raw, out_size, out_flags); nbd_hello_to_buf( &init_raw, out_size );
if (0 > writeloop(fd, &init_raw, sizeof(init_raw))) { if ( 0 > writeloop( fd, &init_raw, sizeof( init_raw ) ) ) {
warn(SHOW_ERRNO("failed to write hello to socket")); warn( SHOW_ERRNO( "failed to write hello to socket" ) );
return 0; return 0;
} }
return 1; return 1;
} }
void fill_request(struct nbd_request_raw *request_raw, uint16_t type, void fill_request(struct nbd_request *request, int type, uint64_t from, uint32_t len)
uint16_t flags, uint64_t from, uint32_t len)
{ {
request_raw->magic = htobe32(REQUEST_MAGIC); request->magic = htobe32(REQUEST_MAGIC);
request_raw->type = htobe16(type); request->type = htobe32(type);
request_raw->flags = htobe16(flags); request->handle.w = (((uint64_t)rand()) << 32) | ((uint64_t)rand());
request_raw->handle.w = request->from = htobe64(from);
(((uint64_t) rand()) << 32) | ((uint64_t) rand()); request->len = htobe32(len);
request_raw->from = htobe64(from);
request_raw->len = htobe32(len);
} }
void read_reply(int fd, uint64_t request_raw_handle, void read_reply(int fd, struct nbd_request *request, struct nbd_reply *reply)
struct nbd_reply *reply)
{ {
struct nbd_reply_raw reply_raw; struct nbd_reply_raw reply_raw;
ERROR_IF_NEGATIVE(readloop ERROR_IF_NEGATIVE(readloop(fd, &reply_raw, sizeof(struct nbd_reply_raw)),
(fd, &reply_raw, sizeof(struct nbd_reply_raw)), "Couldn't read reply");
SHOW_ERRNO("Couldn't read reply"));
nbd_r2h_reply(&reply_raw, reply); nbd_r2h_reply( &reply_raw, reply );
if (reply->magic != REPLY_MAGIC) { if (reply->magic != REPLY_MAGIC) {
error("Reply magic incorrect (%x)", reply->magic); error("Reply magic incorrect (%x)", reply->magic);
@@ -140,94 +125,95 @@ void read_reply(int fd, uint64_t request_raw_handle,
if (reply->error != 0) { if (reply->error != 0) {
error("Server replied with error %d", reply->error); error("Server replied with error %d", reply->error);
} }
if (request_raw_handle != reply_raw.handle.w) { if (request->handle.w != reply->handle.w) {
error("Did not reply with correct handle"); error("Did not reply with correct handle");
} }
} }
void wait_for_data(int fd, int timeout_secs) void wait_for_data( int fd, int timeout_secs )
{ {
fd_set fds; fd_set fds;
struct timeval tv = { timeout_secs, 0 }; struct timeval tv = { timeout_secs, 0 };
int selected; int selected;
FD_ZERO(&fds); FD_ZERO( &fds );
FD_SET(fd, &fds); FD_SET( fd, &fds );
selected = selected = sock_try_select(
sock_try_select(FD_SETSIZE, &fds, NULL, NULL, FD_SETSIZE, &fds, NULL, NULL, timeout_secs >=0 ? &tv : NULL
timeout_secs >= 0 ? &tv : NULL); );
FATAL_IF(-1 == selected, "Select failed"); FATAL_IF( -1 == selected, "Select failed" );
ERROR_IF(0 == selected, "Timed out waiting for reply"); ERROR_IF( 0 == selected, "Timed out waiting for reply" );
} }
void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void socket_nbd_read(int fd, uint64_t from, uint32_t len, int out_fd, void* out_buf, int timeout_secs)
void *out_buf, int timeout_secs)
{ {
struct nbd_request_raw request_raw; struct nbd_request request;
struct nbd_reply reply; struct nbd_reply reply;
fill_request(&request_raw, REQUEST_READ, 0, from, len); fill_request(&request, REQUEST_READ, from, len);
FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)), FATAL_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)),
SHOW_ERRNO("Couldn't write request")); "Couldn't write request");
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);
if (out_buf) { if (out_buf) {
FATAL_IF_NEGATIVE(readloop(fd, out_buf, len), FATAL_IF_NEGATIVE(readloop(fd, out_buf, len),
SHOW_ERRNO("Read failed")); "Read failed");
} else { }
FATAL_IF_NEGATIVE(splice_via_pipe_loop(fd, out_fd, len), else {
"Splice failed"); FATAL_IF_NEGATIVE(
splice_via_pipe_loop(fd, out_fd, len),
"Splice failed"
);
} }
} }
void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd, void socket_nbd_write(int fd, uint64_t from, uint32_t len, int in_fd, void* in_buf, int timeout_secs)
void *in_buf, int timeout_secs)
{ {
struct nbd_request_raw request_raw; struct nbd_request request;
struct nbd_reply reply; struct nbd_reply reply;
fill_request(&request_raw, REQUEST_WRITE, 0, from, len); fill_request(&request, REQUEST_WRITE, from, len);
ERROR_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)), ERROR_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)),
SHOW_ERRNO("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),
SHOW_ERRNO("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_raw.handle.w, &reply); read_reply(fd, &request, &reply);
} }
int socket_nbd_disconnect(int fd) int socket_nbd_disconnect( int fd )
{ {
int success = 1; int success = 1;
struct nbd_request_raw request_raw; struct nbd_request request;
fill_request(&request_raw, REQUEST_DISCONNECT, 0, 0, 0); fill_request( &request, REQUEST_DISCONNECT, 0, 0 );
/* FIXME: This shouldn't be a FATAL error. We should just drop /* FIXME: This shouldn't be a FATAL error. We should just drop
* the mirror without affecting the main server. * the mirror without affecting the main server.
*/ */
FATAL_IF_NEGATIVE(writeloop(fd, &request_raw, sizeof(request_raw)), FATAL_IF_NEGATIVE( writeloop( fd, &request, sizeof( request ) ),
SHOW_ERRNO "Failed to write the disconnect request." );
("Failed to write the disconnect request."));
return success; return success;
} }
#define CHECK_RANGE(error_type) { \ #define CHECK_RANGE(error_type) { \
uint64_t size;\ uint64_t size;\
uint32_t flags;\ int success = socket_nbd_read_hello(params->client, &size); \
int success = socket_nbd_read_hello(params->client, &size, &flags); \
if ( success ) {\ if ( success ) {\
uint64_t endpoint = params->from + params->len; \ uint64_t endpoint = params->from + params->len; \
if (endpoint > size || \ if (endpoint > size || \
@@ -243,26 +229,23 @@ int socket_nbd_disconnect(int fd)
}\ }\
} }
void do_read(struct mode_readwrite_params *params) void do_read(struct mode_readwrite_params* params)
{ {
params->client = params->client = socket_connect(&params->connect_to.generic, &params->connect_from.generic);
socket_connect(&params->connect_to.generic, FATAL_IF_NEGATIVE( params->client, "Couldn't connect." );
&params->connect_from.generic);
FATAL_IF_NEGATIVE(params->client, "Couldn't connect.");
CHECK_RANGE("read"); CHECK_RANGE("read");
socket_nbd_read(params->client, params->from, params->len, socket_nbd_read(params->client, params->from, params->len,
params->data_fd, NULL, 10); params->data_fd, NULL, 10);
close(params->client); close(params->client);
} }
void do_write(struct mode_readwrite_params *params) void do_write(struct mode_readwrite_params* params)
{ {
params->client = params->client = socket_connect(&params->connect_to.generic, &params->connect_from.generic);
socket_connect(&params->connect_to.generic, FATAL_IF_NEGATIVE( params->client, "Couldn't connect." );
&params->connect_from.generic);
FATAL_IF_NEGATIVE(params->client, "Couldn't connect.");
CHECK_RANGE("write"); CHECK_RANGE("write");
socket_nbd_write(params->client, params->from, params->len, socket_nbd_write(params->client, params->from, params->len,
params->data_fd, NULL, 10); params->data_fd, NULL, 10);
close(params->client); close(params->client);
} }

View File

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

View File

@@ -4,32 +4,31 @@
#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, void do_remote_command(char* command, char* socket_name, int argc, char** argv)
char **argv)
{ {
char newline = 10; char newline=10;
int i; int i;
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];
@@ -41,25 +40,28 @@ void do_remote_command(char *command, char *socket_name, int argc,
address.sun_family = AF_UNIX; address.sun_family = AF_UNIX;
strncpy(address.sun_path, socket_name, sizeof(address.sun_path)); strncpy(address.sun_path, socket_name, sizeof(address.sun_path));
FATAL_IF_NEGATIVE(connect FATAL_IF_NEGATIVE(
(remote, (struct sockaddr *) &address, connect(remote, (struct sockaddr*) &address, sizeof(address)),
sizeof(address)), "Couldn't connect to %s", "Couldn't connect to %s", socket_name
socket_name); );
write(remote, command, strlen(command)); write(remote, command, strlen(command));
write(remote, &newline, 1); write(remote, &newline, 1);
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(read_until_newline(remote, response, max_response), FATAL_IF_NEGATIVE(
"Couldn't read response from %s", socket_name); read_until_newline(remote, response, max_response),
"Couldn't read response from %s", socket_name
);
print_response(response); print_response( response );
exit(atoi(response)); exit(atoi(response));
} }

View File

@@ -29,13 +29,13 @@
#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,28 +47,25 @@ 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) { if ( NULL == sig ) { return NULL; }
if ( pipe( fds ) ) {
free( sig );
self_pipe_server_error( errno, ERR_MSG_PIPE );
return NULL; return NULL;
} }
if (pipe(fds)) { if ( fcntl( fds[0], F_SETFL, O_NONBLOCK ) || fcntl( fds[1], F_SETFL, O_NONBLOCK ) ) {
free(sig);
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)) {
int fcntl_err = errno; int fcntl_err = errno;
while (close(fds[0]) == -1 && errno == EINTR); while( close( fds[0] ) == -1 && errno == EINTR );
while (close(fds[1]) == -1 && errno == EINTR); while( close( fds[1] ) == -1 && errno == EINTR );
free(sig); free( sig );
self_pipe_server_error(fcntl_err, ERR_MSG_FCNTL); self_pipe_server_error( fcntl_err, ERR_MSG_FCNTL );
return NULL; return NULL;
} }
@@ -86,15 +83,15 @@ 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;
} }
@@ -109,11 +106,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 );
} }
@@ -121,12 +118,12 @@ 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
@@ -134,17 +131,17 @@ int self_pipe_destroy(struct self_pipe *sig)
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

@@ -9,11 +9,11 @@ struct self_pipe {
}; };
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,148 +9,111 @@
#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, const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size_t len )
size_t len)
{ {
NULLCHECK(sa); NULLCHECK( sa );
NULLCHECK(dest); NULLCHECK( dest );
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, return setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
sizeof(optval));
}
int sock_set_keepalive_params(int fd, int time, int intvl, int probes)
{
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, return setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval) );
sizeof(optval));
} }
int sock_set_tcp_cork(int fd, int optval) int sock_set_tcp_cork( int fd, int optval )
{ {
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 { } else {
flags = flags & (~O_NONBLOCK); flags = flags & (~O_NONBLOCK);
} }
return fcntl(fd, F_SETFL, flags); return fcntl( fd, F_SETFL, flags );
} }
int sock_try_bind(int fd, const struct sockaddr *sa) int sock_try_bind( int fd, const struct sockaddr* sa )
{ {
int bind_result; int bind_result;
char s_address[256]; char s_address[256];
int retry = 10; int retry = 10;
sockaddr_address_string(sa, &s_address[0], 256); sockaddr_address_string( sa, &s_address[0], 256 );
do { do {
bind_result = bind(fd, sa, sockaddr_size(sa)); bind_result = bind( fd, sa, sockaddr_size( sa ) );
if (0 == bind_result) { if ( 0 == bind_result ) {
info("Bound to %s", s_address); info( "Bound to %s", s_address );
break; break;
} else { }
warn(SHOW_ERRNO("Couldn't bind to %s", s_address)); else {
warn( SHOW_ERRNO( "Couldn't bind to %s", s_address ) );
switch (errno) { switch ( errno ) {
/* bind() can give us EACCES, EADDRINUSE, EADDRNOTAVAIL, EBADF, /* bind() can give us EACCES, EADDRINUSE, EADDRNOTAVAIL, EBADF,
* EINVAL, ENOTSOCK, EFAULT, ELOOP, ENAMETOOLONG, ENOENT, * EINVAL, ENOTSOCK, EFAULT, ELOOP, ENAMETOOLONG, ENOENT,
* ENOMEM, ENOTDIR, EROFS * ENOMEM, ENOTDIR, EROFS
@@ -165,61 +128,58 @@ int sock_try_bind(int fd, const struct sockaddr *sa)
case EADDRNOTAVAIL: case EADDRNOTAVAIL:
retry--; retry--;
if (retry) { if (retry) {
debug("retrying"); debug( "retrying" );
sleep(1); sleep( 1 );
} }
continue; continue;
case EADDRINUSE: case EADDRINUSE:
warn("%s in use, giving up.", s_address); warn( "%s in use, giving up.", s_address );
retry = 0; retry = 0;
break; break;
default: default:
warn("giving up"); warn( "giving up" );
retry = 0; retry = 0;
} }
} }
} while (retry); } while ( retry );
return bind_result; return bind_result;
} }
int sock_try_select(int nfds, fd_set * readfds, fd_set * writefds, int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
fd_set * exceptfds, struct timeval *timeout)
{ {
int result; int result;
do { do {
result = select(nfds, readfds, writefds, exceptfds, timeout); result = select(nfds, readfds, writefds, exceptfds, timeout);
if (errno != EINTR) { if ( errno != EINTR ) {
break; break;
} }
} while (result == -1); } while ( result == -1 );
return result; return result;
} }
int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen, int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait )
int wait)
{ {
fd_set fds; fd_set fds;
struct timeval tv = { wait, 0 }; struct timeval tv = { wait, 0 };
int result = 0; int result = 0;
if (sock_set_nonblock(fd, 1) == -1) { if ( sock_set_nonblock( fd, 1 ) == -1 ) {
warn(SHOW_ERRNO warn( SHOW_ERRNO( "Failed to set socket non-blocking for connect()" ) );
("Failed to set socket non-blocking for connect()")); return connect( fd, to, addrlen );
return connect(fd, to, addrlen);
} }
FD_ZERO(&fds); FD_ZERO( &fds );
FD_SET(fd, &fds); FD_SET( fd, &fds );
do { do {
result = connect(fd, to, addrlen); result = connect( fd, to, addrlen );
if (result == -1) { if ( result == -1 ) {
switch (errno) { switch( errno ) {
case EINPROGRESS: case EINPROGRESS:
result = 0; result = 0;
break; /* success */ break; /* success */
@@ -230,66 +190,67 @@ int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen,
*/ */
break; break;
default: default:
warn(SHOW_ERRNO("Failed to connect()")); warn( SHOW_ERRNO( "Failed to connect()" ) );
goto out; goto out;
} }
} }
} while (result == -1); } while ( result == -1 );
if (-1 == sock_try_select(FD_SETSIZE, NULL, &fds, NULL, &tv)) { if ( -1 == sock_try_select( FD_SETSIZE, NULL, &fds, NULL, &tv) ) {
warn(SHOW_ERRNO("failed to select() on non-blocking connect")); warn( SHOW_ERRNO( "failed to select() on non-blocking connect" ) );
result = -1; result = -1;
goto out; goto out;
} }
if (!FD_ISSET(fd, &fds)) { if ( !FD_ISSET( fd, &fds ) ) {
result = -1; result = -1;
errno = ETIMEDOUT; errno = ETIMEDOUT;
goto out; goto out;
} }
int scratch; int scratch;
socklen_t s_size = sizeof(scratch); socklen_t s_size = sizeof( scratch );
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &scratch, &s_size) == -1) { if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &scratch, &s_size ) == -1 ) {
result = -1; result = -1;
warn(SHOW_ERRNO("getsockopt() failed")); warn( SHOW_ERRNO( "getsockopt() failed" ) );
goto out; goto out;
} }
if (scratch == EINPROGRESS) { if ( scratch == EINPROGRESS ) {
scratch = ETIMEDOUT; scratch = ETIMEDOUT;
} }
result = scratch ? -1 : 0; result = scratch ? -1 : 0;
errno = scratch; errno = scratch;
out: out:
if (sock_set_nonblock(fd, 0) == -1) { if ( sock_set_nonblock( fd, 0 ) == -1 ) {
warn(SHOW_ERRNO("Failed to make socket blocking after connect()")); warn( SHOW_ERRNO( "Failed to make socket blocking after connect()" ) );
return -1; return -1;
} }
debug("sock_try_connect: %i", result); debug( "sock_try_connect: %i", result );
return 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,32 +7,16 @@
#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, const char* sockaddr_address_string(const struct sockaddr* sa, char* dest, size_t len);
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);
@@ -42,17 +26,16 @@ 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, int sock_try_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
fd_set * exceptfds, struct timeval *timeout);
/* Try to call connect(), timing out after wait seconds */ /* Try to call connect(), timing out after wait seconds */
int sock_try_connect(int fd, struct sockaddr *to, socklen_t addrlen, int sock_try_connect( int fd, struct sockaddr* to, socklen_t addrlen, int wait );
int wait);
/* Try to call close(), retrying EINTR */ /* Try to call close(), retrying EINTR */
int sock_try_close(int fd); int sock_try_close( int fd );
#endif #endif

View File

@@ -13,7 +13,6 @@
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)
{ {
@@ -25,29 +24,25 @@ 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 {
if (fatal) {
abort();
} else {
pthread_exit((void *) 1);
} }
else {
if ( fatal ) { abort(); }
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) { if (line_level < log_level) { return; }
return;
}
va_list argptr; va_list argptr;
@@ -61,8 +56,9 @@ uint64_t monotonic_time_ms()
struct timespec ts; struct timespec ts;
uint64_t seconds_ms, nanoseconds_ms; uint64_t seconds_ms, nanoseconds_ms;
FATAL_IF_NEGATIVE(clock_gettime(CLOCK_MONOTONIC, &ts), FATAL_IF_NEGATIVE(
SHOW_ERRNO("clock_gettime failed") clock_gettime(CLOCK_MONOTONIC, &ts),
SHOW_ERRNO( "clock_gettime failed" )
); );
seconds_ms = ts.tv_sec; seconds_ms = ts.tv_sec;
@@ -75,17 +71,17 @@ uint64_t monotonic_time_ms()
} }
void *xrealloc(void *ptr, size_t size) void* xrealloc(void* ptr, size_t size)
{ {
void *p = realloc(ptr, size); void* p = realloc(ptr, size);
FATAL_IF_NULL(p, "couldn't xrealloc %d bytes", FATAL_IF_NULL(p, "couldn't xrealloc %d bytes", ptr ? "realloc" : "malloc", size);
ptr ? "realloc" : "malloc", size);
return p; return p;
} }
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,11 +10,10 @@
#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 */ , typedef void (cleanup_handler)(void* /* context */, int /* is fatal? */);
int /* is fatal? */ );
/* set from 0 - 5 depending on what level of verbosity you want */ /* set from 0 - 5 depending on what level of verbosity you want */
extern int log_level; extern int log_level;
@@ -22,19 +21,16 @@ 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) \
@@ -88,7 +84,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);
@@ -96,12 +92,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 %s:%d: "msg"\n", monotonic_time_ms(), levstr(level), getpid(),pthread_self(), log_context, __FILE__, __LINE__, ##__VA_ARGS__ ) #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__ )
#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 */
@@ -163,3 +159,4 @@ uint64_t monotonic_time_ms(void);
#define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); } #define WARN_IF_NEGATIVE( value, msg, ... ) if ( value < 0 ) { warn( msg, ##__VA_ARGS__ ); }
#endif #endif

View File

@@ -5,7 +5,7 @@
#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();
@@ -13,9 +13,10 @@ int main(int argc, char **argv)
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

@@ -19,7 +19,6 @@ static struct option proxy_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE; static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
static char proxy_help_text[] = static char proxy_help_text[] =
"Usage: flexnbd-proxy <options>\n\n" "Usage: flexnbd-proxy <options>\n\n"
@@ -27,29 +26,29 @@ static char proxy_help_text[] =
"We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n" "We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n"
HELP_LINE HELP_LINE
"\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n" "\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n"
"\t--" OPT_PORT "\t--" OPT_PORT ",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
"\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n" "\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n" "\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n"
"\t--" OPT_BIND "\t--" OPT_BIND ",-b <ADDR>\tThe address we connect from, as a proxy.\n"
",-b <ADDR>\tThe address we connect from, as a proxy.\n" "\t--" "\t--" OPT_CACHE ",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n"
OPT_CACHE QUIET_LINE
",-c[=<CACHE-BYTES>]\tUse a RAM read cache of the given size.\n" VERBOSE_LINE;
QUIET_LINE VERBOSE_LINE;
static char proxy_default_cache_size[] = "4096"; static char proxy_default_cache_size[] = "4096";
void read_proxy_param(int c, void read_proxy_param(
int c,
char **downstream_addr, char **downstream_addr,
char **downstream_port, char **downstream_port,
char **upstream_addr, char **upstream_addr,
char **upstream_port, char **upstream_port,
char **bind_addr, char **cache_bytes) char **bind_addr,
char **cache_bytes)
{ {
switch (c) { switch( c ) {
case 'h': case 'h' :
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;
@@ -75,23 +74,23 @@ void read_proxy_param(int c,
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;
@@ -105,10 +104,10 @@ int main(int argc, char *argv[])
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;
@@ -117,31 +116,34 @@ int main(int argc, char *argv[])
srand(time(NULL)); srand(time(NULL));
while (1) { while (1) {
c = getopt_long(argc, argv, proxy_short_options, proxy_options, c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL );
NULL); if ( -1 == c ) { break; }
if (-1 == c) { read_proxy_param( c,
break;
}
read_proxy_param(c,
&downstream_addr, &downstream_addr,
&downstream_port, &downstream_port,
&upstream_addr, &upstream_addr,
&upstream_port, &bind_addr, &cache_bytes); &upstream_port,
&bind_addr,
&cache_bytes
);
} }
if (NULL == downstream_addr) { if ( NULL == downstream_addr ){
fprintf(stderr, "--addr is required.\n"); fprintf( stderr, "--addr is required.\n" );
exit_err(proxy_help_text); exit_err( proxy_help_text );
} else if (NULL == upstream_addr || NULL == upstream_port) { } else if ( NULL == upstream_addr || NULL == upstream_port ){
fprintf(stderr, fprintf( stderr, "both --conn-addr and --conn-port are required.\n" );
"both --conn-addr and --conn-port are required.\n"); exit_err( proxy_help_text );
exit_err(proxy_help_text);
} }
proxy = proxy_create(downstream_addr, proxy = proxy_create(
downstream_addr,
downstream_port, downstream_port,
upstream_addr, upstream_addr,
upstream_port, bind_addr, cache_bytes); upstream_port,
bind_addr,
cache_bytes
);
/* Set these *after* proxy has been assigned to */ /* Set these *after* proxy has been assigned to */
sigaction(SIGTERM, &exit_action, NULL); sigaction(SIGTERM, &exit_action, NULL);
@@ -149,17 +151,21 @@ int main(int argc, char *argv[])
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("Proxying between %s %s (downstream) and %s %s (upstream)", info(
downstream_addr, downstream_port, upstream_addr, "Proxying between %s %s (downstream) and %s %s (upstream)",
upstream_port); downstream_addr, downstream_port, upstream_addr, upstream_port
);
} else { } else {
info("Proxying between %s (downstream) and %s %s (upstream)", info(
downstream_addr, upstream_addr, upstream_port); "Proxying between %s (downstream) and %s %s (upstream)",
downstream_addr, upstream_addr, upstream_port
);
} }
success = do_proxy(proxy); success = do_proxy( proxy );
proxy_destroy(proxy); proxy_destroy( proxy );
return success ? 0 : 1; return success ? 0 : 1;
} }

View File

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

View File

@@ -20,15 +20,14 @@ struct prefetch {
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, int prefetch_contains( struct prefetch *prefetch, uint64_t from, uint32_t len );
uint32_t len); char *prefetch_offset( struct prefetch *prefetch, uint64_t from );
char *prefetch_offset(struct prefetch *prefetch, uint64_t from);
#endif #endif

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
@@ -46,9 +46,6 @@ struct proxier {
/* This is the size we advertise to the downstream server */ /* This is the size we advertise to the downstream server */
uint64_t upstream_size; uint64_t upstream_size;
/* These are the transmission flags sent as part of the handshake */
uint32_t upstream_flags;
/* We transform the raw request header into here */ /* We transform the raw request header into here */
struct nbd_request req_hdr; struct nbd_request req_hdr;
@@ -85,13 +82,16 @@ struct proxier {
/** */ /** */
}; };
struct proxier *proxy_create(char *s_downstream_address, struct proxier* proxy_create(
char *s_downstream_port, char* s_downstream_address,
char *s_upstream_address, char* s_downstream_port,
char *s_upstream_port, char* s_upstream_address,
char *s_upstream_bind, char *s_cache_bytes); char* s_upstream_port,
int do_proxy(struct proxier *proxy); char* s_upstream_bind,
void proxy_cleanup(struct proxier *proxy); char* s_cache_bytes);
void proxy_destroy(struct proxier *proxy); int do_proxy( struct proxier* proxy );
void proxy_cleanup( struct proxier* proxy );
void proxy_destroy( struct proxier* proxy );
#endif #endif

View File

@@ -6,37 +6,34 @@
#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, static int is_included_in_acl(int list_length, struct ip_and_mask (*list)[], union mysockaddr* test)
struct ip_and_mask (*list)[],
union mysockaddr *test)
{ {
NULLCHECK(test); NULLCHECK( test );
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, debug("checking acl entry %d (%d/%d)", i, test->generic.sa_family, entry->ip.family);
entry->ip.family);
if (test->generic.sa_family != entry->ip.family) { if (test->generic.sa_family != entry->ip.family) {
continue; continue;
@@ -44,28 +41,28 @@ static int is_included_in_acl(int list_length,
if (test->generic.sa_family == AF_INET) { if (test->generic.sa_family == AF_INET) {
debug("it's an AF_INET"); debug("it's an AF_INET");
raw_address1 = (unsigned char *) &test->v4.sin_addr; raw_address1 = (unsigned char*) &test->v4.sin_addr;
raw_address2 = (unsigned char *) &entry->ip.v4.sin_addr; raw_address2 = (unsigned char*) &entry->ip.v4.sin_addr;
} else if (test->generic.sa_family == AF_INET6) { }
else if (test->generic.sa_family == AF_INET6) {
debug("it's an AF_INET6"); debug("it's an AF_INET6");
raw_address1 = (unsigned char *) &test->v6.sin6_addr; raw_address1 = (unsigned char*) &test->v6.sin6_addr;
raw_address2 = (unsigned char *) &entry->ip.v6.sin6_addr; raw_address2 = (unsigned char*) &entry->ip.v6.sin6_addr;
} else { }
fatal("Can't check an ACL for this address type."); else {
fatal( "Can't check an ACL for this address type." );
} }
debug("testbits=%d", entry->mask); debug("testbits=%d", entry->mask);
for (testbits = entry->mask; testbits > 0; testbits -= 8) { for (testbits = entry->mask; testbits > 0; testbits -= 8) {
debug("testbits=%d, c1=%02x, c2=%02x", testbits, debug("testbits=%d, c1=%02x, c2=%02x", testbits, raw_address1[0], raw_address2[0]);
raw_address1[0], raw_address2[0]);
if (testbits >= 8) { if (testbits >= 8) {
if (raw_address1[0] != raw_address2[0]) { if (raw_address1[0] != raw_address2[0]) { goto no_match; }
goto no_match;
} }
} else { else {
if ((raw_address1[0] & testmasks[testbits % 8]) != if ((raw_address1[0] & testmasks[testbits%8]) !=
(raw_address2[0] & testmasks[testbits % 8])) { (raw_address2[0] & testmasks[testbits%8]) ) {
goto no_match; goto no_match;
} }
} }
@@ -76,34 +73,36 @@ static int is_included_in_acl(int list_length,
return 1; return 1;
no_match:; no_match: ;
debug("no match"); debug("no match");
} }
return 0; return 0;
} }
int acl_includes(struct acl *acl, union mysockaddr *addr) int acl_includes( struct acl * acl, union mysockaddr * addr )
{ {
NULLCHECK(acl); NULLCHECK( acl );
if (0 == acl->len) { if ( 0 == acl->len ) {
return !(acl->default_deny); return !( acl->default_deny );
} else { }
return is_included_in_acl(acl->len, acl->entries, addr); else {
return is_included_in_acl( acl->len, acl->entries, addr );
} }
} }
int acl_default_deny(struct acl *acl) int acl_default_deny( struct acl * acl )
{ {
NULLCHECK(acl); NULLCHECK( acl );
return acl->default_deny; return acl->default_deny;
} }
void acl_destroy(struct acl *acl) void acl_destroy( struct acl * acl )
{ {
free(acl->entries); free( acl->entries );
acl->len = 0; acl->len = 0;
acl->entries = NULL; acl->entries = NULL;
free(acl); free( acl );
} }

View File

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

@@ -13,7 +13,7 @@
* 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,77 +30,64 @@ 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, static inline void bit_clear_range(bitfield_p b, uint64_t from, uint64_t len)
uint64_t len)
{ {
for (; (from % BITS_PER_WORD) != 0 && len > 0; len--) { for ( ; (from % BITS_PER_WORD) != 0 && len > 0 ; len-- ) {
bit_clear(b, from++); bit_clear( b, from++ );
} }
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++ );
} }
} }
@@ -109,18 +96,16 @@ static inline void bit_clear_range(bitfield_p b, uint64_t from,
* bits that are the same as the first one specified. If ''run_is_set'' is * bits that are the same as the first one specified. If ''run_is_set'' is
* non-NULL, the value of that bit is placed into it. * non-NULL, the value of that bit is placed into it.
*/ */
static inline uint64_t bit_run_count(bitfield_p b, uint64_t from, static inline uint64_t bit_run_count(bitfield_p b, uint64_t from, uint64_t len, int *run_is_set) {
uint64_t len, int *run_is_set)
{
uint64_t count = 0; uint64_t count = 0;
int first_value = bit_get(b, from); int first_value = bit_get(b, from);
bitfield_word_t word_match = first_value ? -1 : 0; bitfield_word_t word_match = first_value ? -1 : 0;
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--) { for ( ; ((from + count) % BITS_PER_WORD) != 0 && len > 0; len--) {
if (bit_has_value(b, from + count, first_value)) { if (bit_has_value(b, from + count, first_value)) {
count++; count++;
} else { } else {
@@ -128,7 +113,7 @@ static inline uint64_t bit_run_count(bitfield_p b, uint64_t from,
} }
} }
for (; len >= BITS_PER_WORD; len -= BITS_PER_WORD) { for ( ; len >= BITS_PER_WORD ; len -= BITS_PER_WORD ) {
if (BIT_WORD(b, from + count) == word_match) { if (BIT_WORD(b, from + count) == word_match) {
count += BITS_PER_WORD; count += BITS_PER_WORD;
} else { } else {
@@ -136,8 +121,8 @@ static inline uint64_t bit_run_count(bitfield_p b, uint64_t from,
} }
} }
for (; len > 0; len--) { for ( ; len > 0; len-- ) {
if (bit_has_value(b, from + count, first_value)) { if ( bit_has_value(b, from + count, first_value) ) {
count++; count++;
} }
} }
@@ -145,297 +130,6 @@ static inline uint64_t bit_run_count(bitfield_p b, uint64_t from,
return count; return count;
} }
enum bitset_stream_events {
BITSET_STREAM_UNSET = 0,
BITSET_STREAM_SET = 1,
BITSET_STREAM_ON = 2,
BITSET_STREAM_OFF = 3
};
#define BITSET_STREAM_EVENTS_ENUM_SIZE 4
struct bitset_stream_entry {
enum bitset_stream_events event;
uint64_t from;
uint64_t len;
};
/** Limit the stream size to 1MB for now.
*
* If this is too small, it'll cause requests to stall as the migration lags
* behind the changes made by those requests.
*/
#define BITSET_STREAM_SIZE ( ( 1024 * 1024 ) / sizeof( struct bitset_stream_entry ) )
struct bitset_stream {
struct bitset_stream_entry entries[BITSET_STREAM_SIZE];
int in;
int out;
int size;
pthread_mutex_t mutex;
pthread_cond_t cond_not_full;
pthread_cond_t cond_not_empty;
uint64_t queued_bytes[BITSET_STREAM_EVENTS_ENUM_SIZE];
};
/** An application of a bitset - a bitset mapping represents a file of ''size''
* broken down into ''resolution''-sized chunks. The bit set is assumed to
* represent one bit per chunk. We also bundle a lock so that the set can be
* written reliably by multiple threads.
*/
struct bitset {
pthread_mutex_t lock;
uint64_t size;
int resolution;
struct bitset_stream *stream;
int stream_enabled;
bitfield_word_t bits[];
};
/** Allocate a bitset for a file of the given size, and chunks of the
* given 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
// bitfield word
size_t bitfield_size =
BIT_WORDS_FOR_SIZE(((size + resolution -
1) / resolution)) * sizeof(bitfield_word_t);
struct bitset *bitset =
xmalloc(sizeof(struct bitset) + (bitfield_size / 8));
bitset->size = size;
bitset->resolution = resolution;
/* don't actually need to call pthread_mutex_destroy ' */
pthread_mutex_init(&bitset->lock, NULL);
bitset->stream = xmalloc(sizeof(struct bitset_stream));
pthread_mutex_init(&bitset->stream->mutex, NULL);
/* 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_empty, NULL);
return bitset;
}
static inline void bitset_free(struct bitset *set)
{
/* TODO: free our mutex... */
free(set->stream);
set->stream = NULL;
free(set);
}
#define INT_FIRST_AND_LAST \
uint64_t first = from/set->resolution, \
last = ((from+len)-1)/set->resolution, \
bitlen = (last-first)+1
#define BITSET_LOCK \
FATAL_IF_NEGATIVE(pthread_mutex_lock(&set->lock), "Error locking bitset")
#define BITSET_UNLOCK \
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
static inline void bitset_stream_enqueue(struct bitset *set,
enum bitset_stream_events event,
uint64_t from, uint64_t len)
{
struct bitset_stream *stream = set->stream;
pthread_mutex_lock(&stream->mutex);
while (stream->size == BITSET_STREAM_SIZE) {
pthread_cond_wait(&stream->cond_not_full, &stream->mutex);
}
stream->entries[stream->in].event = event;
stream->entries[stream->in].from = from;
stream->entries[stream->in].len = len;
stream->queued_bytes[event] += len;
stream->size++;
stream->in++;
stream->in %= BITSET_STREAM_SIZE;
pthread_mutex_unlock(&stream->mutex);
pthread_cond_signal(&stream->cond_not_empty);
return;
}
static inline void bitset_stream_dequeue(struct bitset *set,
struct bitset_stream_entry *out)
{
struct bitset_stream *stream = set->stream;
struct bitset_stream_entry *dequeued;
pthread_mutex_lock(&stream->mutex);
while (stream->size == 0) {
pthread_cond_wait(&stream->cond_not_empty, &stream->mutex);
}
dequeued = &stream->entries[stream->out];
if (out != NULL) {
out->event = dequeued->event;
out->from = dequeued->from;
out->len = dequeued->len;
}
stream->queued_bytes[dequeued->event] -= dequeued->len;
stream->size--;
stream->out++;
stream->out %= BITSET_STREAM_SIZE;
pthread_mutex_unlock(&stream->mutex);
pthread_cond_signal(&stream->cond_not_full);
return;
}
static inline size_t bitset_stream_size(struct bitset *set)
{
size_t size;
pthread_mutex_lock(&set->stream->mutex);
size = set->stream->size;
pthread_mutex_unlock(&set->stream->mutex);
return size;
}
static inline uint64_t bitset_stream_queued_bytes(struct bitset *set,
enum bitset_stream_events
event)
{
uint64_t total;
pthread_mutex_lock(&set->stream->mutex);
total = set->stream->queued_bytes[event];
pthread_mutex_unlock(&set->stream->mutex);
return total;
}
static inline void bitset_enable_stream(struct bitset *set)
{
BITSET_LOCK;
set->stream_enabled = 1;
bitset_stream_enqueue(set, BITSET_STREAM_ON, 0, set->size);
BITSET_UNLOCK;
}
static inline void bitset_disable_stream(struct bitset *set)
{
BITSET_LOCK;
bitset_stream_enqueue(set, BITSET_STREAM_OFF, 0, set->size);
set->stream_enabled = 0;
BITSET_UNLOCK;
}
/** Set the bits in a bitset which correspond to the given bytes in the larger
* file.
*/
static inline void bitset_set_range(struct bitset *set,
uint64_t from, uint64_t len)
{
INT_FIRST_AND_LAST;
BITSET_LOCK;
bit_set_range(set->bits, first, bitlen);
if (set->stream_enabled) {
bitset_stream_enqueue(set, BITSET_STREAM_SET, from, len);
}
BITSET_UNLOCK;
}
/** Set every bit in the bitset. */
static inline void bitset_set(struct bitset *set)
{
bitset_set_range(set, 0, set->size);
}
/** Clear the bits in a bitset which correspond to the given bytes in the
* larger file.
*/
static inline void bitset_clear_range(struct bitset *set,
uint64_t from, uint64_t len)
{
INT_FIRST_AND_LAST;
BITSET_LOCK;
bit_clear_range(set->bits, first, bitlen);
if (set->stream_enabled) {
bitset_stream_enqueue(set, BITSET_STREAM_UNSET, from, len);
}
BITSET_UNLOCK;
}
/** Clear every bit in the bitset. */
static inline void bitset_clear(struct bitset *set)
{
bitset_clear_range(set, 0, set->size);
}
/** As per bitset_run_count but also tells you whether the run it found was set
* or unset, atomically.
*/
static inline uint64_t bitset_run_count_ex(struct bitset *set,
uint64_t from,
uint64_t len, int *run_is_set)
{
uint64_t run;
/* Clip our requests to the end of the bitset, avoiding uint underflow. */
if (from > set->size) {
return 0;
}
len = (len + from) > set->size ? (set->size - from) : len;
INT_FIRST_AND_LAST;
BITSET_LOCK;
run =
bit_run_count(set->bits, first, bitlen,
run_is_set) * set->resolution;
run -= (from % set->resolution);
BITSET_UNLOCK;
return run;
}
/** Counts the number of contiguous bytes that are represented as a run in
* the bit field.
*/
static inline uint64_t bitset_run_count(struct bitset *set,
uint64_t from, uint64_t len)
{
return bitset_run_count_ex(set, from, len, NULL);
}
/** 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)
{
return bit_is_clear(set->bits, at / set->resolution);
}
/** 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)
{
return bit_is_set(set->bits, at / set->resolution);
}
#endif #endif

317
src/server/bitstream.h Normal file
View File

@@ -0,0 +1,317 @@
/*
* bitstream.h
*
* Created on: 13 Oct 2016
* Author: michel
*/
#ifndef SRC_SERVER_BITSTREAM_H_
#define SRC_SERVER_BITSTREAM_H_
#include "bitset.h"
enum bitset_stream_events {
BITSET_STREAM_UNSET = 0,
BITSET_STREAM_SET = 1,
BITSET_STREAM_ON = 2,
BITSET_STREAM_OFF = 3
};
#define BITSET_STREAM_EVENTS_ENUM_SIZE 4
struct bitset_stream_entry {
enum bitset_stream_events event;
uint64_t from;
uint64_t len;
};
/** Limit the stream size to 1MB for now.
*
* If this is too small, it'll cause requests to stall as the migration lags
* behind the changes made by those requests.
*/
#define BITSET_STREAM_SIZE ( ( 1024 * 1024 ) / sizeof( struct bitset_stream_entry ) )
struct bitset_stream {
struct bitset_stream_entry entries[BITSET_STREAM_SIZE];
int in;
int out;
int size;
pthread_mutex_t mutex;
pthread_cond_t cond_not_full;
pthread_cond_t cond_not_empty;
uint64_t queued_bytes[BITSET_STREAM_EVENTS_ENUM_SIZE];
};
/** An application of a bitset - a bitset mapping represents a file of ''size''
* broken down into ''resolution''-sized chunks. The bit set is assumed to
* represent one bit per chunk. We also bundle a lock so that the set can be
* written reliably by multiple threads.
*/
struct bitset {
pthread_mutex_t lock;
uint64_t size;
int resolution;
struct bitset_stream *stream;
int stream_enabled;
bitfield_word_t bits[];
};
/** Allocate a bitset for a file of the given size, and chunks of the
* given 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
// bitfield word
size_t bitfield_size =
BIT_WORDS_FOR_SIZE((( size + resolution - 1 ) / resolution)) * sizeof( bitfield_word_t );
struct bitset *bitset = xmalloc(sizeof( struct bitset ) + ( bitfield_size / 8 ) );
bitset->size = size;
bitset->resolution = resolution;
/* don't actually need to call pthread_mutex_destroy '*/
pthread_mutex_init(&bitset->lock, NULL);
bitset->stream = xmalloc( sizeof( struct bitset_stream ) );
pthread_mutex_init( &bitset->stream->mutex, NULL );
/* 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_empty, NULL );
return bitset;
}
static inline void bitset_free( struct bitset * set )
{
/* TODO: free our mutex... */
free( set->stream );
set->stream = NULL;
free( set );
}
#define INT_FIRST_AND_LAST \
uint64_t first = from/set->resolution, \
last = ((from+len)-1)/set->resolution, \
bitlen = (last-first)+1
#define BITSET_LOCK \
FATAL_IF_NEGATIVE(pthread_mutex_lock(&set->lock), "Error locking bitset")
#define BITSET_UNLOCK \
FATAL_IF_NEGATIVE(pthread_mutex_unlock(&set->lock), "Error unlocking bitset")
static inline void bitset_stream_enqueue(
struct bitset * set,
enum bitset_stream_events event,
uint64_t from,
uint64_t len
)
{
struct bitset_stream * stream = set->stream;
pthread_mutex_lock( &stream->mutex );
while ( stream->size == BITSET_STREAM_SIZE ) {
pthread_cond_wait( &stream->cond_not_full, &stream->mutex );
}
stream->entries[stream->in].event = event;
stream->entries[stream->in].from = from;
stream->entries[stream->in].len = len;
stream->queued_bytes[event] += len;
stream->size++;
stream->in++;
stream->in %= BITSET_STREAM_SIZE;
pthread_mutex_unlock( & stream->mutex );
pthread_cond_signal( &stream->cond_not_empty );
return;
}
static inline void bitset_stream_dequeue(
struct bitset * set,
struct bitset_stream_entry * out
)
{
struct bitset_stream * stream = set->stream;
struct bitset_stream_entry * dequeued;
pthread_mutex_lock( &stream->mutex );
while ( stream->size == 0 ) {
pthread_cond_wait( &stream->cond_not_empty, &stream->mutex );
}
dequeued = &stream->entries[stream->out];
if ( out != NULL ) {
out->event = dequeued->event;
out->from = dequeued->from;
out->len = dequeued->len;
}
stream->queued_bytes[dequeued->event] -= dequeued->len;
stream->size--;
stream->out++;
stream->out %= BITSET_STREAM_SIZE;
pthread_mutex_unlock( &stream->mutex );
pthread_cond_signal( &stream->cond_not_full );
return;
}
static inline size_t bitset_stream_size( struct bitset * set )
{
size_t size;
pthread_mutex_lock( &set->stream->mutex );
size = set->stream->size;
pthread_mutex_unlock( &set->stream->mutex );
return size;
}
static inline uint64_t bitset_stream_queued_bytes(
struct bitset * set,
enum bitset_stream_events event
)
{
uint64_t total;
pthread_mutex_lock( &set->stream->mutex );
total = set->stream->queued_bytes[event];
pthread_mutex_unlock( &set->stream->mutex );
return total;
}
static inline void bitset_enable_stream( struct bitset * set )
{
BITSET_LOCK;
set->stream_enabled = 1;
bitset_stream_enqueue( set, BITSET_STREAM_ON, 0, set->size );
BITSET_UNLOCK;
}
static inline void bitset_disable_stream( struct bitset * set )
{
BITSET_LOCK;
bitset_stream_enqueue( set, BITSET_STREAM_OFF, 0, set->size );
set->stream_enabled = 0;
BITSET_UNLOCK;
}
/** Set the bits in a bitset which correspond to the given bytes in the larger
* file.
*/
static inline void bitset_set_range(
struct bitset * set,
uint64_t from,
uint64_t len)
{
INT_FIRST_AND_LAST;
BITSET_LOCK;
bit_set_range(set->bits, first, bitlen);
if ( set->stream_enabled ) {
bitset_stream_enqueue( set, BITSET_STREAM_SET, from, len );
}
BITSET_UNLOCK;
}
/** Set every bit in the bitset. */
static inline void bitset_set( struct bitset * set )
{
bitset_set_range(set, 0, set->size);
}
/** Clear the bits in a bitset which correspond to the given bytes in the
* larger file.
*/
static inline void bitset_clear_range(
struct bitset * set,
uint64_t from,
uint64_t len)
{
INT_FIRST_AND_LAST;
BITSET_LOCK;
bit_clear_range(set->bits, first, bitlen);
if ( set->stream_enabled ) {
bitset_stream_enqueue( set, BITSET_STREAM_UNSET, from, len );
}
BITSET_UNLOCK;
}
/** Clear every bit in the bitset. */
static inline void bitset_clear( struct bitset * set )
{
bitset_clear_range(set, 0, set->size);
}
/** As per bitset_run_count but also tells you whether the run it found was set
* or unset, atomically.
*/
static inline uint64_t bitset_run_count_ex(
struct bitset * set,
uint64_t from,
uint64_t len,
int* run_is_set
)
{
uint64_t run;
/* Clip our requests to the end of the bitset, avoiding uint underflow. */
if ( from > set->size ) {
return 0;
}
len = ( len + from ) > set->size ? ( set->size - from ) : len;
INT_FIRST_AND_LAST;
BITSET_LOCK;
run = bit_run_count(set->bits, first, bitlen, run_is_set) * set->resolution;
run -= (from % set->resolution);
BITSET_UNLOCK;
return run;
}
/** Counts the number of contiguous bytes that are represented as a run in
* the bit field.
*/
static inline uint64_t bitset_run_count(
struct bitset * set,
uint64_t from,
uint64_t len)
{
return bitset_run_count_ex( set, from, len, NULL );
}
/** 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 )
{
return bit_is_clear(set->bits, at/set->resolution);
}
/** 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 )
{
return bit_is_set(set->bits, at/set->resolution);
}
#endif /* SRC_SERVER_BITSTREAM_H_ */

View File

@@ -3,7 +3,7 @@
#include "ioutil.h" #include "ioutil.h"
#include "sockutil.h" #include "sockutil.h"
#include "util.h" #include "util.h"
#include "bitset.h" #include "bitstream.h"
#include "nbdtypes.h" #include "nbdtypes.h"
#include "self_pipe.h" #include "self_pipe.h"
@@ -18,22 +18,20 @@
// When this signal is invoked, we call shutdown() on the client fd, which // When this signal is invoked, we call shutdown() on the client fd, which
// results in the thread being wound up // results in the thread being wound up
void client_killswitch_hit(int signal void client_killswitch_hit(int signal __attribute__ ((unused)), siginfo_t *info, void *ptr __attribute__ ((unused)))
__attribute__ ((unused)), siginfo_t * info,
void *ptr __attribute__ ((unused)))
{ {
int fd = info->si_value.sival_int; int fd = info->si_value.sival_int;
warn("Killswitch for fd %i activated, calling shutdown on socket", fd); warn( "Killswitch for fd %i activated, calling shutdown on socket", fd );
FATAL_IF(-1 == shutdown(fd, SHUT_RDWR), FATAL_IF(
SHOW_ERRNO -1 == shutdown( fd, SHUT_RDWR ),
("Failed to shutdown() the socket, killing the server") SHOW_ERRNO( "Failed to shutdown() the socket, killing the server" )
); );
} }
struct client *client_create(struct server *serve, int socket) struct client *client_create( struct server *serve, int socket )
{ {
NULLCHECK(serve); NULLCHECK( serve );
struct client *c; struct client *c;
struct sigevent evp = { struct sigevent evp = {
@@ -48,44 +46,44 @@ struct client *client_create(struct server *serve, int socket)
*/ */
evp.sigev_value.sival_int = socket; evp.sigev_value.sival_int = socket;
c = xmalloc(sizeof(struct client)); c = xmalloc( sizeof( struct client ) );
c->stopped = 0; c->stopped = 0;
c->socket = socket; c->socket = socket;
c->serve = serve; c->serve = serve;
c->stop_signal = self_pipe_create(); c->stop_signal = self_pipe_create();
FATAL_IF_NEGATIVE(timer_create FATAL_IF_NEGATIVE(
(CLOCK_MONOTONIC, &evp, &(c->killswitch)), timer_create( CLOCK_MONOTONIC, &evp, &(c->killswitch) ),
SHOW_ERRNO("Failed to create killswitch timer") SHOW_ERRNO( "Failed to create killswitch timer" )
); );
debug("Alloced client %p with socket %d", c, socket); debug( "Alloced client %p with socket %d", c, socket );
return c; return c;
} }
void client_signal_stop(struct client *c) void client_signal_stop( struct client *c)
{ {
NULLCHECK(c); NULLCHECK( c);
debug("client %p: signal stop (%d, %d)", c, c->stop_signal->read_fd, debug("client %p: signal stop (%d, %d)", c,c->stop_signal->read_fd, c->stop_signal->write_fd );
c->stop_signal->write_fd); self_pipe_signal( c->stop_signal );
self_pipe_signal(c->stop_signal);
} }
void client_destroy(struct client *client) void client_destroy( struct client *client )
{ {
NULLCHECK(client); NULLCHECK( client );
FATAL_IF_NEGATIVE(timer_delete(client->killswitch), FATAL_IF_NEGATIVE(
SHOW_ERRNO("Couldn't delete killswitch") timer_delete( client->killswitch ),
SHOW_ERRNO( "Couldn't delete killswitch" )
); );
debug("Destroying stop signal for client %p", client); debug( "Destroying stop signal for client %p", client );
self_pipe_destroy(client->stop_signal); self_pipe_destroy( client->stop_signal );
debug("Freeing client %p", client); debug( "Freeing client %p", client );
free(client); free( client );
} }
@@ -102,13 +100,13 @@ void client_destroy(struct client *client)
* allocated, we can proceed as normal and make one call to writeloop. * allocated, we can proceed as normal and make one call to writeloop.
* *
*/ */
void write_not_zeroes(struct client *client, uint64_t from, uint64_t len) void write_not_zeroes(struct client* client, uint64_t from, uint64_t len)
{ {
NULLCHECK(client); NULLCHECK( client );
NULLCHECK(client->serve); NULLCHECK( client->serve );
NULLCHECK(client->serve->allocation_map); NULLCHECK( client->serve->allocation_map );
struct bitset *map = client->serve->allocation_map; struct bitset * map = client->serve->allocation_map;
while (len > 0) { while (len > 0) {
/* so we have to calculate how much of our input to consider /* so we have to calculate how much of our input to consider
@@ -121,8 +119,7 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
uint64_t run = bitset_run_count(map, from, len); uint64_t run = bitset_run_count(map, from, len);
debug("write_not_zeroes: from=%ld, len=%d, run=%d", from, len, debug("write_not_zeroes: from=%ld, len=%d, run=%d", from, len, run);
run);
if (run > len) { if (run > len) {
run = len; run = len;
@@ -146,13 +143,13 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
} }
*/ */
#define DO_READ(dst, len) ERROR_IF_NEGATIVE( \ #define DO_READ(dst, len) ERROR_IF_NEGATIVE( \
readloop( \ readloop( \
client->socket, \ client->socket, \
(dst), \ (dst), \
(len) \ (len) \
), \ ), \
SHOW_ERRNO("read failed %ld+%d", from, (len)) \ "read failed %ld+%d", from, (len) \
) )
if (bitset_is_set_at(map, from)) { if (bitset_is_set_at(map, from)) {
@@ -164,10 +161,11 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
* sake of the event stream - the actual bytes have changed, and we * sake of the event stream - the actual bytes have changed, and we
* are interested in that fact. * are interested in that fact.
*/ */
bitset_set_range(map, from, run); bitset_set_range( map, from, run );
len -= run; len -= run;
from += run; from += run;
} else { }
else {
char zerobuffer[block_allocation_resolution]; char zerobuffer[block_allocation_resolution];
/* not allocated, read in block_allocation_resoution */ /* not allocated, read in block_allocation_resoution */
while (run > 0) { while (run > 0) {
@@ -185,11 +183,10 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
*/ */
int all_zeros = (zerobuffer[0] == 0) && int all_zeros = (zerobuffer[0] == 0) &&
(0 == (0 == memcmp( zerobuffer, zerobuffer+1, blockrun-1 ));
memcmp(zerobuffer, zerobuffer + 1, blockrun - 1));
if (!all_zeros) { if ( !all_zeros ) {
memcpy(client->mapped + from, zerobuffer, blockrun); memcpy(client->mapped+from, zerobuffer, blockrun);
bitset_set_range(map, from, blockrun); bitset_set_range(map, from, blockrun);
/* at this point we could choose to /* at this point we could choose to
* short-cut the rest of the write for * short-cut the rest of the write for
@@ -212,7 +209,7 @@ void write_not_zeroes(struct client *client, uint64_t from, uint64_t len)
} }
int fd_read_request(int fd, struct nbd_request_raw *out_request) int fd_read_request( int fd, struct nbd_request_raw *out_request)
{ {
return readloop(fd, out_request, sizeof(struct nbd_request_raw)); return readloop(fd, out_request, sizeof(struct nbd_request_raw));
} }
@@ -220,25 +217,22 @@ int fd_read_request(int fd, struct nbd_request_raw *out_request)
/* Returns 1 if *request was filled with a valid request which we should /* Returns 1 if *request was filled with a valid request which we should
* try to honour. 0 otherwise. */ * try to honour. 0 otherwise. */
int client_read_request(struct client *client, int client_read_request( struct client * client , struct nbd_request *out_request, int * disconnected )
struct nbd_request *out_request, int *disconnected)
{ {
NULLCHECK(client); NULLCHECK( client );
NULLCHECK(out_request); NULLCHECK( out_request );
struct nbd_request_raw request_raw; struct nbd_request_raw request_raw;
if (fd_read_request(client->socket, &request_raw) == -1) { if (fd_read_request(client->socket, &request_raw) == -1) {
*disconnected = 1; *disconnected = 1;
switch (errno) { switch( errno ){
case 0: case 0:
warn(SHOW_ERRNO("EOF while reading request")); debug( "EOF while reading request" );
return 0; return 0;
case ECONNRESET: case ECONNRESET:
warn(SHOW_ERRNO("Connection reset while reading request")); debug( "Connection reset while"
return 0; " reading request" );
case ETIMEDOUT:
warn(SHOW_ERRNO("Connection timed out while reading request"));
return 0; return 0;
default: default:
/* FIXME: I've seen this happen, but I /* FIXME: I've seen this happen, but I
@@ -248,15 +242,17 @@ int client_read_request(struct client *client,
* again. It should *probably* be an * again. It should *probably* be an
* error() call, but I want to be sure. * error() call, but I want to be sure.
* */ * */
fatal(SHOW_ERRNO("Error reading request")); fatal("Error reading request: %d, %s",
errno,
strerror( errno ));
} }
} }
nbd_r2h_request(&request_raw, out_request); nbd_r2h_request( &request_raw, out_request );
return 1; return 1;
} }
int fd_write_reply(int fd, uint64_t handle, int error) int fd_write_reply( int fd, uint64_t handle, int error )
{ {
struct nbd_reply reply; struct nbd_reply reply;
struct nbd_reply_raw reply_raw; struct nbd_reply_raw reply_raw;
@@ -265,23 +261,22 @@ int fd_write_reply(int fd, uint64_t handle, int error)
reply.error = error; reply.error = error;
reply.handle.w = handle; reply.handle.w = handle;
nbd_h2r_reply(&reply, &reply_raw); nbd_h2r_reply( &reply, &reply_raw );
debug("Replying with handle=0x%08X, error=%" PRIu32, handle, error); debug( "Replying with handle=0x%08X, error=%"PRIu32, handle, error );
if (-1 == writeloop(fd, &reply_raw, sizeof(reply_raw))) { if( -1 == writeloop( fd, &reply_raw, sizeof( reply_raw ) ) ) {
switch (errno) { switch( errno ) {
case ECONNRESET: case ECONNRESET:
error(SHOW_ERRNO("Connection reset while writing reply")); error( "Connection reset while writing reply" );
break; break;
case EBADF: case EBADF:
fatal(SHOW_ERRNO fatal( "Tried to write to an invalid file descriptor" );
("Tried to write to an invalid file descriptor"));
break; break;
case EPIPE: case EPIPE:
error(SHOW_ERRNO("Remote end closed")); error( "Remote end closed" );
break; break;
default: default:
fatal(SHOW_ERRNO("Unhandled error while writing")); fatal( "Unhandled error while writing: %d", errno );
} }
} }
@@ -294,32 +289,28 @@ int fd_write_reply(int fd, uint64_t handle, int error)
* Returns 1; we don't check for errors on the write. * Returns 1; we don't check for errors on the write.
* TODO: Check for errors on the write. * TODO: Check for errors on the write.
*/ */
int client_write_reply(struct client *client, struct nbd_request *request, int client_write_reply( struct client * client, struct nbd_request *request, int error )
int error)
{ {
return fd_write_reply(client->socket, request->handle.w, error); return fd_write_reply( client->socket, request->handle.w, error);
} }
void client_write_init(struct client *client, uint64_t size) void client_write_init( struct client * client, uint64_t size )
{ {
struct nbd_init init = { {0} }; struct nbd_init init = {{0}};
struct nbd_init_raw init_raw = { {0} }; struct nbd_init_raw init_raw = {{0}};
memcpy(init.passwd, INIT_PASSWD, sizeof(init.passwd)); memcpy( init.passwd, INIT_PASSWD, sizeof( init.passwd ) );
init.magic = INIT_MAGIC; init.magic = INIT_MAGIC;
init.size = size; init.size = size;
/* As more features are implemented, this is the place to advertise memset( init.reserved, 0, 128 );
* them.
*/
init.flags = FLAG_HAS_FLAGS | FLAG_SEND_FLUSH | FLAG_SEND_FUA;
memset(init.reserved, 0, 124);
nbd_h2r_init(&init, &init_raw); nbd_h2r_init( &init, &init_raw );
ERROR_IF_NEGATIVE(writeloop ERROR_IF_NEGATIVE(
(client->socket, &init_raw, sizeof(init_raw)), writeloop(client->socket, &init_raw, sizeof(init_raw)),
SHOW_ERRNO("Couldn't send hello")); "Couldn't send hello"
);
} }
@@ -327,26 +318,35 @@ void client_write_init(struct client *client, uint64_t size)
* client sends a write we can't honour - we need to get rid of the * client sends a write we can't honour - we need to get rid of the
* bytes they've already written before we can look for another request. * bytes they've already written before we can look for another request.
*/ */
void client_flush(struct client *client, size_t len) void client_flush( struct client * client, size_t len )
{ {
int devnull = open("/dev/null", O_WRONLY); int devnull = open("/dev/null", O_WRONLY);
FATAL_IF_NEGATIVE(devnull, SHOW_ERRNO("Couldn't open /dev/null")); FATAL_IF_NEGATIVE( devnull,
"Couldn't open /dev/null: %s", strerror(errno));
int pipes[2]; int pipes[2];
pipe(pipes); pipe( pipes );
const unsigned int flags = SPLICE_F_MORE | SPLICE_F_MOVE; const unsigned int flags = SPLICE_F_MORE | SPLICE_F_MOVE;
size_t spliced = 0; size_t spliced = 0;
while (spliced < len) { while ( spliced < len ) {
ssize_t received = splice(client->socket, NULL, ssize_t received = splice(
client->socket, NULL,
pipes[1], NULL, pipes[1], NULL,
len - spliced, flags); len-spliced, flags );
FATAL_IF_NEGATIVE(received, SHOW_ERRNO("splice error")); FATAL_IF_NEGATIVE( received,
"splice error: %s",
strerror(errno));
ssize_t junked = 0; ssize_t junked = 0;
while (junked < received) { while( junked < received ) {
ssize_t junk; ssize_t junk;
junk = splice(pipes[0], NULL, devnull, NULL, received, flags); junk = splice(
FATAL_IF_NEGATIVE(junk, SHOW_ERRNO("splice error")); pipes[0], NULL,
devnull, NULL,
received, flags );
FATAL_IF_NEGATIVE( junk,
"splice error: %s",
strerror(errno));
junked += junk; junked += junk;
} }
spliced += received; spliced += received;
@@ -354,7 +354,7 @@ void client_flush(struct client *client, size_t len)
debug("Flushed %d bytes", len); debug("Flushed %d bytes", len);
close(devnull); close( devnull );
} }
@@ -363,8 +363,8 @@ void client_flush(struct client *client, size_t len)
* request_err is set to 0 if the client sent a bad request, in which * request_err is set to 0 if the client sent a bad request, in which
* case we drop the connection. * case we drop the connection.
*/ */
int client_request_needs_reply(struct client *client, int client_request_needs_reply( struct client * client,
struct nbd_request request) struct nbd_request request )
{ {
/* The client is stupid, but don't take down the whole server as a result. /* The client is stupid, but don't take down the whole server as a result.
* We send a reply before disconnecting so that at least some indication of * We send a reply before disconnecting so that at least some indication of
@@ -373,31 +373,32 @@ int client_request_needs_reply(struct client *client,
*/ */
if (request.magic != REQUEST_MAGIC) { if (request.magic != REQUEST_MAGIC) {
warn("Bad magic 0x%08X from client", request.magic); warn("Bad magic 0x%08X from client", request.magic);
client_write_reply(client, &request, EBADMSG); client_write_reply( client, &request, EBADMSG );
client->disconnect = 1; // no need to flush client->disconnect = 1; // no need to flush
return 0; return 0;
} }
debug("request type=%" PRIu16 ", flags=%" PRIu16 ", from=%" PRIu64 debug(
", len=%" PRIu32 ", handle=0x%08X", request.type, request.flags, "request type=%"PRIu32", from=%"PRIu64", len=%"PRIu32", handle=0x%08X",
request.from, request.len, request.handle); request.type, request.from, request.len, request.handle
);
/* check it's not out of range. NBD protocol requires ENOSPC to be /* check it's not out of range */
* returned in this instance if ( request.from+request.len > client->serve->size) {
*/ warn("write request %"PRIu64"+%"PRIu32" out of range",
if (request.from + request.len > client->serve->size) { request.from, request.len
warn("write request %" PRIu64 "+%" PRIu32 " out of range", );
request.from, request.len); if ( request.type == REQUEST_WRITE ) {
if (request.type == REQUEST_WRITE) { client_flush( client, request.len );
client_flush(client, request.len);
} }
client_write_reply(client, &request, ENOSPC); client_write_reply( client, &request, EPERM ); /* TODO: Change to ERANGE ? */
client->disconnect = 0; client->disconnect = 0;
return 0; return 0;
} }
switch (request.type) { switch (request.type)
{
case REQUEST_READ: case REQUEST_READ:
break; break;
case REQUEST_WRITE: case REQUEST_WRITE:
@@ -406,114 +407,92 @@ int client_request_needs_reply(struct client *client,
debug("request disconnect"); debug("request disconnect");
client->disconnect = 1; client->disconnect = 1;
return 0; return 0;
case REQUEST_FLUSH:
break;
default: default:
/* NBD prototcol says servers SHOULD return EINVAL to unknown fatal("Unknown request 0x%08X", request.type);
* commands */
warn("Unknown request 0x%08X", request.type);
client_write_reply(client, &request, EINVAL);
client->disconnect = 0;
return 0;
} }
return 1; return 1;
} }
void client_reply_to_read(struct client *client, void client_reply_to_read( struct client* client, struct nbd_request request )
struct nbd_request request)
{ {
off64_t offset; off64_t offset;
debug("request read %ld+%d", request.from, request.len); debug("request read %ld+%d", request.from, request.len);
sock_set_tcp_cork(client->socket, 1); sock_set_tcp_cork( client->socket, 1 );
client_write_reply(client, &request, 0); client_write_reply( client, &request, 0 );
offset = request.from; offset = request.from;
/* If we get cut off partway through this sendfile, we don't /* If we get cut off partway through this sendfile, we don't
* want to kill the server. This should be an error. * want to kill the server. This should be an error.
*/ */
ERROR_IF_NEGATIVE(sendfileloop(client->socket, ERROR_IF_NEGATIVE(
sendfileloop(
client->socket,
client->fileno, client->fileno,
&offset, &offset,
request.len), request.len),
"sendfile failed from=%ld, len=%d", "sendfile failed from=%ld, len=%d",
offset, request.len); offset,
request.len);
sock_set_tcp_cork(client->socket, 0); sock_set_tcp_cork( client->socket, 0 );
} }
void client_reply_to_write(struct client *client, void client_reply_to_write( struct client* client, struct nbd_request request )
struct nbd_request request)
{ {
debug("request write from=%" PRIu64 ", len=%" PRIu32 ", handle=0x%08X", debug("request write from=%"PRIu64", len=%"PRIu32", handle=0x%08X", request.from, request.len, request.handle);
request.from, request.len, request.handle);
if (client->serve->allocation_map_built) { if (client->serve->allocation_map_built) {
write_not_zeroes(client, request.from, request.len); write_not_zeroes( client, request.from, request.len );
} else { }
else {
debug("No allocation map, writing directly."); debug("No allocation map, writing directly.");
/* If we get cut off partway through reading this data: /* If we get cut off partway through reading this data:
* */ * */
ERROR_IF_NEGATIVE(readloop(client->socket, ERROR_IF_NEGATIVE(
readloop( client->socket,
client->mapped + request.from, client->mapped + request.from,
request.len), request.len),
SHOW_ERRNO("reading write data failed from=%ld, len=%d", "reading write data failed from=%ld, len=%d",
request.from, request.len)); request.from,
request.len
);
/* the allocation_map is shared between client threads, and may be /* the allocation_map is shared between client threads, and may be
* being built. We need to reflect the write in it, as it may be in * being built. We need to reflect the write in it, as it may be in
* a position the builder has already gone over. * a position the builder has already gone over.
*/ */
bitset_set_range(client->serve->allocation_map, request.from, bitset_set_range(client->serve->allocation_map, request.from, request.len);
request.len);
} }
// Only flush if FUA is set -- overridden for now to force flush after each if (1) /* not sure whether this is necessary... */
// write. {
// if (request.flags & CMD_FLAG_FUA) { /* multiple of 4K page size */
if (1) { uint64_t from_rounded = request.from & (!0xfff);
/* multiple of page size */
uint64_t from_rounded =
request.from & (~(sysconf(_SC_PAGE_SIZE) - 1));
uint64_t len_rounded = request.len + (request.from - from_rounded); uint64_t len_rounded = request.len + (request.from - from_rounded);
debug("Calling msync from=%" PRIu64 ", len=%" PRIu64 "",
from_rounded, len_rounded);
FATAL_IF_NEGATIVE(msync(client->mapped + from_rounded, FATAL_IF_NEGATIVE(
msync( client->mapped + from_rounded,
len_rounded, len_rounded,
MS_SYNC | MS_INVALIDATE), MS_SYNC | MS_INVALIDATE),
"msync failed %ld %ld", request.from, "msync failed %ld %ld", request.from, request.len
request.len); );
} }
client_write_reply(client, &request, 0); client_write_reply( client, &request, 0);
} }
void client_reply_to_flush(struct client *client,
struct nbd_request request)
{
debug("request flush from=%" PRIu64 ", len=%" PRIu32 ", handle=0x%08X",
request.from, request.len, request.handle);
ERROR_IF_NEGATIVE(msync void client_reply( struct client* client, struct nbd_request request )
(client->mapped, client->mapped_size,
MS_SYNC | MS_INVALIDATE), "flush failed");
client_write_reply(client, &request, 0);
}
void client_reply(struct client *client, struct nbd_request request)
{ {
switch (request.type) { switch (request.type) {
case REQUEST_READ: case REQUEST_READ:
client_reply_to_read(client, request); client_reply_to_read( client, request );
break; break;
case REQUEST_WRITE: case REQUEST_WRITE:
client_reply_to_write(client, request); client_reply_to_write( client, request );
break;
case REQUEST_FLUSH:
client_reply_to_flush(client, request);
break; break;
} }
} }
@@ -522,50 +501,52 @@ void client_reply(struct client *client, struct nbd_request request)
/* Starts a timer that will kill the whole process if disarm is not called /* Starts a timer that will kill the whole process if disarm is not called
* within a timeout (see CLIENT_HANDLE_TIMEOUT). * within a timeout (see CLIENT_HANDLE_TIMEOUT).
*/ */
void client_arm_killswitch(struct client *client) void client_arm_killswitch( struct client* client )
{ {
struct itimerspec its = { struct itimerspec its = {
.it_value = {.tv_nsec = 0,.tv_sec = CLIENT_HANDLER_TIMEOUT}, .it_value = { .tv_nsec = 0, .tv_sec = CLIENT_HANDLER_TIMEOUT },
.it_interval = {.tv_nsec = 0,.tv_sec = 0} .it_interval = { .tv_nsec = 0, .tv_sec = 0 }
}; };
if (!client->serve->use_killswitch) { if ( !client->serve->use_killswitch ) {
return; return;
} }
debug("Arming killswitch"); debug( "Arming killswitch" );
FATAL_IF_NEGATIVE(timer_settime(client->killswitch, 0, &its, NULL), FATAL_IF_NEGATIVE(
SHOW_ERRNO("Failed to arm killswitch") timer_settime( client->killswitch, 0, &its, NULL ),
SHOW_ERRNO( "Failed to arm killswitch" )
); );
return; return;
} }
void client_disarm_killswitch(struct client *client) void client_disarm_killswitch( struct client* client )
{ {
struct itimerspec its = { struct itimerspec its = {
.it_value = {.tv_nsec = 0,.tv_sec = 0}, .it_value = { .tv_nsec = 0, .tv_sec = 0 },
.it_interval = {.tv_nsec = 0,.tv_sec = 0} .it_interval = { .tv_nsec = 0, .tv_sec = 0 }
}; };
if (!client->serve->use_killswitch) { if ( !client->serve->use_killswitch ) {
return; return;
} }
debug("Disarming killswitch"); debug( "Disarming killswitch" );
FATAL_IF_NEGATIVE(timer_settime(client->killswitch, 0, &its, NULL), FATAL_IF_NEGATIVE(
SHOW_ERRNO("Failed to disarm killswitch") timer_settime( client->killswitch, 0, &its, NULL ),
SHOW_ERRNO( "Failed to disarm killswitch" )
); );
return; return;
} }
/* Returns 0 if we should continue trying to serve requests */ /* Returns 0 if we should continue trying to serve requests */
int client_serve_request(struct client *client) int client_serve_request(struct client* client)
{ {
struct nbd_request request = { 0 }; struct nbd_request request = {0};
int stop = 1; int stop = 1;
int disconnected = 0; int disconnected = 0;
fd_set rfds, efds; fd_set rfds, efds;
@@ -579,29 +560,28 @@ int client_serve_request(struct client *client)
* non-blocking. * non-blocking.
*/ */
FD_ZERO(&rfds); FD_ZERO( &rfds );
FD_SET(client->socket, &rfds); FD_SET( client->socket, &rfds );
self_pipe_fd_set(client->stop_signal, &rfds); self_pipe_fd_set( client->stop_signal, &rfds );
FD_ZERO(&efds); FD_ZERO( &efds );
FD_SET(client->socket, &efds); FD_SET( client->socket, &efds );
fd_count = sock_try_select(FD_SETSIZE, &rfds, NULL, &efds, NULL); fd_count = sock_try_select( FD_SETSIZE, &rfds, NULL, &efds, NULL );
if (fd_count == 0) { if ( fd_count == 0 ) {
/* This "can't ever happen" */ /* This "can't ever happen" */
fatal("No FDs selected, and no timeout!"); fatal( "No FDs selected, and no timeout!" );
} else if (fd_count < 0) {
fatal("Select failed");
} }
else if ( fd_count < 0 ) { fatal( "Select failed" ); }
if (self_pipe_fd_isset(client->stop_signal, &rfds)) { if ( self_pipe_fd_isset( client->stop_signal, &rfds ) ){
debug("Client received stop signal."); debug("Client received stop signal.");
return 1; // Don't try to serve more requests return 1; // Don't try to serve more requests
} }
if (FD_ISSET(client->socket, &efds)) { if ( FD_ISSET( client->socket, &efds ) ) {
debug("Client connection closed"); debug( "Client connection closed" );
return 1; return 1;
} }
@@ -618,51 +598,51 @@ int client_serve_request(struct client *client)
* a single byte, and then wait. * a single byte, and then wait.
* *
*/ */
client_arm_killswitch(client); client_arm_killswitch( client );
if (!client_read_request(client, &request, &disconnected)) { if ( !client_read_request( client, &request, &disconnected ) ) {
client_disarm_killswitch(client); client_disarm_killswitch( client );
return stop; return stop;
} }
if (disconnected) { if ( disconnected ) {
client_disarm_killswitch(client); client_disarm_killswitch( client );
return stop; return stop;
} }
if (!client_request_needs_reply(client, request)) { if ( !client_request_needs_reply( client, request ) ) {
client_disarm_killswitch(client); client_disarm_killswitch( client );
return client->disconnect; return client->disconnect;
} }
{ {
if (!server_is_closed(client->serve)) { if ( !server_is_closed( client->serve ) ) {
client_reply(client, request); client_reply( client, request );
stop = 0; stop = 0;
} }
} }
client_disarm_killswitch(client); client_disarm_killswitch( client );
return stop; return stop;
} }
void client_send_hello(struct client *client) void client_send_hello(struct client* client)
{ {
client_write_init(client, client->serve->size); client_write_init( client, client->serve->size );
} }
void client_cleanup(struct client *client, void client_cleanup(struct client* client,
int fatal __attribute__ ((unused))) int fatal __attribute__ ((unused)) )
{ {
info("client cleanup for client %p", client); info("client cleanup for client %p", client);
/* If the thread hits an error, we need to ensure this is off */ /* If the thread hits an error, we need to ensure this is off */
client_disarm_killswitch(client); client_disarm_killswitch( client );
if (client->socket) { if (client->socket) {
FATAL_IF_NEGATIVE(close(client->socket), FATAL_IF_NEGATIVE( close(client->socket),
"Error closing client socket %d", "Error closing client socket %d",
client->socket); client->socket );
debug("Closed client socket fd %d", client->socket); debug("Closed client socket fd %d", client->socket);
client->socket = -1; client->socket = -1;
} }
@@ -670,57 +650,58 @@ void client_cleanup(struct client *client,
munmap(client->mapped, client->serve->size); munmap(client->mapped, client->serve->size);
} }
if (client->fileno) { if (client->fileno) {
FATAL_IF_NEGATIVE(close(client->fileno), FATAL_IF_NEGATIVE( close(client->fileno),
"Error closing file %d", client->fileno); "Error closing file %d",
debug("Closed client file fd %d", client->fileno); client->fileno );
debug("Closed client file fd %d", client->fileno );
client->fileno = -1; client->fileno = -1;
} }
if (server_acl_locked(client->serve)) { if ( server_acl_locked( client->serve ) ) { server_unlock_acl( client->serve ); }
server_unlock_acl(client->serve);
}
} }
void *client_serve(void *client_uncast) void* client_serve(void* client_uncast)
{ {
struct client *client = (struct client *) client_uncast; struct client* client = (struct client*) client_uncast;
error_set_handler((cleanup_handler *) client_cleanup, client); error_set_handler((cleanup_handler*) client_cleanup, client);
info("client: mmaping file"); info("client: mmaping file");
FATAL_IF_NEGATIVE(open_and_mmap(client->serve->filename, FATAL_IF_NEGATIVE(
open_and_mmap(
client->serve->filename,
&client->fileno, &client->fileno,
&client->mapped_size, NULL,
(void **) &client->mapped), (void**) &client->mapped
SHOW_ERRNO("Couldn't open/mmap file %s", ),
client->serve->filename) "Couldn't open/mmap file %s: %s", client->serve->filename, strerror( errno )
); );
FATAL_IF_NEGATIVE(madvise FATAL_IF_NEGATIVE(
(client->mapped, client->serve->size, MADV_RANDOM), madvise( client->mapped, client->serve->size, MADV_RANDOM ),
SHOW_ERRNO("Failed to madvise() %s", SHOW_ERRNO( "Failed to madvise() %s", client->serve->filename )
client->serve->filename)
); );
debug("Opened client file fd %d", client->fileno); debug( "Opened client file fd %d", client->fileno);
debug("client: sending hello"); debug("client: sending hello");
client_send_hello(client); client_send_hello(client);
debug("client: serving requests"); debug("client: serving requests");
while (client_serve_request(client) == 0); while (client_serve_request(client) == 0)
;
debug("client: stopped serving requests"); debug("client: stopped serving requests");
client->stopped = 1; client->stopped = 1;
if (client->disconnect) { if ( client->disconnect ){
debug("client: control arrived"); debug("client: control arrived" );
server_control_arrived(client->serve); server_control_arrived( client->serve );
} }
debug("Cleaning client %p up normally in thread %p", client, debug("Cleaning client %p up normally in thread %p", client, pthread_self());
pthread_self());
client_cleanup(client, 0); client_cleanup(client, 0);
debug("Client thread done"); debug("Client thread done" );
return NULL; return NULL;
} }

View File

@@ -3,7 +3,6 @@
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
#include <inttypes.h>
/** CLIENT_HANDLER_TIMEOUT /** CLIENT_HANDLER_TIMEOUT
* This is the length of time (in seconds) any request can be outstanding for. * This is the length of time (in seconds) any request can be outstanding for.
@@ -30,13 +29,11 @@ struct client {
int socket; int socket;
int fileno; int fileno;
char *mapped; char* mapped;
uint64_t mapped_size; struct self_pipe * stop_signal;
struct self_pipe *stop_signal; struct server* serve; /* FIXME: remove above duplication */
struct server *serve; /* FIXME: remove above duplication */
/* Have we seen a REQUEST_DISCONNECT message? */ /* Have we seen a REQUEST_DISCONNECT message? */
int disconnect; int disconnect;
@@ -48,11 +45,12 @@ struct client {
}; };
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,11 +44,13 @@
#include <unistd.h> #include <unistd.h>
struct control *control_create(struct flexnbd *flexnbd, const char *csn) struct control * control_create(
struct flexnbd * flexnbd,
const char * csn)
{ {
struct control *control = xmalloc(sizeof(struct control)); struct control * control = xmalloc( sizeof( struct control ) );
NULLCHECK(csn); NULLCHECK( csn );
control->flexnbd = flexnbd; control->flexnbd = flexnbd;
control->socket_name = csn; control->socket_name = csn;
@@ -60,32 +62,33 @@ struct control *control_create(struct flexnbd *flexnbd, const char *csn)
} }
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 flexnbd *flexnbd, struct control_client * control_client_create(
int client_fd, struct flexnbd * flexnbd,
struct mbox *state_mbox) int client_fd ,
struct mbox_t * 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;
@@ -95,224 +98,220 @@ struct control_client *control_client_create(struct flexnbd *flexnbd,
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->flexnbd, control_client_create(
client_fd, control->flexnbd,
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 = client_fd = accept( control->control_fd, &client_address.generic, &addrlen );
accept(control->control_fd, &client_address.generic, &addrlen); FATAL_IF( -1 == client_fd, "control accept failed" );
FATAL_IF(-1 == client_fd, "control accept failed");
control_handle_client(control, client_fd); control_handle_client( control, client_fd );
} }
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, "Couldn't create control socket"); FATAL_IF_NEGATIVE(control_fd ,
"Couldn't create control socket");
memset(&bind_address, 0, sizeof(struct sockaddr_un)); memset(&bind_address, 0, sizeof(struct sockaddr_un));
bind_address.sun_family = AF_UNIX; bind_address.sun_family = AF_UNIX;
strncpy(bind_address.sun_path, socket_name, strncpy(bind_address.sun_path, socket_name, sizeof(bind_address.sun_path)-1);
sizeof(bind_address.sun_path) - 1);
//unlink(socket_name); /* ignore failure */ //unlink(socket_name); /* ignore failure */
FATAL_IF_NEGATIVE(bind FATAL_IF_NEGATIVE(
(control_fd, &bind_address, sizeof(bind_address)), bind(control_fd , &bind_address, sizeof(bind_address)),
"Couldn't bind control socket to %s: %s", "Couldn't bind control socket to %s: %s",
socket_name, strerror(errno) socket_name, strerror( errno )
); );
FATAL_IF_NEGATIVE(listen(control_fd, 5), FATAL_IF_NEGATIVE(
"Couldn't listen on control socket"); listen(control_fd , 5),
"Couldn't listen on control socket"
);
return control_fd; return control_fd;
} }
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(struct control *control, void control_cleanup(
int fatal __attribute__ ((unused))) struct control * control,
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, void control_write_mirror_response( mirror_state_t mirror_state, int client_fd )
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(struct control_client *client) mirror_state_t control_client_mirror_wait(
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_t * mbox = client->mirror_state_mbox;
enum mirror_state mirror_state; mirror_state_t mirror_state;
enum mirror_state *contents;
contents = (enum mirror_state *) mbox_receive(mbox); mirror_state = mbox_receive( mbox ).i;
NULLCHECK(contents);
mirror_state = *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;
@@ -340,18 +339,21 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
if (linesc > 2) { if (linesc > 2) {
if (strcmp("exit", lines[2]) == 0) { if (strcmp("exit", lines[2]) == 0) {
action_at_finish = ACTION_EXIT; action_at_finish = ACTION_EXIT;
} else if (strcmp("unlink", lines[2]) == 0) { }
else if (strcmp( "unlink", lines[2]) == 0 ) {
action_at_finish = ACTION_UNLINK; action_at_finish = ACTION_UNLINK;
} else if (strcmp("nothing", lines[2]) == 0) { }
else if (strcmp("nothing", lines[2]) == 0) {
action_at_finish = ACTION_NOTHING; action_at_finish = ACTION_NOTHING;
} else { }
else {
write_socket("1: action must be 'exit' or 'nothing'"); write_socket("1: action must be 'exit' or 'nothing'");
return -1; return -1;
} }
} }
if (linesc > 3) { if (linesc > 3) {
connect_from = xmalloc(sizeof(union mysockaddr)); connect_from = xmalloc( sizeof( union mysockaddr ) );
if (parse_ip_to_sockaddr(&connect_from->generic, lines[3]) == 0) { if (parse_ip_to_sockaddr(&connect_from->generic, lines[3]) == 0) {
write_socket("1: bad bind address"); write_socket("1: bad bind address");
return -1; return -1;
@@ -360,12 +362,12 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
if (linesc > 4) { if (linesc > 4) {
errno = 0; errno = 0;
max_Bps = strtoull(lines[4], NULL, 10); max_Bps = strtoull( lines[4], 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;
} }
} }
@@ -376,85 +378,88 @@ int control_mirror(struct control_client *client, int linesc, char **lines)
return -1; return -1;
} }
struct server *serve = flexnbd_server(flexnbd); struct server * serve = flexnbd_server(flexnbd);
server_lock_start_mirror(serve); server_lock_start_mirror( serve );
{ {
if (server_mirror_can_start(serve)) { if ( server_mirror_can_start( serve ) ) {
serve->mirror_super = mirror_super_create(serve->filename, serve->mirror_super = mirror_super_create(
serve->filename,
connect_to, connect_to,
connect_from, connect_from,
max_Bps, max_Bps ,
action_at_finish, action_at_finish,
client-> client->mirror_state_mbox );
mirror_state_mbox);
serve->mirror = serve->mirror_super->mirror; serve->mirror = serve->mirror_super->mirror;
server_prevent_mirror_start(serve); server_prevent_mirror_start( serve );
} else { } else {
if (serve->mirror_super) { if ( serve->mirror_super ) {
warn("Tried to start a second mirror run"); warn( "Tried to start a second mirror run" );
write_socket("1: mirror already running"); write_socket( "1: mirror already running" );
} else { } else {
warn("Cannot start mirroring, shutting down"); warn( "Cannot start mirroring, shutting down" );
write_socket("1: shutting down"); write_socket( "1: shutting down" );
} }
} }
} }
server_unlock_start_mirror(serve); server_unlock_start_mirror( serve );
/* Do this outside the lock to minimise the length of time the /* Do this outside the lock to minimise the length of time the
* sighandler can block the serve thread * sighandler can block the serve thread
*/ */
if (serve->mirror_super) { if ( serve->mirror_super ) {
FATAL_IF(0 != pthread_create(&serve->mirror_super->thread, FATAL_IF( 0 != pthread_create(
&serve->mirror_super->thread,
NULL, NULL,
mirror_super_runner, mirror_super_runner,
serve), serve
"Failed to create mirror thread"); ),
"Failed to create mirror thread"
);
debug("Control thread mirror super waiting"); debug("Control thread mirror super waiting");
enum mirror_state state = control_client_mirror_wait(client); mirror_state_t state =
control_client_mirror_wait( client );
debug("Control thread writing response"); debug("Control thread writing response");
control_write_mirror_response(state, client->socket); control_write_mirror_response( state, client->socket );
} }
debug("Control thread going away."); debug( "Control thread going away." );
return 0; return 0;
} }
int control_mirror_max_bps(struct control_client *client, int linesc, int control_mirror_max_bps( struct control_client* client, int linesc, char** lines )
char **lines)
{ {
NULLCHECK(client); NULLCHECK( client );
NULLCHECK(client->flexnbd); NULLCHECK( client->flexnbd );
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;
} }
@@ -462,152 +467,161 @@ int control_mirror_max_bps(struct control_client *client, int linesc,
#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 { }
flexnbd_replace_acl(flexnbd, new_acl); else {
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(struct control_client *client, int control_break(
struct control_client* client,
int linesc __attribute__ ((unused)), int linesc __attribute__ ((unused)),
char **lines __attribute__ ((unused)) char** lines __attribute__((unused))
) )
{ {
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, "1: mirror completed\n", 20); write( client->socket,
} else { "1: mirror completed\n", 20 );
info("Mirror successfully stopped."); }
write(client->socket, "0: mirror stopped\n", 18); else {
info( "Mirror successfully stopped." );
write( client->socket,
"0: mirror stopped\n", 18 );
result = 1; 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(struct control_client *client, int control_status(
struct control_client* client,
int linesc __attribute__ ((unused)), int linesc __attribute__ ((unused)),
char **lines __attribute__ ((unused)) char** lines __attribute__((unused))
) )
{ {
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) { if (client->socket) { close(client->socket); }
close(client->socket);
}
/* This is wrongness */ /* This is wrongness */
if (server_acl_locked(client->flexnbd->serve)) { if ( server_acl_locked( client->flexnbd->serve ) ) { server_unlock_acl( client->flexnbd->serve ); }
server_unlock_acl(client->flexnbd->serve);
}
control_client_destroy(client); control_client_destroy( client );
} }
/** 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"); else if (strcmp(lines[0], "acl") == 0) {
if (control_acl(client, linesc - 1, lines + 1) < 0) { info("acl command received" );
if (control_acl(client, linesc-1, lines+1) < 0) {
debug("acl command failed"); debug("acl command failed");
} }
} else if (strcmp(lines[0], "mirror") == 0) { }
info("mirror command received"); else if (strcmp(lines[0], "mirror") == 0) {
if (control_mirror(client, linesc - 1, lines + 1) < 0) { info("mirror command received" );
if (control_mirror(client, linesc-1, lines+1) < 0) {
debug("mirror command failed"); debug("mirror command failed");
} }
} else if (strcmp(lines[0], "break") == 0) {
info("break command received");
if (control_break(client, linesc - 1, lines + 1) < 0) {
debug("break command failed");
} }
} else if (strcmp(lines[0], "status") == 0) { else if (strcmp(lines[0], "break") == 0) {
info("status command received"); info( "break command received" );
if (control_status(client, linesc - 1, lines + 1) < 0) { if ( control_break( client, linesc-1, lines+1) < 0) {
debug( "break command failed" );
}
}
else if (strcmp(lines[0], "status") == 0) {
info("status command received" );
if (control_status(client, linesc-1, lines+1) < 0) {
debug("status command failed"); debug("status command failed");
} }
} else if (strcmp(lines[0], "mirror_max_bps") == 0) { } else if ( strcmp( lines[0], "mirror_max_bps" ) == 0 ) {
info("mirror_max_bps command received"); info( "mirror_max_bps command received" );
if (control_mirror_max_bps(client, linesc - 1, lines + 1) < 0) { if( control_mirror_max_bps( client, linesc-1, lines+1 ) < 0 ) {
debug("mirror_max_bps command failed"); debug( "mirror_max_bps command failed" );
} }
} else { }
else {
write(client->socket, "10: unknown command\n", 23); write(client->socket, "10: unknown command\n", 23);
} }
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,14 +13,14 @@ 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
@@ -31,28 +31,29 @@ struct control {
* 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_t * 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_t * mirror_state_mbox;
}; };
struct control *control_create(struct flexnbd *, struct control * control_create(
const char *control_socket_name); struct flexnbd *,
void control_signal_close(struct control *); const char * control_socket_name );
void control_destroy(struct control *); void control_signal_close( struct control * );
void control_destroy( struct control * );
void *control_runner(void *); void * control_runner( void * );
void accept_control_connection(struct server *params, int client_fd, void accept_control_connection(struct server* params, int client_fd, union mysockaddr* client_address);
union mysockaddr *client_address); void serve_open_control_socket(struct server* params);
void serve_open_control_socket(struct server *params);
#endif #endif

View File

@@ -46,28 +46,31 @@ 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(struct flexnbd *flexnbd, void flexnbd_create_shared(
const char *s_ctrl_sock) struct flexnbd * flexnbd,
const char * s_ctrl_sock)
{ {
NULLCHECK(flexnbd); NULLCHECK( flexnbd );
if (s_ctrl_sock) { if ( s_ctrl_sock ){
flexnbd->control = control_create(flexnbd, s_ctrl_sock); flexnbd->control =
} else { control_create( flexnbd, s_ctrl_sock );
}
else {
flexnbd->control = NULL; flexnbd->control = NULL;
} }
@@ -75,57 +78,67 @@ void flexnbd_create_shared(struct flexnbd *flexnbd,
} }
struct flexnbd *flexnbd_create_serving(char *s_ip_address, struct flexnbd * flexnbd_create_serving(
char *s_port, char* s_ip_address,
char *s_file, char* s_port,
char *s_ctrl_sock, char* s_file,
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, flexnbd->serve = server_create(
flexnbd,
s_ip_address, s_ip_address,
s_port, s_port,
s_file, s_file,
default_deny, default_deny,
acl_entries, acl_entries,
s_acl_entries, s_acl_entries,
max_nbd_clients, use_killswitch, 1); max_nbd_clients,
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(0 == sigaction(CLIENT_KILLSWITCH_SIGNAL, &act, NULL), FATAL_UNLESS(
"Installing client killswitch signal failed"); 0 == sigaction( CLIENT_KILLSWITCH_SIGNAL, &act, NULL ),
"Installing client killswitch signal failed"
);
} }
return flexnbd; return flexnbd;
} }
struct flexnbd *flexnbd_create_listening(char *s_ip_address, struct flexnbd * flexnbd_create_listening(
char *s_port, char* s_ip_address,
char *s_file, char* s_port,
char *s_ctrl_sock, char* s_file,
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, flexnbd->serve = server_create(
flexnbd,
s_ip_address, s_ip_address,
s_port, s_port,
s_file, s_file,
default_deny, default_deny,
acl_entries, s_acl_entries, 1, 0, 0); acl_entries,
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.
@@ -133,116 +146,120 @@ struct flexnbd *flexnbd_create_listening(char *s_ip_address,
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(control_thread, FATAL_UNLESS( 0 == pthread_create(
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", filename, strerror(errno)); "Couldn't chmod %s: %s",
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

@@ -16,48 +16,51 @@ 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(char *s_ip_address, struct flexnbd * flexnbd_create_serving(
char *s_port, char* s_ip_address,
char *s_file, char* s_port,
char *s_ctrl_sock, char* s_file,
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(char *s_ip_address, struct flexnbd * flexnbd_create_listening(
char *s_port, char* s_ip_address,
char *s_file, char* s_port,
char *s_ctrl_sock, char* s_file,
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,47 +4,48 @@
#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();
} }
@@ -52,22 +53,23 @@ int flexthread_mutex_lock(struct flexthread_mutex *ftm)
} }
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

@@ -19,11 +19,11 @@ struct flexthread_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

@@ -1,77 +1,73 @@
#include "mbox.h" #include "mbox.h"
#include "util.h" #include "util.h"
#include <sys/socket.h>
#include <pthread.h> #include <pthread.h>
struct mbox *mbox_create(void) DEFINE_FIFO(mbox_item_t, mbox_fifo);
#define ARRAY_SIZE(w) (sizeof(w) / sizeof((w)[0]))
mbox_p mbox_create( void )
{ {
struct mbox *mbox = xmalloc(sizeof(struct mbox)); mbox_p mbox = xmalloc( sizeof( struct mbox_t ) );
FATAL_UNLESS(0 == pthread_cond_init(&mbox->filled_cond, NULL),
"Failed to initialise a condition variable"); int sv[2];
FATAL_UNLESS(0 == pthread_cond_init(&mbox->emptied_cond, NULL), FATAL_UNLESS(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0,
"Failed to initialise a condition variable"); "Failed to socketpair");
FATAL_UNLESS(0 == pthread_mutex_init(&mbox->mutex, NULL), mbox->signalw = sv[0];
"Failed to initialise a mutex"); mbox->signalr = sv[1];
return mbox; return mbox;
} }
void mbox_post(struct mbox *mbox, void *contents) void mbox_post( mbox_p mbox, mbox_item_t item )
{ {
pthread_mutex_lock(&mbox->mutex); mbox_fifo_write(&mbox->fifo, item);
{ {
if (mbox->full) { uint8_t w;
pthread_cond_wait(&mbox->emptied_cond, &mbox->mutex); FATAL_UNLESS((write(mbox->signalw, &w, 1)) == 1,
"Write to socketpair");
} }
mbox->contents = contents; }
mbox->full = 1;
while (0 != pthread_cond_signal(&mbox->filled_cond));
mbox_item_t mbox_contents( mbox_p mbox )
{
const mbox_item_t zero = {0};
return mbox_fifo_isempty(&mbox->fifo) ?
zero :
mbox_fifo_read_at(&mbox->fifo, 0);
}
int mbox_is_full( mbox_p mbox )
{
return mbox_fifo_isfull(&mbox->fifo);
}
mbox_item_t mbox_receive( mbox_p mbox )
{
NULLCHECK( mbox );
while (mbox_fifo_isempty(&mbox->fifo)) {
uint8_t w;
FATAL_UNLESS((read(mbox->signalr, &w, 1)) == 1,
"Read from socketpair");
} }
pthread_mutex_unlock(&mbox->mutex);
return mbox_fifo_read(&mbox->fifo);
} }
void *mbox_contents(struct mbox *mbox) void mbox_destroy( mbox_p mbox )
{ {
return mbox->contents; NULLCHECK( mbox );
}
close(mbox->signalw);
close(mbox->signalr);
int mbox_is_full(struct mbox *mbox) free( mbox );
{
return mbox->full;
}
void *mbox_receive(struct mbox *mbox)
{
NULLCHECK(mbox);
void *result;
pthread_mutex_lock(&mbox->mutex);
{
if (!mbox->full) {
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);
return result;
}
void mbox_destroy(struct mbox *mbox)
{
NULLCHECK(mbox);
while (0 != pthread_cond_destroy(&mbox->emptied_cond));
while (0 != pthread_cond_destroy(&mbox->filled_cond));
while (0 != pthread_mutex_destroy(&mbox->mutex));
free(mbox);
} }

View File

@@ -11,45 +11,43 @@
#include <pthread.h> #include <pthread.h>
#include <stdint.h>
#include "fifo_declare.h"
typedef union {
uint64_t i;
void * p;
} mbox_item_t;
DECLARE_FIFO(mbox_item_t, mbox_fifo, 8);
typedef struct mbox_t {
mbox_fifo_t fifo;
// socketpair() ends
int signalw, signalr;
} mbox_t, *mbox_p;
struct mbox { /* Create an mbox_t. */
void *contents; mbox_p mbox_create(void);
/** Marker to tell us if there's content in the box.
* Keeping this separate allows us to use NULL for the contents.
*/
int full;
/** This gets signaled by mbox_post, and waited on by
* mbox_receive */
pthread_cond_t filled_cond;
/** This is signaled by mbox_receive, and waited on by mbox_post */
pthread_cond_t emptied_cond;
pthread_mutex_t mutex;
};
/* Create an mbox. */
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( mbox_p , mbox_item_t item);
/* 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 *); mbox_item_t mbox_contents( mbox_p );
/* 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( mbox_p );
/* 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 *); mbox_item_t mbox_receive( mbox_p );
/* 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( mbox_p );
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
#include "bitset.h" #include "bitset.h"
#include "self_pipe.h" #include "self_pipe.h"
enum mirror_state;
#include "serve.h" #include "serve.h"
#include "mbox.h" #include "mbox.h"
@@ -57,14 +57,14 @@ enum mirror_state;
#define MS_REQUEST_LIMIT_SECS 60 #define MS_REQUEST_LIMIT_SECS 60
#define MS_REQUEST_LIMIT_SECS_F 60.0 #define MS_REQUEST_LIMIT_SECS_F 60.0
enum mirror_finish_action { typedef enum {
ACTION_EXIT, ACTION_EXIT = 0,
ACTION_UNLINK, ACTION_UNLINK,
ACTION_NOTHING ACTION_NOTHING
}; } mirror_finish_action_t;
enum mirror_state { typedef enum {
MS_UNKNOWN, MS_UNKNOWN = 0,
MS_INIT, MS_INIT,
MS_GO, MS_GO,
MS_ABANDONED, MS_ABANDONED,
@@ -73,36 +73,36 @@ enum mirror_state {
MS_FAIL_REJECTED, MS_FAIL_REJECTED,
MS_FAIL_NO_HELLO, MS_FAIL_NO_HELLO,
MS_FAIL_SIZE_MISMATCH MS_FAIL_SIZE_MISMATCH
}; } mirror_state_t;
struct mirror { typedef struct mirror_t {
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; mirror_finish_action_t 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; mirror_state_t 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_t * 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. */
@@ -110,14 +110,14 @@ struct mirror {
/* Running count of all bytes we've transferred */ /* Running count of all bytes we've transferred */
uint64_t all_dirty; uint64_t all_dirty;
}; } mirror_t, *mirror_p;
struct mirror_super { typedef struct mirror_super_t {
struct mirror *mirror; mirror_p mirror;
pthread_t thread; pthread_t thread;
struct mbox *state_mbox; struct mbox_t * state_mbox;
}; } mirror_super_t, *mirror_super_p;
@@ -127,13 +127,15 @@ struct mirror_super {
struct server; struct server;
struct flexnbd; struct flexnbd;
struct mirror_super *mirror_super_create(const char *filename, mirror_super_p mirror_super_create(
union mysockaddr *connect_to, const char * filename,
union mysockaddr *connect_from, union mysockaddr * connect_to,
union mysockaddr * connect_from,
uint64_t max_Bps, uint64_t max_Bps,
enum mirror_finish_action mirror_finish_action_t action_at_finish,
action_at_finish, struct mbox_t * state_mbox
struct mbox *state_mbox); );
void *mirror_super_runner(void *serve_uncast); void * mirror_super_runner( void * serve_uncast );
#endif #endif

View File

@@ -19,7 +19,6 @@ static struct option serve_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char serve_short_options[] = "hl:p:f:s:dk" SOPT_QUIET SOPT_VERBOSE; static char serve_short_options[] = "hl:p:f:s:dk" SOPT_QUIET SOPT_VERBOSE;
static char serve_help_text[] = static char serve_help_text[] =
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n" "Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
@@ -29,9 +28,10 @@ static char serve_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to serve on.\n"
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n" "\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n" "\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
"\t--" OPT_KILLSWITCH "\t--" OPT_KILLSWITCH",-k \tKill the server if a request takes 120 seconds.\n"
",-k \tKill the server if a request takes 120 seconds.\n" SOCK_LINE SOCK_LINE
VERBOSE_LINE QUIET_LINE; VERBOSE_LINE
QUIET_LINE;
static struct option listen_options[] = { static struct option listen_options[] = {
@@ -45,7 +45,6 @@ static struct option listen_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char listen_short_options[] = "hl:p:f:s:d" SOPT_QUIET SOPT_VERBOSE; static char listen_short_options[] = "hl:p:f:s:d" SOPT_QUIET SOPT_VERBOSE;
static char listen_help_text[] = static char listen_help_text[] =
"Usage: flexnbd " CMD_LISTEN " <options> [<acl_address>*]\n\n" "Usage: flexnbd " CMD_LISTEN " <options> [<acl_address>*]\n\n"
@@ -55,7 +54,9 @@ static char listen_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to listen on.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to listen on.\n"
"\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n" "\t--" OPT_FILE ",-f <FILE>\tThe file to serve.\n"
"\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n" "\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n"
SOCK_LINE VERBOSE_LINE QUIET_LINE; SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option read_options[] = { static struct option read_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -68,7 +69,6 @@ static struct option read_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char read_short_options[] = "hl:p:F:S:b:" SOPT_QUIET SOPT_VERBOSE; static char read_short_options[] = "hl:p:F:S:b:" SOPT_QUIET SOPT_VERBOSE;
static char read_help_text[] = static char read_help_text[] =
"Usage: flexnbd " CMD_READ " <options>\n\n" "Usage: flexnbd " CMD_READ " <options>\n\n"
@@ -78,20 +78,24 @@ static char read_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to read from.\n"
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n" "\t--" OPT_FROM ",-F <OFFSET>\tByte offset to read from.\n"
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n" "\t--" OPT_SIZE ",-S <SIZE>\tBytes to read.\n"
BIND_LINE VERBOSE_LINE QUIET_LINE; BIND_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option *write_options = read_options; static struct option *write_options = read_options;
static char *write_short_options = read_short_options; static char *write_short_options = read_short_options;
static char write_help_text[] = static char write_help_text[] =
"Usage: flexnbd " CMD_WRITE " <options>\n\n" "Usage: flexnbd " CMD_WRITE" <options>\n\n"
"Write SIZE bytes from stdin to a server at ADDR:PORT, starting at OFFSET.\n\n" "Write SIZE bytes from stdin to a server at ADDR:PORT, starting at OFFSET.\n\n"
HELP_LINE HELP_LINE
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to write to.\n" "\t--" OPT_ADDR ",-l <ADDR>\tThe address to write to.\n"
"\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to write to.\n"
"\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n" "\t--" OPT_FROM ",-F <OFFSET>\tByte offset to write from.\n"
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n" "\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n"
BIND_LINE VERBOSE_LINE QUIET_LINE; BIND_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option acl_options[] = { static struct option acl_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -100,12 +104,14 @@ static struct option acl_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char acl_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE; static char acl_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
static char acl_help_text[] = static char acl_help_text[] =
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n" "Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
"Set the access control list for a server with control socket SOCK.\n\n" "Set the access control list for a server with control socket SOCK.\n\n"
HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE; HELP_LINE
SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option mirror_speed_options[] = { static struct option mirror_speed_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -115,12 +121,15 @@ static struct option mirror_speed_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char mirror_speed_short_options[] = "hs:m:" SOPT_QUIET SOPT_VERBOSE; static char mirror_speed_short_options[] = "hs:m:" SOPT_QUIET SOPT_VERBOSE;
static char mirror_speed_help_text[] = static char mirror_speed_help_text[] =
"Usage: flexnbd " CMD_MIRROR_SPEED " <options>\n\n" "Usage: flexnbd " CMD_MIRROR_SPEED " <options>\n\n"
"Set the maximum speed of a migration from a mirring server listening on SOCK.\n\n" "Set the maximum speed of a migration from a mirring server listening on SOCK.\n\n"
HELP_LINE SOCK_LINE MAX_SPEED_LINE VERBOSE_LINE QUIET_LINE; HELP_LINE
SOCK_LINE
MAX_SPEED_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option mirror_options[] = { static struct option mirror_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -133,7 +142,6 @@ static struct option mirror_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char mirror_short_options[] = "hs:l:p:ub:" SOPT_QUIET SOPT_VERBOSE; static char mirror_short_options[] = "hs:l:p:ub:" SOPT_QUIET SOPT_VERBOSE;
static char mirror_help_text[] = static char mirror_help_text[] =
"Usage: flexnbd " CMD_MIRROR " <options>\n\n" "Usage: flexnbd " CMD_MIRROR " <options>\n\n"
@@ -143,7 +151,9 @@ static char mirror_help_text[] =
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n" "\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n"
SOCK_LINE SOCK_LINE
"\t--" OPT_UNLINK ",-u\tUnlink the local file when done.\n" "\t--" OPT_UNLINK ",-u\tUnlink the local file when done.\n"
BIND_LINE VERBOSE_LINE QUIET_LINE; BIND_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option break_options[] = { static struct option break_options[] = {
GETOPT_HELP, GETOPT_HELP,
@@ -152,12 +162,14 @@ static struct option break_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char break_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE; static char break_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
static char break_help_text[] = static char break_help_text[] =
"Usage: flexnbd " CMD_BREAK " <options>\n\n" "Usage: flexnbd " CMD_BREAK " <options>\n\n"
"Stop mirroring from the server with control socket SOCK.\n\n" "Stop mirroring from the server with control socket SOCK.\n\n"
HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE; HELP_LINE
SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
static struct option status_options[] = { static struct option status_options[] = {
@@ -167,12 +179,14 @@ static struct option status_options[] = {
GETOPT_VERBOSE, GETOPT_VERBOSE,
{0} {0}
}; };
static char status_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE; static char status_short_options[] = "hs:" SOPT_QUIET SOPT_VERBOSE;
static char status_help_text[] = static char status_help_text[] =
"Usage: flexnbd " CMD_STATUS " <options>\n\n" "Usage: flexnbd " CMD_STATUS " <options>\n\n"
"Get the status for a server with control socket SOCK.\n\n" "Get the status for a server with control socket SOCK.\n\n"
HELP_LINE SOCK_LINE VERBOSE_LINE QUIET_LINE; HELP_LINE
SOCK_LINE
VERBOSE_LINE
QUIET_LINE;
char help_help_text_arr[] = char help_help_text_arr[] =
"Usage: flexnbd <cmd> [cmd options]\n\n" "Usage: flexnbd <cmd> [cmd options]\n\n"
@@ -186,26 +200,26 @@ char help_help_text_arr[] =
"\tflexnbd mirror-speed\n" "\tflexnbd mirror-speed\n"
"\tflexnbd break\n" "\tflexnbd break\n"
"\tflexnbd status\n" "\tflexnbd status\n"
"\tflexnbd help\n\n" "See flexnbd help <cmd> for further info\n"; "\tflexnbd help\n\n"
"See flexnbd help <cmd> for further info\n";
/* Slightly odd array/pointer pair to stop the compiler from complaining /* Slightly odd array/pointer pair to stop the compiler from complaining
* about symbol sizes * about symbol sizes
*/ */
char *help_help_text = help_help_text_arr; char * help_help_text = help_help_text_arr;
void do_read(struct mode_readwrite_params *params); void do_read(struct mode_readwrite_params* params);
void do_write(struct mode_readwrite_params *params); void do_write(struct mode_readwrite_params* params);
void do_remote_command(char *command, char *mode, int argc, char **argv); void do_remote_command(char* command, char* mode, int argc, char** argv);
void read_serve_param(int c, char **ip_addr, char **ip_port, char **file, void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char **sock, int *default_deny, int *use_killswitch )
char **sock, int *default_deny, int *use_killswitch)
{ {
switch (c) { switch(c){
case 'h': case 'h':
fprintf(stdout, "%s\n", serve_help_text); fprintf(stdout, "%s\n", serve_help_text );
exit(0); exit( 0 );
case 'l': case 'l':
*ip_addr = optarg; *ip_addr = optarg;
break; break;
@@ -231,20 +245,22 @@ void read_serve_param(int c, char **ip_addr, char **ip_port, char **file,
*use_killswitch = 1; *use_killswitch = 1;
break; break;
default: default:
exit_err(serve_help_text); exit_err( serve_help_text );
break; break;
} }
} }
void read_listen_param(int c, void read_listen_param( int c,
char **ip_addr, char **ip_addr,
char **ip_port, char **ip_port,
char **file, char **sock, int *default_deny) char **file,
char **sock,
int *default_deny )
{ {
switch (c) { switch(c){
case 'h': case 'h':
fprintf(stdout, "%s\n", listen_help_text); fprintf(stdout, "%s\n", listen_help_text );
exit(0); exit(0);
case 'l': case 'l':
*ip_addr = optarg; *ip_addr = optarg;
@@ -268,19 +284,17 @@ void read_listen_param(int c,
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err(listen_help_text); exit_err( listen_help_text );
break; break;
} }
} }
void read_readwrite_param(int c, char **ip_addr, char **ip_port, void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **bind_addr, char **from, char **size, char *err_text )
char **bind_addr, char **from, char **size,
char *err_text)
{ {
switch (c) { switch(c){
case 'h': case 'h':
fprintf(stdout, "%s\n", err_text); fprintf(stdout, "%s\n", err_text );
exit(0); exit( 0 );
case 'l': case 'l':
*ip_addr = optarg; *ip_addr = optarg;
break; break;
@@ -303,17 +317,17 @@ void read_readwrite_param(int c, char **ip_addr, char **ip_port,
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err(err_text); exit_err( err_text );
break; break;
} }
} }
void read_sock_param(int c, char **sock, char *help_text) void read_sock_param( int c, char **sock, char *help_text )
{ {
switch (c) { switch(c){
case 'h': case 'h':
fprintf(stdout, "%s\n", help_text); fprintf( stdout, "%s\n", help_text );
exit(0); exit( 0 );
case 's': case 's':
*sock = optarg; *sock = optarg;
break; break;
@@ -324,22 +338,26 @@ void read_sock_param(int c, char **sock, char *help_text)
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err(help_text); exit_err( help_text );
break; break;
} }
} }
void read_acl_param(int c, char **sock) void read_acl_param( int c, char **sock )
{ {
read_sock_param(c, sock, acl_help_text); read_sock_param( c, sock, acl_help_text );
} }
void read_mirror_speed_param(int c, char **sock, char **max_speed) void read_mirror_speed_param(
int c,
char **sock,
char **max_speed
)
{ {
switch (c) { switch( c ) {
case 'h': case 'h':
fprintf(stdout, "%s\n", mirror_speed_help_text); fprintf( stdout, "%s\n", mirror_speed_help_text );
exit(0); exit( 0 );
case 's': case 's':
*sock = optarg; *sock = optarg;
break; break;
@@ -353,21 +371,24 @@ void read_mirror_speed_param(int c, char **sock, char **max_speed)
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err(mirror_speed_help_text); exit_err( mirror_speed_help_text );
break; break;
} }
} }
void read_mirror_param(int c, void read_mirror_param(
int c,
char **sock, char **sock,
char **ip_addr, char **ip_addr,
char **ip_port, int *unlink, char **bind_addr) char **ip_port,
int *unlink,
char **bind_addr )
{ {
switch (c) { switch( c ){
case 'h': case 'h':
fprintf(stdout, "%s\n", mirror_help_text); fprintf( stdout, "%s\n", mirror_help_text );
exit(0); exit( 0 );
case 's': case 's':
*sock = optarg; *sock = optarg;
break; break;
@@ -390,17 +411,17 @@ void read_mirror_param(int c,
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err(mirror_help_text); exit_err( mirror_help_text );
break; break;
} }
} }
void read_break_param(int c, char **sock) void read_break_param( int c, char **sock )
{ {
switch (c) { switch( c ) {
case 'h': case 'h':
fprintf(stdout, "%s\n", break_help_text); fprintf( stdout, "%s\n", break_help_text );
exit(0); exit( 0 );
case 's': case 's':
*sock = optarg; *sock = optarg;
break; break;
@@ -411,18 +432,18 @@ void read_break_param(int c, char **sock)
log_level = VERBOSE_LOG_LEVEL; log_level = VERBOSE_LOG_LEVEL;
break; break;
default: default:
exit_err(break_help_text); exit_err( break_help_text );
break; break;
} }
} }
void read_status_param(int c, char **sock) void read_status_param( int c, char **sock )
{ {
read_sock_param(c, sock, status_help_text); read_sock_param( c, sock, status_help_text );
} }
int mode_serve(int argc, char *argv[]) int mode_serve( int argc, char *argv[] )
{ {
int c; int c;
char *ip_addr = NULL; char *ip_addr = NULL;
@@ -435,44 +456,35 @@ int mode_serve(int argc, char *argv[])
int success; int success;
struct flexnbd *flexnbd; struct flexnbd * flexnbd;
while (1) { while (1) {
c = getopt_long(argc, argv, serve_short_options, serve_options, c = getopt_long(argc, argv, serve_short_options, serve_options, NULL);
NULL); if ( c == -1 ) { break; }
if (c == -1) {
break; read_serve_param( c, &ip_addr, &ip_port, &file, &sock, &default_deny, &use_killswitch );
} }
read_serve_param(c, &ip_addr, &ip_port, &file, &sock, if ( NULL == ip_addr || NULL == ip_port ) {
&default_deny, &use_killswitch);
}
if (NULL == ip_addr || NULL == ip_port) {
err = 1; err = 1;
fprintf(stderr, "both --addr and --port are required.\n"); fprintf( stderr, "both --addr and --port are required.\n" );
} }
if (NULL == file) { if ( NULL == file ) {
err = 1; err = 1;
fprintf(stderr, "--file is required\n"); fprintf( stderr, "--file is required\n" );
}
if (err) {
exit_err(serve_help_text);
} }
if ( err ) { exit_err( serve_help_text ); }
flexnbd = flexnbd = flexnbd_create_serving( ip_addr, ip_port, file, sock, default_deny, argc - optind, argv + optind, MAX_NBD_CLIENTS, use_killswitch );
flexnbd_create_serving(ip_addr, ip_port, file, sock, default_deny, info( "Serving file %s", file );
argc - optind, argv + optind, success = flexnbd_serve( flexnbd );
MAX_NBD_CLIENTS, use_killswitch); flexnbd_destroy( flexnbd );
info("Serving file %s", file);
success = flexnbd_serve(flexnbd);
flexnbd_destroy(flexnbd);
return success ? 0 : 1; return success ? 0 : 1;
} }
int mode_listen(int argc, char *argv[]) int mode_listen( int argc, char *argv[] )
{ {
int c; int c;
char *ip_addr = NULL; char *ip_addr = NULL;
@@ -484,39 +496,36 @@ int mode_listen(int argc, char *argv[])
int success; int success;
struct flexnbd *flexnbd; struct flexnbd * flexnbd;
while (1) { while (1) {
c = getopt_long(argc, argv, listen_short_options, listen_options, c = getopt_long(argc, argv, listen_short_options, listen_options, NULL);
NULL); if ( c == -1 ) { break; }
if (c == -1) {
break; read_listen_param( c, &ip_addr, &ip_port,
&file, &sock, &default_deny );
} }
read_listen_param(c, &ip_addr, &ip_port, if ( NULL == ip_addr || NULL == ip_port ) {
&file, &sock, &default_deny);
}
if (NULL == ip_addr || NULL == ip_port) {
err = 1; err = 1;
fprintf(stderr, "both --addr and --port are required.\n"); fprintf( stderr, "both --addr and --port are required.\n" );
} }
if (NULL == file) { if ( NULL == file ) {
err = 1; err = 1;
fprintf(stderr, "--file is required\n"); fprintf( stderr, "--file is required\n" );
}
if (err) {
exit_err(listen_help_text);
} }
if ( err ) { exit_err( listen_help_text ); }
flexnbd = flexnbd_create_listening(ip_addr, flexnbd = flexnbd_create_listening(
ip_addr,
ip_port, ip_port,
file, file,
sock, sock,
default_deny, default_deny,
argc - optind, argv + optind); argc - optind,
success = flexnbd_serve(flexnbd); argv + optind);
flexnbd_destroy(flexnbd); success = flexnbd_serve( flexnbd );
flexnbd_destroy( flexnbd );
return success ? 0 : 1; return success ? 0 : 1;
} }
@@ -536,60 +545,67 @@ int mode_listen(int argc, char *argv[])
* char *s_length, * char *s_length,
* char *s_filename ) * char *s_filename )
*/ */
void params_readwrite(int write_not_read, void params_readwrite(
struct mode_readwrite_params *out, int write_not_read,
char *s_ip_address, struct mode_readwrite_params* out,
char *s_port, char* s_ip_address,
char *s_bind_address, char* s_port,
char *s_from, char *s_length_or_filename) char* s_bind_address,
char* s_from,
char* s_length_or_filename
)
{ {
FATAL_IF_NULL(s_ip_address, "No IP address supplied"); FATAL_IF_NULL(s_ip_address, "No IP address supplied");
FATAL_IF_NULL(s_port, "No port number supplied"); FATAL_IF_NULL(s_port, "No port number supplied");
FATAL_IF_NULL(s_from, "No from supplied"); FATAL_IF_NULL(s_from, "No from supplied");
FATAL_IF_NULL(s_length_or_filename, "No length supplied"); FATAL_IF_NULL(s_length_or_filename, "No length supplied");
FATAL_IF_ZERO(parse_ip_to_sockaddr FATAL_IF_ZERO(
(&out->connect_to.generic, s_ip_address), parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address),
"Couldn't parse connection address '%s'", s_ip_address); "Couldn't parse connection address '%s'",
s_ip_address
);
if (s_bind_address != NULL && if (s_bind_address != NULL &&
parse_ip_to_sockaddr(&out->connect_from.generic, parse_ip_to_sockaddr(&out->connect_from.generic, s_bind_address) == 0) {
s_bind_address) == 0) {
fatal("Couldn't parse bind address '%s'", s_bind_address); fatal("Couldn't parse bind address '%s'", s_bind_address);
} }
parse_port(s_port, &out->connect_to.v4); parse_port( s_port, &out->connect_to.v4 );
long signed_from = atol(s_from); long signed_from = atol(s_from);
FATAL_IF_NEGATIVE(signed_from, FATAL_IF_NEGATIVE( signed_from,
"Can't read from a negative offset %d.", "Can't read from a negative offset %d.", signed_from);
signed_from);
out->from = signed_from; out->from = signed_from;
if (write_not_read) { if (write_not_read) {
if (s_length_or_filename[0] - 48 < 10) { if (s_length_or_filename[0]-48 < 10) {
out->len = atol(s_length_or_filename); out->len = atol(s_length_or_filename);
out->data_fd = 0; out->data_fd = 0;
} else { }
out->data_fd = open(s_length_or_filename, O_RDONLY); else {
out->data_fd = open(
s_length_or_filename, O_RDONLY);
FATAL_IF_NEGATIVE(out->data_fd, FATAL_IF_NEGATIVE(out->data_fd,
"Couldn't open %s", s_length_or_filename); "Couldn't open %s", s_length_or_filename);
off64_t signed_len = lseek64(out->data_fd, 0, SEEK_END); off64_t signed_len = lseek64(out->data_fd, 0, SEEK_END);
FATAL_IF_NEGATIVE(signed_len, FATAL_IF_NEGATIVE(signed_len,
"Couldn't find length of %s", "Couldn't find length of %s", s_length_or_filename);
s_length_or_filename);
out->len = signed_len; out->len = signed_len;
FATAL_IF_NEGATIVE(lseek64(out->data_fd, 0, SEEK_SET), FATAL_IF_NEGATIVE(
"Couldn't rewind %s", s_length_or_filename); lseek64(out->data_fd, 0, SEEK_SET),
"Couldn't rewind %s", s_length_or_filename
);
} }
} else { }
else {
out->len = atol(s_length_or_filename); out->len = atol(s_length_or_filename);
out->data_fd = 1; out->data_fd = 1;
} }
} }
int mode_read(int argc, char *argv[]) int mode_read( int argc, char *argv[] )
{ {
int c; int c;
char *ip_addr = NULL; char *ip_addr = NULL;
@@ -601,38 +617,31 @@ int mode_read(int argc, char *argv[])
struct mode_readwrite_params readwrite; struct mode_readwrite_params readwrite;
while (1) { while (1){
c = getopt_long(argc, argv, read_short_options, read_options, c = getopt_long(argc, argv, read_short_options, read_options, NULL);
NULL);
if (c == -1) { if ( c == -1 ) { break; }
break;
read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size, read_help_text );
} }
read_readwrite_param(c, &ip_addr, &ip_port, &bind_addr, &from, if ( NULL == ip_addr || NULL == ip_port ) {
&size, read_help_text);
}
if (NULL == ip_addr || NULL == ip_port) {
err = 1; err = 1;
fprintf(stderr, "both --addr and --port are required.\n"); fprintf( stderr, "both --addr and --port are required.\n" );
} }
if (NULL == from || NULL == size) { if ( NULL == from || NULL == size ) {
err = 1; err = 1;
fprintf(stderr, "both --from and --size are required.\n"); fprintf( stderr, "both --from and --size are required.\n" );
}
if (err) {
exit_err(read_help_text);
} }
if ( err ) { exit_err( read_help_text ); }
memset(&readwrite, 0, sizeof(readwrite)); memset( &readwrite, 0, sizeof( readwrite ) );
params_readwrite(0, &readwrite, ip_addr, ip_port, bind_addr, from, params_readwrite( 0, &readwrite, ip_addr, ip_port, bind_addr, from, size );
size); do_read( &readwrite );
do_read(&readwrite);
return 0; return 0;
} }
int mode_write(int argc, char *argv[]) int mode_write( int argc, char *argv[] )
{ {
int c; int c;
char *ip_addr = NULL; char *ip_addr = NULL;
@@ -644,246 +653,232 @@ int mode_write(int argc, char *argv[])
struct mode_readwrite_params readwrite; struct mode_readwrite_params readwrite;
while (1) { while (1){
c = getopt_long(argc, argv, write_short_options, write_options, c = getopt_long(argc, argv, write_short_options, write_options, NULL);
NULL); if ( c == -1 ) { break; }
if (c == -1) {
break; read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size, write_help_text );
} }
read_readwrite_param(c, &ip_addr, &ip_port, &bind_addr, &from, if ( NULL == ip_addr || NULL == ip_port ) {
&size, write_help_text);
}
if (NULL == ip_addr || NULL == ip_port) {
err = 1; err = 1;
fprintf(stderr, "both --addr and --port are required.\n"); fprintf( stderr, "both --addr and --port are required.\n" );
} }
if (NULL == from || NULL == size) { if ( NULL == from || NULL == size ) {
err = 1; err = 1;
fprintf(stderr, "both --from and --size are required.\n"); fprintf( stderr, "both --from and --size are required.\n" );
}
if (err) {
exit_err(write_help_text);
} }
if ( err ) { exit_err( write_help_text ); }
memset(&readwrite, 0, sizeof(readwrite)); memset( &readwrite, 0, sizeof( readwrite ) );
params_readwrite(1, &readwrite, ip_addr, ip_port, bind_addr, from, params_readwrite( 1, &readwrite, ip_addr, ip_port, bind_addr, from, size );
size); do_write( &readwrite );
do_write(&readwrite);
return 0; return 0;
} }
int mode_acl(int argc, char *argv[]) int mode_acl( int argc, char *argv[] )
{ {
int c; int c;
char *sock = NULL; char *sock = NULL;
while (1) { while (1) {
c = getopt_long(argc, argv, acl_short_options, acl_options, NULL); c = getopt_long( argc, argv, acl_short_options, acl_options, NULL );
if (c == -1) { if ( c == -1 ) { break; }
break; read_acl_param( c, &sock );
}
read_acl_param(c, &sock);
} }
if (NULL == sock) { if ( NULL == sock ){
fprintf(stderr, "--sock is required.\n"); fprintf( stderr, "--sock is required.\n" );
exit_err(acl_help_text); exit_err( acl_help_text );
} }
/* Don't use the CMD_ACL macro here, "acl" is the remote command /* Don't use the CMD_ACL macro here, "acl" is the remote command
* name, not the cli option * name, not the cli option
*/ */
do_remote_command("acl", sock, argc - optind, argv + optind); do_remote_command( "acl", sock, argc - optind, argv + optind );
return 0; return 0;
} }
int mode_mirror_speed(int argc, char *argv[]) int mode_mirror_speed( int argc, char *argv[] )
{ {
int c; int c;
char *sock = NULL; char *sock = NULL;
char *speed = NULL; char *speed = NULL;
while (1) { while( 1 ) {
c = getopt_long(argc, argv, mirror_speed_short_options, c = getopt_long( argc, argv, mirror_speed_short_options, mirror_speed_options, NULL );
mirror_speed_options, NULL); if ( -1 == c ) { break; }
if (-1 == c) { read_mirror_speed_param( c, &sock, &speed );
break;
}
read_mirror_speed_param(c, &sock, &speed);
} }
if (NULL == sock) { if ( NULL == sock ) {
fprintf(stderr, "--sock is required.\n"); fprintf( stderr, "--sock is required.\n" );
exit_err(mirror_speed_help_text); exit_err( mirror_speed_help_text );
} }
if (NULL == speed) { if ( NULL == speed ) {
fprintf(stderr, "--max-speed is required.\n"); fprintf( stderr, "--max-speed is required.\n");
exit_err(mirror_speed_help_text); exit_err( mirror_speed_help_text );
} }
do_remote_command("mirror_max_bps", sock, 1, &speed); do_remote_command( "mirror_max_bps", sock, 1, &speed );
return 0; return 0;
} }
int mode_mirror(int argc, char *argv[]) int mode_mirror( int argc, char *argv[] )
{ {
int c; int c;
char *sock = NULL; char *sock = NULL;
char *remote_argv[4] = { 0 }; char *remote_argv[4] = {0};
int err = 0; int err = 0;
int unlink = 0; int unlink = 0;
remote_argv[2] = "exit"; remote_argv[2] = "exit";
while (1) { while (1) {
c = getopt_long(argc, argv, mirror_short_options, mirror_options, c = getopt_long( argc, argv, mirror_short_options, mirror_options, NULL);
NULL); if ( -1 == c ) { break; }
if (-1 == c) { read_mirror_param( c,
break;
}
read_mirror_param(c,
&sock, &sock,
&remote_argv[0], &remote_argv[0],
&remote_argv[1], &unlink, &remote_argv[3]); &remote_argv[1],
&unlink,
&remote_argv[3] );
} }
if (NULL == sock) { if ( NULL == sock ){
fprintf(stderr, "--sock is required.\n"); fprintf( stderr, "--sock is required.\n" );
err = 1; err = 1;
} }
if (NULL == remote_argv[0] || NULL == remote_argv[1]) { if ( NULL == remote_argv[0] || NULL == remote_argv[1] ) {
fprintf(stderr, "both --addr and --port are required.\n"); fprintf( stderr, "both --addr and --port are required.\n");
err = 1; err = 1;
} }
if (err) { if ( err ) { exit_err( mirror_help_text ); }
exit_err(mirror_help_text); if ( unlink ) { remote_argv[2] = "unlink"; }
}
if (unlink) {
remote_argv[2] = "unlink";
}
if (remote_argv[3] == NULL) { if (remote_argv[3] == NULL) {
do_remote_command("mirror", sock, 3, remote_argv); do_remote_command( "mirror", sock, 3, remote_argv );
} else { }
do_remote_command("mirror", sock, 4, remote_argv); else {
do_remote_command( "mirror", sock, 4, remote_argv );
} }
return 0; return 0;
} }
int mode_break(int argc, char *argv[]) int mode_break( int argc, char *argv[] )
{ {
int c; int c;
char *sock = NULL; char *sock = NULL;
while (1) { while (1) {
c = getopt_long(argc, argv, break_short_options, break_options, c = getopt_long( argc, argv, break_short_options, break_options, NULL );
NULL); if ( -1 == c ) { break; }
if (-1 == c) { read_break_param( c, &sock );
break;
}
read_break_param(c, &sock);
} }
if (NULL == sock) { if ( NULL == sock ){
fprintf(stderr, "--sock is required.\n"); fprintf( stderr, "--sock is required.\n" );
exit_err(break_help_text); exit_err( break_help_text );
} }
do_remote_command("break", sock, argc - optind, argv + optind); do_remote_command( "break", sock, argc - optind, argv + optind );
return 0; return 0;
} }
int mode_status(int argc, char *argv[]) int mode_status( int argc, char *argv[] )
{ {
int c; int c;
char *sock = NULL; char *sock = NULL;
while (1) { while (1) {
c = getopt_long(argc, argv, status_short_options, status_options, c = getopt_long( argc, argv, status_short_options, status_options, NULL );
NULL); if ( -1 == c ) { break; }
if (-1 == c) { read_status_param( c, &sock );
break;
}
read_status_param(c, &sock);
} }
if (NULL == sock) { if ( NULL == sock ){
fprintf(stderr, "--sock is required.\n"); fprintf( stderr, "--sock is required.\n" );
exit_err(status_help_text); exit_err( status_help_text );
} }
do_remote_command("status", sock, argc - optind, argv + optind); do_remote_command( "status", sock, argc - optind, argv + optind );
return 0; return 0;
} }
int mode_help(int argc, char *argv[]) int mode_help( int argc, char *argv[] )
{ {
char *cmd; char *cmd;
char *help_text = NULL; char *help_text = NULL;
if (argc < 1) { if ( argc < 1 ){
help_text = help_help_text; help_text = help_help_text;
} else { } else {
cmd = argv[0]; cmd = argv[0];
if (IS_CMD(CMD_SERVE, cmd)) { if (IS_CMD( CMD_SERVE, cmd ) ) {
help_text = serve_help_text; help_text = serve_help_text;
} else if (IS_CMD(CMD_LISTEN, cmd)) { } else if ( IS_CMD( CMD_LISTEN, cmd ) ) {
help_text = listen_help_text; help_text = listen_help_text;
} else if (IS_CMD(CMD_READ, cmd)) { } else if ( IS_CMD( CMD_READ, cmd ) ) {
help_text = read_help_text; help_text = read_help_text;
} else if (IS_CMD(CMD_WRITE, cmd)) { } else if ( IS_CMD( CMD_WRITE, cmd ) ) {
help_text = write_help_text; help_text = write_help_text;
} else if (IS_CMD(CMD_ACL, cmd)) { } else if ( IS_CMD( CMD_ACL, cmd ) ) {
help_text = acl_help_text; help_text = acl_help_text;
} else if (IS_CMD(CMD_MIRROR, cmd)) { } else if ( IS_CMD( CMD_MIRROR, cmd ) ) {
help_text = mirror_help_text; help_text = mirror_help_text;
} else if (IS_CMD(CMD_STATUS, cmd)) { } else if ( IS_CMD( CMD_STATUS, cmd ) ) {
help_text = status_help_text; help_text = status_help_text;
} else { } else { exit_err( help_help_text ); }
exit_err(help_help_text);
}
} }
fprintf(stdout, "%s\n", help_text); fprintf( stdout, "%s\n", help_text );
return 0; return 0;
} }
void mode(char *mode, int argc, char **argv) void mode(char* mode, int argc, char **argv)
{ {
if (IS_CMD(CMD_SERVE, mode)) { if ( IS_CMD( CMD_SERVE, mode ) ) {
exit(mode_serve(argc, argv)); exit( mode_serve( argc, argv ) );
} else if (IS_CMD(CMD_LISTEN, mode)) { }
exit(mode_listen(argc, argv)); else if ( IS_CMD( CMD_LISTEN, mode ) ) {
} else if (IS_CMD(CMD_READ, mode)) { exit( mode_listen( argc, argv ) );
mode_read(argc, argv); }
} else if (IS_CMD(CMD_WRITE, mode)) { else if ( IS_CMD( CMD_READ, mode ) ) {
mode_write(argc, argv); mode_read( argc, argv );
} else if (IS_CMD(CMD_ACL, mode)) { }
mode_acl(argc, argv); else if ( IS_CMD( CMD_WRITE, mode ) ) {
} else if (IS_CMD(CMD_MIRROR_SPEED, mode)) { mode_write( argc, argv );
mode_mirror_speed(argc, argv); }
} else if (IS_CMD(CMD_MIRROR, mode)) { else if ( IS_CMD( CMD_ACL, mode ) ) {
mode_mirror(argc, argv); mode_acl( argc, argv );
} else if (IS_CMD(CMD_BREAK, mode)) { } else if ( IS_CMD ( CMD_MIRROR_SPEED, mode ) ) {
mode_break(argc, argv); mode_mirror_speed( argc, argv );
} else if (IS_CMD(CMD_STATUS, mode)) { }
mode_status(argc, argv); else if ( IS_CMD( CMD_MIRROR, mode ) ) {
} else if (IS_CMD(CMD_HELP, mode)) { mode_mirror( argc, argv );
mode_help(argc - 1, argv + 1); }
} else { else if ( IS_CMD( CMD_BREAK, mode ) ) {
mode_help(argc - 1, argv + 1); mode_break( argc, argv );
exit(1); }
else if ( IS_CMD( CMD_STATUS, mode ) ) {
mode_status( argc, argv );
}
else if ( IS_CMD( CMD_HELP, mode ) ) {
mode_help( argc-1, argv+1 );
}
else {
mode_help( argc-1, argv+1 );
exit( 1 );
} }
exit(0); exit(0);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -10,54 +10,51 @@
#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_t * mirror;
struct mirror_super *mirror_super; struct mirror_super_t * 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;
@@ -76,7 +73,7 @@ struct server {
* 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;
@@ -103,54 +100,56 @@ struct server {
int success; int success;
}; };
struct server *server_create(struct flexnbd *flexnbd, struct server * server_create(
char *s_ip_address, struct flexnbd * flexnbd,
char *s_port, char* s_ip_address,
char *s_file, char* s_port,
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;
@@ -165,3 +164,4 @@ struct mode_readwrite_params {
#endif #endif

View File

@@ -2,42 +2,39 @@
#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) < if ( ( serve->mirror->migration_started ) < status->migration_duration ) {
status->migration_duration) {
status->migration_duration -= serve->mirror->migration_started; status->migration_duration -= serve->mirror->migration_started;
} else { } else {
status->migration_duration = 0; status->migration_duration = 0;
} }
status->migration_duration /= 1000; status->migration_duration /= 1000;
status->migration_speed = server_mirror_bps(serve); status->migration_speed = server_mirror_bps( serve );
status->migration_speed_limit = status->migration_speed_limit = serve->mirror->max_bytes_per_second;
serve->mirror->max_bytes_per_second;
status->migration_seconds_left = server_mirror_eta(serve); status->migration_seconds_left = server_mirror_eta( serve );
status->migration_bytes_left = status->migration_bytes_left = server_mirror_bytes_remaining( serve );
server_mirror_bytes_remaining(serve);
} }
server_unlock_start_mirror(serve); server_unlock_start_mirror( serve );
return status; return status;
@@ -51,22 +48,22 @@ 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 );
}; };
} }
@@ -75,8 +72,9 @@ int status_write(struct status *status, int fd)
} }
void status_destroy(struct status *status) void status_destroy( struct status * status )
{ {
NULLCHECK(status); NULLCHECK( status );
free(status); free( status );
} }

View File

@@ -90,14 +90,15 @@ struct status {
}; };
/** 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,42 +1,39 @@
# 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.#{$PROCESS_ID}.#{Time.now.to_i}.1" @filename1 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.1"
@filename2 = "/tmp/.flexnbd.test.#{$PROCESS_ID}.#{Time.now.to_i}.2" @filename2 = "/tmp/.flexnbd.test.#{$$}.#{Time.now.to_i}.2"
@ip = '127.0.0.1' @ip = "127.0.0.1"
@available_ports = [*40_000..41_000] - listening_ports @available_ports = [*40000..41000] - listening_ports
@port1 = @available_ports.shift @port1 = @available_ports.shift
@port2 = @available_ports.shift @port2 = @available_ports.shift
@nbd1 = FlexNBD::FlexNBD.new('../../build/flexnbd', @ip, @port1) @nbd1 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port1)
@nbd2 = FlexNBD::FlexNBD.new('../../build/flexnbd', @ip, @port2) @nbd2 = FlexNBD::FlexNBD.new("../../build/flexnbd", @ip, @port2)
@fake_pid = nil @fake_pid = nil
end end
def blocksize=(b)
raise RuntimeError, "Unable to change blocksize after files have been opened" if @file1 or @file2
@blocksize = b
end
def prefetch_proxy! def prefetch_proxy!
@nbd1.prefetch_proxy = true @nbd1.prefetch_proxy = true
@nbd2.prefetch_proxy = true @nbd2.prefetch_proxy = true
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
@@ -45,26 +42,29 @@ class Environment
@nbd2.serve(@filename2, *acl) @nbd2.serve(@filename2, *acl)
end end
def listen1(*acl)
@nbd1.listen(@filename1, *(acl.empty? ? @acl1 : acl)) def listen1( *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,20 +73,23 @@ 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)
@nbd1.write(0, data) def write1( data )
@nbd1.write( 0, data )
end end
def writefile1(data) def writefile1(data)
@@ -97,54 +100,63 @@ 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.exist?(f) File.unlink(f) if File.exists?(f)
end end
end end
def run_fake(name, addr, port, sock = nil)
fakedir = File.join(File.dirname(__FILE__), 'fakes') def run_fake( name, addr, port, sock=nil )
fakeglob = File.join(fakedir, name) + '*' fakedir = File.join( File.dirname( __FILE__ ), "fakes" )
fake = Dir[fakeglob].sort.find do |fn| fakeglob = File.join( fakedir, name ) + "*"
File.executable?(fn) fake = Dir[fakeglob].sort.find { |fn|
end File.executable?( fn )
}
raise "no fake executable at #{fakeglob}" unless fake raise "no fake executable at #{fakeglob}" unless fake
raise 'no addr' unless addr raise "no addr" unless addr
raise 'no port' unless port raise "no port" unless port
@fake_pid = fork do @fake_pid = fork do
exec [fake, addr, port, @nbd1.pid, sock].map(&:to_s).join(' ') exec [fake, addr, port, @nbd1.pid, sock].map{|x| x.to_s}.join(" ")
end end
sleep(0.5) sleep(0.5)
end end
def fake_reports_success def fake_reports_success
_, status = Process.waitpid2(@fake_pid) _,status = Process.waitpid2( @fake_pid )
@fake_pid = nil @fake_pid = nil
status.success? status.success?
end end
end # class Environment end # class Environment

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Open a server, accept a client, then cancel the migration by issuing # Open a server, accept a client, then cancel the migration by issuing
# a break command. # a break command.
@@ -6,27 +8,28 @@ 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)
raise 'Unexpected control response' unless fail "Unexpected control response" unless
ctrl.read =~ /0: mirror stopped/ ctrl.read =~ /0: mirror stopped/
client2 = nil client2 = nil
begin begin
client2 = server.accept('Expected timeout') client2 = server.accept( "Expected timeout" )
raise 'Unexpected reconnection' fail "Unexpected reconnection"
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end
client.close client.close
exit(0) exit(0)

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Receive a mirror, and disconnect after sending the entrust reply but # Receive a mirror, and disconnect after sending the entrust reply but
# before it can send the disconnect signal. # before it can send the disconnect signal.
# #
@@ -9,25 +11,26 @@ 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
warn 'done' $stderr.puts "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,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Open a server, accept a client, then we expect a single write # Open a server, accept a client, then we expect a single write
# followed by an entrust. However, we disconnect after the write so # followed by an entrust. However, we disconnect after the write so
# the entrust will fail. We don't expect a reconnection: the sender # the entrust will fail. We don't expect a reconnection: the sender
@@ -8,16 +10,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,16 +1,19 @@
#!/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,8 +26,8 @@ client.close
# Invert the sense of the timeout exception, since we *don't* want a # Invert the sense of the timeout exception, since we *don't* want a
# connection attempt # connection attempt
begin begin
server.accept('Expected timeout') server.accept( "Expected timeout" )
raise 'Unexpected reconnection' fail "Unexpected reconnection"
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Open a socket, say hello, receive a write, then sleep for > # Open a socket, say hello, receive a write, then sleep for >
# MS_REQUEST_LIMIT_SECS seconds. This should tell the source that the # MS_REQUEST_LIMIT_SECS seconds. This should tell the source that the
# write has gone MIA, and we expect a reconnect. # write has gone MIA, and we expect a reconnect.
@@ -7,24 +9,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.key?('FLEXNBD_MS_REQUEST_LIMIT_SECS') sleep_time = if ENV.has_key?('FLEXNBD_MS_REQUEST_LIMIT_SECS')
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'].to_f ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'].to_f
else else
FlexNBD::MS_REQUEST_LIMIT_SECS FlexNBD::MS_REQUEST_LIMIT_SECS
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 )
raise 'Unexpected reconnection' fail "Unexpected reconnection"
rescue Timeout::Error rescue Timeout::Error
# expected #expected
end end
end end
client1.write_hello(magic: :wrong) client1.write_hello( :magic => :wrong )
t.join t.join

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
raise 'Unexpected reconnection.' fail "Unexpected reconnection."
rescue Timeout::Error rescue Timeout::Error
end end
end end
client.write_hello(size: :wrong) client.write_hello( :size => :wrong )
t.join t.join
# Now check that the source closed the first socket (yes, this was an # Now check that the source closed the first socket (yes, this was an
# actual bug) # actual bug)
raise "Didn't close socket" unless client.disconnected? fail "Didn't close socket" unless client.disconnected?
exit 0 exit 0

View File

@@ -7,16 +7,18 @@ 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
raise 'Unexpected reconnection' fail "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,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Accept a connection, write hello, wait for a write request, read the # Accept a connection, write hello, wait for a write request, read the
# data, then write back a reply with a bad magic field. We then # data, then write back a reply with a bad magic field. We then
# expect a reconnect. # expect a reconnect.
@@ -7,13 +9,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,11 +25,12 @@ client.close
system "kill -CONT #{srv_pid}" system "kill -CONT #{srv_pid}"
sleep(0.25) sleep(0.25)
begin begin
client2 = FakeSource.new(addr, port, 'Expected timeout') client2 = FakeSource.new( addr, port, "Expected timeout" )
raise 'Unexpected reconnection' fail "Unexpected reconnection"
rescue Timeout::Error rescue Timeout::Error
# expected # expected
end end

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

View File

@@ -12,12 +12,13 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting.')
client = FakeSource.new( addr, port, "Timed out connecting." )
client.read_hello client.read_hello
client.close client.close
sleep(0.2) sleep(0.2)
FakeSource.new(addr, port, 'Timed out reconnecting.') FakeSource.new( addr, port, "Timed out reconnecting." )
exit(0) exit(0)

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# We connect, pause the server, issue a write request, disconnect, # We connect, pause the server, issue a write request, disconnect,
# then cont the server. This ensures that our disconnect happens # then cont the server. This ensures that our disconnect happens
# while the server is trying to read the write data. # while the server is trying to read the write data.
@@ -8,11 +10,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}"
@@ -22,7 +24,7 @@ system "kill -CONT #{srv_pid}"
sleep(0.25) sleep(0.25)
# ...and can we reconnect? # ...and can we reconnect?
client2 = FakeSource.new(addr, port, 'Timed out connecting') client2 = FakeSource.new( addr, port, "Timed out connecting" )
client2.close client2.close
exit(0) exit(0)

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# We connect, pause the server, issue a write request, send data, # We connect, pause the server, issue a write request, send data,
# disconnect, then cont the server. This ensures that our disconnect # disconnect, then cont the server. This ensures that our disconnect
# happens before the server can try to write the reply. # happens before the server can try to write the reply.
@@ -8,13 +10,13 @@ include FlexNBD
addr, port, srv_pid = *ARGV addr, port, srv_pid = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting') client = FakeSource.new( addr, port, "Timed out connecting" )
client.read_hello client.read_hello
system "kill -STOP #{srv_pid}" system "kill -STOP #{srv_pid}"
client.write_write_request(0, 8) client.write_write_request( 0, 8 )
client.write_data('12345678') client.write_data( "12345678" )
client.close client.close
system "kill -CONT #{srv_pid}" system "kill -CONT #{srv_pid}"
@@ -25,7 +27,7 @@ system "kill -CONT #{srv_pid}"
sleep(0.25) sleep(0.25)
# ...and can we reconnect? # ...and can we reconnect?
client2 = FakeSource.new(addr, port, 'Timed out reconnecting') client2 = FakeSource.new( addr, port, "Timed out reconnecting" )
client2.close client2.close
exit(0) exit(0)

View File

@@ -8,9 +8,10 @@ include FlexNBD
addr, port, srv_pid, newaddr, newport = *ARGV addr, port, srv_pid, newaddr, newport = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting') client = FakeSource.new( addr, port, "Timed out connecting" )
client.write_read_request(0, 8) client.write_read_request( 0, 8 )
client.read_raw(4) client.read_raw( 4 )
client.close client.close
exit(0) exit(0)

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,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# We connect from a local address which should be blocked, sleep for a # We connect from a local address which should be blocked, sleep for a
# bit, then try to read from the socket. We should get an instant EOF # bit, then try to read from the socket. We should get an instant EOF
# as we've been cut off by the destination. # as we've been cut off by the destination.
@@ -9,9 +11,10 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting', '127.0.0.6') client = FakeSource.new( addr, port, "Timed out connecting", "127.0.0.6" )
sleep(0.25) sleep( 0.25 )
rsp = client.disconnected? ? 0 : 1 rsp = client.disconnected? ? 0 : 1
client.close client.close
exit(rsp) exit(rsp)

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,9 +9,10 @@ include FlexNBD
addr, port, srv_pid, newaddr, newport = *ARGV addr, port, srv_pid, newaddr, newport = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting') client = FakeSource.new( addr, port, "Timed out connecting" )
client.send_mirror client.send_mirror()
sleep(1) sleep(1)
exit(0)
exit( 0 )

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# encoding: utf-8
# Connect, read the hello then make a write request with an impossible # Connect, read the hello then make a write request with an impossible
# (from,len) pair. We expect an error response, and not to be # (from,len) pair. We expect an error response, and not to be
# disconnected. # disconnected.
@@ -11,20 +13,20 @@ include FlexNBD
addr, port = *ARGV addr, port = *ARGV
client = FakeSource.new(addr, port, 'Timed out connecting') client = FakeSource.new( addr, port, "Timed out connecting" )
hello = client.read_hello hello = client.read_hello
client.write_write_request(hello[:size] + 1, 32, 'myhandle') client.write_write_request( hello[:size]+1, 32, "myhandle" )
client.write_data('1' * 32) client.write_data("1"*32)
response = client.read_response response = client.read_response
raise 'Not an error' if response[:error] == 0 fail "Not an error" if response[:error] == 0
raise 'Wrong handle' unless response[:handle] == 'myhandle' fail "Wrong handle" unless "myhandle" == response[:handle]
client.write_write_request(0, 32) client.write_write_request( 0, 32 )
client.write_data('2' * 32) client.write_data( "2"*32 )
success_response = client.read_response success_response = client.read_response
raise 'Second write failed' unless success_response[:error] == 0 fail "Second write failed" unless success_response[:error] == 0
client.close client.close
exit(0) exit(0)

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,14 +31,15 @@ class FileWriter
self self
end end
# Returns what the data ought to be at the given offset and length # Returns what the data ought to be at the given offset and length
# #
def read_original(off, len) def read_original( off, len )
patterns = @pattern.split('') patterns = @pattern.split( "" )
patterns.zip((0...patterns.length).to_a) patterns.zip( (0...patterns.length).to_a ).
.map do |blk, blk_off| map { |blk, blk_off|
data(blk, blk_off) data(blk, blk_off)
end.join[off...(off + len)] }.join[off...(off+len)]
end end
# Read what's actually in the file # Read what's actually in the file
@@ -59,16 +60,16 @@ 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
@@ -76,49 +77,51 @@ class FileWriter
raise "Unknown character '#{block}'" raise "Unknown character '#{block}'"
end end
end end
end end
if $PROGRAM_NAME == __FILE__ if __FILE__==$0
require 'tempfile' require 'tempfile'
require 'test/unit' require 'test/unit'
class FileWriterTest < Test::Unit::TestCase class FileWriterTest < Test::Unit::TestCase
def test_read_original_zeros def test_read_original_zeros
Tempfile.open('test_read_original_zeros') do |tempfile| Tempfile.open("test_read_original_zeros") do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4096) file = FileWriter.new( tempfile.path, 4096 )
file.write('0') file.write( "0" )
assert_equal file.read(0, 4096), file.read_original(0, 4096) assert_equal file.read( 0, 4096 ), file.read_original( 0, 4096 )
assert(file.untouched?(0, 4096), 'Untouched file was touched.') assert( file.untouched?(0,4096) , "Untouched file was touched." )
end end
end end
def test_read_original_offsets def test_read_original_offsets
Tempfile.open('test_read_original_offsets') do |tempfile| Tempfile.open("test_read_original_offsets") do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4096) file = FileWriter.new( tempfile.path, 4096 )
file.write('f') file.write( "f" )
assert_equal file.read(0, 4096), file.read_original(0, 4096) assert_equal file.read( 0, 4096 ), file.read_original( 0, 4096 )
assert(file.untouched?(0, 4096), 'Untouched file was touched.') assert( file.untouched?(0,4096) , "Untouched file was touched." )
end end
end end
def test_file_size def test_file_size
Tempfile.open('test_file_size') do |tempfile| Tempfile.open("test_file_size") do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4096) file = FileWriter.new( tempfile.path, 4096 )
file.write('f') file.write( "f" )
assert_equal 4096, File.stat(tempfile.path).size assert_equal 4096, File.stat( tempfile.path ).size
end end
end end
def test_read_original_size def test_read_original_size
Tempfile.open('test_read_original_offsets') do |tempfile| Tempfile.open("test_read_original_offsets") do |tempfile|
tempfile.close tempfile.close
file = FileWriter.new(tempfile.path, 4) file = FileWriter.new( tempfile.path, 4)
file.write('f' * 4) file.write( "f"*4 )
assert_equal 4, file.read_original(0, 4).length assert_equal 4, file.read_original(0, 4).length
end end
end end
end end
end end

View File

@@ -4,26 +4,28 @@ require 'open3'
require 'timeout' require 'timeout'
require 'rexml/document' require 'rexml/document'
require 'rexml/streamlistener' require 'rexml/streamlistener'
require 'English'
Thread.abort_on_exception = true Thread.abort_on_exception = true
class Executor class Executor
attr_reader :pid attr_reader :pid
def run(cmd) def run( cmd )
@pid = fork { exec cmd } @pid = fork do exec cmd end
end end
end # class Executor end # class Executor
class ValgrindExecutor class ValgrindExecutor
attr_reader :pid attr_reader :pid
def run(cmd) def run( cmd )
@pid = fork { exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" } @pid = fork do exec "valgrind --track-origins=yes --suppressions=custom.supp #{cmd}" end
end end
end # class ValgrindExecutor end # class ValgrindExecutor
class ValgrindKillingExecutor class ValgrindKillingExecutor
attr_reader :pid attr_reader :pid
@@ -31,10 +33,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
@@ -54,104 +56,115 @@ 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"
@killer.call(@error) if @found if @found
when 'pid' @killer.call( @error )
@error.pid = @text end
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 { exec("valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd) } @pid = fork do exec( "valgrind --suppressions=custom.supp --xml=yes --xml-fd=#{io_w.fileno} " + cmd ) end
launch_watch_thread(@pid, @io_r) launch_watch_thread( @pid, @io_r )
@pid @pid
end end
def call(err)
warn '*' * 72 def call( err )
warn '* Valgrind error spotted:' $stderr.puts "*"*72
warn err.to_s.split("\n").map { |s| " #{s}" } $stderr.puts "* Valgrind error spotted:"
warn '*' * 72 $stderr.puts err.to_s.split("\n").map{|s| " #{s}"}
Process.kill('KILL', @pid) $stderr.puts "*"*72
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.
# #
@@ -160,7 +173,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
@@ -176,17 +189,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
@@ -195,20 +209,21 @@ module FlexNBD
@ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}" @ctrl = "/tmp/.flexnbd.ctrl.#{Time.now.to_i}.#{rand}"
@ip = ip @ip = ip
@port = port @port = port
@pid = @wait_thread = nil
@kill = [] @kill = []
@prefetch_proxy = false @prefetch_proxy = false
end end
def debug? def debug?
!!@do_debug !!@do_debug
end end
def debug(msg) def debug( msg )
warn msg if debug? $stderr.puts msg if debug?
end end
def serve_cmd(file, acl)
def serve_cmd( file, acl )
"#{bin} serve "\ "#{bin} serve "\
"--addr #{ip} "\ "--addr #{ip} "\
"--port #{port} "\ "--port #{port} "\
@@ -218,7 +233,8 @@ 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} "\
@@ -228,17 +244,18 @@ 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} "\
@@ -247,7 +264,8 @@ 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} "\
@@ -256,29 +274,30 @@ 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
@@ -293,64 +312,58 @@ 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.exist?(ctrl) File.unlink(ctrl) if File.exists?(ctrl)
debug(cmd) debug( cmd )
@pid = @executor.run(cmd) @pid = @executor.run( cmd )
until File.socket?(ctrl) while !File.socket?(ctrl)
pid, status = Process.wait2(@pid, Process::WNOHANG) pid, status = Process.wait2(@pid, Process::WNOHANG)
raise "server did not start (#{cmd})" if pid raise "server did not start (#{cmd})" if pid
sleep 0.1 sleep 0.1
end end
start_wait_thread(@pid)
start_wait_thread( @pid )
at_exit { kill } at_exit { kill }
end end
private :run_serve_cmd private :run_serve_cmd
def serve(file, *acl)
cmd = serve_cmd(file, acl) def serve( file, *acl)
run_serve_cmd(cmd) cmd = serve_cmd( file, acl )
sleep(0.2) until File.exist?(ctrl) run_serve_cmd( cmd )
sleep( 0.2 ) until File.exists?( ctrl )
end end
def listen(file, *acl) def listen(file, *acl)
run_serve_cmd(listen_cmd(file, acl)) run_serve_cmd( listen_cmd( file, acl ) )
end end
def tcp_server_open? def tcp_server_open?
# raises if the other side doesn't accept() # raises if the other side doesn't accept()
sock = begin sock = TCPSocket.new(ip, port) rescue nil
TCPSocket.new(ip, port)
rescue StandardError
nil
end
success = !!sock success = !!sock
if sock ( sock.close rescue nil) if sock
(begin
sock.close
rescue StandardError
nil
end)
end
success success
end end
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)
@@ -358,29 +371,31 @@ 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?
raise "flexnbd quit with a bad signal: #{status.inspect}" unless fail "flexnbd quit with a bad signal: #{status.inspect}" unless
@kill.include? status.termsig @kill.include? status.termsig
else else
raise "flexnbd quit with a bad status: #{status.inspect}" unless fail "flexnbd quit with a bad status: #{status.inspect}" unless
@kill.include? status.exitstatus @kill.include? status.exitstatus
end end
else else
warn "flexnbd #{self.pid} quit" $stderr.puts "flexnbd #{self.pid} quit"
raise "flexnbd #{self.pid} quit early with status #{status.to_i}" fail "flexnbd #{self.pid} quit early with status #{status.to_i}"
end end
end end
end end
def can_die(*status) def can_die(*status)
status = [0] if status.empty? status = [0] if status.empty?
@kill += status @kill += status
@@ -392,7 +407,7 @@ module FlexNBD
can_die(1) can_die(1)
if @pid if @pid
begin begin
Process.kill('INT', @pid) Process.kill("INT", @pid)
rescue Errno::ESRCH => e rescue Errno::ESRCH => e
# already dead. Presumably this means it went away after a # already dead. Presumably this means it went away after a
# can_die() call. # can_die() call.
@@ -402,60 +417,63 @@ 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, 'NBD read failed' unless $CHILD_STATUS.success? raise IOError.new "NBD read failed" unless $?.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, 'NBD write failed' unless $CHILD_STATUS.success? raise IOError.new "NBD write failed" unless $?.success?
nil nil
end end
def join def join
@wait_thread.join @wait_thread.join
end end
def mirror_unchecked(dest_ip, dest_port, _bandwidth = nil, _action = nil, timeout = nil)
cmd = mirror_cmd(dest_ip, dest_port)
debug(cmd)
maybe_timeout(cmd, timeout) def mirror_unchecked( dest_ip, dest_port, bandwidth=nil, action=nil, timeout=nil )
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)
maybe_timeout(cmd, timeout) def mirror_unlink( dest_ip, dest_port, timeout=nil )
cmd = mirror_unlink_cmd( dest_ip, dest_port )
debug( cmd )
maybe_timeout( cmd, timeout )
end end
def maybe_timeout(cmd, timeout = nil)
stdout = '' def maybe_timeout(cmd, timeout=nil )
stderr = '' stdout, stderr = "",""
stat = nil stat = nil
run = proc do run = Proc.new do
# Ruby 1.9 changed the popen3 api. instead of 3 args, the block # Ruby 1.9 changed the popen3 api. instead of 3 args, the block
# gets 4. Not only that, but it no longer sets $?, so we have to # gets 4. Not only that, but it no longer sets $?, so we have to
# go elsewhere for the process' exit status. # go elsewhere for the process' exit status.
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 ||= $CHILD_STATUS stat ||= $?
end end
if timeout if timeout
@@ -467,73 +485,85 @@ module FlexNBD
[stdout, stderr, stat] [stdout, stderr, stat]
end end
def mirror(dest_ip, dest_port, bandwidth = nil, action = nil)
stdout, stderr, status = mirror_unchecked(dest_ip, dest_port, bandwidth, action) def mirror(dest_ip, dest_port, bandwidth=nil, action=nil)
raise IOError, "Migrate command failed\n" + stderr unless status.success? stdout, stderr, status = mirror_unchecked( dest_ip, dest_port, bandwidth, action )
raise IOError.new( "Migrate command failed\n" + stderr) unless status.success?
stdout stdout
end end
def break(timeout = nil)
cmd = break_cmd
debug(cmd)
maybe_timeout(cmd, timeout)
def break(timeout=nil)
cmd = break_cmd
debug( cmd )
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)
o, e = maybe_timeout(cmd, timeout) def status( timeout = nil )
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,7 +1,10 @@
# encoding: utf-8
module FlexNBD module FlexNBD
def self.binary(str)
def self.binary( str )
if str.respond_to? :force_encoding if str.respond_to? :force_encoding
str.force_encoding 'ASCII-8BIT' str.force_encoding "ASCII-8BIT"
else else
str str
end end
@@ -10,33 +13,36 @@ module FlexNBD
# eeevil is his one and only name... # eeevil is his one and only name...
def self.read_constants def self.read_constants
parents = [] parents = []
current = File.expand_path('.') current = File.expand_path(".")
while current != '/' while current != "/"
parents << current parents << current
current = File.expand_path(File.join(current, '..')) current = File.expand_path( File.join( current, ".." ) )
end end
source_root = parents.find do |dirname| source_root = parents.find do |dirname|
File.directory?(File.join(dirname, 'src')) File.directory?( File.join( dirname, "src" ) )
end end
raise 'No source root!' unless source_root fail "No source root!" unless source_root
headers = Dir[File.join(source_root, 'src', '{common,proxy,server}', '*.h')] headers = Dir[File.join( source_root, "src", "{common,proxy,server}","*.h" ) ]
headers.each do |header_filename| headers.each do |header_filename|
txt_lines = File.readlines(header_filename) txt_lines = File.readlines( header_filename )
txt_lines.each do |line| txt_lines.each do |line|
if line =~ /^#\s*define\s+([A-Z0-9_]+)\s+(\d+)\s*$/ if line =~ /^#\s*define\s+([A-Z0-9_]+)\s+(\d+)\s*$/
# Bodge until I can figure out what to do with #ifdefs # Bodge until I can figure out what to do with #ifdefs
const_set(Regexp.last_match(1), Regexp.last_match(2).to_i) unless const_defined?(Regexp.last_match(1)) const_set($1, $2.to_i) unless const_defined?( $1 )
end
end end
end end
end end
read_constants end
read_constants()
REQUEST_MAGIC = binary("\x25\x60\x95\x13") unless defined?(REQUEST_MAGIC) REQUEST_MAGIC = binary("\x25\x60\x95\x13") unless defined?(REQUEST_MAGIC)
REPLY_MAGIC = binary("\x67\x44\x66\x98") unless defined?(REPLY_MAGIC) REPLY_MAGIC = binary("\x67\x44\x66\x98") unless defined?(REPLY_MAGIC)
end # module FlexNBD end # module FlexNBD

View File

@@ -1,3 +1,5 @@
# encoding: utf-8
require 'socket' require 'socket'
require 'timeout' require 'timeout'
@@ -5,104 +7,114 @@ 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 = {})
@sock.write('NBDMAGIC') def write_hello( opts = {} )
@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)
len.times { sock.write(rand(256).chr) } def write_rand( sock, len )
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 end
rescue Timeout::Error rescue Timeout::Error
return false return false
end end
end
def write_reply(handle, err = 0, opts = {}) def write_reply( handle, err=0, opts={} )
if opts[:magic] == :wrong if opts[:magic] == :wrong
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)
@sock.read(len) def read_data( 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 = {})
write_hello def receive_mirror( opts = {} )
write_hello()
loop do loop do
req = read_request req = read_request
case req[:type] case req[:type]
when 1 when 1
read_data(req[:len]) read_data( req[:len] )
write_reply(req[:handle]) write_reply( req[:handle] )
when 65_536 when 65536
write_reply(req[:handle], opts[:err] == :entrust ? 1 : 0) write_reply( req[:handle], opts[:err] == :entrust ? 1 : 0 )
break break
else else
raise "Unexpected request: #{req.inspect}" raise "Unexpected request: #{req.inspect}"
@@ -117,13 +129,16 @@ module FlexNBD
raise "Not a disconnect: #{req.inspect}" raise "Not a disconnect: #{req.inspect}"
end end
end end
end # class Client end # class Client
def initialize(addr, port)
@sock = TCPServer.new(addr, port) def initialize( addr, port )
@sock = TCPServer.new( addr, port )
end end
def accept(err_msg = 'Timed out waiting for a connection', timeout = 5)
def accept( err_msg = "Timed out waiting for a connection", timeout = 5)
client_sock = nil client_sock = nil
begin begin
@@ -131,16 +146,21 @@ module FlexNBD
client_sock = @sock.accept client_sock = @sock.accept
end end
rescue Timeout::Error rescue Timeout::Error
raise Timeout::Error, err_msg raise Timeout::Error.new(err_msg)
end end
client_sock client_sock
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,117 +1,112 @@
# 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)
timing_out(2, err_msg) do def initialize( addr, port, err_msg, source_addr=nil, source_port=0 )
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
warn 'Connection refused, retrying' $stderr.puts "Connection refused, retrying"
sleep(0.2) sleep(0.2)
retry retry
end end
end end
end end
def close def close
@sock.close @sock.close
end end
def read_hello
timing_out(::FlexNBD::MS_HELLO_TIME_SECS,
'Timed out waiting for hello.') do
raise 'No hello.' unless (hello = @sock.read(152)) &&
hello.length == 152
passwd_s = hello[0..7] def read_hello()
magic = hello[8..15].unpack('Q>').first timing_out( ::FlexNBD::MS_HELLO_TIME_SECS,
size = hello[16..23].unpack('Q>').first "Timed out waiting for hello." ) do
flags = hello[24..27].unpack('L>').first fail "No hello." unless (hello = @sock.read( 152 )) &&
reserved = hello[28..-1] hello.length==152
return { passwd: passwd_s, magic: magic, size: size, flags: flags, reserved: reserved } magic_s = hello[0..7]
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
@sock.write(magic) def send_request( type, handle="myhandle", from=0, len=0, magic=REQUEST_MAGIC )
@sock.write([flags].pack('n')) fail "Bad handle" unless handle.length == 8
@sock.write([type].pack('n'))
@sock.write(handle) @sock.write( magic )
@sock.write([n64(from)].pack('q')) @sock.write( [type].pack( 'N' ) )
@sock.write([len].pack('N')) @sock.write( handle )
@sock.write( [n64( from )].pack( 'q' ) )
@sock.write( [len].pack( 'N' ) )
end end
def write_write_request(from, len, handle = 'myhandle')
send_request(1, handle, from, len) def write_write_request( from, len, handle="myhandle" )
send_request( 1, handle, from, len )
end end
def write_write_request_with_fua(from, len, handle = 'myhandle')
send_request(1, handle, from, len, REQUEST_MAGIC, 1) def write_entrust_request( handle="myhandle" )
send_request( 65536, handle )
end end
def write_flush_request(handle = 'myhandle') def write_disconnect_request( handle="myhandle" )
send_request(3, handle, 0, 0) send_request( 2, handle )
end end
def write_entrust_request(handle = 'myhandle') def write_read_request( from, len, handle="myhandle" )
send_request(65_536, handle) send_request( 0, "myhandle", from, len )
end end
def write_disconnect_request(handle = 'myhandle')
send_request(2, handle) def write_data( data )
@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)
write_write_request(from, data.length) def write( from, data )
write_data(data) write_write_request( from, data.length )
write_data( data )
end end
def write_with_fua(from, data)
write_write_request_with_fua(from, data.length)
write_data(data)
end
def flush
write_flush_request
end
def read_response def read_response
magic = @sock.read(4) magic = @sock.read(4)
@@ -119,26 +114,30 @@ module FlexNBD
handle = @sock.read(8) handle = @sock.read(8)
{ {
magic: magic, :magic => magic,
error: error_s.unpack('N').first, :error => error_s.unpack("N").first,
handle: handle :handle => handle
} }
end end
def disconnected? def disconnected?
result = nil result = nil
Timeout.timeout(2) { result = @sock.read(1).nil? } Timeout.timeout( 2 ) { result = ( @sock.read(1) == nil ) }
result result
end end
def timing_out(time, msg)
Timeout.timeout(time) do def timing_out( time, msg )
begin
Timeout.timeout( time ) do
yield yield
end end
rescue Timeout::Error rescue Timeout::Error
warn msg $stderr.puts msg
exit 1 exit 1
end end
end
private private
@@ -155,5 +154,7 @@ module FlexNBD
((b & 0x000000000000ff00) << 40) | ((b & 0x000000000000ff00) << 40) |
((b & 0x00000000000000ff) << 56) ((b & 0x00000000000000ff) << 56)
end end
end # class FakeSource end # class FakeSource
end # module FlexNBD end # module FlexNBD

View File

@@ -1,55 +0,0 @@
require 'tempfile'
#
# LdPreload is a little wrapper for using LD_PRELOAD loggers to pick up system
# calls when testing flexnbd.
#
module LdPreload
#
# This takes an object name, sets up a temporary log file, whose name is
# recorded in the environment as OUTPUT_obj_name, where obj_name is the
# name of the preload module to build and load.
def with_ld_preload(obj_name)
@ld_preload_logs ||= {}
flunk 'Can only load a preload module once!' if @ld_preload_logs[obj_name]
system("make -C ld_preloads/ #{obj_name}.o > /dev/null") ||
flunk("Failed to build object #{obj_name}")
orig_env = ENV['LD_PRELOAD']
ENV['LD_PRELOAD'] = [orig_env, File.expand_path("./ld_preloads/#{obj_name}.o")].compact.join(' ')
# Open the log, and stick it in a hash
@ld_preload_logs[obj_name] = Tempfile.new(obj_name)
ENV['OUTPUT_' + obj_name] = @ld_preload_logs[obj_name].path
yield
ensure
if @ld_preload_logs[obj_name]
@ld_preload_logs[obj_name].close
@ld_preload_logs.delete(obj_name)
end
ENV['LD_PRELOAD'] = orig_env
end
def read_ld_preload_log(obj_name)
lines = []
lines << @ld_preload_logs[obj_name].readline.chomp until
@ld_preload_logs[obj_name].eof?
lines
end
#
# The next to methods assume the log file has one entry per line, and that
# each entry is a series of values separated by colons.
#
def parse_ld_preload_logs(obj_name)
read_ld_preload_log(obj_name).map do |l|
l.split(':').map { |i| i =~ /^\d+$/ ? i.to_i : i }
end
end
def assert_func_call(loglines, args, msg)
re = Regexp.new('^' + args.join(':'))
assert(loglines.any? { |l| l.match(re) }, msg)
end
end

View File

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

View File

@@ -1,33 +0,0 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
typedef int (*real_msync_t)(void *addr, size_t length, int flags);
int real_msync(void *addr, size_t length, int flags) {
return ((real_msync_t)dlsym(RTLD_NEXT, "msync"))(addr, length, flags);
}
/*
* Noddy LD_PRELOAD wrapper to catch msync calls, and log them to a file.
*/
int msync(void *addr, size_t length, int flags) {
FILE *fd;
char *fn;
int retval;
retval = real_msync(addr, length, flags);
fn = getenv("OUTPUT_msync_logger");
if ( fn != NULL ) {
fd = fopen(fn,"a");
fprintf(fd,"msync:%d:%i:%i:%i\n", addr, length, flags, retval);
fclose(fd);
}
return retval;
}

View File

@@ -1,38 +0,0 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
/*
* Noddy LD_PRELOAD wrapper to catch setsockopt calls, and log them to a file.
*/
typedef int (*real_setsockopt_t)(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int real_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
return ((real_setsockopt_t)dlsym(RTLD_NEXT, "setsockopt"))(sockfd, level, optname, optval, optlen);
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
FILE *fd;
char *fn;
int retval;
retval = real_setsockopt(sockfd, level, optname, optval, optlen);
fn = getenv("OUTPUT_setsockopt_logger");
/*
* Only interested in catching non-null 4-byte (integer) values
*/
if ( fn != NULL && optval != NULL && optlen == 4) {
fd = fopen(fn,"a");
fprintf(fd,"setsockopt:%i:%i:%i:%i:%i\n", sockfd, level, optname, *(int *)optval, retval);
fclose(fd);
}
return retval;
}

2
tests/acceptance/nbd_scenarios Executable file → Normal file
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,33 +1,26 @@
# 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[:passwd] assert_equal "NBDMAGIC", result[:magic]
assert_equal override_size || @env.file1.size, result[:size] assert_equal override_size || @env.file1.size, result[:size]
yield client yield client
ensure ensure
begin client.close rescue nil
client.close
rescue StandardError
nil
end
end end
end end
@@ -39,11 +32,11 @@ module ProxyTests
with_proxied_client do |client| with_proxied_client do |client|
(0..3).each do |n| (0..3).each do |n|
offset = n * 4096 offset = n * 4096
client.write_read_request(offset, 4096, 'myhandle') client.write_read_request(offset, 4096, "myhandle")
rsp = client.read_response rsp = client.read_response
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 'myhandle', rsp[:handle] assert_equal "myhandle", rsp[:handle]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
orig_data = @env.file1.read(offset, 4096) orig_data = @env.file1.read(offset, 4096)
@@ -52,8 +45,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
@@ -66,30 +59,16 @@ 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
@@ -99,7 +78,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
@@ -110,7 +89,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
@@ -131,32 +110,32 @@ 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.timeout(15) { client.read_response } rsp = timeout(15) { client.read_response }
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
assert_equal req1[:handle], rsp[:handle] assert_equal req1[:handle], rsp[:handle]
data = client.read_raw(4096) data = client.read_raw( 4096 )
assert_equal((b * 4096), data, 'Wrong data returned') assert_equal( (b * 4096), data, "Wrong data returned" )
sc2.close sc2.close
server.close server.close
end end
end end
def test_write_request_retried_when_upstream_dies_partway def test_write_request_retried_when_upstream_dies_partway
maker = make_fake_server maker = make_fake_server
with_ld_preload('setsockopt_logger') do
with_proxied_client(4096) do |client| with_proxied_client(4096) do |client|
server, sc1 = maker.value server, sc1 = maker.value
# 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
@@ -164,12 +143,8 @@ module ProxyTests
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" )
# Read the setsockopt logs, so we can check that TCP_NODELAY is re-set
# later
read_ld_preload_log('setsockopt_logger')
# Kill the server again, now we're sure the read request has been sent once # Kill the server again, now we're sure the read request has been sent once
sc1.close sc1.close
@@ -181,60 +156,39 @@ module ProxyTests
# And once reconnected, it should resend an identical request. # And once reconnected, it should resend an identical request.
req2 = sc2.read_request req2 = sc2.read_request
assert_equal req1, req2 assert_equal req1, req2
data2 = sc2.read_data(4096) data2 = sc2.read_data( 4096 )
assert_equal data1, data2 assert_equal data1, data2
# 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] )
# Check it to make sure it's correct # Check it to make sure it's correct
rsp = Timeout.timeout(15) { client.read_response } rsp = timeout(15) { client.read_response }
assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal ::FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error] assert_equal 0, rsp[:error]
assert_equal req1[:handle], rsp[:handle] assert_equal req1[:handle], rsp[:handle]
sc2.close sc2.close
server.close server.close
# Check TCP_NODELAY was set on the upstream socket
log = read_ld_preload_log('setsockopt_logger')
assert_func_call(log,
['setsockopt', 3,
Socket::SOL_TCP, Socket::TCP_NODELAY, 1, 0],
'TCP_NODELAY not set on upstream fd 3')
end
end end
end end
def test_only_one_client_can_connect_to_proxy_at_a_time def test_only_one_client_can_connect_to_proxy_at_a_time
with_proxied_client do |_client| with_proxied_client do |client|
c2 = nil c2 = nil
assert_raises(Timeout::Error) do assert_raises(Timeout::Error) do
Timeout.timeout(1) do timeout(1) do
c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)") c2 = FlexNBD::FakeSource.new(@env.ip, @env.port2, "Couldn't connect to proxy (2)")
c2.read_hello c2.read_hello
end end
end end
if c2 c2.close rescue nil if c2
begin
c2.close
rescue StandardError
nil
end
end
end
end end
def test_maximum_write_request_size
# Defined in src/common/nbdtypes.h
nbd_max_block_size = 32 * 1024 * 1024
@env.writefile1('0' * 40 * 1024)
with_proxied_client do |client|
# This will crash with EPIPE if the proxy dies.
client.write(0, b * nbd_max_block_size)
rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 0, rsp[:error]
end
end end
end end

View File

@@ -1,10 +1,13 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
class TestDestErrorHandling < Test::Unit::TestCase class TestDestErrorHandling < Test::Unit::TestCase
def setup def setup
@env = Environment.new @env = Environment.new
@env.writefile1('0' * 4) @env.writefile1( "0" * 4 )
@env.listen1 @env.listen1
end end
@@ -12,77 +15,89 @@ class TestDestErrorHandling < Test::Unit::TestCase
@env.cleanup @env.cleanup
end end
def test_hello_blocked_by_disconnect_causes_error_not_fatal def test_hello_blocked_by_disconnect_causes_error_not_fatal
run_fake('source/close_after_connect') run_fake( "source/close_after_connect" )
assert_no_control assert_no_control
end end
# # This is disabled while CLIENT_MAX_WAIT_SECS is removed =begin
# def test_hello_goes_astray_causes_timeout_error # This is disabled while CLIENT_MAX_WAIT_SECS is removed
# run_fake( "source/hang_after_hello" ) def test_hello_goes_astray_causes_timeout_error
# assert_no_control run_fake( "source/hang_after_hello" )
# end assert_no_control
end
=end
def test_sigterm_has_bad_exit_status def test_sigterm_has_bad_exit_status
@env.nbd1.can_die(1) @env.nbd1.can_die(1)
run_fake('source/sigterm_after_hello') run_fake( "source/sigterm_after_hello" )
end end
def test_disconnect_after_hello_causes_error_not_fatal def test_disconnect_after_hello_causes_error_not_fatal
run_fake('source/close_after_hello') run_fake( "source/close_after_hello" )
assert_no_control assert_no_control
end end
def test_partial_read_causes_error def test_partial_read_causes_error
run_fake('source/close_mid_read') run_fake( "source/close_mid_read" )
end end
def test_double_connect_during_hello def test_double_connect_during_hello
run_fake('source/connect_during_hello') run_fake( "source/connect_during_hello" )
end end
def test_acl_rejection def test_acl_rejection
@env.acl1('127.0.0.1') @env.acl1("127.0.0.1")
run_fake('source/connect_from_banned_ip') run_fake( "source/connect_from_banned_ip")
end end
def test_bad_write def test_bad_write
run_fake('source/write_out_of_range') run_fake( "source/write_out_of_range" )
end end
def test_disconnect_before_write_data_causes_error def test_disconnect_before_write_data_causes_error
run_fake('source/close_after_write') run_fake( "source/close_after_write" )
end end
def test_disconnect_before_write_reply_causes_error def test_disconnect_before_write_reply_causes_error
# Note that this is an odd case: writing the reply doesn't fail. # Note that this is an odd case: writing the reply doesn't fail.
# The test passes because the next attempt by flexnbd to read a # The test passes because the next attempt by flexnbd to read a
# request returns EOF. # request returns EOF.
run_fake('source/close_after_write_data') run_fake( "source/close_after_write_data" )
end end
def test_straight_migration def test_straight_migration
@env.nbd1.can_die(0) @env.nbd1.can_die(0)
run_fake('source/successful_transfer') run_fake( "source/successful_transfer" )
end end
private
def run_fake(name) private
@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,3 +1,5 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
require 'flexnbd/constants' require 'flexnbd/constants'
@@ -17,18 +19,20 @@ class TestHappyPath < Test::Unit::TestCase
@env.cleanup @env.cleanup
end end
def test_read1 def test_read1
@env.writefile1('f' * 64) @env.writefile1("f"*64)
@env.serve1 @env.serve1
[0, 12, 63].each do |num| [0, 12, 63].each do |num|
assert_equal( assert_equal(
bin(@env.nbd1.read(num * @env.blocksize, @env.blocksize)), bin( @env.nbd1.read(num*@env.blocksize, @env.blocksize) ),
bin(@env.file1.read(num * @env.blocksize, @env.blocksize)) bin( @env.file1.read(num*@env.blocksize, @env.blocksize) )
) )
end end
[124, 1200, 10_028, 25_488].each do |num| [124, 1200, 10028, 25488].each do |num|
assert_equal(bin(@env.nbd1.read(num, 4)), bin(@env.file1.read(num, 4))) assert_equal(bin(@env.nbd1.read(num, 4)), bin(@env.file1.read(num, 4)))
end end
end end
@@ -36,14 +40,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
@@ -51,105 +55,115 @@ 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 }
assert !File.file?(@env.filename1) Timeout.timeout(10) do @env.nbd1.join end
assert !File.file?( @env.filename1 )
end end
def test_write_to_high_block def test_write_to_high_block
# #
# This test does not work on 32 bit platforms. # This test does not work on 32 bit platforms.
# #
skip('Not relevant on 32-bit platforms') if ['a'].pack('p').size < 8 skip("Not relevant on 32-bit platforms") if ( ["a"].pack("p").size < 8 )
# Create a large file, then try to write to somewhere after the 2G boundary # Create a large file, then try to write to somewhere after the 2G boundary
@env.truncate1 '4G' @env.truncate1 "4G"
@env.serve1 @env.serve1
@env.nbd1.write(2**31 + 2**29, '12345678') @env.nbd1.write( 2**31+2**29, "12345678" )
sleep(1) sleep(1)
assert_equal '12345678', @env.nbd1.read(2**31 + 2**29, 8) assert_equal "12345678", @env.nbd1.read( 2**31+2**29, 8 )
end end
def test_set_acl def test_set_acl
# Just check that we get sane feedback here # Just check that we get sane feedback here
@env.writefile1('f' * 4) @env.writefile1( "f"*4 )
@env.serve1 @env.serve1
_, stderr = @env.acl1('127.0.0.1') _,stderr = @env.acl1("127.0.0.1")
assert_no_match(/^(F|E):/, stderr) assert_no_match( /^(F|E):/, stderr )
end end
def test_write_more_than_one_run def test_write_more_than_one_run
one_mb = 2**20 one_mb = 2**20
data = "\0" * 256 * one_mb data = "\0" * 256 * one_mb
File.open(@env.filename1, 'wb') { |f| f.write('1' * 256 * one_mb) } File.open(@env.filename1, "wb") do |f| f.write( "1" * 256 * one_mb ) end
@env.serve1 @env.serve1
sleep 5 sleep 5
@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 = format("Read non-zeros after offset %x:\n", (i * one_mb)) msg = "Read non-zeros after offset %x:\n"%(i * one_mb)
msg += `hexdump #{@env.filename1} | head -n5` msg += `hexdump #{@env.filename1} | head -n5`
raise msg fail msg
end end
i += 1 i += 1
end end
end end
end end
end end

View File

@@ -2,6 +2,7 @@ require 'test/unit'
require 'environment' require 'environment'
require 'proxy_tests' require 'proxy_tests'
class TestPrefetchProxyMode < Test::Unit::TestCase class TestPrefetchProxyMode < Test::Unit::TestCase
include ProxyTests include ProxyTests
@@ -9,7 +10,7 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
super super
@env = Environment.new @env = Environment.new
@env.prefetch_proxy! @env.prefetch_proxy!
@env.writefile1('f' * 16) @env.writefile1( "f" * 16 )
end end
def teardown def teardown
@@ -17,3 +18,5 @@ class TestPrefetchProxyMode < Test::Unit::TestCase
super super
end end
end end

View File

@@ -2,13 +2,14 @@ require 'test/unit'
require 'environment' require 'environment'
require 'proxy_tests' require 'proxy_tests'
class TestProxyMode < Test::Unit::TestCase class TestProxyMode < Test::Unit::TestCase
include ProxyTests include ProxyTests
def setup def setup
super super
@env = Environment.new @env = Environment.new
@env.writefile1('f' * 16) @env.writefile1( "f" * 16 )
end end
def teardown def teardown
@@ -16,3 +17,4 @@ class TestProxyMode < Test::Unit::TestCase
super super
end end
end end

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,78 +18,69 @@ class TestServeMode < Test::Unit::TestCase
end end
def connect_to_server def connect_to_server
@env.writefile1('0') client = FlexNBD::FakeSource.new(@env.ip, @env.port1, "Connecting to server failed")
@env.serve1
client = FlexNBD::FakeSource.new(@env.ip, @env.port1, 'Connecting to server failed')
begin begin
result = client.read_hello result = client.read_hello
assert_equal 'NBDMAGIC', result[:passwd] assert_equal "NBDMAGIC", result[:magic]
assert_equal 0x00420281861253, result[:magic]
assert_equal @env.file1.size, result[:size] assert_equal @env.file1.size, result[:size]
# See src/common/nbdtypes.h for the various flags. At the moment we
# support HAS_FLAGS (1), SEND_FLUSH (4), SEND_FUA (8)
assert_equal (1 | 4 | 8), result[:flags]
assert_equal "\x0" * 124, result[:reserved]
yield client yield client
ensure ensure
begin client.close rescue nil
client.close
rescue StandardError
nil
end
end end
end end
def test_bad_request_magic_receives_error_response def test_bad_request_magic_receives_error_response
connect_to_server do |client| connect_to_server do |client|
# replace REQUEST_MAGIC with all 0s to make it look bad # replace REQUEST_MAGIC with all 0s to make it look bad
client.send_request(0, 'myhandle', 0, 0, "\x00\x00\x00\x00") client.send_request( 0, "myhandle", 0, 0, "\x00\x00\x00\x00" )
rsp = client.read_response rsp = client.read_response
assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic] assert_equal FlexNBD::REPLY_MAGIC, rsp[:magic]
assert_equal 'myhandle', rsp[:handle] assert_equal "myhandle", rsp[:handle]
assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}" assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
# The client should be disconnected now # The client should be disconnected now
assert client.disconnected?, 'Server not disconnected' assert client.disconnected?, "Server not disconnected"
end end
end end
def test_long_write_on_top_of_short_write_is_respected def test_long_write_on_top_of_short_write_is_respected
connect_to_server do |client| connect_to_server do |client|
# Start with a file of all-zeroes. # Start with a file of all-zeroes.
client.write(0, "\x00" * @env.file1.size) client.write( 0, "\x00" * @env.file1.size )
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]
# NBD protocol suggests ENOSPC (28) is returned assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
assert_equal 28, rsp[:error], 'Server sent incorrect response'
# Ensure we're not disconnected by sending a request. We don't care about # Ensure we're not disconnected by sending a request. We don't care about
# whether the reply is good or not, here. # whether the reply is good or not, here.
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
@@ -97,127 +88,23 @@ 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]
# NBD protocol suggests ENOSPC (28) is returned assert rsp[:error] != 0, "Server sent success reply back: #{rsp[:error]}"
assert_equal 28, rsp[:error], 'Server sent incorrect response'
# Ensure we're not disconnected by sending a request. We don't care about # Ensure we're not disconnected by sending a request. We don't care about
# whether the reply is good or not, here. # whether the reply is good or not, here.
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,105 +1,126 @@
# encoding: utf-8
require 'test/unit' require 'test/unit'
require 'environment' require 'environment'
class TestSourceErrorHandling < Test::Unit::TestCase class TestSourceErrorHandling < Test::Unit::TestCase
def setup def setup
@old_env = ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] @old_env = ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS']
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = '4.0' ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = "4.0"
@env = Environment.new @env = Environment.new
@env.writefile1('f' * 4) @env.writefile1( "f" * 4 )
@env.serve1 @env.serve1
end end
def teardown def teardown
@env.nbd1.can_die(0) @env.nbd1.can_die(0)
@env.cleanup @env.cleanup
ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = @old_env ENV['FLEXNBD_MS_REQUEST_LIMIT_SECS'] = @old_env
end end
def expect_term_during_migration def expect_term_during_migration
@env.nbd1.can_die(1, 9) @env.nbd1.can_die(1,9)
end end
def test_failure_to_connect_reported_in_mirror_cmd_response def test_failure_to_connect_reported_in_mirror_cmd_response
stdout, stderr = @env.mirror12_unchecked stdout, stderr = @env.mirror12_unchecked
expect_term_during_migration expect_term_during_migration
assert_match(/failed to connect/, stderr) assert_match( /failed to connect/, stderr )
end end
def test_sigterm_after_hello_quits_with_status_of_1 def test_sigterm_after_hello_quits_with_status_of_1
expect_term_during_migration expect_term_during_migration
run_fake('dest/sigterm_after_hello') run_fake( "dest/sigterm_after_hello" )
end end
def test_destination_hangs_after_connect_reports_error_at_source def test_destination_hangs_after_connect_reports_error_at_source
run_fake('dest/hang_after_connect', run_fake( "dest/hang_after_connect",
err: /Remote server failed to respond/) :err => /Remote server failed to respond/ )
end end
def test_destination_rejects_connection_reports_error_at_source def test_destination_rejects_connection_reports_error_at_source
run_fake('dest/reject_acl', run_fake( "dest/reject_acl",
err: /Mirror was rejected/) :err => /Mirror was rejected/ )
end end
def test_wrong_size_causes_disconnect def test_wrong_size_causes_disconnect
run_fake('dest/hello_wrong_size', run_fake( "dest/hello_wrong_size",
err: /Remote size does not match local size/) :err => /Remote size does not match local size/ )
end end
def test_wrong_magic_causes_disconnect def test_wrong_magic_causes_disconnect
expect_term_during_migration expect_term_during_migration
run_fake('dest/hello_wrong_magic', run_fake( "dest/hello_wrong_magic",
err: /Mirror was rejected/) :err => /Mirror was rejected/ )
end end
def test_disconnect_after_hello_causes_retry def test_disconnect_after_hello_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake('dest/close_after_hello', run_fake( "dest/close_after_hello",
out: /Mirror started/) :out => /Mirror started/ )
end end
def test_write_times_out_causes_retry def test_write_times_out_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake('dest/hang_after_write') run_fake( "dest/hang_after_write" )
end end
def test_rejected_write_causes_retry def test_rejected_write_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake('dest/error_on_write') run_fake( "dest/error_on_write" )
end end
def test_disconnect_before_write_reply_causes_retry def test_disconnect_before_write_reply_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake('dest/close_after_write') run_fake( "dest/close_after_write" )
end end
def test_bad_write_reply_causes_retry def test_bad_write_reply_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake('dest/write_wrong_magic') run_fake( "dest/write_wrong_magic" )
end end
def test_pre_entrust_disconnect_causes_retry def test_pre_entrust_disconnect_causes_retry
expect_term_during_migration expect_term_during_migration
run_fake('dest/close_after_writes') run_fake( "dest/close_after_writes" )
end end
def test_cancel_migration def test_cancel_migration
run_fake('dest/break_after_hello') run_fake( "dest/break_after_hello" )
end end
private private
def run_fake(name, opts = {}) def run_fake(name, opts = {})
@env.run_fake(name, @env.ip, @env.port2, @env.nbd1.ctrl) @env.run_fake( name, @env.ip, @env.port2, @env.nbd1.ctrl )
stdout, stderr = @env.mirror12_unchecked stdout, stderr = @env.mirror12_unchecked
assert_success assert_success
assert_match(opts[:err], stderr) if opts[:err] assert_match( opts[:err], stderr ) if opts[:err]
assert_match(opts[:out], stdout) if opts[:out] assert_match( opts[:out], stdout ) if opts[:out]
[stdout, stderr] return stdout, stderr
end end
def assert_success(msg = nil) def assert_success( msg=nil )
assert @env.fake_reports_success, msg || 'Fake failed' assert @env.fake_reports_success, msg || "Fake failed"
end end
end # class TestSourceErrorHandling end # class TestSourceErrorHandling

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

@@ -9,98 +9,102 @@ require 'tmpdir'
Thread.abort_on_exception = true Thread.abort_on_exception = true
class TestWriteDuringMigration < Test::Unit::TestCase class TestWriteDuringMigration < Test::Unit::TestCase
def setup 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
@write_data = 'foo!' * 2048 # 8K write @size = 20*1024*1024 # 20MB
@write_data = "foo!" * 2048 # 8K write
@source_port = 9990 @source_port = 9990
@dest_port = 9991 @dest_port = 9991
@source_sock = 'src.sock' @source_sock = "src.sock"
@dest_sock = 'dst.sock' @dest_sock = "dst.sock"
@source_file = 'src.file' @source_file = "src.file"
@dest_file = 'dst.file' @dest_file = "dst.file"
end end
def teardown def teardown
[@dst_proc, @src_proc].each do |pid| [@dst_proc, @src_proc].each do |pid|
next unless pid if pid
begin Process.kill( "KILL", pid ) rescue nil
Process.kill('KILL', pid)
rescue StandardError
nil
end end
end end
end end
def debug_arg def debug_arg
ENV['DEBUG'] ? '--verbose' : '' ENV['DEBUG'] ? "--verbose" : ""
end end
def launch_servers def launch_servers
@dst_proc = fork do @dst_proc = fork() {
cmd = "#{@flexnbd} listen -l 127.0.0.1 -p #{@dest_port} -f #{@dest_file} -s #{@dest_sock} #{debug_arg}" cmd = "#{@flexnbd} listen -l 127.0.0.1 -p #{@dest_port} -f #{@dest_file} -s #{@dest_sock} #{debug_arg}"
exec cmd exec cmd
end }
@src_proc = fork do @src_proc = fork() {
cmd = "#{@flexnbd} serve -l 127.0.0.1 -p #{@source_port} -f #{@source_file} -s #{@source_sock} #{debug_arg}" cmd = "#{@flexnbd} serve -l 127.0.0.1 -p #{@source_port} -f #{@source_file} -s #{@source_sock} #{debug_arg}"
exec cmd exec cmd
end }
begin begin
awaiting = nil awaiting = nil
Timeout.timeout(10) do Timeout.timeout(10) do
awaiting = :source awaiting = :source
sleep 0.1 until File.exist?(@source_sock) sleep 0.1 while !File.exists?( @source_sock )
awaiting = :dest awaiting = :dest
sleep 0.1 until File.exist?(@dest_sock) sleep 0.1 while !File.exists?( @dest_sock )
end end
rescue Timeout::Error rescue Timeout::Error
case awaiting case awaiting
when :source when :source
raise "Couldn't get a source socket." fail "Couldn't get a source socket."
when :dest when :dest
raise "Couldn't get a destination socket." fail "Couldn't get a destination socket."
else else
raise "Something went wrong I don't understand." fail "Something went wrong I don't understand."
end end
end end
end end
def make_files
def make_files()
FileUtils.touch(@source_file) FileUtils.touch(@source_file)
File.truncate(@source_file, @size) File.truncate(@source_file, @size)
FileUtils.touch(@dest_file) FileUtils.touch(@dest_file)
File.truncate(@dest_file, @size) File.truncate(@dest_file, @size)
File.open(@source_file, 'wb') { |f| f.write 'a' * @size } File.open(@source_file, "wb"){|f| f.write "a"*@size }
end end
def start_mirror def start_mirror
UNIXSocket.open(@source_sock) do |sock| UNIXSocket.open(@source_sock) {|sock|
sock.write(['mirror', '127.0.0.1', @dest_port.to_s, 'exit'].join("\x0A") + "\x0A\x0A") sock.write(["mirror", "127.0.0.1", @dest_port.to_s, "exit"].join("\x0A") + "\x0A\x0A")
sock.flush sock.flush
rsp = sock.readline rsp = sock.readline
end }
end end
def wait_for_quit
Timeout.timeout(10) do def wait_for_quit()
Timeout.timeout( 10 ) do
start_time = Time.now start_time = Time.now
dst_result = Process.waitpid2(@dst_proc) dst_result = Process::waitpid2(@dst_proc)
src_result = Process.waitpid2(@src_proc) src_result = Process::waitpid2(@src_proc)
end end
end end
def source_writer def source_writer
client = FlexNBD::FakeSource.new('127.0.0.1', @source_port, 'Timed out connecting') client = FlexNBD::FakeSource.new( "127.0.0.1", @source_port, "Timed out connecting" )
offsets = Range.new(0, (@size - @write_data.size) / 4096).to_a offsets = Range.new(0, (@size - @write_data.size) / 4096 ).to_a
loop do loop do
begin begin
client.write(offsets[rand(offsets.size)] * 4096, @write_data) client.write(offsets[rand(offsets.size)] * 4096, @write_data)
rescue StandardError => err rescue => err
# We expect a broken write at some point, so ignore it # We expect a broken write at some point, so ignore it
break break
end end
@@ -111,32 +115,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
@@ -144,21 +148,24 @@ class TestWriteDuringMigration < Test::Unit::TestCase
end end
def test_many_clients_during_migration def test_many_clients_during_migration
Dir.mktmpdir do |tmpdir| Dir.mktmpdir() do |tmpdir|
Dir.chdir(tmpdir) do Dir.chdir( tmpdir ) do
make_files make_files()
launch_servers launch_servers()
src_writers_1 = (1..5).collect { Thread.new { source_writer } } src_writers_1 = (1..5).collect { Thread.new { source_writer } }
start_mirror start_mirror()
src_writers_2 = (1..5).collect { Thread.new { source_writer } } src_writers_2 = (1..5).collect { Thread.new { source_writer } }
wait_for_quit wait_for_quit()
(src_writers_1 + src_writers_2).each(&:join) ( src_writers_1 + src_writers_2 ).each {|t| t.join }
assert_both_sides_identical assert_both_sides_identical
end end
end end end end
end end

View File

@@ -4,178 +4,179 @@
#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)
{
char *lines[] = { "127.0.0.1" };
struct acl *acl = acl_create(1, lines, 0);
fail_unless(1 == acl->len, "Incorrect length."); START_TEST( test_parses_single_line )
fail_if(NULL == acl->entries, "No entries present."); {
char *lines[] = {"127.0.0.1"};
struct acl * acl = acl_create( 1, lines, 0 );
fail_unless( 1 == acl->len, "Incorrect length." );
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, fail_unless(entry->ip.family == e0.family, "entry 0 has wrong family!");
"entry 0 has wrong family!");
entry = &(*acl->entries)[1]; entry = &(*acl->entries)[1];
fail_unless(entry->ip.family == e1.family, fail_unless(entry->ip.family == e1.family, "entry 1 has wrong family!");
"entry 1 has wrong family!");
} }
END_TEST END_TEST
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), fail_unless( acl_includes( acl, &e0 ), "Included address 0 wasn't covered" );
"Included address 0 wasn't covered"); fail_unless( acl_includes( acl, &e1 ), "Included address 1 wasn't covered" );
fail_unless(acl_includes(acl, &e1),
"Included address 1 wasn't covered");
} }
END_TEST END_TEST
START_TEST(test_doesnt_include_other_address)
START_TEST( test_doesnt_include_other_address )
{ {
char *lines[] = { "127.0.0.1" }; char *lines[] = {"127.0.0.1"};
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");
@@ -188,19 +189,14 @@ Suite * acl_suite(void)
tcase_add_test(tc_includes, test_parses_multiple_lines); tcase_add_test(tc_includes, test_parses_multiple_lines);
tcase_add_test(tc_includes, test_includes_single_address); tcase_add_test(tc_includes, test_includes_single_address);
tcase_add_test(tc_includes, tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv4);
test_includes_single_address_when_netmask_specified_ipv4); tcase_add_test(tc_includes, test_includes_single_address_when_netmask_specified_ipv6);
tcase_add_test(tc_includes,
test_includes_single_address_when_netmask_specified_ipv6);
tcase_add_test(tc_includes, tcase_add_test(tc_includes, test_includes_single_address_when_multiple_entries_exist);
test_includes_single_address_when_multiple_entries_exist);
tcase_add_test(tc_includes, test_doesnt_include_other_address); tcase_add_test(tc_includes, test_doesnt_include_other_address);
tcase_add_test(tc_includes, tcase_add_test(tc_includes, test_doesnt_include_other_address_when_netmask_specified);
test_doesnt_include_other_address_when_netmask_specified); tcase_add_test(tc_includes, test_doesnt_include_other_address_when_multiple_entries_exist);
tcase_add_test(tc_includes,
test_doesnt_include_other_address_when_multiple_entries_exist);
tcase_add_test(tc_includes, test_default_deny_rejects); tcase_add_test(tc_includes, test_default_deny_rejects);
tcase_add_test(tc_includes, test_default_accept_rejects); tcase_add_test(tc_includes, test_default_accept_rejects);
@@ -230,3 +226,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -10,7 +10,7 @@
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)); \
@@ -27,7 +27,7 @@ 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)); \
@@ -37,14 +37,14 @@ START_TEST(test_bit_clear)
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");
@@ -64,20 +64,20 @@ START_TEST(test_bit_ranges)
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(longs[i] == (1ULL << i) - 1, fail_unless(
longs[i] == (1ULL<<i)-1,
"longs[%ld] = %lx SHOULD BE %lx", "longs[%ld] = %lx SHOULD BE %lx",
i, longs[i], (1ULL << i) - 1); i, longs[i], (1ULL<<i)-1
);
fail_unless(longs[i + 1] == 0, "bit_set_range overshot at i=%d", fail_unless(longs[i+1] == 0, "bit_set_range overshot at i=%d", i);
i);
} }
for (i = 0; i < 64; i++) { for (i=0; i<64; i++) {
bit_clear_range(buffer, i * 64, i); bit_clear_range(buffer, i*64, i);
fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d", fail_unless(longs[i] == 0, "bit_clear_range didn't work at i=%d", i);
i);
} }
} }
END_TEST END_TEST
@@ -85,25 +85,27 @@ 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, 56,97,22,12,83,1,45,80,85,51,64,40,63,67,75,64,94,81,79,62
81, 79, 62
}; };
memset(buffer, 0, 256); memset(buffer,0,256);
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(run == runs[i], fail_unless(
"run %d should have been %d, was %d", i, runs[i], run); run == runs[i],
"run %d should have been %d, was %d",
i, runs[i], run
);
ptr += runs[i]; ptr += runs[i];
} }
} }
@@ -111,23 +113,23 @@ 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;
@@ -143,315 +145,313 @@ START_TEST(test_bitset)
} }
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, ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_ON ) );
bitset_stream_queued_bytes(map, BITSET_STREAM_ON)); ck_assert_int_eq( 80, bitset_stream_queued_bytes( map, BITSET_STREAM_SET ) );
ck_assert_int_eq(80, ck_assert_int_eq( 82, bitset_stream_queued_bytes( map, BITSET_STREAM_UNSET ) );
bitset_stream_queued_bytes(map, BITSET_STREAM_SET)); ck_assert_int_eq( 64, bitset_stream_queued_bytes( map, BITSET_STREAM_OFF ) );
ck_assert_int_eq(82, bitset_free( map );
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");
@@ -471,8 +471,7 @@ Suite * bitset_suite(void)
tcase_add_test(tc_bitset, test_bitset_set_range); tcase_add_test(tc_bitset, test_bitset_set_range);
tcase_add_test(tc_bitset, test_bitset_clear_range); tcase_add_test(tc_bitset, test_bitset_clear_range);
tcase_add_test(tc_bitset, test_bitset_set_range_doesnt_push_to_stream); tcase_add_test(tc_bitset, test_bitset_set_range_doesnt_push_to_stream);
tcase_add_test(tc_bitset, tcase_add_test(tc_bitset, test_bitset_clear_range_doesnt_push_to_stream);
test_bitset_clear_range_doesnt_push_to_stream);
suite_add_tcase(s, tc_bitset); suite_add_tcase(s, tc_bitset);
@@ -498,3 +497,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -9,79 +9,83 @@
#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); client_signal_stop( c );
int client_serve_request(struct client *); int client_serve_request( struct client *);
fail_unless(1 == client_serve_request(c), "Didn't quit on stop."); fail_unless( 1 == client_serve_request( c ), "Didn't quit on stop." );
close(fds[0]); close( fds[0] );
close(fds[1]); 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");
@@ -95,7 +99,7 @@ Suite * client_suite(void)
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);
@@ -115,3 +119,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -4,25 +4,26 @@
#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;
} }
@@ -38,3 +39,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -3,27 +3,29 @@
#include <check.h> #include <check.h>
START_TEST(test_listening_assigns_sock) START_TEST( test_listening_assigns_sock )
{ {
struct flexnbd *flexnbd = flexnbd_create_listening("127.0.0.1", struct flexnbd * flexnbd = flexnbd_create_listening(
"127.0.0.1",
"4777", "4777",
"fakefile", "fakefile",
"fakesock", "fakesock",
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;
} }
@@ -39,3 +41,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -4,39 +4,38 @@
#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), fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held before lock" );
"Flexthread_mutex is held before lock"); flexthread_mutex_lock( ftm );
flexthread_mutex_lock(ftm); fail_unless( flexthread_mutex_held( ftm ), "Flexthread_mutex is not held inside lock" );
fail_unless(flexthread_mutex_held(ftm), flexthread_mutex_unlock( ftm );
"Flexthread_mutex is not held inside lock"); fail_if( flexthread_mutex_held( ftm ), "Flexthread_mutex is held after unlock" );
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);
@@ -60,3 +59,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -2,116 +2,117 @@
#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); nread = read_until_newline( fds[0], buf, 5 );
ck_assert_int_eq(5, nread); 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); nread = read_until_newline( fds[0], buf, 5 );
ck_assert_int_eq('\0', buf[4]); 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 *tc_read_lines_until_blankline = tcase_create("read_lines_until_blankline");
tcase_create("read_lines_until_blankline");
tcase_add_test(tc_read_until_newline, tcase_add_test(tc_read_until_newline, test_read_until_newline_returns_line_length_plus_null);
test_read_until_newline_returns_line_length_plus_null); tcase_add_test(tc_read_until_newline, test_read_until_newline_inserts_null);
tcase_add_test(tc_read_until_newline, tcase_add_test(tc_read_until_newline, test_read_empty_line_inserts_null);
test_read_until_newline_inserts_null);
tcase_add_test(tc_read_until_newline,
test_read_empty_line_inserts_null);
tcase_add_test(tc_read_until_newline, test_read_eof_returns_err); tcase_add_test(tc_read_until_newline, test_read_eof_returns_err);
tcase_add_test(tc_read_until_newline, test_read_eof_fills_line); tcase_add_test(tc_read_until_newline, test_read_eof_fills_line );
tcase_add_test(tc_read_lines_until_blankline, tcase_add_test(tc_read_lines_until_blankline, test_read_lines_until_blankline );
test_read_lines_until_blankline);
suite_add_tcase(s, tc_read_until_newline); suite_add_tcase(s, tc_read_until_newline);
suite_add_tcase(s, tc_read_lines_until_blankline); suite_add_tcase(s, tc_read_lines_until_blankline);
@@ -130,3 +131,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

View File

@@ -4,65 +4,68 @@
#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_t * 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)) == fail_if( memcmp( &cond_zero, &mbox->emptied_cond, sizeof( cond_zero ) ) == 0 ,
0, "Condition variable not allocated"); "Condition variable not allocated" );
} }
END_TEST END_TEST
START_TEST(test_post_stores_value)
START_TEST( test_post_stores_value )
{ {
struct mbox *mbox = mbox_create(); struct mbox_t * mbox = mbox_create();
void *deadbeef = (void *) 0xDEADBEEF; void * deadbeef = (void *)0xDEADBEEF;
mbox_post(mbox, deadbeef); mbox_post( mbox, deadbeef );
fail_unless(deadbeef == mbox_contents(mbox), fail_unless( deadbeef == mbox_contents( mbox ),
"Contents were not posted"); "Contents were not posted" );
} }
END_TEST END_TEST
void *mbox_receive_runner(void *mbox_uncast)
{
struct mbox *mbox = (struct mbox *) mbox_uncast;
void *contents = NULL;
contents = mbox_receive(mbox); mbox_item_t mbox_receive_runner( void * mbox_uncast )
return contents; {
struct mbox_t * mbox = (struct mbox_t *)mbox_uncast;
void * contents = NULL;
return mbox_receive( mbox );
} }
START_TEST(test_receive_blocks_until_post) START_TEST( test_receive_blocks_until_post )
{ {
struct mbox *mbox = mbox_create(); struct mbox_t * 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 * deadbeef = (void *)0xDEADBEEF;
void *retval = NULL; void * retval =NULL;
usleep(10000); usleep(10000);
fail_unless(EBUSY == pthread_tryjoin_np(receiver, &retval), fail_unless( EBUSY == pthread_tryjoin_np( receiver, &retval ),
"Receiver thread wasn't blocked"); "Receiver thread wasn't blocked");
mbox_post(mbox, deadbeef); mbox_post( mbox, deadbeef );
fail_unless(0 == pthread_join(receiver, &retval), fail_unless( 0 == pthread_join( receiver, &retval ),
"Failed to join the receiver thread"); "Failed to join the receiver thread" );
fail_unless(retval == deadbeef, "Return value was wrong"); fail_unless( retval == deadbeef,
"Return value was wrong" );
} }
END_TEST END_TEST
Suite * mbox_suite(void)
Suite* mbox_suite(void)
{ {
Suite *s = suite_create("mbox"); Suite *s = suite_create("mbox");
TCase *tc_create = tcase_create("create"); TCase *tc_create = tcase_create("create");
@@ -70,8 +73,8 @@ Suite * mbox_suite(void)
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);
@@ -97,3 +100,4 @@ int main(void)
srunner_free(sr); srunner_free(sr);
return (number_failed == 0) ? 0 : 1; return (number_failed == 0) ? 0 : 1;
} }

Some files were not shown because too many files have changed in this diff Show More