From 3cbae1636b9d2f7e463675e5600d60d74a84962b Mon Sep 17 00:00:00 2001 Message-Id: <3cbae1636b9d2f7e463675e5600d60d74a84962b.1374754302.git.minovotn@redhat.com> In-Reply-To: <5d75a8513d08b33975bdf5971871c0c977167cd1.1374754301.git.minovotn@redhat.com> References: <5d75a8513d08b33975bdf5971871c0c977167cd1.1374754301.git.minovotn@redhat.com> From: Gerd Hoffmann Date: Mon, 24 Jun 2013 07:05:48 +0200 Subject: [PATCH 37/65] chardev: add socket chardev support to chardev-add (qmp) RH-Author: Gerd Hoffmann Message-id: <1372057576-26450-38-git-send-email-kraxel@redhat.com> Patchwork-id: 52125 O-Subject: [RHEL-6.5 qemu-kvm PATCH v2 37/65] chardev: add socket chardev support to chardev-add (qmp) Bugzilla: 676568 RH-Acked-by: Laszlo Ersek RH-Acked-by: Hans de Goede RH-Acked-by: Luiz Capitulino qemu_chr_open_socket is split into two functions. All initialization after creating the socket file handler is split away into the new qemu_chr_open_socket_fd function. chr->filename doesn't get filled from QemuOpts any more. Qemu gathers the information using getsockname and getnameinfo instead. This way it will also work correctly for file handles passed via file descriptor passing. Finally qmp_chardev_open_socket() is the actual qmp hotplug implementation which basically just calls socket_listen or socket_connect and the new qemu_chr_open_socket_fd function. Signed-off-by: Gerd Hoffmann (cherry picked from commit f6bd5d6ec514939c421fcd411d1a39bc7dad0948) Conflicts: qemu-char.c --- qapi-schema.json | 28 ++++++++- qemu-char.c | 170 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 140 insertions(+), 58 deletions(-) Signed-off-by: Michal Novotny --- qapi-schema.json | 28 ++++++++- qemu-char.c | 170 +++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 140 insertions(+), 58 deletions(-) diff --git a/qapi-schema.json b/qapi-schema.json index 430b91e..3b27dd9 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -439,6 +439,27 @@ 'type' : 'ChardevPortKind'} } ## +# @ChardevSocket: +# +# Configuration info for socket chardevs. +# +# @addr: socket address to listen on (server=true) +# or connect to (server=false) +# @server: #optional create server socket (default: true) +# @wait: #optional wait for connect (not used for server +# sockets, default: false) +# @nodelay: #optional set TCP_NODELAY socket option (default: false) +# @telnet: #optional enable telnet protocol (default: false) +# +# Since: 1.4 +## +{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*server' : 'bool', + '*wait' : 'bool', + '*nodelay' : 'bool', + '*telnet' : 'bool' } } + +## # @ChardevBackend: # # Configuration info for the new chardev backend. @@ -447,9 +468,10 @@ ## { 'type': 'ChardevDummy', 'data': { } } -{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', - 'port' : 'ChardevPort', - 'null' : 'ChardevDummy' } } +{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', + 'port' : 'ChardevPort', + 'socket' : 'ChardevSocket', + 'null' : 'ChardevDummy' } } ## # @ChardevReturn: diff --git a/qemu-char.c b/qemu-char.c index 1c18391..ef73321 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2462,10 +2462,90 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, + bool is_listen, bool is_telnet, + bool is_waitconnect, + Error **errp) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + const char *left = "", *right = ""; + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + + memset(&ss, 0, ss_len); + if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) { + error_setg(errp, "getsockname: %s", strerror(errno)); + return NULL; + } + + chr = g_malloc0(sizeof(CharDriverState)); + s = g_malloc0(sizeof(TCPCharDriver)); + + s->connected = 0; + s->fd = -1; + s->listen_fd = -1; + s->msgfd = -1; + + chr->filename = g_malloc(256); + switch (ss.ss_family) { +#ifndef _WIN32 + case AF_UNIX: + s->is_unix = 1; + snprintf(chr->filename, 256, "unix:%s%s", + ((struct sockaddr_un *)(&ss))->sun_path, + is_listen ? ",server" : ""); + break; +#endif + case AF_INET6: + left = "["; + right = "]"; + /* fall through */ + case AF_INET: + s->do_nodelay = do_nodelay; + getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); + snprintf(chr->filename, 256, "%s:%s:%s%s%s%s", + is_telnet ? "telnet" : "tcp", + left, host, right, serv, + is_listen ? ",server" : ""); + break; + } + + chr->opaque = s; + chr->chr_write = tcp_chr_write; + chr->chr_close = tcp_chr_close; + chr->get_msgfd = tcp_get_msgfd; + chr->chr_add_watch = tcp_chr_add_watch; + + if (is_listen) { + s->listen_fd = fd; + s->listen_chan = io_channel_from_socket(s->listen_fd); + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); + if (is_telnet) { + s->do_telnetopt = 1; + } + } else { + s->connected = 1; + s->fd = fd; + socket_set_nodelay(fd); + s->chan = io_channel_from_socket(s->fd); + tcp_chr_connect(chr); + } + + if (is_listen && is_waitconnect) { + printf("QEMU waiting for connection on: %s\n", + chr->filename); + tcp_chr_accept(s->listen_chan, G_IO_IN, chr); + socket_set_nonblock(s->listen_fd); + } + return chr; +} + +static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +{ + CharDriverState *chr = NULL; Error *local_err = NULL; int fd = -1; int is_listen; @@ -2482,9 +2562,6 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (!is_listen) is_waitconnect = 0; - chr = qemu_mallocz(sizeof(CharDriverState)); - s = qemu_mallocz(sizeof(TCPCharDriver)); - if (is_unix) { if (is_listen) { fd = unix_listen_opts(opts, &local_err); @@ -2504,58 +2581,14 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (!is_waitconnect) socket_set_nonblock(fd); - s->connected = 0; - s->fd = -1; - s->listen_fd = -1; - s->msgfd = -1; - s->is_unix = is_unix; - s->do_nodelay = do_nodelay && !is_unix; - - chr->opaque = s; - chr->chr_write = tcp_chr_write; - chr->chr_close = tcp_chr_close; - chr->get_msgfd = tcp_get_msgfd; - chr->chr_add_watch = tcp_chr_add_watch; - - if (is_listen) { - s->listen_fd = fd; - s->listen_chan = io_channel_from_socket(s->listen_fd); - s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); - if (is_telnet) { - s->do_telnetopt = 1; - } - } else { - s->connected = 1; - s->fd = fd; - socket_set_nodelay(fd); - s->chan = io_channel_from_socket(s->fd); - tcp_chr_connect(chr); - } - - /* for "info chardev" monitor command */ - chr->filename = qemu_malloc(256); - if (is_unix) { - snprintf(chr->filename, 256, "unix:%s%s", - qemu_opt_get(opts, "path"), - qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); - } else if (is_telnet) { - snprintf(chr->filename, 256, "telnet:%s:%s%s", - qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"), - qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); - } else { - snprintf(chr->filename, 256, "tcp:%s:%s%s", - qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"), - qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); - } - - if (is_listen && is_waitconnect) { - printf("QEMU waiting for connection on: %s\n", - chr->filename); - tcp_chr_accept(s->listen_chan, G_IO_IN, chr); - socket_set_nonblock(s->listen_fd); + chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet, + is_waitconnect, &local_err); + if (error_is_set(&local_err)) { + goto fail; } return chr; + fail: if (local_err) { qerror_report_err(local_err); @@ -2564,8 +2597,10 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (fd >= 0) { closesocket(fd); } - g_free(s); - g_free(chr); + if (chr) { + g_free(chr->opaque); + g_free(chr); + } return NULL; } @@ -3090,6 +3125,28 @@ static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp) #endif /* WIN32 */ +static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, + Error **errp) +{ + SocketAddress *addr = sock->addr; + bool do_nodelay = sock->has_nodelay ? sock->nodelay : false; + bool is_listen = sock->has_server ? sock->server : true; + bool is_telnet = sock->has_telnet ? sock->telnet : false; + bool is_waitconnect = sock->has_wait ? sock->wait : false; + int fd; + + if (is_listen) { + fd = socket_listen(addr, errp); + } else { + fd = socket_connect(addr, errp, NULL, NULL); + } + if (error_is_set(errp)) { + return NULL; + } + return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, + is_telnet, is_waitconnect, errp); +} + ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, Error **errp) { @@ -3110,6 +3167,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, case CHARDEV_BACKEND_KIND_PORT: chr = qmp_chardev_open_port(backend->port, errp); break; + case CHARDEV_BACKEND_KIND_SOCKET: + chr = qmp_chardev_open_socket(backend->socket, errp); + break; case CHARDEV_BACKEND_KIND_NULL: chr = qemu_chr_open_null(NULL); break; -- 1.7.11.7