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.
This commit is contained in:
Alex Young
2012-05-30 15:19:40 +01:00
parent a01621dc1e
commit 0c62e66a70
3 changed files with 512 additions and 83 deletions

View File

@@ -32,75 +32,79 @@
#include <unistd.h>
#include <signal.h>
void syntax()
#include <getopt.h>
#include "options.h"
void exit_err( char *msg )
{
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]"
" flexnbd status <control socket>\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(&params, 0, sizeof(params));
if (strcmp(mode, "serve") == 0) {
if (argc >= 3) {
params_serve(&params.serve, argv[0], argv[1], argv[2], argc-3, argv+3);
do_serve(&params.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, &params.readwrite, argv[0], argv[1], argv[2], argv[3]);
do_read(&params.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, &params.readwrite, argv[0], argv[1], argv[2], argv[3]);
do_write(&params.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;
}