/* 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 .
*/
/** main() function for parsing and dispatching commands. Each mode has
* a corresponding structure which is filled in and passed to a do_ function
* elsewhere in the program.
*/
#include "flexnbd.h"
#include "serve.h"
#include "util.h"
#include "control.h"
#include "status.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "acl.h"
int flexnbd_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;
}
void flexnbd_create_shared(struct flexnbd *flexnbd,
const char *s_ctrl_sock)
{
NULLCHECK(flexnbd);
if (s_ctrl_sock) {
flexnbd->control = control_create(flexnbd, s_ctrl_sock);
} else {
flexnbd->control = NULL;
}
flexnbd->signal_fd = flexnbd_build_signal_fd();
}
struct flexnbd *flexnbd_create_serving(char *s_ip_address,
char *s_port,
char *s_file,
char *s_ctrl_sock,
int default_deny,
int acl_entries,
char **s_acl_entries,
int max_nbd_clients,
int use_killswitch)
{
struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd));
flexnbd->serve = server_create(flexnbd,
s_ip_address,
s_port,
s_file,
default_deny,
acl_entries,
s_acl_entries,
max_nbd_clients, use_killswitch, 1);
flexnbd_create_shared(flexnbd, s_ctrl_sock);
// Beats installing one handler per client instance
if (use_killswitch) {
struct sigaction act = {
.sa_sigaction = client_killswitch_hit,
.sa_flags = SA_RESTART | SA_SIGINFO
};
FATAL_UNLESS(0 == sigaction(CLIENT_KILLSWITCH_SIGNAL, &act, NULL),
"Installing client killswitch signal failed");
}
return flexnbd;
}
struct flexnbd *flexnbd_create_listening(char *s_ip_address,
char *s_port,
char *s_file,
char *s_ctrl_sock,
int default_deny,
int acl_entries,
char **s_acl_entries)
{
struct flexnbd *flexnbd = xmalloc(sizeof(struct flexnbd));
flexnbd->serve = server_create(flexnbd,
s_ip_address,
s_port,
s_file,
default_deny,
acl_entries, s_acl_entries, 1, 0, 0);
flexnbd_create_shared(flexnbd, s_ctrl_sock);
// listen can't use killswitch, as mirror may pause on sending things
// for a very long time.
return flexnbd;
}
void flexnbd_spawn_control(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
NULLCHECK(flexnbd->control);
pthread_t *control_thread = &flexnbd->control->thread;
FATAL_UNLESS(0 == pthread_create(control_thread,
NULL,
control_runner,
flexnbd->control),
"Couldn't create the control thread");
}
void flexnbd_stop_control(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
NULLCHECK(flexnbd->control);
control_signal_close(flexnbd->control);
pthread_t tid = flexnbd->control->thread;
FATAL_UNLESS(0 == pthread_join(tid, NULL),
"Failed joining the control thread");
debug("Control thread %p pthread_join returned", tid);
}
int flexnbd_signal_fd(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
return flexnbd->signal_fd;
}
void flexnbd_destroy(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
if (flexnbd->control) {
control_destroy(flexnbd->control);
}
close(flexnbd->signal_fd);
free(flexnbd);
}
struct server *flexnbd_server(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
return flexnbd->serve;
}
void flexnbd_replace_acl(struct flexnbd *flexnbd, struct acl *acl)
{
NULLCHECK(flexnbd);
server_replace_acl(flexnbd_server(flexnbd), acl);
}
struct status *flexnbd_status_create(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
struct status *status;
status = status_create(flexnbd_server(flexnbd));
return status;
}
void flexnbd_set_server(struct flexnbd *flexnbd, struct server *serve)
{
NULLCHECK(flexnbd);
flexnbd->serve = serve;
}
/* Get the default_deny of the current server object. */
int flexnbd_default_deny(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
return server_default_deny(flexnbd->serve);
}
void make_writable(const char *filename)
{
NULLCHECK(filename);
FATAL_IF_NEGATIVE(chmod(filename, S_IWUSR),
"Couldn't chmod %s: %s", filename, strerror(errno));
}
int flexnbd_serve(struct flexnbd *flexnbd)
{
NULLCHECK(flexnbd);
int success;
struct self_pipe *open_signal = NULL;
if (flexnbd->control) {
debug("Spawning control thread");
flexnbd_spawn_control(flexnbd);
open_signal = flexnbd->control->open_signal;
}
success = do_serve(flexnbd->serve, open_signal);
debug("do_serve success is %d", success);
if (flexnbd->control) {
debug("Stopping control thread");
flexnbd_stop_control(flexnbd);
debug("Control thread stopped");
}
return success;
}