nbd: convert qemu-nbd server to use I/O channels for connection setup

This converts the qemu-nbd server to use the QIOChannelSocket
class for initial listener socket setup and accepting of client
connections. Actual I/O is still being performed against the
socket file descriptor using the POSIX socket APIs.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1455129674-17255-5-git-send-email-berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Daniel P. Berrange 2016-02-10 18:41:02 +00:00 committed by Paolo Bonzini
parent 064097d919
commit d0d6ff584d

View File

@ -22,19 +22,17 @@
#include "block/block_int.h"
#include "block/nbd.h"
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
#include "qemu/config-file.h"
#include "block/snapshot.h"
#include "qapi/util.h"
#include "qapi/qmp/qstring.h"
#include "qom/object_interfaces.h"
#include "io/channel-socket.h"
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <signal.h>
#include <libgen.h>
#include <pthread.h>
@ -53,7 +51,8 @@ static int persistent = 0;
static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
static int shared = 1;
static int nb_fds;
static int server_fd;
static QIOChannelSocket *server_ioc;
static int server_watch = -1;
static void usage(const char *name)
{
@ -236,19 +235,21 @@ static void *nbd_client_thread(void *arg)
char *device = arg;
off_t size;
uint32_t nbdflags;
int fd, sock;
QIOChannelSocket *sioc;
int fd;
int ret;
pthread_t show_parts_thread;
Error *local_error = NULL;
sock = socket_connect(saddr, &local_error, NULL, NULL);
if (sock < 0) {
sioc = qio_channel_socket_new();
if (qio_channel_socket_connect_sync(sioc,
saddr,
&local_error) < 0) {
error_report_err(local_error);
goto out;
}
ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
ret = nbd_receive_negotiate(sioc->fd, NULL, &nbdflags,
&size, &local_error);
if (ret < 0) {
if (local_error) {
@ -264,7 +265,7 @@ static void *nbd_client_thread(void *arg)
goto out_socket;
}
ret = nbd_init(fd, sock, nbdflags, size);
ret = nbd_init(fd, sioc->fd, nbdflags, size);
if (ret < 0) {
goto out_fd;
}
@ -285,13 +286,14 @@ static void *nbd_client_thread(void *arg)
goto out_fd;
}
close(fd);
object_unref(OBJECT(sioc));
kill(getpid(), SIGTERM);
return (void *) EXIT_SUCCESS;
out_fd:
close(fd);
out_socket:
closesocket(sock);
object_unref(OBJECT(sioc));
out:
kill(getpid(), SIGTERM);
return (void *) EXIT_FAILURE;
@ -308,7 +310,7 @@ static void nbd_export_closed(NBDExport *exp)
state = TERMINATED;
}
static void nbd_update_server_fd_handler(int fd);
static void nbd_update_server_watch(void);
static void nbd_client_closed(NBDClient *client)
{
@ -316,37 +318,51 @@ static void nbd_client_closed(NBDClient *client)
if (nb_fds == 0 && !persistent && state == RUNNING) {
state = TERMINATE;
}
nbd_update_server_fd_handler(server_fd);
nbd_update_server_watch();
nbd_client_put(client);
}
static void nbd_accept(void *opaque)
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque)
{
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
QIOChannelSocket *cioc;
int fd;
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
if (fd < 0) {
perror("accept");
return;
cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
NULL);
if (!cioc) {
return TRUE;
}
if (state >= TERMINATE) {
close(fd);
return;
object_unref(OBJECT(cioc));
return TRUE;
}
nb_fds++;
nbd_update_server_fd_handler(server_fd);
nbd_client_new(exp, fd, nbd_client_closed);
nbd_update_server_watch();
fd = dup(cioc->fd);
if (fd >= 0) {
nbd_client_new(exp, fd, nbd_client_closed);
}
object_unref(OBJECT(cioc));
return TRUE;
}
static void nbd_update_server_fd_handler(int fd)
static void nbd_update_server_watch(void)
{
if (nbd_can_accept()) {
qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd);
if (server_watch == -1) {
server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
G_IO_IN,
nbd_accept,
NULL, NULL);
}
} else {
qemu_set_fd_handler(fd, NULL, NULL, NULL);
if (server_watch != -1) {
g_source_remove(server_watch);
server_watch = -1;
}
}
}
@ -433,7 +449,6 @@ int main(int argc, char **argv)
int flags = BDRV_O_RDWR;
int partition = -1;
int ret = 0;
int fd;
bool seen_cache = false;
bool seen_discard = false;
bool seen_aio = false;
@ -632,15 +647,15 @@ int main(int argc, char **argv)
}
if (disconnect) {
fd = open(argv[optind], O_RDWR);
if (fd < 0) {
int nbdfd = open(argv[optind], O_RDWR);
if (nbdfd < 0) {
error_report("Cannot open %s: %s", argv[optind],
strerror(errno));
exit(EXIT_FAILURE);
}
nbd_disconnect(fd);
nbd_disconnect(nbdfd);
close(fd);
close(nbdfd);
printf("%s disconnected\n", argv[optind]);
@ -773,8 +788,9 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
fd = socket_listen(saddr, &local_err);
if (fd < 0) {
server_ioc = qio_channel_socket_new();
if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) {
object_unref(OBJECT(server_ioc));
error_report_err(local_err);
return 1;
}
@ -792,8 +808,7 @@ int main(int argc, char **argv)
memset(&client_thread, 0, sizeof(client_thread));
}
server_fd = fd;
nbd_update_server_fd_handler(fd);
nbd_update_server_watch();
/* now when the initialization is (almost) complete, chdir("/")
* to free any busy filesystems */