Rewrote error & log functions to be more general, use longjmp to get out of

trouble and into predictable cleanup functions (one for each of serve,
client & control contexts).  We use 'fatal' to mean 'kill the thread' and
'error' to mean 'don't kill the thread', assuming some recovery action,
except I don't use error anywhere yet.
This commit is contained in:
Matthew Bloch
2012-06-09 02:25:12 +01:00
parent 8691533d88
commit b546539ab8
12 changed files with 259 additions and 174 deletions

View File

@@ -97,7 +97,7 @@ void write_not_zeroes(struct client* client, off64_t from, int len)
fprintf(stderr, "\n");
}
#define DO_READ(dst, len) CLIENT_ERROR_ON_FAILURE( \
#define DO_READ(dst, len) FATAL_IF_NEGATIVE( \
readloop( \
client->socket, \
(dst), \
@@ -168,7 +168,7 @@ int client_read_request( struct client * client , struct nbd_request *out_reques
FD_ZERO(&fds);
FD_SET(client->socket, &fds);
self_pipe_fd_set( client->stop_signal, &fds );
CLIENT_ERROR_ON_FAILURE(select(FD_SETSIZE, &fds, NULL, NULL, NULL),
FATAL_IF_NEGATIVE(select(FD_SETSIZE, &fds, NULL, NULL, NULL),
"select() failed");
if ( self_pipe_fd_isset( client->stop_signal, &fds ) )
@@ -180,7 +180,7 @@ int client_read_request( struct client * client , struct nbd_request *out_reques
return 0; /* neat point to close the socket */
}
else {
CLIENT_ERROR_ON_FAILURE(-1, "Error reading request");
FATAL_IF_NEGATIVE(-1, "Error reading request");
}
}
@@ -223,7 +223,7 @@ void client_write_init( struct client * client, uint64_t size )
nbd_h2r_init( &init, &init_raw );
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
writeloop(client->socket, &init_raw, sizeof(init_raw)),
"Couldn't send hello"
);
@@ -239,7 +239,7 @@ int client_request_needs_reply( struct client * client, struct nbd_request reque
debug("request type %d", request.type);
if (request.magic != REQUEST_MAGIC)
CLIENT_ERROR("Bad magic %08x", request.magic);
fatal("Bad magic %08x", request.magic);
switch (request.type)
{
@@ -265,7 +265,7 @@ int client_request_needs_reply( struct client * client, struct nbd_request reque
return 0;
default:
CLIENT_ERROR("Unknown request %08x", request.type);
fatal("Unknown request %08x", request.type);
}
return 1;
}
@@ -279,7 +279,7 @@ void client_reply_to_read( struct client* client, struct nbd_request request )
client_write_reply( client, &request, 0);
offset = request.from;
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
sendfileloop(
client->socket,
client->fileno,
@@ -298,7 +298,7 @@ void client_reply_to_write( struct client* client, struct nbd_request request )
write_not_zeroes( client, request.from, request.len );
}
else {
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
readloop(
client->socket,
client->mapped + request.from,
@@ -315,7 +315,7 @@ void client_reply_to_write( struct client* client, struct nbd_request request )
uint64_t from_rounded = request.from & (!0xfff);
uint64_t len_rounded = request.len + (request.from - from_rounded);
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
msync(
client->mapped + from_rounded,
len_rounded,
@@ -371,12 +371,26 @@ void client_send_hello(struct client* client)
client_write_init( client, client->serve->size );
}
void client_cleanup(struct client* client, int fatal)
{
info("client cleanup");
if (client->socket)
close(client->socket);
if (client->mapped)
munmap(client->mapped, client->serve->size);
if (client->fileno)
close(client->fileno);
}
void* client_serve(void* client_uncast)
{
struct client* client = (struct client*) client_uncast;
error_set_handler((cleanup_handler*) client_cleanup, client);
//client_open_file(client);
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
open_and_mmap(
client->serve->filename,
&client->fileno,
@@ -390,14 +404,13 @@ void* client_serve(void* client_uncast)
while (client_serve_request(client) == 0)
;
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
close(client->socket),
"Couldn't close socket %d",
client->socket
);
close(client->fileno);
munmap(client->mapped, client->serve->size);
client_cleanup(client, 0);
return NULL;
}

View File

@@ -115,6 +115,13 @@ void* mirror_runner(void* serve_params_uncast)
written += run;
}
current += run;
if (serve->mirror->signal_abandon) {
if (pass == last_pass)
server_unlock_io( serve );
close(serve->mirror->client);
goto abandon_mirror;
}
}
/* if we've not written anything */
@@ -122,6 +129,7 @@ void* mirror_runner(void* serve_params_uncast)
pass = last_pass;
}
/* a successful finish ends here */
switch (serve->mirror->action_at_finish)
{
case ACTION_PROXY:
@@ -131,19 +139,20 @@ void* mirror_runner(void* serve_params_uncast)
break;
case ACTION_EXIT:
debug("exit!");
close(serve->mirror->client);
serve_signal_close( serve );
/* fall through */
case ACTION_NOTHING:
debug("nothing!");
close(serve->mirror->client);
}
server_unlock_io( serve );
abandon_mirror:
free(serve->mirror->dirty_map);
free(serve->mirror);
serve->mirror = NULL; /* and we're gone */
server_unlock_io( serve );
return NULL;
}
@@ -226,7 +235,7 @@ int control_mirror(struct control_params* client, int linesc, char** lines)
mirror->max_bytes_per_second = max_bytes_per_second;
mirror->action_at_finish = action_at_finish;
CLIENT_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
open_and_mmap(
client->serve->filename,
&map_fd,
@@ -242,7 +251,7 @@ int control_mirror(struct control_params* client, int linesc, char** lines)
client->serve->mirror = mirror;
CLIENT_ERROR_ON_FAILURE( /* FIXME should free mirror on error */
FATAL_IF_NEGATIVE( /* FIXME should free mirror on error */
pthread_create(
&mirror->thread,
NULL,
@@ -287,6 +296,13 @@ int control_status(struct control_params* client, int linesc, char** lines)
return 0;
}
void control_cleanup(struct control_params* client, int fatal)
{
if (client->socket)
close(client->socket);
free(client);
}
/** Master command parser for control socket connections, delegates quickly */
void* control_serve(void* client_uncast)
{
@@ -294,6 +310,8 @@ void* control_serve(void* client_uncast)
char **lines = NULL;
int finished=0;
error_set_handler((cleanup_handler*) control_cleanup, client);
while (!finished) {
int i, linesc;
linesc = read_lines_until_blankline(client->socket, 256, &lines);
@@ -326,8 +344,8 @@ void* control_serve(void* client_uncast)
free(lines);
}
close(client->socket);
free(client);
control_cleanup(client, 0);
return NULL;
}
@@ -340,7 +358,7 @@ void accept_control_connection(struct server* params, int client_fd, union mysoc
control_params->socket = client_fd;
control_params->serve = params;
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
pthread_create(
&control_thread,
NULL,
@@ -359,7 +377,7 @@ void serve_open_control_socket(struct server* params)
return;
params->control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
SERVER_ERROR_ON_FAILURE(params->control_fd ,
FATAL_IF_NEGATIVE(params->control_fd ,
"Couldn't create control socket");
memset(&bind_address, 0, sizeof(bind_address));
@@ -368,13 +386,13 @@ void serve_open_control_socket(struct server* params)
unlink(params->control_socket_name); /* ignore failure */
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
bind(params->control_fd , &bind_address, sizeof(bind_address)),
"Couldn't bind control socket to %s",
params->control_socket_name
);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
listen(params->control_fd , 5),
"Couldn't listen on control socket"
);

View File

@@ -57,16 +57,16 @@ void params_serve(
{
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");
FATAL_IF_NULL(s_ip_address, "No IP address supplied");
FATAL_IF_NULL(s_port, "No port number supplied");
FATAL_IF_NULL(s_file, "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);
FATAL_IF_ZERO(
parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address),
"Couldn't parse server address '%s' (use 0 if "
"you want to bind to all IPs)",
s_ip_address
);
/* control_socket_name is optional. It just won't get created if
* we pass NULL. */
@@ -74,11 +74,11 @@ void params_serve(
out->acl = acl_create( acl_entries, s_acl_entries, default_deny );
if (out->acl && out->acl->len != acl_entries)
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[out->acl->len]);
fatal("Bad ACL entry '%s'", s_acl_entries[out->acl->len]);
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");
fatal("Port number must be >= 0 and <= 65535");
out->bind_to.v4.sin_port = htobe16(out->bind_to.v4.sin_port);
out->filename = s_file;
@@ -111,26 +111,24 @@ void params_readwrite(
char* s_length_or_filename
)
{
if (s_ip_address == NULL)
SERVER_ERROR("No IP address supplied");
if (s_port == NULL)
SERVER_ERROR("No port number supplied");
if (s_from == NULL)
SERVER_ERROR("No from supplied");
if (s_length_or_filename == NULL)
SERVER_ERROR("No length supplied");
FATAL_IF_NULL(s_ip_address, "No IP address supplied");
FATAL_IF_NULL(s_port, "No port number supplied");
FATAL_IF_NULL(s_from, "No from supplied");
FATAL_IF_NULL(s_length_or_filename, "No length supplied");
if (parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address) == 0)
SERVER_ERROR("Couldn't parse connection address '%s'",
s_ip_address);
FATAL_IF_ZERO(
parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address),
"Couldn't parse connection address '%s'",
s_ip_address
);
if (s_bind_address != NULL && parse_ip_to_sockaddr(&out->connect_from.generic, s_bind_address) == 0)
SERVER_ERROR("Couldn't parse bind address '%s'", s_bind_address);
fatal("Couldn't parse bind address '%s'", s_bind_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)
SERVER_ERROR("Port number must be >= 0 and <= 65535");
fatal("Port number must be >= 0 and <= 65535");
out->connect_to.v4.sin_port = htobe16(out->connect_to.v4.sin_port);
out->from = atol(s_from);
@@ -143,12 +141,12 @@ void params_readwrite(
else {
out->data_fd = open(
s_length_or_filename, O_RDONLY);
SERVER_ERROR_ON_FAILURE(out->data_fd,
FATAL_IF_NEGATIVE(out->data_fd,
"Couldn't open %s", s_length_or_filename);
out->len = lseek64(out->data_fd, 0, SEEK_END);
SERVER_ERROR_ON_FAILURE(out->len,
FATAL_IF_NEGATIVE(out->len,
"Couldn't find length of %s", s_length_or_filename);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
lseek64(out->data_fd, 0, SEEK_SET),
"Couldn't rewind %s", s_length_or_filename
);
@@ -189,7 +187,7 @@ void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char
*default_deny = 1;
break;
case 'v':
set_debug(1);
log_level = 0;
break;
default:
exit_err( serve_help_text );
@@ -221,7 +219,7 @@ void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **bind_ad
*bind_addr = optarg;
break;
case 'v':
set_debug(1);
log_level = 0;
break;
default:
exit_err( read_help_text );
@@ -240,7 +238,7 @@ void read_sock_param( int c, char **sock, char *help_text )
*sock = optarg;
break;
case 'v':
set_debug(1);
log_level = 0;
break;
default:
exit_err( help_text );
@@ -272,7 +270,7 @@ void read_mirror_param( int c, char **sock, char **ip_addr, char **ip_port, char
case 'b':
*bind_addr = optarg;
case 'v':
set_debug(1);
log_level = 0;
break;
default:
exit_err( mirror_help_text );
@@ -538,7 +536,6 @@ int main(int argc, char** argv)
{
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
error_init();
set_debug(0);
if (argc < 2) {
exit_err( help_help_text );

View File

@@ -1,8 +1,8 @@
#ifndef __IOUTIL_H
#define __IOUTIL_H
#include "serve.h"
struct bitset_mapping; /* don't need whole of bitset.h here */
/** Returns a bit field representing which blocks are allocated in file
* descriptor ''fd''. You must supply the size, and the resolution at which
@@ -10,7 +10,7 @@
* allocated blocks at a finer resolution than you've asked for, any block
* or part block will count as "allocated" with the corresponding bit set.
*/
char* build_allocation_map(int fd, off64_t size, int resolution);
struct bitset_mapping* build_allocation_map(int fd, off64_t size, int resolution);
/** Repeat a write() operation that succeeds partially until ''size'' bytes
* are written, or an error is returned, when it returns -1 as usual.

View File

@@ -10,15 +10,15 @@
int socket_connect(struct sockaddr* to, struct sockaddr* from)
{
int fd = socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0);
SERVER_ERROR_ON_FAILURE(fd, "Couldn't create client socket");
FATAL_IF_NEGATIVE(fd, "Couldn't create client socket");
if (NULL != from)
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
bind(fd, from, sizeof(struct sockaddr_in6)),
"bind() failed"
);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
connect(fd, to, sizeof(struct sockaddr_in6)),"connect failed"
);
return fd;
@@ -27,12 +27,12 @@ int socket_connect(struct sockaddr* to, struct sockaddr* from)
off64_t socket_nbd_read_hello(int fd)
{
struct nbd_init init;
SERVER_ERROR_ON_FAILURE(readloop(fd, &init, sizeof(init)),
FATAL_IF_NEGATIVE(readloop(fd, &init, sizeof(init)),
"Couldn't read init");
if (strncmp(init.passwd, INIT_PASSWD, 8) != 0)
SERVER_ERROR("wrong passwd");
fatal("wrong passwd");
if (be64toh(init.magic) != INIT_MAGIC)
SERVER_ERROR("wrong magic (%x)", be64toh(init.magic));
fatal("wrong magic (%x)", be64toh(init.magic));
return be64toh(init.size);
}
@@ -48,14 +48,14 @@ void fill_request(struct nbd_request *request, int type, int from, int len)
void read_reply(int fd, struct nbd_request *request, struct nbd_reply *reply)
{
SERVER_ERROR_ON_FAILURE(readloop(fd, reply, sizeof(*reply)),
FATAL_IF_NEGATIVE(readloop(fd, reply, sizeof(*reply)),
"Couldn't read reply");
if (be32toh(reply->magic) != REPLY_MAGIC)
SERVER_ERROR("Reply magic incorrect (%p)", be32toh(reply->magic));
fatal("Reply magic incorrect (%p)", be32toh(reply->magic));
if (be32toh(reply->error) != 0)
SERVER_ERROR("Server replied with error %d", be32toh(reply->error));
fatal("Server replied with error %d", be32toh(reply->error));
if (strncmp(request->handle, reply->handle, 8) != 0)
SERVER_ERROR("Did not reply with correct handle");
fatal("Did not reply with correct handle");
}
void socket_nbd_read(int fd, off64_t from, int len, int out_fd, void* out_buf)
@@ -64,16 +64,16 @@ void socket_nbd_read(int fd, off64_t from, int len, int out_fd, void* out_buf)
struct nbd_reply reply;
fill_request(&request, REQUEST_READ, from, len);
SERVER_ERROR_ON_FAILURE(writeloop(fd, &request, sizeof(request)),
FATAL_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)),
"Couldn't write request");
read_reply(fd, &request, &reply);
if (out_buf) {
SERVER_ERROR_ON_FAILURE(readloop(fd, out_buf, len),
FATAL_IF_NEGATIVE(readloop(fd, out_buf, len),
"Read failed");
}
else {
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
splice_via_pipe_loop(fd, out_fd, len),
"Splice failed"
);
@@ -86,15 +86,15 @@ void socket_nbd_write(int fd, off64_t from, int len, int in_fd, void* in_buf)
struct nbd_reply reply;
fill_request(&request, REQUEST_WRITE, from, len);
SERVER_ERROR_ON_FAILURE(writeloop(fd, &request, sizeof(request)),
FATAL_IF_NEGATIVE(writeloop(fd, &request, sizeof(request)),
"Couldn't write request");
if (in_buf) {
SERVER_ERROR_ON_FAILURE(writeloop(fd, in_buf, len),
FATAL_IF_NEGATIVE(writeloop(fd, in_buf, len),
"Write failed");
}
else {
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
splice_via_pipe_loop(in_fd, fd, len),
"Splice failed"
);
@@ -106,7 +106,7 @@ void socket_nbd_write(int fd, off64_t from, int len, int in_fd, void* in_buf)
#define CHECK_RANGE(error_type) { \
off64_t size = socket_nbd_read_hello(params->client); \
if (params->from < 0 || (params->from + params->len) > size) \
SERVER_ERROR(error_type \
fatal(error_type \
" request %d+%d is out of range given size %d", \
params->from, params->len, size\
); \

View File

@@ -17,12 +17,12 @@ void do_remote_command(char* command, char* socket_name, int argc, char** argv)
memset(&address, 0, sizeof(address));
SERVER_ERROR_ON_FAILURE(remote, "Couldn't create client socket");
FATAL_IF_NEGATIVE(remote, "Couldn't create client socket");
address.sun_family = AF_UNIX;
strncpy(address.sun_path, socket_name, sizeof(address.sun_path));
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
connect(remote, (struct sockaddr*) &address, sizeof(address)),
"Couldn't connect to %s", socket_name
);
@@ -35,7 +35,7 @@ void do_remote_command(char* command, char* socket_name, int argc, char** argv)
}
write(remote, &newline, 1);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
read_until_newline(remote, response, max_response),
"Couldn't read response from %s", socket_name
);

View File

@@ -35,8 +35,7 @@ void self_pipe_server_error( int err, char *msg )
strerror_r( err, errbuf, 1024 );
debug(msg);
SERVER_ERROR( "%s\t%s", msg, errbuf );
fatal( "%s\t%s", msg, errbuf );
}
/**

View File

@@ -46,7 +46,7 @@ int server_lock_io( struct server * serve)
{
NULLCHECK( serve );
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
pthread_mutex_lock(&serve->l_io),
"Problem with I/O lock"
);
@@ -59,7 +59,7 @@ void server_unlock_io( struct server* serve )
{
NULLCHECK( serve );
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
pthread_mutex_unlock(&serve->l_io),
"Problem with I/O unlock"
);
@@ -75,26 +75,26 @@ void serve_open_server_socket(struct server* params)
params->server_fd= socket(params->bind_to.generic.sa_family == AF_INET ?
PF_INET : PF_INET6, SOCK_STREAM, 0);
SERVER_ERROR_ON_FAILURE(params->server_fd,
FATAL_IF_NEGATIVE(params->server_fd,
"Couldn't create server socket");
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
setsockopt(params->server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)),
"Couldn't set SO_REUSEADDR"
);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
setsockopt(params->server_fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)),
"Couldn't set TCP_NODELAY"
);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
bind(params->server_fd, &params->bind_to.generic,
sizeof(params->bind_to)),
"Couldn't bind server to IP address"
);
SERVER_ERROR_ON_FAILURE(
FATAL_IF_NEGATIVE(
listen(params->server_fd, params->tcp_backlog),
"Couldn't listen on server socket"
);
@@ -121,7 +121,7 @@ int tryjoin_client_thread( struct client_tbl_entry *entry, int (*joinfunc)(pthre
if (joinfunc(entry->thread, &status) != 0) {
if (errno != EBUSY)
SERVER_ERROR_ON_FAILURE(-1, "Problem with joining thread");
FATAL_IF_NEGATIVE(-1, "Problem with joining thread");
}
else {
debug("nbd thread %p exited (%s) with status %ld",
@@ -188,8 +188,6 @@ int cleanup_and_find_client_slot(struct server* params)
}
}
if ( -1 == slot ) { debug( "No client slot found." ); }
return slot;
}
@@ -230,7 +228,7 @@ int server_should_accept_client(
debug( "Rejecting client %s: Access control error", s_client_address );
debug( "We %s have an acl, and default_deny is %s",
(params->acl ? "do" : "do not"),
(params->default_deny ? "true" : "false") );
(params->acl->default_deny ? "true" : "false") );
write(client_fd, "Access control error", 20);
return 0;
}
@@ -263,6 +261,7 @@ void accept_nbd_client(
slot = cleanup_and_find_client_slot(params);
if (slot < 0) {
warn("too many clients to accept connection");
write(client_fd, "Too many clients", 16);
close(client_fd);
return;
@@ -298,6 +297,8 @@ void server_close_clients( struct server *params )
{
NULLCHECK(params);
info("closing all clients");
int i, j;
struct client_tbl_entry *entry;
@@ -318,6 +319,7 @@ void server_close_clients( struct server *params )
void serve_accept_loop(struct server* params)
{
NULLCHECK( params );
info("accept loop starting");
while (1) {
int activity_fd, client_fd;
union mysockaddr client_address;
@@ -330,7 +332,7 @@ void serve_accept_loop(struct server* params)
if (params->control_socket_name)
FD_SET(params->control_fd, &fds);
SERVER_ERROR_ON_FAILURE(select(FD_SETSIZE, &fds,
FATAL_IF_NEGATIVE(select(FD_SETSIZE, &fds,
NULL, NULL, NULL), "select() failed");
if ( self_pipe_fd_isset( params->close_signal, &fds ) ){
@@ -343,11 +345,11 @@ void serve_accept_loop(struct server* params)
client_fd = accept(activity_fd, &client_address.generic, &socklen);
if (activity_fd == params->server_fd) {
debug("Accepted nbd client socket");
info("Accepted nbd client socket");
accept_nbd_client(params, client_fd, &client_address);
}
if (activity_fd == params->control_fd) {
debug("Accepted control client socket");
info("Accepted control client socket");
accept_control_connection(params, client_fd, &client_address);
}
@@ -364,10 +366,10 @@ void serve_init_allocation_map(struct server* params)
int fd = open(params->filename, O_RDONLY);
off64_t size;
SERVER_ERROR_ON_FAILURE(fd, "Couldn't open %s", params->filename);
FATAL_IF_NEGATIVE(fd, "Couldn't open %s", params->filename);
size = lseek64(fd, 0, SEEK_END);
params->size = size;
SERVER_ERROR_ON_FAILURE(size, "Couldn't find size of %s",
FATAL_IF_NEGATIVE(size, "Couldn't find size of %s",
params->filename);
params->allocation_map =
build_allocation_map(fd, size, block_allocation_resolution);
@@ -379,34 +381,43 @@ void serve_init_allocation_map(struct server* params)
void serve_signal_close( struct server * serve )
{
NULLCHECK( serve );
info("signalling close");
self_pipe_signal( serve->close_signal );
}
/** Closes sockets, frees memory and waits for all client threads to finish */
void serve_cleanup(struct server* params)
void serve_cleanup(struct server* params, int fatal)
{
NULLCHECK( params );
info("cleaning up");
int i;
close(params->server_fd);
close(params->control_fd);
if (params->server_fd)
close(params->server_fd);
if (params->control_fd)
close(params->control_fd);
if (params->acl)
free(params->acl);
//free(params->filename);
if (params->control_socket_name)
//free(params->control_socket_name);
;
pthread_mutex_destroy(&params->l_io);
if (params->proxy_fd);
close(params->proxy_fd);
self_pipe_destroy( params->close_signal );
if (params->close_signal)
self_pipe_destroy( params->close_signal );
free(params->allocation_map);
if (params->allocation_map)
free(params->allocation_map);
if (params->mirror)
debug("mirror thread running! this should not happen!");
if (params->mirror) {
pthread_t mirror_t = params->mirror->thread;
params->mirror->signal_abandon = 1;
pthread_join(mirror_t, NULL);
}
for (i=0; i < MAX_NBD_CLIENTS; i++) {
void* status;
@@ -423,17 +434,18 @@ void do_serve(struct server* params)
{
NULLCHECK( params );
error_set_handler((cleanup_handler*) serve_cleanup, params);
pthread_mutex_init(&params->l_io, NULL);
params->close_signal = self_pipe_create();
if ( NULL == params->close_signal) {
SERVER_ERROR( "close signal creation failed" );
fatal( "close signal creation failed" );
}
serve_open_server_socket(params);
serve_open_control_socket(params);
serve_init_allocation_map(params);
serve_accept_loop(params);
serve_cleanup(params);
serve_cleanup(params, 0);
}

View File

@@ -22,6 +22,8 @@ enum mirror_finish_action {
struct mirror_status {
pthread_t thread;
/* set to 1, then join thread to make mirror terminate early */
int signal_abandon;
int client;
char *filename;
off64_t max_bytes_per_second;

View File

@@ -9,47 +9,43 @@
#include "util.h"
static pthread_t main_thread;
static int global_debug;
pthread_key_t cleanup_handler_key;
int log_level = 1;
void error_init()
{
main_thread = pthread_self();
pthread_key_create(&cleanup_handler_key, free);
}
void error(int consult_errno, int fatal, int close_socket, pthread_mutex_t* unlock, const char* format, ...)
void error_handler(int fatal)
{
DECLARE_ERROR_CONTEXT(context);
if (!context) {
pthread_exit((void*) 1);
}
longjmp(context->jmp, 1);
}
void mylog(int line_level, const char* format, ...)
{
va_list argptr;
fprintf(stderr, "*** ");
if (line_level < log_level)
return;
va_start(argptr, format);
vfprintf(stderr, format, argptr);
va_end(argptr);
if (consult_errno) {
fprintf(stderr, " (errno=%d, %s)", errno, strerror(errno));
}
if (close_socket) { close(close_socket); }
if (unlock) { pthread_mutex_unlock(unlock); }
fprintf(stderr, "\n");
if (fatal || pthread_equal(pthread_self(), main_thread)) {
exit(1);
}
else {
fprintf(stderr, "Killing Thread\n");
pthread_exit((void*) 1);
}
}
void* xrealloc(void* ptr, size_t size)
{
void* p = realloc(ptr, size);
if (p == NULL)
SERVER_ERROR("couldn't xrealloc %d bytes", size);
FATAL_IF_NULL(p, "couldn't xrealloc %d bytes", ptr ? "realloc" : "malloc", size);
return p;
}
@@ -60,26 +56,3 @@ void* xmalloc(size_t size)
return p;
}
void set_debug(int value) {
global_debug = value;
}
#ifdef DEBUG
# include <sys/times.h>
# include <stdarg.h>
void debug(const char *msg, ...) {
va_list argp;
va_start( argp, msg );
if ( global_debug ) {
fprintf(stderr, "%08x %4d: ", (int) pthread_self(), (int) clock() );
vfprintf(stderr, msg, argp);
fprintf(stderr, "\n");
}
va_end( argp );
}
#endif

View File

@@ -3,36 +3,106 @@
#include <stdio.h>
#include <pthread.h>
void error_init();
void error(int consult_errno, int fatal, int close_socket, pthread_mutex_t* unlock, const char* format, ...);
#include <string.h>
#include <errno.h>
void* xrealloc(void* ptr, size_t size);
void* xmalloc(size_t size);
void set_debug(int value);
typedef void (cleanup_handler)(void* /* context */, int /* is fatal? */);
/* set from 0 - 5 depending on what level of verbosity you want */
extern int log_level;
/* set up the error globals */
void error_init();
/* error_set_handler must be a macro not a function due to setjmp stack rules */
#include <setjmp.h>
struct error_handler_context {
jmp_buf jmp;
cleanup_handler* handler;
void* data;
};
#define DECLARE_ERROR_CONTEXT(name) \
struct error_handler_context *name = (struct error_handler_context*) \
pthread_getspecific(cleanup_handler_key)
/* clean up with the given function & data when error_handler() is invoked,
* non-fatal errors will also return here (if that's dangerous, use fatal()
* instead of error()).
*
* error handlers are thread-local, so you need to call this when starting a
* new thread.
*/
extern pthread_key_t cleanup_handler_key;
#define error_set_handler(cleanfn, cleandata) \
{ \
DECLARE_ERROR_CONTEXT(old); \
struct error_handler_context *context = \
xmalloc(sizeof(struct error_handler_context)); \
context->handler = (cleanfn); \
context->data = (cleandata); \
\
switch (setjmp(context->jmp)) \
{ \
case 0: /* setup time */ \
if (old) \
free(old); \
pthread_setspecific(cleanup_handler_key, context); \
break; \
case 1: /* fatal error, terminate thread */ \
context->handler(context->data, 1); \
pthread_exit((void*) 1); \
abort(); \
case 2: /* non-fatal error, return to context of error handler setup */ \
context->handler(context->data, 0); \
default: \
abort(); \
} \
}
/* invoke the error handler - longjmps away, don't use directly */
void error_handler(int fatal);
/* mylog a line at the given level (0 being most verbose) */
void mylog(int line_level, const char* format, ...);
#ifdef DEBUG
void debug(const char*msg, ...);
# define debug(msg, ...) mylog(0, "%s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
#else
/* no-op */
# define debug( msg, ...)
# define debug(msg, ...) /* no-op */
#endif
#define CLIENT_ERROR(msg, ...) \
error(0, 0, client->socket, &client->serve->l_io, msg, ##__VA_ARGS__)
#define CLIENT_ERROR_ON_FAILURE(test, msg, ...) \
if (test < 0) { error(1, 0, client->socket, &client->serve->l_io, msg, ##__VA_ARGS__); }
/* informational message, not expected to be compiled out */
#define info(msg, ...) mylog(1, "%s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
#define SERVER_ERROR(msg, ...) \
error(0, 1, 0, NULL, msg, ##__VA_ARGS__)
#define SERVER_ERROR_ON_FAILURE(test, msg, ...) \
if (test < 0) { error(1, 1, 0, NULL, msg, ##__VA_ARGS__); }
/* messages that might indicate a problem */
#define warn(msg, ...) mylog(2, "%s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__)
/* mylog a message and invoke the error handler to recover */
#define error(msg, ...) { \
mylog(3, "*** %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__); \
error_handler(0); \
}
#define NULLCHECK(x) \
do { if ( NULL == (x) ) { SERVER_ERROR( "Null " #x "." ); } } while(0)
/* mylog a message and invoke the error handler to kill the current thread */
#define fatal(msg, ...) { \
mylog(4, "*** %s:%d: " msg, __FILE__, __LINE__, ##__VA_ARGS__); \
error_handler(1); \
}
#define ERROR_IF_NULL(value, msg, ...) if (NULL == value) error(msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno))
#define ERROR_IF_NEGATIVE(value, msg, ...) if (value < 0) error(msg, ##__VA_ARGS__)
#define ERROR_IF_ZERO(value, msg, ...) if (0 == value) error(msg, ##__VA_ARGS__)
#define FATAL_IF_NULL(value, msg, ...) if (NULL == value) fatal(msg, ##__VA_ARGS__)
#define FATAL_IF_NEGATIVE(value, msg, ...) if (value < 0) fatal(msg " (errno=%d, %s)", ##__VA_ARGS__, errno, strerror(errno))
#define FATAL_IF_ZERO(value, msg, ...) if (0 == value) fatal(msg, ##__VA_ARGS__)
#define NULLCHECK(value) FATAL_IF_NULL(value, "BUG: " #value " is null")
#endif

View File

@@ -2,6 +2,7 @@
#include <stdio.h>
#include "acl.h"
#include "util.h"
START_TEST( test_null_acl )
{
@@ -110,11 +111,11 @@ Suite* acl_suite()
int main(void)
{
set_debug(1);
int number_failed;
Suite *s = acl_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
log_level = 0;
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;