Files
flexnbd-c/src/control.c

550 lines
13 KiB
C
Raw Normal View History

/* 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/>.
*/
/** The control server responds on a UNIX socket and services our "remote"
* commands which are used for changing the access control list, initiating
* a mirror process, or asking for status. The protocol is pretty simple -
* after connecting the client sends a series of LF-terminated lines, followed
* by a blank line (i.e. double LF). The first line is taken to be the command
* name to invoke, and the lines before the double LF are its arguments.
*
* These commands can be invoked remotely from the command line, with the
* client code to be found in remote.c
*/
2012-06-22 10:05:41 +01:00
#include "control.h"
2012-06-27 15:45:33 +01:00
#include "mirror.h"
#include "serve.h"
#include "util.h"
#include "ioutil.h"
#include "parse.h"
#include "readwrite.h"
#include "bitset.h"
2012-06-06 12:41:03 +01:00
#include "self_pipe.h"
2012-06-07 17:47:43 +01:00
#include "acl.h"
2012-06-22 10:05:41 +01:00
#include "status.h"
2012-06-27 15:45:33 +01:00
#include "mbox.h"
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <unistd.h>
2012-06-22 10:05:41 +01:00
struct control * control_create(
struct flexnbd * flexnbd,
const char * csn)
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
struct control * control = xmalloc( sizeof( struct control ) );
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
NULLCHECK( csn );
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
control->flexnbd = flexnbd;
control->socket_name = csn;
control->close_signal = self_pipe_create();
control->mirror_state_mbox = mbox_create();
2012-06-27 15:45:33 +01:00
return control;
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
void control_signal_close( struct control * control)
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( control );
self_pipe_signal( control->close_signal );
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
void control_destroy( struct control * control )
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( control );
mbox_destroy( control->mirror_state_mbox );
2012-06-27 15:45:33 +01:00
self_pipe_destroy( control->close_signal );
free( control );
}
struct control_client * control_client_create(
struct flexnbd * flexnbd,
int client_fd ,
struct mbox * state_mbox )
{
2012-06-27 15:45:33 +01:00
NULLCHECK( flexnbd );
2012-06-27 15:45:33 +01:00
struct control_client * control_client =
xmalloc( sizeof( struct control_client ) );
2012-06-27 15:45:33 +01:00
control_client->socket = client_fd;
control_client->flexnbd = flexnbd;
control_client->mirror_state_mbox = state_mbox;
2012-06-27 15:45:33 +01:00
return control_client;
}
2012-06-27 15:45:33 +01:00
void control_client_destroy( struct control_client * client )
2012-06-13 15:45:59 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( client );
free( client );
2012-06-13 15:45:59 +01:00
}
2012-06-27 15:45:33 +01:00
void control_respond(struct control_client * client);
2012-06-27 15:45:33 +01:00
void control_handle_client( struct control * control, int client_fd )
2012-06-13 15:45:59 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( control );
NULLCHECK( control->flexnbd );
struct control_client * control_client =
control_client_create(
control->flexnbd,
client_fd ,
control->mirror_state_mbox);
2012-06-27 15:45:33 +01:00
/* We intentionally don't spawn a thread for the client here.
* This is to avoid having more than one thread potentially
* waiting on the migration commit status.
*/
2012-06-27 15:45:33 +01:00
control_respond( control_client );
2012-06-13 15:45:59 +01:00
}
2012-06-27 15:45:33 +01:00
void control_accept_client( struct control * control )
{
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
int client_fd;
union mysockaddr client_address;
socklen_t addrlen = sizeof( union mysockaddr );
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
client_fd = accept( control->control_fd, &client_address.generic, &addrlen );
FATAL_IF( -1 == client_fd, "control accept failed" );
2012-06-13 15:45:59 +01:00
2012-06-27 15:45:33 +01:00
control_handle_client( control, client_fd );
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
int control_accept( struct control * control )
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( control );
2012-06-27 15:45:33 +01:00
fd_set fds;
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
FD_ZERO( &fds );
FD_SET( control->control_fd, &fds );
self_pipe_fd_set( control->close_signal, &fds );
2012-06-28 13:29:22 +01:00
debug("Control thread selecting");
2012-06-27 15:45:33 +01:00
FATAL_UNLESS( 0 < select( FD_SETSIZE, &fds, NULL, NULL, NULL ),
"Control select failed." );
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
if ( self_pipe_fd_isset( control->close_signal, &fds ) ){
return 0;
}
2012-06-27 15:45:33 +01:00
if ( FD_ISSET( control->control_fd, &fds ) ) {
control_accept_client( control );
}
2012-06-27 15:45:33 +01:00
return 1;
2012-06-22 10:05:41 +01:00
}
2012-06-13 15:45:59 +01:00
2012-06-22 10:05:41 +01:00
2012-06-27 15:45:33 +01:00
void control_accept_loop( struct control * control )
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
while( control_accept( control ) );
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
int open_control_socket( const char * socket_name )
{
struct sockaddr_un bind_address;
int control_fd;
2012-06-27 15:45:33 +01:00
if (!socket_name) {
fatal( "Tried to open a control socket without a socket name" );
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
FATAL_IF_NEGATIVE(control_fd ,
"Couldn't create control socket");
memset(&bind_address, 0, sizeof(struct sockaddr_un));
bind_address.sun_family = AF_UNIX;
strncpy(bind_address.sun_path, socket_name, sizeof(bind_address.sun_path)-1);
//unlink(socket_name); /* ignore failure */
2012-06-27 15:45:33 +01:00
FATAL_IF_NEGATIVE(
bind(control_fd , &bind_address, sizeof(bind_address)),
"Couldn't bind control socket to %s: %s",
socket_name, strerror( errno )
2012-06-27 15:45:33 +01:00
);
FATAL_IF_NEGATIVE(
listen(control_fd , 5),
"Couldn't listen on control socket"
);
return control_fd;
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
void control_listen(struct control* control)
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( control );
control->control_fd = open_control_socket( control->socket_name );
2012-06-22 10:05:41 +01:00
}
2012-06-27 15:45:33 +01:00
void control_serve( struct control * control )
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
NULLCHECK( control );
control_listen( control );
while( control_accept( control ) );
2012-06-22 10:05:41 +01:00
}
void control_cleanup(
struct control * control,
int fatal __attribute__((unused)) )
{
NULLCHECK( control );
unlink( control->socket_name );
close( control->control_fd );
}
2012-06-27 15:45:33 +01:00
void * control_runner( void * control_uncast )
2012-06-22 10:05:41 +01:00
{
debug("Control thread");
2012-06-27 15:45:33 +01:00
NULLCHECK( control_uncast );
struct control * control = (struct control *)control_uncast;
2012-06-22 10:05:41 +01:00
error_set_handler( (cleanup_handler*)control_cleanup, control );
2012-06-27 15:45:33 +01:00
control_serve( control );
control_cleanup( control, 0 );
return NULL;
}
2012-06-13 15:45:59 +01:00
2012-06-27 15:45:33 +01:00
#define write_socket(msg) write(client_fd, (msg "\n"), strlen((msg))+1)
2012-06-27 15:45:33 +01:00
void control_write_mirror_response( enum mirror_state mirror_state, int client_fd )
2012-06-22 10:05:41 +01:00
{
2012-06-27 15:45:33 +01:00
switch (mirror_state) {
case MS_INIT:
case MS_UNKNOWN:
write_socket( "1: Mirror failed to initialise" );
fatal( "Impossible mirror state: %d", mirror_state );
case MS_FAIL_CONNECT:
write_socket( "1: Mirror failed to connect");
break;
case MS_FAIL_REJECTED:
write_socket( "1: Mirror was rejected" );
break;
case MS_FAIL_NO_HELLO:
write_socket( "1: Remote server failed to respond");
break;
case MS_FAIL_SIZE_MISMATCH:
write_socket( "1: Remote size does not match local size" );
break;
case MS_GO:
case MS_DONE: /* Yes, I know we know better, but it's simpler this way */
write_socket( "0: Mirror started" );
break;
default:
fatal( "Unhandled mirror state: %d", mirror_state );
2012-06-22 10:05:41 +01:00
}
}
2012-06-27 15:45:33 +01:00
#undef write_socket
2012-06-22 10:05:41 +01:00
/* Call this in the thread where you want to receive the mirror state */
enum mirror_state control_client_mirror_wait(
struct control_client* client)
{
NULLCHECK( client );
NULLCHECK( client->mirror_state_mbox );
struct mbox * mbox = client->mirror_state_mbox;
enum mirror_state mirror_state;
enum mirror_state * contents;
contents = (enum mirror_state*)mbox_receive( mbox );
NULLCHECK( contents );
mirror_state = *contents;
free( contents );
return mirror_state;
}
2012-06-27 15:45:33 +01:00
#define write_socket(msg) write(client->socket, (msg "\n"), strlen((msg))+1)
/** Command parser to start mirror process from socket input */
2012-06-27 15:45:33 +01:00
int control_mirror(struct control_client* client, int linesc, char** lines)
{
2012-06-13 15:45:59 +01:00
NULLCHECK( client );
2012-06-27 15:45:33 +01:00
struct flexnbd * flexnbd = client->flexnbd;
2012-06-22 10:05:41 +01:00
union mysockaddr *connect_to = xmalloc( sizeof( union mysockaddr ) );
union mysockaddr *connect_from = NULL;
uint64_t max_Bps = 0;
int action_at_finish;
int raw_port;
if (linesc < 2) {
write_socket("1: mirror takes at least two parameters");
return -1;
}
2012-06-22 10:05:41 +01:00
if (parse_ip_to_sockaddr(&connect_to->generic, lines[0]) == 0) {
write_socket("1: bad IP address");
return -1;
}
raw_port = atoi(lines[1]);
if (raw_port < 0 || raw_port > 65535) {
write_socket("1: bad IP port number");
return -1;
}
2012-06-22 10:05:41 +01:00
connect_to->v4.sin_port = htobe16(raw_port);
action_at_finish = ACTION_EXIT;
if (linesc > 2) {
if (strcmp("exit", lines[2]) == 0) {
action_at_finish = ACTION_EXIT;
2012-06-11 14:34:17 +01:00
}
else if (strcmp( "unlink", lines[2]) == 0 ) {
action_at_finish = ACTION_UNLINK;
}
else if (strcmp("nothing", lines[2]) == 0) {
action_at_finish = ACTION_NOTHING;
2012-06-11 14:34:17 +01:00
}
else {
write_socket("1: action must be 'exit' or 'nothing'");
return -1;
}
}
if (linesc > 3) {
connect_from = xmalloc( sizeof( union mysockaddr ) );
2012-07-24 09:21:40 +01:00
if (parse_ip_to_sockaddr(&connect_from->generic, lines[3]) == 0) {
write_socket("1: bad bind address");
return -1;
}
}
if (linesc > 4) {
max_Bps = atoi(lines[2]);
}
if (linesc > 5) {
write_socket("1: unrecognised parameters to mirror");
return -1;
}
struct server * serve = flexnbd_server(flexnbd);
if ( serve->mirror_super ) {
warn( "Tried to start a second mirror run" );
write_socket( "1: mirror already running" );
} else {
serve->mirror_super = mirror_super_create(
serve->filename,
connect_to,
connect_from,
max_Bps ,
action_at_finish,
client->mirror_state_mbox );
serve->mirror = serve->mirror_super->mirror;
FATAL_IF( 0 != pthread_create(
&serve->mirror_super->thread,
NULL,
mirror_super_runner,
serve
),
"Failed to create mirror thread"
);
debug("Control thread mirror super waiting");
enum mirror_state state =
control_client_mirror_wait( client );
debug("Control thread writing response");
control_write_mirror_response( state, client->socket );
2012-06-27 15:45:33 +01:00
}
2012-06-22 10:05:41 +01:00
debug( "Control thread going away." );
return 0;
}
2012-06-27 15:45:33 +01:00
#undef write_socket
/** Command parser to alter access control list from socket input */
2012-06-27 15:45:33 +01:00
int control_acl(struct control_client* client, int linesc, char** lines)
{
2012-06-07 17:47:43 +01:00
NULLCHECK( client );
2012-06-27 15:45:33 +01:00
NULLCHECK( client->flexnbd );
struct flexnbd * flexnbd = client->flexnbd;
int default_deny = flexnbd_default_deny( flexnbd );
struct acl * new_acl = acl_create( linesc, lines, default_deny );
2012-06-07 17:47:43 +01:00
if (new_acl->len != linesc) {
2012-07-16 11:38:01 +01:00
warn("Bad ACL spec: %s", lines[new_acl->len] );
write(client->socket, "1: bad spec: ", 13);
2012-06-07 17:47:43 +01:00
write(client->socket, lines[new_acl->len],
2012-06-27 15:45:33 +01:00
strlen(lines[new_acl->len]));
write(client->socket, "\n", 1);
2012-06-07 17:47:43 +01:00
acl_destroy( new_acl );
}
else {
2012-06-27 15:45:33 +01:00
flexnbd_replace_acl( flexnbd, new_acl );
2012-07-16 11:38:01 +01:00
info("ACL set");
write( client->socket, "0: updated\n", 11);
}
2012-06-27 15:45:33 +01:00
return 0;
}
int control_break(
struct control_client* client,
int linesc __attribute__ ((unused)),
char** lines __attribute__((unused))
)
{
NULLCHECK( client );
NULLCHECK( client->flexnbd );
int result = 0;
struct flexnbd* flexnbd = client->flexnbd;
struct server * serve = flexnbd_server( flexnbd );
if ( server_is_mirroring( serve ) ) {
info( "Signaling to abandon mirror" );
server_abandon_mirror( serve );
debug( "Abandon signaled" );
if ( server_is_closed( serve ) ) {
info( "Mirror completed while canceling" );
write( client->socket,
"1: mirror completed\n", 20 );
}
else {
info( "Mirror successfully stopped." );
write( client->socket,
"0: mirror stopped\n", 18 );
result = 1;
}
} else {
warn( "Not mirroring." );
write( client->socket, "1: not mirroring\n", 17 );
}
return result;
}
/** FIXME: add some useful statistics */
int control_status(
2012-06-27 15:45:33 +01:00
struct control_client* client,
int linesc __attribute__ ((unused)),
char** lines __attribute__((unused))
)
{
2012-06-22 10:05:41 +01:00
NULLCHECK( client );
2012-06-27 15:45:33 +01:00
NULLCHECK( client->flexnbd );
struct status * status = flexnbd_status_create( client->flexnbd );
2012-06-22 10:05:41 +01:00
write( client->socket, "0: ", 3 );
status_write( status, client->socket );
status_destroy( status );
return 0;
}
void control_client_cleanup(struct control_client* client,
int fatal __attribute__ ((unused)) )
{
2012-06-11 14:34:17 +01:00
if (client->socket) { close(client->socket); }
/* This is wrongness */
if ( server_io_locked( client->flexnbd->serve ) ) { server_unlock_io( client->flexnbd->serve ); }
if ( server_acl_locked( client->flexnbd->serve ) ) { server_unlock_acl( client->flexnbd->serve ); }
2012-06-27 15:45:33 +01:00
control_client_destroy( client );
}
/** Master command parser for control socket connections, delegates quickly */
2012-06-27 15:45:33 +01:00
void control_respond(struct control_client * client)
{
char **lines = NULL;
2012-06-28 13:29:22 +01:00
error_set_handler((cleanup_handler*) control_client_cleanup, client);
2012-06-28 13:29:22 +01:00
int i, linesc;
linesc = read_lines_until_blankline(client->socket, 256, &lines);
if (linesc < 1)
{
write(client->socket, "9: missing command\n", 19);
/* ignore failure */
}
else if (strcmp(lines[0], "acl") == 0) {
info("acl command received" );
if (control_acl(client, linesc-1, lines+1) < 0) {
debug("acl command failed");
}
2012-06-28 13:29:22 +01:00
}
else if (strcmp(lines[0], "mirror") == 0) {
info("mirror command received" );
if (control_mirror(client, linesc-1, lines+1) < 0) {
debug("mirror command failed");
}
2012-06-28 13:29:22 +01:00
}
else if (strcmp(lines[0], "break") == 0) {
info( "break command received" );
if ( control_break( client, linesc-1, lines+1) < 0) {
debug( "break command failed" );
}
}
2012-06-28 13:29:22 +01:00
else if (strcmp(lines[0], "status") == 0) {
info("status command received" );
if (control_status(client, linesc-1, lines+1) < 0) {
debug("status command failed");
2012-06-11 14:34:17 +01:00
}
}
2012-06-28 13:29:22 +01:00
else {
write(client->socket, "10: unknown command\n", 23);
}
for (i=0; i<linesc; i++) {
free(lines[i]);
}
free(lines);
control_client_cleanup(client, 0);
2012-06-22 10:05:41 +01:00
debug("control command handled" );
}