Merge
This commit is contained in:
62
Rakefile
62
Rakefile
@@ -1,7 +1,12 @@
|
||||
require 'rake_utils/debian'
|
||||
include RakeUtils::DSL
|
||||
|
||||
DEBUG = true
|
||||
|
||||
SOURCES = %w( flexnbd ioutil readwrite serve util parse control remote )
|
||||
OBJECTS = SOURCES.map { |s| "#{s}.o" }
|
||||
ALL_SOURCES =FileList['src/*']
|
||||
SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ }
|
||||
OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" )
|
||||
|
||||
LIBS = %w( pthread )
|
||||
CCFLAGS = %w( -Wall )
|
||||
LDFLAGS = []
|
||||
@@ -15,53 +20,72 @@ if DEBUG
|
||||
end
|
||||
|
||||
desc "Build flexnbd binary"
|
||||
rule 'default' => 'flexnbd'
|
||||
task :flexnbd => 'build/flexnbd'
|
||||
|
||||
namespace "test" do
|
||||
desc "Run all tests"
|
||||
task 'run' => ["unit", "scenarios"]
|
||||
|
||||
|
||||
desc "Build C tests"
|
||||
task 'build' => TEST_MODULES.map { |n| "tests/check_#{n}" }
|
||||
|
||||
task 'build' => TEST_MODULES.map { |n| "build/tests/check_#{n}" }
|
||||
|
||||
desc "Run C tests"
|
||||
task 'unit' => 'build' do
|
||||
TEST_MODULES.each do |n|
|
||||
ENV['EF_DISABLE_BANNER'] = '1'
|
||||
sh "./tests/check_#{n}"
|
||||
sh "build/tests/check_#{n}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc "Run NBD test scenarios"
|
||||
task 'scenarios' => 'flexnbd' do
|
||||
sh "cd tests; ruby nbd_scenarios"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def gcc_link(target, objects)
|
||||
FileUtils.mkdir_p File.dirname( target )
|
||||
|
||||
sh "gcc #{LDFLAGS.join(' ')} "+
|
||||
LIBS.map { |l| "-l#{l}" }.join(" ")+
|
||||
" -I src" +
|
||||
" -o #{target} "+
|
||||
objects.join(" ")
|
||||
end
|
||||
|
||||
rule 'flexnbd' => OBJECTS do |t|
|
||||
rule 'build/flexnbd' => OBJECTS do |t|
|
||||
gcc_link(t.name, t.sources)
|
||||
end
|
||||
|
||||
rule(/tests\/check_[a-z]+$/ => [ proc { |target| [target+".o", "util.o"] } ]) do |t|
|
||||
gcc_link(t.name, t.sources + [LIBCHECK])
|
||||
TEST_MODULES.each do |m|
|
||||
deps = ["tests/check_#{m}.c", "build/util.o"]
|
||||
maybe_obj_name = "build/#{m}.o"
|
||||
|
||||
deps << maybe_obj_name if OBJECTS.include?( maybe_obj_name )
|
||||
|
||||
file "build/tests/check_#{m}" => deps do |t|
|
||||
gcc_link(t.name, deps + [LIBCHECK])
|
||||
end
|
||||
end
|
||||
|
||||
rule '.o' => '.c' do |t|
|
||||
sh "gcc -I. -c #{CCFLAGS.join(' ')} -o #{t.name} #{t.source} "
|
||||
|
||||
OBJECTS.zip( SOURCES ).each do |o,c|
|
||||
file o => c do |t|
|
||||
FileUtils.mkdir_p File.dirname( o )
|
||||
sh "gcc -Isrc -c #{CCFLAGS.join(' ')} -o #{o} #{c} "
|
||||
end
|
||||
end
|
||||
|
||||
desc "Remove all build targets, binaries and temporary files"
|
||||
rule 'clean' do
|
||||
sh "rm -f *~ flexnbd " + (
|
||||
OBJECTS +
|
||||
TEST_MODULES.map { |n| ["tests/check_#{n}", "tests/check_#{n}.o"] }.flatten
|
||||
).
|
||||
join(" ")
|
||||
task :clean do
|
||||
sh "rm -rf *~ build"
|
||||
end
|
||||
|
||||
namespace :pkg do
|
||||
deb do |t|
|
||||
t.code_files = ALL_SOURCES + ["Rakefile"]
|
||||
t.pkg_name = "flexnbd"
|
||||
t.generate_changelog!
|
||||
end
|
||||
end
|
||||
|
198
debian/changelog
vendored
Normal file
198
debian/changelog
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
flexnbd (0.0.1-33) unstable; urgency=low
|
||||
|
||||
* Added tag 0.0.1 for changeset 27409c2c1313 [r33]
|
||||
|
||||
-- Alex Young <alex@bytemark.co.uk> Wed, 30 May 2012 17:11:10 +0100
|
||||
|
||||
flexnbd (0.0.1-31) unstable; urgency=low
|
||||
|
||||
* Fixed bug where ACL was accidentally deleted when being set from control [r31]
|
||||
|
||||
-- mbloch <mbloch> Wed, 30 May 2012 13:03:02 +0100
|
||||
|
||||
flexnbd (0.0.1-30) unstable; urgency=low
|
||||
|
||||
* Fix the usage message [r30]
|
||||
|
||||
-- nick <nick@bytemark.co.uk> Wed, 30 May 2012 11:28:32 +0100
|
||||
|
||||
flexnbd (0.0.1-29) unstable; urgency=low
|
||||
|
||||
* Fixed race in tests. [r29]
|
||||
|
||||
-- mbloch <mbloch> Tue, 29 May 2012 17:01:54 +0100
|
||||
|
||||
flexnbd (0.0.1-28) unstable; urgency=low
|
||||
|
||||
* Added getopt_long command-line handling. [r28]
|
||||
|
||||
-- Alex Young <alex@bytemark.co.uk> Wed, 30 May 2012 15:19:40 +0100
|
||||
|
||||
flexnbd (0.0.1-27) unstable; urgency=low
|
||||
|
||||
* Added .h files to the Rakefile [r27]
|
||||
|
||||
-- Alex Young <alex@bytemark.co.uk> Wed, 30 May 2012 15:06:06 +0100
|
||||
|
||||
flexnbd (0.0.1-26) unstable; urgency=low
|
||||
|
||||
* Rearranged the project to have src/ and build/ directories [r26]
|
||||
|
||||
-- Alex Young <alex@bytemark.co.uk> Wed, 30 May 2012 09:51:20 +0100
|
||||
|
||||
flexnbd (0.0.1-25) unstable; urgency=low
|
||||
|
||||
* Added .INCOMPLETE hack to aid with marking finished transfers. [r25]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Tue, 29 May 2012 11:24:24 +0100
|
||||
|
||||
flexnbd (0.0.1-24) unstable; urgency=low
|
||||
|
||||
* Added mirror write barrier / final pass stuff & clean exit afterwards. [r24]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Tue, 29 May 2012 04:03:28 +0100
|
||||
|
||||
flexnbd (0.0.1-23) unstable; urgency=low
|
||||
|
||||
* Lots of errors spotted by Alex fixed, added mutexes to accept & I/O, added [r23]
|
||||
|
||||
-- mbloch <mbloch> Tue, 29 May 2012 00:59:12 +0100
|
||||
|
||||
flexnbd (0.0.1-22) unstable; urgency=low
|
||||
|
||||
* Added another write/read test, fixed bugs in splice() usage and IPv6 [r22]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Sun, 27 May 2012 14:40:16 +0100
|
||||
|
||||
flexnbd (0.0.1-21) unstable; urgency=low
|
||||
|
||||
* First few external tests with test/unit, some minor tidying of internal data [r21]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Thu, 24 May 2012 01:39:35 +0100
|
||||
|
||||
flexnbd (0.0.1-20) unstable; urgency=low
|
||||
|
||||
* Pulled some duplicated code out of control.c into [r20]
|
||||
|
||||
-- mbloch <mbloch> Wed, 23 May 2012 14:03:30 +0100
|
||||
|
||||
flexnbd (0.0.1-19) unstable; urgency=low
|
||||
|
||||
* Split control-socket functions into separate file. [r19]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Wed, 23 May 2012 00:42:14 +0100
|
||||
|
||||
flexnbd (0.0.1-18) unstable; urgency=low
|
||||
|
||||
* Fixed mirroring to work (error reporting suspect though). [r18]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Tue, 22 May 2012 00:22:06 +0100
|
||||
|
||||
flexnbd (0.0.1-17) unstable; urgency=low
|
||||
|
||||
* Initial, untested mirror implementation and resolved some type confusion [r17]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Mon, 21 May 2012 04:03:17 +0100
|
||||
|
||||
flexnbd (0.0.1-16) unstable; urgency=low
|
||||
|
||||
* More valgrind-found bugs, extracted open_and_mmap from main code. [r16]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Mon, 21 May 2012 04:00:45 +0100
|
||||
|
||||
flexnbd (0.0.1-15) unstable; urgency=low
|
||||
|
||||
* Fixed some uninitialised variables courtesy of valgrind. [r15]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Mon, 21 May 2012 03:59:43 +0100
|
||||
|
||||
flexnbd (0.0.1-14) unstable; urgency=low
|
||||
|
||||
* Mostly finished bitset tests, fixed test build to include utilities, remove [r14]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Mon, 21 May 2012 03:17:32 +0100
|
||||
|
||||
flexnbd (0.0.1-13) unstable; urgency=low
|
||||
|
||||
* Tweaks to bitset.h, established a C test framework. [r13]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Sun, 20 May 2012 14:38:46 +0100
|
||||
|
||||
flexnbd (0.0.1-12) unstable; urgency=low
|
||||
|
||||
* Fixed segfaulting access control, allowed change to acl via control socket. [r12]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Sat, 19 May 2012 12:48:03 +0100
|
||||
|
||||
flexnbd (0.0.1-11) unstable; urgency=low
|
||||
|
||||
* Added dummy control socket answering / changed serve_accept_loop to use [r11]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Fri, 18 May 2012 23:39:16 +0100
|
||||
|
||||
flexnbd (0.0.1-10) unstable; urgency=low
|
||||
|
||||
* Added control socket, doesn't do anything yet. [r10]
|
||||
|
||||
-- mbloch <mbloch> Fri, 18 May 2012 18:44:34 +0100
|
||||
|
||||
flexnbd (0.0.1-9) unstable; urgency=low
|
||||
|
||||
* Added .hgignore file [r9]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Fri, 18 May 2012 13:25:54 +0100
|
||||
|
||||
flexnbd (0.0.1-8) unstable; urgency=low
|
||||
|
||||
* Stopped NBD writes from committing all-zero blocks to disc (tentative, needs [r8]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Fri, 18 May 2012 13:24:35 +0100
|
||||
|
||||
flexnbd (0.0.1-7) unstable; urgency=low
|
||||
|
||||
* Split code out into separate compilation units (first pass, anyway). [r7]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Thu, 17 May 2012 20:14:22 +0100
|
||||
|
||||
flexnbd (0.0.1-6) unstable; urgency=low
|
||||
|
||||
* Non-functioning commit, half-way through adding sparse bitmap feature. [r6]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Thu, 17 May 2012 11:54:25 +0100
|
||||
|
||||
flexnbd (0.0.1-5) unstable; urgency=low
|
||||
|
||||
* Added write mode. [r5]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Wed, 16 May 2012 11:58:41 +0100
|
||||
|
||||
flexnbd (0.0.1-4) unstable; urgency=low
|
||||
|
||||
* Added working read via splice syscall. [r4]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Wed, 16 May 2012 03:20:09 +0100
|
||||
|
||||
flexnbd (0.0.1-3) unstable; urgency=low
|
||||
|
||||
* Added Rakefile [r3]
|
||||
|
||||
-- mbloch <mbloch> Wed, 16 May 2012 01:27:14 +0100
|
||||
|
||||
flexnbd (0.0.1-2) unstable; urgency=low
|
||||
|
||||
* Silly bug fixes, added ACL support, added parser for read/write requests. [r2]
|
||||
|
||||
-- mbloch <mbloch> Tue, 15 May 2012 18:40:58 +0100
|
||||
|
||||
flexnbd (0.0.1-1) unstable; urgency=low
|
||||
|
||||
* Some debugging, got it to serve. [r1]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Tue, 15 May 2012 03:16:19 +0100
|
||||
|
||||
flexnbd (0.0.1-0) unstable; urgency=low
|
||||
|
||||
* It compiles :) [r0]
|
||||
|
||||
-- Matthew Bloch <matthew@bytemark.co.uk> Tue, 15 May 2012 02:42:03 +0100
|
||||
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
7
|
14
debian/control
vendored
Normal file
14
debian/control
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
Source: flexnbd
|
||||
Section: unknown
|
||||
Priority: extra
|
||||
Maintainer: Alex Young <alex@bytemark.co.uk>
|
||||
Build-Depends: cdbs, debhelper (>= 7), ruby, rake, gcc
|
||||
Standards-Version: 3.8.1
|
||||
Homepage: http://bigv.io/
|
||||
|
||||
Package: flexnbd
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: FlexNBD server
|
||||
An NBD server offering push-mirroring and intelligent sparse file handling
|
||||
|
53
debian/copyright
vendored
Normal file
53
debian/copyright
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
This work was packaged for Debian by:
|
||||
|
||||
Alex Young <alex@bytemark.co.uk> on Wed, 30 May 2012 16:46:58 +0100
|
||||
|
||||
It was downloaded from:
|
||||
|
||||
<url://example.com>
|
||||
|
||||
Upstream Author(s):
|
||||
|
||||
<put author's name and email here>
|
||||
<likewise for another author>
|
||||
|
||||
Copyright:
|
||||
|
||||
<Copyright (C) YYYY Firstname Lastname>
|
||||
<likewise for another author>
|
||||
|
||||
License:
|
||||
|
||||
### SELECT: ###
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
### OR ###
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
##########
|
||||
|
||||
This package 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
||||
|
||||
The Debian packaging is:
|
||||
|
||||
Copyright (C) 2012 Alex Young <alex@bytemark.co.uk>
|
||||
|
||||
you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
# Please also look if there are files or directories which have a
|
||||
# different copyright/license attached and list them here.
|
1
debian/flexnbd.install
vendored
Normal file
1
debian/flexnbd.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build/flexnbd usr/bin
|
14
debian/rules
vendored
Executable file
14
debian/rules
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
rake flexnbd
|
||||
|
||||
override_dh_auto_clean:
|
||||
rake clean
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
222
flexnbd.c
222
flexnbd.c
@@ -1,222 +0,0 @@
|
||||
/* FlexNBD server (C) Bytemark Hosting 2012
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** main() function for parsing and dispatching commands. Each mode has
|
||||
* a corresponding structure which is filled in and passed to a do_ function
|
||||
* elsewhere in the program.
|
||||
*/
|
||||
|
||||
#include "params.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
void syntax()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Syntax: flexnbd serve <listen IP address> <port> <file> \\\n"
|
||||
" [full path to control socket] \\\n"
|
||||
" [allowed connection addresses ...]\n"
|
||||
" flexnbd read <IP address> <port> <offset> <length> > data\n"
|
||||
" flexnbd write <IP address> <port> <offset> <length> < data\n"
|
||||
" flexnbd write <IP address> <port> <offset> <file to write>\n"
|
||||
" flexnbd acl <control socket> [allowed connection addresses ...]\n"
|
||||
" flexnbd mirror <control socket> <dst IP address> <dst port>\n"
|
||||
" [bytes per second] [proxy|nothing|exit]\n"
|
||||
" flexnbd status <control socket>\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void params_serve(
|
||||
struct mode_serve_params* out,
|
||||
char* s_ip_address,
|
||||
char* s_port,
|
||||
char* s_file,
|
||||
int acl_entries,
|
||||
char** s_acl_entries /* first may actually be path to control socket */
|
||||
)
|
||||
{
|
||||
int parsed;
|
||||
|
||||
out->tcp_backlog = 10; /* does this need to be settable? */
|
||||
|
||||
if (s_ip_address == NULL)
|
||||
SERVER_ERROR("No IP address supplied");
|
||||
if (s_port == NULL)
|
||||
SERVER_ERROR("No port number supplied");
|
||||
if (s_file == NULL)
|
||||
SERVER_ERROR("No filename supplied");
|
||||
|
||||
if (parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address) == 0)
|
||||
SERVER_ERROR("Couldn't parse server address '%s' (use 0 if "
|
||||
"you want to bind to all IPs)", s_ip_address);
|
||||
|
||||
out->control_socket_name = NULL;
|
||||
|
||||
if (acl_entries > 0 && s_acl_entries[0][0] == '/') {
|
||||
out->control_socket_name = s_acl_entries[0];
|
||||
s_acl_entries++;
|
||||
acl_entries--;
|
||||
}
|
||||
|
||||
out->acl_entries = acl_entries;
|
||||
parsed = parse_acl(&out->acl, acl_entries, s_acl_entries);
|
||||
if (parsed != acl_entries)
|
||||
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[parsed]);
|
||||
|
||||
out->bind_to.v4.sin_port = atoi(s_port);
|
||||
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)
|
||||
SERVER_ERROR("Port number must be >= 0 and <= 65535");
|
||||
out->bind_to.v4.sin_port = htobe16(out->bind_to.v4.sin_port);
|
||||
|
||||
out->filename = s_file;
|
||||
out->filename_incomplete = xmalloc(strlen(s_file)+11);
|
||||
strcpy(out->filename_incomplete, s_file);
|
||||
strcpy(out->filename_incomplete + strlen(s_file), ".INCOMPLETE");
|
||||
}
|
||||
|
||||
void params_readwrite(
|
||||
int write_not_read,
|
||||
struct mode_readwrite_params* out,
|
||||
char* s_ip_address,
|
||||
char* s_port,
|
||||
char* s_from,
|
||||
char* s_length_or_filename
|
||||
)
|
||||
{
|
||||
if (s_ip_address == NULL)
|
||||
SERVER_ERROR("No IP address supplied");
|
||||
if (s_port == NULL)
|
||||
SERVER_ERROR("No port number supplied");
|
||||
if (s_from == NULL)
|
||||
SERVER_ERROR("No from supplied");
|
||||
if (s_length_or_filename == NULL)
|
||||
SERVER_ERROR("No length supplied");
|
||||
|
||||
if (parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address) == 0)
|
||||
SERVER_ERROR("Couldn't parse connection address '%s'",
|
||||
s_ip_address);
|
||||
|
||||
/* FIXME: duplicated from above */
|
||||
out->connect_to.v4.sin_port = atoi(s_port);
|
||||
if (out->connect_to.v4.sin_port < 0 || out->connect_to.v4.sin_port > 65535)
|
||||
SERVER_ERROR("Port number must be >= 0 and <= 65535");
|
||||
out->connect_to.v4.sin_port = htobe16(out->connect_to.v4.sin_port);
|
||||
|
||||
out->from = atol(s_from);
|
||||
|
||||
if (write_not_read) {
|
||||
if (s_length_or_filename[0]-48 < 10) {
|
||||
out->len = atol(s_length_or_filename);
|
||||
out->data_fd = 0;
|
||||
}
|
||||
else {
|
||||
out->data_fd = open(
|
||||
s_length_or_filename, O_RDONLY);
|
||||
SERVER_ERROR_ON_FAILURE(out->data_fd,
|
||||
"Couldn't open %s", s_length_or_filename);
|
||||
out->len = lseek64(out->data_fd, 0, SEEK_END);
|
||||
SERVER_ERROR_ON_FAILURE(out->len,
|
||||
"Couldn't find length of %s", s_length_or_filename);
|
||||
SERVER_ERROR_ON_FAILURE(
|
||||
lseek64(out->data_fd, 0, SEEK_SET),
|
||||
"Couldn't rewind %s", s_length_or_filename
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out->len = atol(s_length_or_filename);
|
||||
out->data_fd = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void do_serve(struct mode_serve_params* params);
|
||||
void do_read(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);
|
||||
|
||||
union mode_params {
|
||||
struct mode_serve_params serve;
|
||||
struct mode_readwrite_params readwrite;
|
||||
};
|
||||
|
||||
void mode(char* mode, int argc, char **argv)
|
||||
{
|
||||
union mode_params params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
if (strcmp(mode, "serve") == 0) {
|
||||
if (argc >= 3) {
|
||||
params_serve(¶ms.serve, argv[0], argv[1], argv[2], argc-3, argv+3);
|
||||
do_serve(¶ms.serve);
|
||||
}
|
||||
else {
|
||||
syntax();
|
||||
}
|
||||
}
|
||||
else if (strcmp(mode, "read") == 0 ) {
|
||||
if (argc == 4) {
|
||||
params_readwrite(0, ¶ms.readwrite, argv[0], argv[1], argv[2], argv[3]);
|
||||
do_read(¶ms.readwrite);
|
||||
}
|
||||
else {
|
||||
syntax();
|
||||
}
|
||||
}
|
||||
else if (strcmp(mode, "write") == 0 ) {
|
||||
if (argc == 4) {
|
||||
params_readwrite(1, ¶ms.readwrite, argv[0], argv[1], argv[2], argv[3]);
|
||||
do_write(¶ms.readwrite);
|
||||
}
|
||||
else {
|
||||
syntax();
|
||||
}
|
||||
}
|
||||
else if (strcmp(mode, "acl") == 0 || strcmp(mode, "mirror") == 0 || strcmp(mode, "status") == 0) {
|
||||
if (argc >= 1) {
|
||||
do_remote_command(mode, argv[0], argc-1, argv+1);
|
||||
}
|
||||
else {
|
||||
syntax();
|
||||
}
|
||||
}
|
||||
else {
|
||||
syntax();
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||
error_init();
|
||||
|
||||
if (argc < 2)
|
||||
syntax();
|
||||
mode(argv[1], argc-2, argv+2); /* never returns */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
511
src/flexnbd.c
Normal file
511
src/flexnbd.c
Normal file
@@ -0,0 +1,511 @@
|
||||
/* FlexNBD server (C) Bytemark Hosting 2012
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** main() function for parsing and dispatching commands. Each mode has
|
||||
* a corresponding structure which is filled in and passed to a do_ function
|
||||
* elsewhere in the program.
|
||||
*/
|
||||
|
||||
#include "params.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <getopt.h>
|
||||
#include "options.h"
|
||||
|
||||
|
||||
void exit_err( char *msg )
|
||||
{
|
||||
fprintf( stderr, msg );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
void params_serve(
|
||||
struct mode_serve_params* out,
|
||||
char* s_ip_address,
|
||||
char* s_port,
|
||||
char* s_file,
|
||||
char *s_ctrl_sock,
|
||||
int acl_entries,
|
||||
char** s_acl_entries /* first may actually be path to control socket */
|
||||
)
|
||||
{
|
||||
int parsed;
|
||||
|
||||
out->tcp_backlog = 10; /* does this need to be settable? */
|
||||
|
||||
if (s_ip_address == NULL)
|
||||
SERVER_ERROR("No IP address supplied");
|
||||
if (s_port == NULL)
|
||||
SERVER_ERROR("No port number supplied");
|
||||
if (s_file == NULL)
|
||||
SERVER_ERROR("No filename supplied");
|
||||
|
||||
if (parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address) == 0)
|
||||
SERVER_ERROR("Couldn't parse server address '%s' (use 0 if "
|
||||
"you want to bind to all IPs)", s_ip_address);
|
||||
|
||||
/* control_socket_name is optional. It just won't get created if
|
||||
* we pass NULL. */
|
||||
out->control_socket_name = s_ctrl_sock;
|
||||
|
||||
out->acl_entries = acl_entries;
|
||||
parsed = parse_acl(&out->acl, acl_entries, s_acl_entries);
|
||||
if (parsed != acl_entries)
|
||||
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[parsed]);
|
||||
|
||||
out->bind_to.v4.sin_port = atoi(s_port);
|
||||
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)
|
||||
SERVER_ERROR("Port number must be >= 0 and <= 65535");
|
||||
out->bind_to.v4.sin_port = htobe16(out->bind_to.v4.sin_port);
|
||||
|
||||
out->filename = s_file;
|
||||
out->filename_incomplete = xmalloc(strlen(s_file)+11);
|
||||
strcpy(out->filename_incomplete, s_file);
|
||||
strcpy(out->filename_incomplete + strlen(s_file), ".INCOMPLETE");
|
||||
}
|
||||
|
||||
/* TODO: Separate this function.
|
||||
* It should be:
|
||||
* params_read( struct mode_readwrite_params* out,
|
||||
* char *s_ip_address,
|
||||
* char *s_port,
|
||||
* char *s_from,
|
||||
* char *s_length )
|
||||
* params_write( struct mode_readwrite_params* out,
|
||||
* char *s_ip_address,
|
||||
* char *s_port,
|
||||
* char *s_from,
|
||||
* char *s_length,
|
||||
* char *s_filename )
|
||||
*/
|
||||
void params_readwrite(
|
||||
int write_not_read,
|
||||
struct mode_readwrite_params* out,
|
||||
char* s_ip_address,
|
||||
char* s_port,
|
||||
char* s_from,
|
||||
char* s_length_or_filename
|
||||
)
|
||||
{
|
||||
if (s_ip_address == NULL)
|
||||
SERVER_ERROR("No IP address supplied");
|
||||
if (s_port == NULL)
|
||||
SERVER_ERROR("No port number supplied");
|
||||
if (s_from == NULL)
|
||||
SERVER_ERROR("No from supplied");
|
||||
if (s_length_or_filename == NULL)
|
||||
SERVER_ERROR("No length supplied");
|
||||
|
||||
if (parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address) == 0)
|
||||
SERVER_ERROR("Couldn't parse connection address '%s'",
|
||||
s_ip_address);
|
||||
|
||||
/* FIXME: duplicated from above */
|
||||
out->connect_to.v4.sin_port = atoi(s_port);
|
||||
if (out->connect_to.v4.sin_port < 0 || out->connect_to.v4.sin_port > 65535)
|
||||
SERVER_ERROR("Port number must be >= 0 and <= 65535");
|
||||
out->connect_to.v4.sin_port = htobe16(out->connect_to.v4.sin_port);
|
||||
|
||||
out->from = atol(s_from);
|
||||
|
||||
if (write_not_read) {
|
||||
if (s_length_or_filename[0]-48 < 10) {
|
||||
out->len = atol(s_length_or_filename);
|
||||
out->data_fd = 0;
|
||||
}
|
||||
else {
|
||||
out->data_fd = open(
|
||||
s_length_or_filename, O_RDONLY);
|
||||
SERVER_ERROR_ON_FAILURE(out->data_fd,
|
||||
"Couldn't open %s", s_length_or_filename);
|
||||
out->len = lseek64(out->data_fd, 0, SEEK_END);
|
||||
SERVER_ERROR_ON_FAILURE(out->len,
|
||||
"Couldn't find length of %s", s_length_or_filename);
|
||||
SERVER_ERROR_ON_FAILURE(
|
||||
lseek64(out->data_fd, 0, SEEK_SET),
|
||||
"Couldn't rewind %s", s_length_or_filename
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out->len = atol(s_length_or_filename);
|
||||
out->data_fd = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void do_serve(struct mode_serve_params* params);
|
||||
void do_read(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 read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char **sock )
|
||||
{
|
||||
switch(c){
|
||||
case 'h':
|
||||
fprintf(stdout, serve_help_text );
|
||||
exit( 0 );
|
||||
break;
|
||||
case 'l':
|
||||
*ip_addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
*ip_port = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
*file = optarg;
|
||||
break;
|
||||
case 's':
|
||||
*sock = optarg;
|
||||
break;
|
||||
default:
|
||||
exit_err( serve_help_text );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **from, char **size)
|
||||
{
|
||||
switch(c){
|
||||
case 'h':
|
||||
fprintf(stdout, read_help_text );
|
||||
exit( 0 );
|
||||
break;
|
||||
case 'l':
|
||||
*ip_addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
*ip_port = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
*from = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
*size = optarg;
|
||||
break;
|
||||
default:
|
||||
exit_err( read_help_text );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_sock_param( int c, char **sock, char *help_text )
|
||||
{
|
||||
switch(c){
|
||||
case 'h':
|
||||
fprintf( stdout, help_text );
|
||||
exit( 0 );
|
||||
break;
|
||||
case 's':
|
||||
*sock = optarg;
|
||||
break;
|
||||
default:
|
||||
exit_err( help_text );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_acl_param( int c, char **sock )
|
||||
{
|
||||
read_sock_param( c, sock, acl_help_text );
|
||||
}
|
||||
|
||||
void read_mirror_param( int c, char **sock, char **ip_addr, char **ip_port )
|
||||
{
|
||||
switch( c ){
|
||||
case 'h':
|
||||
fprintf( stdout, mirror_help_text );
|
||||
exit( 0 );
|
||||
break;
|
||||
case 's':
|
||||
*sock = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
*ip_addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
*ip_port = optarg;
|
||||
break;
|
||||
default:
|
||||
exit_err( mirror_help_text );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_status_param( int c, char **sock )
|
||||
{
|
||||
read_sock_param( c, sock, status_help_text );
|
||||
}
|
||||
|
||||
int mode_serve( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
char *ip_addr = NULL;
|
||||
char *ip_port = NULL;
|
||||
char *file = NULL;
|
||||
char *sock = NULL;
|
||||
int err = 0;
|
||||
|
||||
struct mode_serve_params serve;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long(argc, argv, serve_short_options, serve_options, NULL);
|
||||
if ( c == -1 ) break;
|
||||
read_serve_param( c, &ip_addr, &ip_port, &file, &sock );
|
||||
}
|
||||
|
||||
if ( NULL == ip_addr || NULL == ip_port ) {
|
||||
err = 1;
|
||||
fprintf( stderr, "both --addr and --port are required.\n" );
|
||||
}
|
||||
if ( NULL == file ) {
|
||||
err = 1;
|
||||
fprintf( stderr, "--file is required\n" );
|
||||
}
|
||||
if ( err ) { exit_err( serve_help_text ); }
|
||||
|
||||
memset( &serve, 0, sizeof( serve ) );
|
||||
params_serve( &serve, ip_addr, ip_port, file, sock, argc - optind, argv + optind );
|
||||
do_serve( &serve );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode_read( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
char *ip_addr = NULL;
|
||||
char *ip_port = NULL;
|
||||
char *from = NULL;
|
||||
char *size = NULL;
|
||||
int err = 0;
|
||||
|
||||
struct mode_readwrite_params readwrite;
|
||||
|
||||
while (1){
|
||||
c = getopt_long(argc, argv, read_short_options, read_options, NULL);
|
||||
if ( c == -1 ) break;
|
||||
read_readwrite_param( c, &ip_addr, &ip_port, &from, &size );
|
||||
}
|
||||
|
||||
if ( NULL == ip_addr || NULL == ip_port ) {
|
||||
err = 1;
|
||||
fprintf( stderr, "both --addr and --port are required.\n" );
|
||||
}
|
||||
if ( NULL == from || NULL == size ) {
|
||||
err = 1;
|
||||
fprintf( stderr, "both --from and --size are required.\n" );
|
||||
}
|
||||
if ( err ) { exit_err( read_help_text ); }
|
||||
|
||||
memset( &readwrite, 0, sizeof( readwrite ) );
|
||||
params_readwrite( 0, &readwrite, ip_addr, ip_port, from, size );
|
||||
do_read( &readwrite );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode_write( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
char *ip_addr = NULL;
|
||||
char *ip_port = NULL;
|
||||
char *from = NULL;
|
||||
char *size = NULL;
|
||||
int err = 0;
|
||||
|
||||
struct mode_readwrite_params readwrite;
|
||||
|
||||
while (1){
|
||||
c = getopt_long(argc, argv, write_short_options, write_options, NULL);
|
||||
if ( c == -1 ) break;
|
||||
read_readwrite_param( c, &ip_addr, &ip_port, &from, &size );
|
||||
}
|
||||
|
||||
if ( NULL == ip_addr || NULL == ip_port ) {
|
||||
err = 1;
|
||||
fprintf( stderr, "both --addr and --port are required.\n" );
|
||||
}
|
||||
if ( NULL == from || NULL == size ) {
|
||||
err = 1;
|
||||
fprintf( stderr, "both --from and --size are required.\n" );
|
||||
}
|
||||
if ( err ) { exit_err( write_help_text ); }
|
||||
|
||||
memset( &readwrite, 0, sizeof( readwrite ) );
|
||||
params_readwrite( 1, &readwrite, ip_addr, ip_port, from, size );
|
||||
do_write( &readwrite );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode_acl( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
char *sock = NULL;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long( argc, argv, acl_short_options, acl_options, NULL );
|
||||
if ( c == -1 ) break;
|
||||
read_acl_param( c, &sock );
|
||||
}
|
||||
|
||||
if ( NULL == sock ){
|
||||
fprintf( stderr, "--sock is required.\n" );
|
||||
exit_err( acl_help_text );
|
||||
}
|
||||
|
||||
/* Don't use the CMD_ACL macro here, "acl" is the remote command
|
||||
* name, not the cli option
|
||||
*/
|
||||
do_remote_command( "acl", sock, argc - optind, argv + optind );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mode_mirror( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
char *sock = NULL;
|
||||
char *remote_argv[3] = {0};
|
||||
int err = 0;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long( argc, argv, mirror_short_options, mirror_options, NULL);
|
||||
if ( -1 == c ) break;
|
||||
read_mirror_param( c, &sock, &remote_argv[0], &remote_argv[1] );
|
||||
}
|
||||
|
||||
if ( NULL == sock ){
|
||||
fprintf( stderr, "--sock is required.\n" );
|
||||
err = 1;
|
||||
}
|
||||
if ( NULL == remote_argv[0] || NULL == remote_argv[1] ) {
|
||||
fprintf( stderr, "both --addr and --port are required.\n");
|
||||
err = 1;
|
||||
}
|
||||
if ( err ) { exit_err( mirror_help_text ); }
|
||||
|
||||
do_remote_command( "mirror", sock, 2, remote_argv );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mode_status( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
char *sock = NULL;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long( argc, argv, status_short_options, status_options, NULL );
|
||||
if ( -1 == c ) break;
|
||||
read_status_param( c, &sock );
|
||||
}
|
||||
|
||||
if ( NULL == sock ){
|
||||
fprintf( stderr, "--sock is required.\n" );
|
||||
exit_err( acl_help_text );
|
||||
}
|
||||
|
||||
do_remote_command( "status", sock, argc - optind, argv + optind );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mode_help( int argc, char *argv[] )
|
||||
{
|
||||
char *cmd;
|
||||
char *help_text;
|
||||
|
||||
if ( argc < 1 ){
|
||||
help_text = help_help_text;
|
||||
} else {
|
||||
cmd = argv[0];
|
||||
if (IS_CMD( CMD_SERVE, cmd ) ) {
|
||||
help_text = serve_help_text;
|
||||
} else if ( IS_CMD( CMD_READ, cmd ) ) {
|
||||
help_text = read_help_text;
|
||||
} else if ( IS_CMD( CMD_WRITE, cmd ) ) {
|
||||
help_text = write_help_text;
|
||||
} else if ( IS_CMD( CMD_ACL, cmd ) ) {
|
||||
help_text = acl_help_text;
|
||||
} else if ( IS_CMD( CMD_MIRROR, cmd ) ) {
|
||||
help_text = mirror_help_text;
|
||||
} else if ( IS_CMD( CMD_STATUS, cmd ) ) {
|
||||
help_text = status_help_text;
|
||||
} else { exit_err( help_help_text ); }
|
||||
}
|
||||
|
||||
fprintf( stdout, help_text );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void mode(char* mode, int argc, char **argv)
|
||||
{
|
||||
if ( IS_CMD( CMD_SERVE, mode ) ) {
|
||||
mode_serve( argc, argv );
|
||||
}
|
||||
else if ( IS_CMD( CMD_READ, mode ) ) {
|
||||
mode_read( argc, argv );
|
||||
}
|
||||
else if ( IS_CMD( CMD_WRITE, mode ) ) {
|
||||
mode_write( argc, argv );
|
||||
}
|
||||
else if ( IS_CMD( CMD_ACL, mode ) ) {
|
||||
mode_acl( argc, argv );
|
||||
}
|
||||
else if ( IS_CMD( CMD_MIRROR, mode ) ) {
|
||||
mode_mirror( argc, argv );
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||
error_init();
|
||||
|
||||
if (argc < 2) {
|
||||
exit_err( help_help_text );
|
||||
}
|
||||
mode(argv[1], argc-1, argv+1); /* never returns */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
134
src/options.h
Normal file
134
src/options.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#define GETOPT_ARG(x,s) {(x), 1, 0, (s)}
|
||||
#define GETOPT_FLAG(x,v) {(x), 0, 0, (v)}
|
||||
|
||||
#define OPT_HELP "help"
|
||||
#define OPT_ADDR "addr"
|
||||
#define OPT_PORT "port"
|
||||
#define OPT_FILE "file"
|
||||
#define OPT_SOCK "sock"
|
||||
#define OPT_FROM "from"
|
||||
#define OPT_SIZE "size"
|
||||
|
||||
#define CMD_SERVE "serve"
|
||||
#define CMD_READ "read"
|
||||
#define CMD_WRITE "write"
|
||||
#define CMD_ACL "acl"
|
||||
#define CMD_MIRROR "mirror"
|
||||
#define CMD_STATUS "status"
|
||||
#define CMD_HELP "help"
|
||||
#define LEN_CMD_MAX 6
|
||||
|
||||
#define PATH_LEN_MAX 1024
|
||||
#define ADDR_LEN_MAX 64
|
||||
|
||||
|
||||
#define IS_CMD(x,c) (strncmp((x),(c),(LEN_CMD_MAX)) == 0)
|
||||
|
||||
#define GETOPT_HELP GETOPT_FLAG( OPT_HELP, 'h' )
|
||||
#define GETOPT_ADDR GETOPT_ARG( OPT_ADDR, 'l' )
|
||||
#define GETOPT_PORT GETOPT_ARG( OPT_PORT, 'p' )
|
||||
#define GETOPT_FILE GETOPT_ARG( OPT_FILE, 'f' )
|
||||
#define GETOPT_SOCK GETOPT_ARG( OPT_SOCK, 's' )
|
||||
#define GETOPT_FROM GETOPT_ARG( OPT_FROM, 'F' )
|
||||
#define GETOPT_SIZE GETOPT_ARG( OPT_SIZE, 'S' )
|
||||
|
||||
static struct option serve_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_ADDR,
|
||||
GETOPT_PORT,
|
||||
GETOPT_FILE,
|
||||
GETOPT_SOCK,
|
||||
{0}
|
||||
};
|
||||
static char serve_short_options[] = "hl:p:f:s:";
|
||||
static char serve_help_text[] =
|
||||
"Usage: flexnbd " CMD_SERVE " <options> [<acl address>*]\n\n"
|
||||
"Serve FILE from ADDR:PORT, with an optional control socket at SOCK.\n\n"
|
||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address 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_SOCK ",-s <SOCK>\tPath to the control socket to open.\n";
|
||||
|
||||
static struct option read_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_ADDR,
|
||||
GETOPT_PORT,
|
||||
GETOPT_FROM,
|
||||
GETOPT_SIZE,
|
||||
{0}
|
||||
};
|
||||
static char read_short_options[] = "hl:p:F:S:";
|
||||
static char read_help_text[] =
|
||||
"Usage: flexnbd " CMD_READ " <options>\n\n"
|
||||
"Read SIZE bytes from a server at ADDR:PORT to stdout, starting at OFFSET.\n\n"
|
||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address 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_SIZE ",-S <SIZE>\tBytes to read.\n";
|
||||
|
||||
|
||||
static struct option *write_options = read_options;
|
||||
static char *write_short_options = read_short_options;
|
||||
static char write_help_text[] =
|
||||
"Usage: flexnbd " CMD_WRITE" <options>\n\n"
|
||||
"Write SIZE bytes from stdin to a server at ADDR:PORT, starting at OFFSET.\n\n"
|
||||
"\t--" OPT_HELP ",-h\tThis text.\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_FROM ",-F <OFFSET>\tByte offset to write from.\n"
|
||||
"\t--" OPT_SIZE ",-S <SIZE>\tBytes to write.\n";
|
||||
|
||||
struct option acl_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_SOCK,
|
||||
{0}
|
||||
};
|
||||
static char acl_short_options[] = "hs:";
|
||||
static char acl_help_text[] =
|
||||
"Usage: flexnbd " CMD_ACL " <options> [<acl address>+]\n\n"
|
||||
"Set the access control list for a server with control socket SOCK.\n\n"
|
||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n";
|
||||
|
||||
struct option mirror_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_SOCK,
|
||||
GETOPT_ADDR,
|
||||
GETOPT_PORT,
|
||||
{0}
|
||||
};
|
||||
static char mirror_short_options[] = "hs:l:p:";
|
||||
static char mirror_help_text[] =
|
||||
"Usage: flexnbd " CMD_MIRROR " <options>\n\n"
|
||||
"Start mirroring from the server with control socket SOCK to one at ADDR:PORT.\n\n"
|
||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n"
|
||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address to mirror to.\n"
|
||||
"\t--" OPT_PORT ",-p <PORT>\tThe port to mirror to.\n";
|
||||
|
||||
|
||||
struct option status_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_SOCK,
|
||||
{0}
|
||||
};
|
||||
static char status_short_options[] = "hs:";
|
||||
static char status_help_text[] =
|
||||
"Usage: flexnbd " CMD_STATUS " <options>\n\n"
|
||||
"Get the status for a server with control socket SOCK.\n\n"
|
||||
"\t--" OPT_HELP ",-h\tThis text.\n"
|
||||
"\t--" OPT_SOCK ",-s <SOCK>\tPath to the control socket.\n";
|
||||
|
||||
static char help_help_text[] =
|
||||
"Usage: flexnbd <cmd> [cmd options]\n\n"
|
||||
"Commands:\n"
|
||||
"\tflexnbd serve\n"
|
||||
"\tflexnbd read\n"
|
||||
"\tflexnbd write\n"
|
||||
"\tflexnbd acl\n"
|
||||
"\tflexnbd mirror\n"
|
||||
"\tflexnbd status\n"
|
||||
"\tflexnbd help\n\n"
|
||||
"See flexnbd help <cmd> for further info\n";
|
@@ -16,7 +16,13 @@ class FlexNBD
|
||||
def serve(ip, port, file, *acl)
|
||||
File.unlink(ctrl) if File.exists?(ctrl)
|
||||
@pid = fork do
|
||||
exec("#{@bin} serve #{ip} #{port} #{file} #{ctrl} #{acl.join(' ')}")
|
||||
cmd ="#{@bin} serve "\
|
||||
"--addr #{ip} "\
|
||||
"--port #{port} "\
|
||||
"--file #{file} "\
|
||||
"--sock #{ctrl} "\
|
||||
"#{acl.join(' ')}"
|
||||
exec(cmd)
|
||||
end
|
||||
sleep 0.1 until File.socket?(ctrl)
|
||||
end
|
||||
@@ -27,14 +33,22 @@ class FlexNBD
|
||||
end
|
||||
|
||||
def read(offset, length)
|
||||
IO.popen("#{@bin} read #{ip} #{port} #{offset} #{length}","r") do |fh|
|
||||
IO.popen("#{@bin} read "\
|
||||
"--addr #{ip} "\
|
||||
"--port #{port} "\
|
||||
"--from #{offset} "\
|
||||
"--size #{length}","r") do |fh|
|
||||
return fh.read
|
||||
end
|
||||
raise "read failed" unless $?.success?
|
||||
end
|
||||
|
||||
def write(offset, data)
|
||||
IO.popen("#{@bin} write #{ip} #{port} #{offset} #{data.length}","w") do |fh|
|
||||
IO.popen("#{@bin} write "\
|
||||
"--addr #{ip} "\
|
||||
"--port #{port} "\
|
||||
"--from #{offset} "\
|
||||
"--size #{data.length}","w") do |fh|
|
||||
fh.write(data)
|
||||
end
|
||||
raise "write failed" unless $?.success?
|
||||
|
@@ -13,39 +13,39 @@ class NBDScenarios < Test::Unit::TestCase
|
||||
@available_ports = [*40000..41000] - listening_ports
|
||||
@port1 = @available_ports.shift
|
||||
@port2 = @available_ports.shift
|
||||
@nbd1 = FlexNBD.new("../flexnbd", @ip, @port1)
|
||||
end
|
||||
|
||||
@nbd1 = FlexNBD.new("../build/flexnbd", @ip, @port1)
|
||||
end
|
||||
|
||||
def teardown
|
||||
@nbd1.kill rescue nil
|
||||
[@filename1, @filename2].each do |f|
|
||||
File.unlink(f) if File.exists?(f)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_read1
|
||||
writefile1("f"*64)
|
||||
serve1
|
||||
|
||||
|
||||
[0, 12, 63].each do |num|
|
||||
|
||||
|
||||
assert_equal(
|
||||
@nbd1.read(num*@blocksize, @blocksize),
|
||||
@nbd1.read(num*@blocksize, @blocksize),
|
||||
@file1.read(num*@blocksize, @blocksize)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
[124, 1200, 10028, 25488].each do |num|
|
||||
assert_equal(@nbd1.read(num, 4), @file1.read(num, 4))
|
||||
end
|
||||
end
|
||||
|
||||
# Check that we're not
|
||||
|
||||
# Check that we're not
|
||||
#
|
||||
def test_writeread1
|
||||
writefile1("0"*64)
|
||||
serve1
|
||||
|
||||
|
||||
[0, 12, 63].each do |num|
|
||||
data = "X"*@blocksize
|
||||
@nbd1.write(num*@blocksize, data)
|
||||
@@ -53,14 +53,14 @@ class NBDScenarios < Test::Unit::TestCase
|
||||
assert_equal(data, @nbd1.read(num*@blocksize, data.size))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Check that we're not overstepping or understepping where our writes end
|
||||
# up.
|
||||
#
|
||||
def test_writeread2
|
||||
writefile1("0"*1024)
|
||||
serve1
|
||||
|
||||
|
||||
d0 = "\0"*@blocksize
|
||||
d1 = "X"*@blocksize
|
||||
(0..63).each do |num|
|
||||
@@ -70,16 +70,16 @@ class NBDScenarios < Test::Unit::TestCase
|
||||
assert_equal(d0, @nbd1.read(((2*num)+1)*@blocksize, d0.size))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def serve1(*acl)
|
||||
@nbd1.serve(@ip, @port1, @filename1, *acl)
|
||||
end
|
||||
|
||||
|
||||
def writefile1(data)
|
||||
@file1 = TestFileWriter.new(@filename1, @blocksize).write(data)
|
||||
end
|
||||
|
||||
|
||||
def listening_ports
|
||||
`netstat -ltn`.
|
||||
split("\n").
|
||||
|
Reference in New Issue
Block a user