diff --git a/README.proxy.txt b/README.proxy.txt new file mode 100644 index 0000000..8b20abf --- /dev/null +++ b/README.proxy.txt @@ -0,0 +1,182 @@ +FLEXNBD-PROXY(1) +================ +:doctype: manpage + +NAME +---- + +flexnbd-proxy - A simple NBD proxy + +SYNOPSIS +-------- + +*flexnbd-proxy* ['OPTIONS'] + +DESCRIPTION +----------- + +flexnbd-proxy is a simple NBD proxy server that implements resilient +connection logic for the client. It connects to an upstream NBD server +and allows a single client to connect to it. All server properties are +proxied to the client, and the client connection is kept alive across +reconnections to the upstream server. If the upstream goes away while +an NBD request is in-flight then the proxy (silently, from the point +of view of the client) reconnects and retransmits the request, before +returning the response to the client. + +USAGE +----- + + $ flexnbd-proxy --addr --port + --conn-addr --conn-port [--bind ] [option]* + +Proxy requests from an NBD client to an NBD server, resiliently. Only one +client can be connected (to the address specified by --addr and --port) at a +time, and ACLs cannot be applied to the client, as they can be to clients +connecting directly to a flexnbd in serve mode. + +On starting up, the proxy will attempt to connect to the server specified by +--conn-addr and --conn-port (from the address specified by --bind, if given). If +it fails, then the process will die with an error exit status. + +Assuming a successful connection to the `upstream` server is made, the proxy +will then start listening on the address specified by --addr and --port, waiting +for `downstream` to connect to it (this will be your NBD client). The client +will be given the same hello message as the proxy was given by the server. + +When connected, any request the client makes will be read by the proxy and sent +to the server. If the server goes away for any reason, the proxy will remember +the request and regularly (~ every 5 seconds) try to reconnect to the server. +Upon reconnection, the request is sent and a reply is waited for. When a reply +is received, it is sent back to the client. + +When the client disconnects, cleanly or otherwise, the proxy goes back to +waiting for a new client to connect. The connection to the server is maintained +at that point, in case it is needed again. + +Only one request may be in-flight at a time under the current architecture; that +doesn't seem to slow things down much relative to alternative options, but may +be changed in the future if it becomes an issue. + +Options +~~~~~~~ + +*--addr, -l ADDR*: + The address to listen on. Required. + +*--port, -p PORT*: + The port to listen on. Required. + +*--conn-addr, -C ADDR*: + The address of the NBD server to connect to. Required. + +*--conn-port, -P PORT*: + The port of the NBD server to connect to. Required. + +*--help, -h* : + Show command or global help. + +*--verbose, -v* : + Output all available log information to STDERR. + +*--quiet, -q* : + Output as little log information as possible to STDERR. + + +LOGGING +------- +Log output is sent to STDERR. If --quiet is set, no output will be seen +unless the program termintes abnormally. If neither --quiet nor +--verbose are set, no output will be seen unless something goes wrong +with a specific request. If --verbose is given, every available log +message will be seen (which, for a debug build, is many). It is not an +error to set both --verbose and --quiet. The last one wins. + +The log line format is: + + : :: + +*LEVEL*: + This will be one of 'D', 'I', 'W', 'E', 'F' in increasing order of +severity. If flexnbd is started with the --quiet flag, only 'F' will be +seen. If it is started with the --verbose flag, any from 'I' upwards +will be seen. Only if you have a debug build and start it with +--verbose will you see 'D' entries. + +*PID*: + This is the process ID. + +*THREAD*: + flexnbd-proxy is currently single-threaded, so this should be the same +for all lines. That may not be the case in the future. + +*SOURCEFILE:SOURCELINE*: + Identifies where in the source code this log line can be found. + +*MSG*: + A short message describing what's happening, how it's being done, or +if you're very lucky *why* it's going on. + +Proxying +~~~~~~~~ + +The main point of the proxy mode is to allow clients that would otherwise break +when the NBD server goes away (during a migration, for instance) to see a +persistent TCP connection throughout the process, instead of needing its own +reconnection logic. + +For maximum reliability, the proxy process would be run on the same machine as +the actual NBD client; an example might look like: + + nbd-server-1$ flexnbd serve -l 10.0.0.1 -p 4777 myfile [...] + + nbd-client-1$ flexnbd-proxy -l 127.0.0.1 -p 4777 -C 10.0.0.1 -P 4777 + nbd-client-1$ nbd-client -c 127.0.0.1 4777 /dev/nbd0 + + nbd-server-2$ flexnbd listen -l 10.0.0.2 -p 4777 -f myfile [...] + + nbd-server-1$ flexnbd mirror --addr 10.0.0.2 -p 4777 [...] + +Upon completing the migration, the mirroring and listening flexnbd servers will +both exit. With the proxy mediating requests, this does not break the TCP +connection that nbd-client is holding open. If no requests are in-flight, it +will not notice anything at all; if requests are in-flight, then the reply may +take longer than usual to be returned. + +When flexnbd is restarted in serve mode on the second server: + + nbd-server-2$ flexnbd serve -l 10.0.0.1 -p 4777 -f myfile [...] + +The proxy notices and reconnects, fulfiling any request it has in its buffer. +The data in myfile has been moved between physical servers without the nbd +client process having to be disturbed at all. + +BUGS +---- + +Should be reported to nick@bytemark.co.uk. + +Current issues include: + +* Only old-style NBD negotiation is supported +* Only one request may be in-flight at a time +* All I/O is blocking, and signals terminate the process immediately +* No UNIX socket support +* FLUSH and TRIM commands, and the FUA flag, are not supported +* DISCONNECT requests do not get passed through to the NBD server +* No active timeout-retry of requests - we trust the kernel's idea of failure + +AUTHOR +------ + +Written by Alex Young . +Original concept and core code by Matthew Bloch . +Some additions by Nick Thomas + +COPYING +------- + +Copyright (c) 2012 Bytemark Hosting Ltd. Free use of this software is +granted under the terms of the GNU General Public License version 3 or +later. + diff --git a/README.txt b/README.txt index 3c92a9d..1dd7ef6 100644 --- a/README.txt +++ b/README.txt @@ -85,55 +85,6 @@ Options ^^^^^^^ As for 'serve'. -proxy -~~~~~ - - $ flexnbd proxy --addr --port - --conn-addr --conn-port [--bind ] [global option]* - -Proxy requests from an NBD client to an NBD server, resiliently. Only one -client can be connected (to the address specified by --addr and --port) at a -time, and ACLs cannot be applied to the client, as they can be to clients -connecting directly to a flexnbd in serve mode. - -On starting up, the proxy will attempt to connect to the server specified by ---conn-addr and --conn-port (from the address specified by --bind, if given). If -it fails, then the process will die with an error exit status. - -Assuming a successful connection to the `upstream` server is made, the proxy -will then start listening on the address specified by --addr and --port, waiting -for `downstream` to connect to it (this will be your NBD client). The client -will be given the same hello message as the proxy was given by the server. - -When connected, any request the client makes will be read by the proxy and sent -to the server. If the server goes away for any reason, the proxy will remember -the request and regularly (~ every 5 seconds) try to reconnect to the server. -Upon reconnection, the request is sent and a reply is waited for. When a reply -is received, it is sent back to the client. - -When the client disconnects, cleanly or otherwise, the proxy goes back to -waiting for a new client to connect. The connection to the server is maintained -at that point, in case it is needed again. - -Only one request may be in-flight at a time under the current architecture; that -doesn't seem to slow things down much relative to alternative options, but may -be changed in the future if it becomes an issue. - -Options -^^^^^^^ - -*--addr, -l ADDR*: - The address to listen on. Required. - -*--port, -p PORT*: - The port to listen on. Required. - -*--conn-addr, -C ADDR*: - The address of the NBD server to connect to. Required. - -*--conn-port, -P PORT*: - The port of the NBD server to connect to. Required. - mirror ~~~~~~ @@ -415,40 +366,6 @@ Note that because the file is so small in this case, we see the source server quit soon after we start the migration, and the destination exited at roughly the same time. -Proxying -~~~~~~~~ - -The main point of the proxy mode is to allow clients that would otherwise break -when the NBD server goes away (during a migration, for instance) to see a -persistent TCP connection throughout the process, instead of needing its own -reconnection logic. - -For maximum reliability, the proxy process would be run on the same machine as -the actual NBD client; an example might look like: - - nbd-server-1$ flexnbd serve -l 10.0.0.1 -p 4777 myfile [...] - - nbd-client-1$ flexnbd proxy -l 127.0.0.1 -p 4777 -C 10.0.0.1 -P 4777 - nbd-client-1$ nbd-client -c 127.0.0.1 4777 /dev/nbd0 - - nbd-server-2$ flexnbd listen -l 10.0.0.2 -p 4777 -f myfile [...] - - nbd-server-1$ flexnbd mirror --addr 10.0.0.2 -p 4777 [...] - -Upon completing the migration, the mirroring and listening flexnbd servers will -both exit. With the proxy mediating requests, this does not break the TCP -connection that nbd-client is holding open. If no requests are in-flight, it -will not notice anything at all; if requests are in-flight, then the reply will -take longer than usual to be returned. - -When flexnbd is restarted in serve mode on the second server: - - nbd-server-2$ flexnbd serve -l 10.0.0.1 -p 4777 -f myfile [...] - -The proxy notices and reconnects, fulfiling any request it has in its buffer. -The data in myfile has been moved between physical servers without the nbd -client process having to be disturbed at all. - BUGS ---- diff --git a/Rakefile b/Rakefile index 8cea82a..68546cc 100644 --- a/Rakefile +++ b/Rakefile @@ -7,9 +7,17 @@ CC=ENV['CC'] || "gcc" DEBUG = ENV.has_key?('DEBUG') && %w|yes y ok 1 true t|.include?(ENV['DEBUG']) -ALL_SOURCES =FileList['src/*'] -SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ } -OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" ) +ALL_SOURCES = FileList['src/*'] + +PROXY_ONLY_SOURCES = FileList['src/{proxy-main,proxy}.c'] +PROXY_ONLY_OBJECTS = PROXY_ONLY_SOURCES.pathmap( "%{^src,build}X.o" ) + +SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ } - PROXY_ONLY_SOURCES +OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" ) - PROXY_ONLY_OBJECTS + +PROXY_SOURCES = FileList['src/{ioutil,nbdtypes,readwrite,sockutil,util,parse}.c'] + PROXY_ONLY_SOURCES +PROXY_OBJECTS = PROXY_SOURCES.pathmap( "%{^src,build}X.o" ) + TEST_SOURCES = FileList['tests/unit/*.c'] TEST_OBJECTS = TEST_SOURCES.pathmap( "%{^tests/unit,build/tests}X.o" ) @@ -38,26 +46,38 @@ if DEBUG end desc "Build the binary and man page" -task :build => ['build/flexnbd', 'build/flexnbd.1.gz'] +task :build => [:flexnbd, :flexnbd_proxy, :man] task :default => :build -desc "Build just the binary" +desc "Build just the flexnbd binary" task :flexnbd => "build/flexnbd" +desc "Build just the flexnbd-proxy binary" +task :flexnbd_proxy => "build/flexnbd-proxy" + def check(m) "build/tests/check_#{m}" end file "README.txt" +file "README.proxy.txt" + +def manpage(name, src) + FileUtils.mkdir_p( "build" ) + sh "a2x --destination-dir build --format manpage #{src}" + sh "gzip -f build/#{name}" +end file "build/flexnbd.1.gz" => "README.txt" do - FileUtils.mkdir_p( "build" ) - sh "a2x --destination-dir build --format manpage README.txt" - sh "gzip -f build/flexnbd.1" + manpage("flexnbd.1", "README.txt") +end + +file "build/flexnbd-proxy.1.gz" => "README.proxy.txt" do + manpage("flexnbd-proxy.1", "README.proxy.txt") end desc "Build just the man page" -task :man => "build/flexnbd.1.gz" +task :man => ["build/flexnbd.1.gz", "build/flexnbd-proxy.1.gz"] namespace "test" do @@ -83,7 +103,7 @@ namespace "test" do end desc "Run NBD test scenarios" - task 'scenarios' => 'flexnbd' do + task 'scenarios' => ['build/flexnbd', 'build/flexnbd-proxy'] do sh "cd tests/acceptance; ruby nbd_scenarios -v" end end @@ -109,6 +129,10 @@ def headers(c) `#{CC} -Isrc -MM #{c}`.gsub("\\\n", " ").split(" ")[2..-1] end +rule 'build/flexnbd-proxy' => PROXY_OBJECTS do |t| + gcc_link(t.name, t.sources) +end + rule 'build/flexnbd' => OBJECTS do |t| gcc_link(t.name, t.sources) end @@ -125,7 +149,6 @@ file check("client") => build/parse.o build/client.o build/serve.o - build/proxy.o build/acl.o build/ioutil.o build/mbox.o @@ -161,7 +184,6 @@ file check("serve") => build/client.o build/flexthread.o build/serve.o - build/proxy.o build/flexnbd.o build/mirror.o build/status.o @@ -179,7 +201,6 @@ file check("readwrite") => build/client.o build/self_pipe.o build/serve.o - build/proxy.o build/parse.o build/acl.o build/flexthread.o @@ -213,22 +234,20 @@ file check("flexnbd") => build/nbdtypes.o build/readwrite.o build/mirror.o - build/serve.o - build/proxy.o} do |t| + build/serve.o} do |t| gcc_link t.name, t.prerequisites + [LIBCHECK] end file check("control") => - %w{build/tests/check_control.o} + OBJECTS - ["build/main.o"] do |t| + %w{build/tests/check_control.o} + OBJECTS - ["build/main.o", 'build/proxy-main.o', 'build/proxy.o'] do |t| gcc_link t.name, t.prerequisites + [LIBCHECK] end - (TEST_MODULES- %w{control flexnbd acl client serve readwrite util}).each do |m| tgt = "build/tests/check_#{m}.o" maybe_obj_name = "build/#{m}.o" - # Take it out in case we're testing util.o or ioutil.o + # Take it out in case we're testing one of the utils deps = ["build/ioutil.o", "build/util.o", "build/sockutil.o"] - [maybe_obj_name] # Add it back in if it's something we need to compile @@ -244,6 +263,10 @@ OBJECTS.zip( SOURCES ).each do |o,c| file o => [c]+headers(c) do |t| gcc_compile( o, c ) end end +PROXY_ONLY_OBJECTS.zip( PROXY_ONLY_SOURCES).each do |o, c| + file o => [c]+headers(c) do |t| gcc_compile( o, c ) end +end + TEST_OBJECTS.zip( TEST_SOURCES ).each do |o,c| file o => [c] + headers(c) do |t| gcc_compile( o, c ) end end @@ -255,10 +278,9 @@ end namespace :pkg do deb do |t| - t.code_files = ALL_SOURCES + ["Rakefile", "README.txt"] + t.code_files = ALL_SOURCES + ["Rakefile", "README.txt", "README.proxy.txt"] t.pkg_name = "flexnbd" t.generate_changelog! end end - diff --git a/debian/flexnbd.install b/debian/flexnbd.install index 0deb27f..27e69ad 100644 --- a/debian/flexnbd.install +++ b/debian/flexnbd.install @@ -1,2 +1,5 @@ -build/flexnbd usr/bin -build/flexnbd.1.gz usr/share/man/man1 +build/flexnbd usr/bin +build/flexnbd-proxy usr/bin +build/flexnbd.1.gz usr/share/man/man1 +build/flexnbd-proxy.1.gz usr/share/man/man1 + diff --git a/src/flexnbd.c b/src/flexnbd.c index ec0e75e..59c9f09 100644 --- a/src/flexnbd.c +++ b/src/flexnbd.c @@ -128,27 +128,6 @@ struct flexnbd * flexnbd_create_listening( return flexnbd; } - -struct flexnbd * flexnbd_create_proxying( - char* s_downstream_address, - char* s_downstream_port, - char* s_upstream_address, - char* s_upstream_port, - char* s_upstream_bind -) -{ - struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) ); - flexnbd->proxy = proxy_create( - flexnbd, - s_downstream_address, - s_downstream_port, - s_upstream_address, - s_upstream_port, - s_upstream_bind); - flexnbd_create_shared( flexnbd, NULL ); - return flexnbd; -} - void flexnbd_spawn_control(struct flexnbd * flexnbd ) { NULLCHECK( flexnbd ); @@ -274,14 +253,3 @@ int flexnbd_serve( struct flexnbd * flexnbd ) return success; } -int flexnbd_proxy( struct flexnbd * flexnbd ) -{ - NULLCHECK( flexnbd ); - int success; - - success = do_proxy( flexnbd->proxy ); - debug("do_proxy success is %d", success ); - - return success; -} - diff --git a/src/flexnbd.h b/src/flexnbd.h index 7fc9f22..9e226ef 100644 --- a/src/flexnbd.h +++ b/src/flexnbd.h @@ -17,9 +17,6 @@ struct flexnbd { */ struct server * serve; - /* In proxy mode, this is filled instead of serve, above */ - struct proxier * proxy; - /* We only have a control object if a control socket name was * passed on the command line. */ @@ -50,14 +47,6 @@ struct flexnbd * flexnbd_create_listening( int acl_entries, char** s_acl_entries ); -struct flexnbd * flexnbd_create_proxying( - char* s_downstream_address, - char* s_downstream_port, - char* s_upstream_address, - char* s_upstream_port, - char* s_upstream_bind -); - void flexnbd_destroy( struct flexnbd * ); enum mirror_state; enum mirror_state flexnbd_get_mirror_state( struct flexnbd * ); diff --git a/src/main.c b/src/main.c index b8f52ba..daf99b4 100644 --- a/src/main.c +++ b/src/main.c @@ -3,7 +3,6 @@ #include - int main(int argc, char** argv) { signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */ diff --git a/src/mode.c b/src/mode.c index f1af346..3122583 100644 --- a/src/mode.c +++ b/src/mode.c @@ -56,30 +56,6 @@ static char listen_help_text[] = VERBOSE_LINE QUIET_LINE; -static struct option proxy_options[] = { - GETOPT_HELP, - GETOPT_ADDR, - GETOPT_PORT, - GETOPT_CONNECT_ADDR, - GETOPT_CONNECT_PORT, - GETOPT_BIND, - GETOPT_QUIET, - GETOPT_VERBOSE, - {0} -}; -static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE; -static char proxy_help_text[] = - "Usage: flexnbd " CMD_PROXY " \n\n" - "Resiliently proxy an NBD connection between client and server\n\n" - HELP_LINE - "\t--" OPT_ADDR ",-l \tThe address we will bind to as a proxy.\n" - "\t--" OPT_PORT ",-p \tThe port we will bind to as a proxy.\n" - "\t--" OPT_CONNECT_ADDR ",-C \tAddress of the proxied server.\n" - "\t--" OPT_CONNECT_PORT ",-P \tPort of the proxied server.\n" - "\t--" OPT_BIND ",-b \tThe address we connect from, as a proxy.\n" - QUIET_LINE - VERBOSE_LINE; - static struct option read_options[] = { GETOPT_HELP, GETOPT_ADDR, @@ -197,7 +173,6 @@ char help_help_text_arr[] = "Commands:\n" "\tflexnbd serve\n" "\tflexnbd listen\n" - "\tflexnbd proxy\n" "\tflexnbd read\n" "\tflexnbd write\n" "\tflexnbd acl\n" @@ -416,46 +391,6 @@ void read_break_param( int c, char **sock ) } -void read_proxy_param( - int c, - char **downstream_addr, - char **downstream_port, - char **upstream_addr, - char **upstream_port, - char **bind_addr ) -{ - switch( c ) { - case 'h' : - fprintf( stdout, "%s\n", proxy_help_text ); - exit( 0 ); - break; - case 'l': - *downstream_addr = optarg; - break; - case 'p': - *downstream_port = optarg; - break; - case 'C': - *upstream_addr = optarg; - break; - case 'P': - *upstream_port = optarg; - break; - case 'b': - *bind_addr = optarg; - break; - case 'q': - log_level = QUIET_LOG_LEVEL; - break; - case 'v': - log_level = VERBOSE_LOG_LEVEL; - break; - default: - exit_err( proxy_help_text ); - break; - } -} - void read_status_param( int c, char **sock ) { read_sock_param( c, sock, status_help_text ); @@ -799,56 +734,6 @@ int mode_status( int argc, char *argv[] ) return 0; } -int mode_proxy( int argc, char *argv[] ) -{ - int c; - struct flexnbd * flexnbd; - char *downstream_addr = NULL; - char *downstream_port = NULL; - char *upstream_addr = NULL; - char *upstream_port = NULL; - char *bind_addr = NULL; - int success; - - while (1) { - c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL ); - if ( -1 == c ) { break; } - read_proxy_param( c, - &downstream_addr, - &downstream_port, - &upstream_addr, - &upstream_port, - &bind_addr - ); - } - - if ( NULL == downstream_addr || NULL == downstream_port ){ - fprintf( stderr, "both --addr and --port are required.\n" ); - exit_err( proxy_help_text ); - } else if ( NULL == upstream_addr || NULL == upstream_port ){ - fprintf( stderr, "both --conn-addr and --conn-port are required.\n" ); - exit_err( proxy_help_text ); - } - - flexnbd = flexnbd_create_proxying( - downstream_addr, - downstream_port, - upstream_addr, - upstream_port, - bind_addr - ); - - info( - "Proxying between %s %s (downstream) and %s %s (upstream)", - downstream_addr, downstream_port, upstream_addr, upstream_port - ); - - success = flexnbd_proxy( flexnbd ); - flexnbd_destroy( flexnbd ); - - return success ? 0 : 1; -} - int mode_help( int argc, char *argv[] ) { char *cmd; @@ -872,8 +757,6 @@ int mode_help( int argc, char *argv[] ) help_text = mirror_help_text; } else if ( IS_CMD( CMD_STATUS, cmd ) ) { help_text = status_help_text; - } else if ( IS_CMD( CMD_PROXY, cmd ) ) { - help_text = proxy_help_text; } else { exit_err( help_help_text ); } } @@ -907,8 +790,6 @@ void mode(char* mode, int argc, char **argv) } else if ( IS_CMD( CMD_STATUS, mode ) ) { mode_status( argc, argv ); - } else if ( IS_CMD( CMD_PROXY, mode ) ) { - mode_proxy( argc, argv ); } else if ( IS_CMD( CMD_HELP, mode ) ) { mode_help( argc-1, argv+1 ); diff --git a/src/mode.h b/src/mode.h index ed67f06..d62168b 100644 --- a/src/mode.h +++ b/src/mode.h @@ -25,7 +25,6 @@ void mode(char* mode, int argc, char **argv); #define CMD_SERVE "serve" #define CMD_LISTEN "listen" -#define CMD_PROXY "proxy" #define CMD_READ "read" #define CMD_WRITE "write" #define CMD_ACL "acl" diff --git a/src/proxy-main.c b/src/proxy-main.c new file mode 100644 index 0000000..b9ae6b4 --- /dev/null +++ b/src/proxy-main.c @@ -0,0 +1,172 @@ +#include +#include + +#include "mode.h" +#include "util.h" +#include "sockutil.h" +#include "proxy.h" + + +static struct option proxy_options[] = { + GETOPT_HELP, + GETOPT_ADDR, + GETOPT_PORT, + GETOPT_CONNECT_ADDR, + GETOPT_CONNECT_PORT, + GETOPT_BIND, + GETOPT_QUIET, + GETOPT_VERBOSE, + {0} +}; +static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE; +static char proxy_help_text[] = + "Usage: flexnbd-proxy \n\n" + "Resiliently proxy an NBD connection between client and server\n\n" + HELP_LINE + "\t--" OPT_ADDR ",-l \tThe address we will bind to as a proxy.\n" + "\t--" OPT_PORT ",-p \tThe port we will bind to as a proxy.\n" + "\t--" OPT_CONNECT_ADDR ",-C \tAddress of the proxied server.\n" + "\t--" OPT_CONNECT_PORT ",-P \tPort of the proxied server.\n" + "\t--" OPT_BIND ",-b \tThe address we connect from, as a proxy.\n" + QUIET_LINE + VERBOSE_LINE; + +void read_proxy_param( + int c, + char **downstream_addr, + char **downstream_port, + char **upstream_addr, + char **upstream_port, + char **bind_addr ) +{ + switch( c ) { + case 'h' : + fprintf( stdout, "%s\n", proxy_help_text ); + exit( 0 ); + break; + case 'l': + *downstream_addr = optarg; + break; + case 'p': + *downstream_port = optarg; + break; + case 'C': + *upstream_addr = optarg; + break; + case 'P': + *upstream_port = optarg; + break; + case 'b': + *bind_addr = optarg; + break; + case 'q': + log_level = QUIET_LOG_LEVEL; + break; + case 'v': + log_level = VERBOSE_LOG_LEVEL; + break; + default: + exit_err( proxy_help_text ); + break; + } +} + +/* Stolen from flexnbd.c, wil change in the near future so no point DRYing */ +int build_signal_fd(void) +{ + sigset_t mask; + int sfd; + + sigemptyset( &mask ); + sigaddset( &mask, SIGTERM ); + sigaddset( &mask, SIGQUIT ); + sigaddset( &mask, SIGINT ); + + FATAL_UNLESS( 0 == pthread_sigmask( SIG_BLOCK, &mask, NULL ), + "Signal blocking failed" ); + + sfd = signalfd( -1, &mask, 0 ); + FATAL_IF( -1 == sfd, "Failed to get a signal fd" ); + + return sfd; +} + +struct proxier* flexnbd_create_proxying( + int signal_fd, + char* s_downstream_address, + char* s_downstream_port, + char* s_upstream_address, + char* s_upstream_port, + char* s_upstream_bind +) +{ + struct proxier* proxy = proxy_create( + signal_fd, + s_downstream_address, + s_downstream_port, + s_upstream_address, + s_upstream_port, + s_upstream_bind + ); + + return proxy; +} + + +int main( int argc, char *argv[] ) +{ + int c; + struct proxier * proxy; + char *downstream_addr = NULL; + char *downstream_port = NULL; + char *upstream_addr = NULL; + char *upstream_port = NULL; + char *bind_addr = NULL; + int signal_fd; + int success; + + signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */ + error_init(); + + while (1) { + c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL ); + if ( -1 == c ) { break; } + read_proxy_param( c, + &downstream_addr, + &downstream_port, + &upstream_addr, + &upstream_port, + &bind_addr + ); + } + + if ( NULL == downstream_addr || NULL == downstream_port ){ + fprintf( stderr, "both --addr and --port are required.\n" ); + exit_err( proxy_help_text ); + } else if ( NULL == upstream_addr || NULL == upstream_port ){ + fprintf( stderr, "both --conn-addr and --conn-port are required.\n" ); + exit_err( proxy_help_text ); + } + + signal_fd = build_signal_fd(); + + proxy = flexnbd_create_proxying( + signal_fd, + downstream_addr, + downstream_port, + upstream_addr, + upstream_port, + bind_addr + ); + + info( + "Proxying between %s %s (downstream) and %s %s (upstream)", + downstream_addr, downstream_port, upstream_addr, upstream_port + ); + + success = do_proxy( proxy ); + sock_try_close( signal_fd ); + + return success ? 0 : 1; +} + diff --git a/src/proxy.c b/src/proxy.c index 4026c0c..5676939 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -11,18 +11,16 @@ #include struct proxier* proxy_create( - struct flexnbd* flexnbd, + int signal_fd, char* s_downstream_address, char* s_downstream_port, char* s_upstream_address, char* s_upstream_port, char* s_upstream_bind ) { - NULLCHECK( flexnbd ); - struct proxier* out; out = xmalloc( sizeof( struct proxier ) ); - out->flexnbd = flexnbd; + out->signal_fd = signal_fd; FATAL_IF_NULL(s_downstream_address, "Listen address not specified"); NULLCHECK( s_downstream_address ); @@ -172,13 +170,11 @@ int proxy_should_exit( struct proxier* params, fd_set *check_fds, int wait ) fd_set internal_fds; fd_set* fds = check_fds; - int signal_fd = flexnbd_signal_fd( params->flexnbd ); - if ( NULL == check_fds ) { fds = &internal_fds; FD_ZERO( fds ); - FD_SET( signal_fd, fds ); + FD_SET( params->signal_fd, fds ); FATAL_IF_NEGATIVE( sock_try_select(FD_SETSIZE, fds, NULL, NULL, &tv), @@ -186,7 +182,7 @@ int proxy_should_exit( struct proxier* params, fd_set *check_fds, int wait ) ); } - if ( FD_ISSET( signal_fd, fds ) ) { + if ( FD_ISSET( params->signal_fd, fds ) ) { info( "Stop signal received" ); return 1; } @@ -410,7 +406,6 @@ int proxy_accept( struct proxier* params ) NULLCHECK( params ); int client_fd; - int signal_fd = flexnbd_signal_fd( params->flexnbd ); fd_set fds; int should_continue = 1; @@ -421,7 +416,7 @@ int proxy_accept( struct proxier* params ) FD_ZERO(&fds); FD_SET(params->listen_fd, &fds); - FD_SET(signal_fd, &fds); + FD_SET(params->signal_fd, &fds); FATAL_IF_NEGATIVE( sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL), diff --git a/src/proxy.h b/src/proxy.h index 4ca1017..5b22b57 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -55,10 +55,13 @@ struct proxier { /* We transform the raw reply header into here */ struct nbd_reply rsp_hdr; + + /* File descriptor that signal handlers write to */ + int signal_fd; }; struct proxier* proxy_create( - struct flexnbd * flexnbd, + int signal_fd, char* s_downstream_address, char* s_downstream_port, char* s_upstream_address, diff --git a/tests/acceptance/flexnbd.rb b/tests/acceptance/flexnbd.rb index d5b1dbf..1e854cb 100644 --- a/tests/acceptance/flexnbd.rb +++ b/tests/acceptance/flexnbd.rb @@ -242,7 +242,7 @@ module FlexNBD end def proxy_cmd( connect_ip, connect_port ) - "#{bin} proxy "\ + "#{bin}-proxy "\ "--addr #{ip} "\ "--port #{port} "\ "--conn-addr #{connect_ip} "\