From 78329585221254c1102cc3cd9141319a2ace13f4 Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 09:51:20 +0100 Subject: [PATCH 1/7] Rearranged the project to have src/ and build/ directories This simplifies keeping everything clean. --- .hgignore | 2 ++ Rakefile | 45 ++++++++++++++++++++++------------ bitset.h => src/bitset.h | 0 control.c => src/control.c | 0 control.h => src/control.h | 0 flexnbd.c => src/flexnbd.c | 0 ioutil.c => src/ioutil.c | 0 ioutil.h => src/ioutil.h | 0 nbdtypes.h => src/nbdtypes.h | 0 params.h => src/params.h | 0 parse.c => src/parse.c | 0 parse.h => src/parse.h | 0 readwrite.c => src/readwrite.c | 0 readwrite.h => src/readwrite.h | 0 remote.c => src/remote.c | 0 serve.c => src/serve.c | 0 util.c => src/util.c | 0 util.h => src/util.h | 0 tests/nbd_scenarios | 32 ++++++++++++------------ 19 files changed, 48 insertions(+), 31 deletions(-) rename bitset.h => src/bitset.h (100%) rename control.c => src/control.c (100%) rename control.h => src/control.h (100%) rename flexnbd.c => src/flexnbd.c (100%) rename ioutil.c => src/ioutil.c (100%) rename ioutil.h => src/ioutil.h (100%) rename nbdtypes.h => src/nbdtypes.h (100%) rename params.h => src/params.h (100%) rename parse.c => src/parse.c (100%) rename parse.h => src/parse.h (100%) rename readwrite.c => src/readwrite.c (100%) rename readwrite.h => src/readwrite.h (100%) rename remote.c => src/remote.c (100%) rename serve.c => src/serve.c (100%) rename util.c => src/util.c (100%) rename util.h => src/util.h (100%) diff --git a/.hgignore b/.hgignore index 9cec915..1b4b5a4 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,5 @@ .o$ ~$ ^flexnbd$ +^build/ +^pkg/ diff --git a/Rakefile b/Rakefile index a8d47cc..89f9cf1 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,8 @@ DEBUG = true -SOURCES = %w( flexnbd ioutil readwrite serve util parse control remote ) -OBJECTS = SOURCES.map { |s| "#{s}.o" } +SOURCES = FileList['src/*.c'] +OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" ) + LIBS = %w( pthread ) CCFLAGS = %w( -Wall ) LDFLAGS = [] @@ -15,52 +16,66 @@ 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 + + sh "rm -rf *~ build " + ( TEST_MODULES.map { |n| ["tests/check_#{n}", "tests/check_#{n}.o"] }.flatten ). join(" ") diff --git a/bitset.h b/src/bitset.h similarity index 100% rename from bitset.h rename to src/bitset.h diff --git a/control.c b/src/control.c similarity index 100% rename from control.c rename to src/control.c diff --git a/control.h b/src/control.h similarity index 100% rename from control.h rename to src/control.h diff --git a/flexnbd.c b/src/flexnbd.c similarity index 100% rename from flexnbd.c rename to src/flexnbd.c diff --git a/ioutil.c b/src/ioutil.c similarity index 100% rename from ioutil.c rename to src/ioutil.c diff --git a/ioutil.h b/src/ioutil.h similarity index 100% rename from ioutil.h rename to src/ioutil.h diff --git a/nbdtypes.h b/src/nbdtypes.h similarity index 100% rename from nbdtypes.h rename to src/nbdtypes.h diff --git a/params.h b/src/params.h similarity index 100% rename from params.h rename to src/params.h diff --git a/parse.c b/src/parse.c similarity index 100% rename from parse.c rename to src/parse.c diff --git a/parse.h b/src/parse.h similarity index 100% rename from parse.h rename to src/parse.h diff --git a/readwrite.c b/src/readwrite.c similarity index 100% rename from readwrite.c rename to src/readwrite.c diff --git a/readwrite.h b/src/readwrite.h similarity index 100% rename from readwrite.h rename to src/readwrite.h diff --git a/remote.c b/src/remote.c similarity index 100% rename from remote.c rename to src/remote.c diff --git a/serve.c b/src/serve.c similarity index 100% rename from serve.c rename to src/serve.c diff --git a/util.c b/src/util.c similarity index 100% rename from util.c rename to src/util.c diff --git a/util.h b/src/util.h similarity index 100% rename from util.h rename to src/util.h diff --git a/tests/nbd_scenarios b/tests/nbd_scenarios index faa4767..7392112 100644 --- a/tests/nbd_scenarios +++ b/tests/nbd_scenarios @@ -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"). From a01621dc1e84ed4fa5ebeb438fc2a5dedd9de569 Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 15:06:06 +0100 Subject: [PATCH 2/7] Added .h files to the Rakefile --- Rakefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 89f9cf1..beebc72 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,7 @@ DEBUG = true -SOURCES = FileList['src/*.c'] +ALL_SOURCES =FileList['src/*'] +SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ } OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" ) LIBS = %w( pthread ) From 0c62e66a70769427f5b42665d05a17b39ea53117 Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 15:19:40 +0100 Subject: [PATCH 3/7] Added getopt_long command-line handling. All parameters now have switches. The one gotcha is the parameter which was overloaded - s_length_or_filename to params_readwrite - is only pretending to be a length at the moment. If you pass a filename it'll still work, but the help messages don't mention that. I'll split the parameter into two in a later commit. --- src/flexnbd.c | 449 ++++++++++++++++++++++++++++++++++++++--------- src/options.h | 126 +++++++++++++ tests/flexnbd.rb | 20 ++- 3 files changed, 512 insertions(+), 83 deletions(-) create mode 100644 src/options.h diff --git a/src/flexnbd.c b/src/flexnbd.c index 56c804c..e255cc5 100644 --- a/src/flexnbd.c +++ b/src/flexnbd.c @@ -32,75 +32,79 @@ #include #include -void syntax() +#include +#include "options.h" + + +void exit_err( char *msg ) { - fprintf(stderr, - "Syntax: flexnbd serve \\\n" - " [full path to control socket] \\\n" - " [allowed connection addresses ...]\n" - " flexnbd read > data\n" - " flexnbd write < data\n" - " flexnbd write \n" - " flexnbd acl [allowed connection addresses ...]\n" - " flexnbd mirror \n" - " [bytes per second] [proxy|nothing|exit]" - " flexnbd status \n" - ); - exit(1); + fprintf( stderr, msg ); + exit( 1 ); } void params_serve( - struct mode_serve_params* out, - char* s_ip_address, - char* s_port, + 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 */ + char** s_acl_entries ) { 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--; - } - + + /* 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_ip_address, char* s_port, char* s_from, char* s_length_or_filename @@ -114,11 +118,11 @@ void params_readwrite( 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'", + 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) @@ -126,7 +130,7 @@ void params_readwrite( 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); @@ -157,66 +161,351 @@ 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 { +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) { - 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(); - } + if ( IS_CMD( CMD_SERVE, mode ) ) { + mode_serve( argc, argv ); } - 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 ( IS_CMD( CMD_READ, mode ) ) { + mode_read( argc, argv ); } - 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 ( IS_CMD( CMD_WRITE, mode ) ) { + mode_write( argc, argv ); } - 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 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 { - syntax(); + 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) - syntax(); - mode(argv[1], argc-2, argv+2); /* never returns */ - + error_init(); + + if (argc < 2) { + exit_err( help_help_text ); + } + mode(argv[1], argc-1, argv+1); /* never returns */ + return 0; } diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..8c50345 --- /dev/null +++ b/src/options.h @@ -0,0 +1,126 @@ +#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) + +static struct option serve_options[] = { + GETOPT_FLAG( OPT_HELP, 'h' ), + GETOPT_ARG( OPT_ADDR, 'l' ), + GETOPT_ARG( OPT_PORT, 'p' ), + GETOPT_ARG( OPT_FILE, 'f' ), + GETOPT_ARG( OPT_SOCK, 's' ), + {0} +}; +static char serve_short_options[] = "hl:p:f:s:"; +static char serve_help_text[] = + "Usage: flexnbd " CMD_SERVE " [*]\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 \tThe address to serve on.\n" + "\t--" OPT_PORT ",-p \tThe port to serve on.\n" + "\t--" OPT_FILE ",-f \tThe file to serve.\n" + "\t--" OPT_SOCK ",-s \tPath to the control socket to open.\n"; + +static struct option read_options[] = { + GETOPT_FLAG( OPT_HELP, 'h' ), + GETOPT_ARG( OPT_ADDR, 'l' ), + GETOPT_ARG( OPT_PORT, 'p' ), + GETOPT_ARG( OPT_FROM, 'F' ), + GETOPT_ARG( OPT_SIZE, 'S' ), + {0} +}; +static char read_short_options[] = "hl:p:F:S:"; +static char read_help_text[] = + "Usage: flexnbd " CMD_READ " \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 \tThe address to read from.\n" + "\t--" OPT_PORT ",-p \tThe port to read from.\n" + "\t--" OPT_FROM ",-F \tByte offset to read from.\n" + "\t--" OPT_SIZE ",-S \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" \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 \tThe address to write to.\n" + "\t--" OPT_PORT ",-p \tThe port to write to.\n" + "\t--" OPT_FROM ",-F \tByte offset to write from.\n" + "\t--" OPT_SIZE ",-S \tBytes to write.\n"; + +struct option acl_options[] = { + GETOPT_FLAG( OPT_HELP, 'h' ), + GETOPT_ARG( OPT_SOCK, 's' ), + {0} +}; +static char acl_short_options[] = "hs:"; +static char acl_help_text[] = + "Usage: flexnbd " CMD_ACL " [+]\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 \tPath to the control socket.\n"; + +struct option mirror_options[] = { + GETOPT_FLAG( OPT_HELP, 'h' ), + GETOPT_ARG( OPT_SOCK, 's' ), + GETOPT_ARG( OPT_ADDR, 'l' ), + GETOPT_ARG( OPT_PORT, 'p' ), + {0} +}; +static char mirror_short_options[] = "hs:l:p:"; +static char mirror_help_text[] = + "Usage: flexnbd " CMD_MIRROR " \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 \tPath to the control socket.\n" + "\t--" OPT_ADDR ",-l \tThe address to mirror to.\n" + "\t--" OPT_PORT ",-p \tThe port to mirror to.\n"; + + +struct option status_options[] = { + GETOPT_FLAG( OPT_HELP, 'h' ), + GETOPT_ARG( OPT_SOCK, 's' ), + {0} +}; +static char status_short_options[] = "hs:"; +static char status_help_text[] = + "Usage: flexnbd " CMD_STATUS " \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 \tPath to the control socket.\n"; + +static char help_help_text[] = + "Usage: flexnbd [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 for further info\n"; diff --git a/tests/flexnbd.rb b/tests/flexnbd.rb index 58a014d..aac0d6e 100644 --- a/tests/flexnbd.rb +++ b/tests/flexnbd.rb @@ -15,7 +15,13 @@ class FlexNBD def serve(ip, port, file, *acl) @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 end @@ -25,14 +31,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? From fe08084144aec38924f2bdabce6448ba80742503 Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 17:11:10 +0100 Subject: [PATCH 4/7] Added tag 0.0.1 for changeset 27409c2c1313 From 15c3133458f107e1ab1403cbfac0584a0f4d9077 Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 17:33:38 +0100 Subject: [PATCH 5/7] Simplify option definition with som handy macros --- src/options.h | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/options.h b/src/options.h index 8c50345..6dee399 100644 --- a/src/options.h +++ b/src/options.h @@ -24,12 +24,20 @@ #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_FLAG( OPT_HELP, 'h' ), - GETOPT_ARG( OPT_ADDR, 'l' ), - GETOPT_ARG( OPT_PORT, 'p' ), - GETOPT_ARG( OPT_FILE, 'f' ), - GETOPT_ARG( OPT_SOCK, 's' ), + GETOPT_HELP, + GETOPT_ADDR, + GETOPT_PORT, + GETOPT_FILE, + GETOPT_SOCK, {0} }; static char serve_short_options[] = "hl:p:f:s:"; @@ -43,12 +51,12 @@ static char serve_help_text[] = "\t--" OPT_SOCK ",-s \tPath to the control socket to open.\n"; static struct option read_options[] = { - GETOPT_FLAG( OPT_HELP, 'h' ), - GETOPT_ARG( OPT_ADDR, 'l' ), - GETOPT_ARG( OPT_PORT, 'p' ), - GETOPT_ARG( OPT_FROM, 'F' ), - GETOPT_ARG( OPT_SIZE, 'S' ), - {0} + 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[] = @@ -73,8 +81,8 @@ static char write_help_text[] = "\t--" OPT_SIZE ",-S \tBytes to write.\n"; struct option acl_options[] = { - GETOPT_FLAG( OPT_HELP, 'h' ), - GETOPT_ARG( OPT_SOCK, 's' ), + GETOPT_HELP, + GETOPT_SOCK, {0} }; static char acl_short_options[] = "hs:"; @@ -85,10 +93,10 @@ static char acl_help_text[] = "\t--" OPT_SOCK ",-s \tPath to the control socket.\n"; struct option mirror_options[] = { - GETOPT_FLAG( OPT_HELP, 'h' ), - GETOPT_ARG( OPT_SOCK, 's' ), - GETOPT_ARG( OPT_ADDR, 'l' ), - GETOPT_ARG( OPT_PORT, 'p' ), + GETOPT_HELP, + GETOPT_SOCK, + GETOPT_ADDR, + GETOPT_PORT, {0} }; static char mirror_short_options[] = "hs:l:p:"; @@ -102,8 +110,8 @@ static char mirror_help_text[] = struct option status_options[] = { - GETOPT_FLAG( OPT_HELP, 'h' ), - GETOPT_ARG( OPT_SOCK, 's' ), + GETOPT_HELP, + GETOPT_SOCK, {0} }; static char status_short_options[] = "hs:"; From f21dd9e88818f7f0883c8293bf743b9c56b77deb Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 17:35:07 +0100 Subject: [PATCH 6/7] Basic debian packaging Add a build dependency on rake_utils, but we get simple debian packages out of it. --- Rakefile | 18 ++-- debian/changelog | 198 +++++++++++++++++++++++++++++++++++++++++ debian/compat | 1 + debian/control | 14 +++ debian/copyright | 53 +++++++++++ debian/flexnbd.install | 1 + debian/rules | 14 +++ debian/source/format | 1 + 8 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/flexnbd.install create mode 100755 debian/rules create mode 100644 debian/source/format diff --git a/Rakefile b/Rakefile index beebc72..da25c4b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,6 @@ +require 'rake_utils/debian' +include RakeUtils::DSL + DEBUG = true ALL_SOURCES =FileList['src/*'] @@ -75,9 +78,14 @@ OBJECTS.zip( SOURCES ).each do |o,c| end desc "Remove all build targets, binaries and temporary files" -rule 'clean' do - sh "rm -rf *~ build " + ( - 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 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..a789000 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,198 @@ +flexnbd (0.0.1-33) unstable; urgency=low + + * Added tag 0.0.1 for changeset 27409c2c1313 [r33] + + -- Alex Young 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 Wed, 30 May 2012 13:03:02 +0100 + +flexnbd (0.0.1-30) unstable; urgency=low + + * Fix the usage message [r30] + + -- nick Wed, 30 May 2012 11:28:32 +0100 + +flexnbd (0.0.1-29) unstable; urgency=low + + * Fixed race in tests. [r29] + + -- 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Fri, 18 May 2012 18:44:34 +0100 + +flexnbd (0.0.1-9) unstable; urgency=low + + * Added .hgignore file [r9] + + -- Matthew Bloch 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 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 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 Thu, 17 May 2012 11:54:25 +0100 + +flexnbd (0.0.1-5) unstable; urgency=low + + * Added write mode. [r5] + + -- Matthew Bloch 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 Wed, 16 May 2012 03:20:09 +0100 + +flexnbd (0.0.1-3) unstable; urgency=low + + * Added Rakefile [r3] + + -- 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 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 Tue, 15 May 2012 03:16:19 +0100 + +flexnbd (0.0.1-0) unstable; urgency=low + + * It compiles :) [r0] + + -- Matthew Bloch Tue, 15 May 2012 02:42:03 +0100 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..458d46a --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: flexnbd +Section: unknown +Priority: extra +Maintainer: Alex Young +Build-Depends: cdbs, debhelper (>= 7), ruby, rake, gcc +Standards-Version: 3.8.1 +Homepage: http://bigv.io/ + +Package: flexnbd +Architecture: all +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: FlexNBD server + An NBD server offering push-mirroring and intelligent sparse file handling + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..dba1271 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,53 @@ +This work was packaged for Debian by: + + Alex Young on Wed, 30 May 2012 16:46:58 +0100 + +It was downloaded from: + + + +Upstream Author(s): + + + + +Copyright: + + + + +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 + +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 + +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. diff --git a/debian/flexnbd.install b/debian/flexnbd.install new file mode 100644 index 0000000..6c73f84 --- /dev/null +++ b/debian/flexnbd.install @@ -0,0 +1 @@ +build/flexnbd usr/bin diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..8303c60 --- /dev/null +++ b/debian/rules @@ -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 diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) From 42599fe01e2dee38a83c75b6f482723035caa34e Mon Sep 17 00:00:00 2001 From: Alex Young Date: Wed, 30 May 2012 18:11:32 +0100 Subject: [PATCH 7/7] Make sure we build arch-specific packages --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 458d46a..fd0f26b 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.8.1 Homepage: http://bigv.io/ Package: flexnbd -Architecture: all +Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: FlexNBD server An NBD server offering push-mirroring and intelligent sparse file handling