qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/3] qemu-char: add -chardev exit-on-eof option


From: Stefan Hajnoczi
Subject: [Qemu-devel] [PATCH 2/3] qemu-char: add -chardev exit-on-eof option
Date: Thu, 24 Jul 2014 17:39:24 +0100

When QEMU is executed as part of a test case or from a script, it is
usually desirable to exit if the parent process terminates.  This
ensures that "leaked" QEMU processes do not continue consuming resources
after their parent has died.

This patch adds the -chardev exit-on-eof option causing socket and pipe
chardevs to exit QEMU upon close.  This happens when a parent process
deliberately closes its file descriptor but also when the kernel cleans
up a crashed process.

Signed-off-by: Stefan Hajnoczi <address@hidden>
---
 include/sysemu/char.h |  1 +
 qapi-schema.json      | 23 ++++++++++++++++-------
 qemu-char.c           | 34 ++++++++++++++++++++++++++++------
 qemu-options.hx       | 19 +++++++++++++------
 4 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0bbd631..382b320 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -86,6 +86,7 @@ struct CharDriverState {
     guint fd_in_tag;
     QemuOpts *opts;
     QTAILQ_ENTRY(CharDriverState) next;
+    bool exit_on_eof;
 };
 
 /**
diff --git a/qapi-schema.json b/qapi-schema.json
index b11aad2..9b13da1 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2630,10 +2630,13 @@
 # @device: The name of the special file for the device,
 #          i.e. /dev/ttyS0 on Unix or COM1: on Windows
 # @type: What kind of device this is.
+# @exit-on-eof: #optional terminate when other side closes the pipe
+#               (default: false, since: 2.2)
 #
 # Since: 1.4
 ##
-{ 'type': 'ChardevHostdev', 'data': { 'device' : 'str' } }
+{ 'type': 'ChardevHostdev', 'data': { 'device' : 'str',
+                                      '*exit-on-eof' : 'bool' } }
 
 ##
 # @ChardevSocket:
@@ -2648,14 +2651,17 @@
 # @nodelay: #optional set TCP_NODELAY socket option (default: false)
 # @telnet: #optional enable telnet protocol on server
 #          sockets (default: false)
+# @exit-on-eof: #optional terminate when other side closes socket
+#               (default: false, since: 2.2)
 #
 # Since: 1.4
 ##
-{ 'type': 'ChardevSocket', 'data': { 'addr'     : 'SocketAddress',
-                                     '*server'  : 'bool',
-                                     '*wait'    : 'bool',
-                                     '*nodelay' : 'bool',
-                                     '*telnet'  : 'bool' } }
+{ 'type': 'ChardevSocket', 'data': { 'addr'         : 'SocketAddress',
+                                     '*server'      : 'bool',
+                                     '*wait'        : 'bool',
+                                     '*nodelay'     : 'bool',
+                                     '*telnet'      : 'bool',
+                                     '*exit-on-eof' : 'bool' } }
 
 ##
 # @ChardevUdp:
@@ -2689,10 +2695,13 @@
 # @signal: #optional Allow signals (such as SIGINT triggered by ^C)
 #          be delivered to qemu.  Default: true in -nographic mode,
 #          false otherwise.
+# @exit-on-eof: #optional terminate when other side sends EOF
+#               (default: false, since: 2.2)
 #
 # Since: 1.5
 ##
-{ 'type': 'ChardevStdio', 'data': { '*signal' : 'bool' } }
+{ 'type': 'ChardevStdio', 'data': { '*signal' : 'bool',
+                                    '*exit-on-eof' : 'bool' } }
 
 ##
 # @ChardevSpiceChannel:
diff --git a/qemu-char.c b/qemu-char.c
index 7acc03f..9015bc9 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -110,9 +110,16 @@ void qemu_chr_be_event(CharDriverState *s, int event)
             break;
     }
 
-    if (!s->chr_event)
-        return;
-    s->chr_event(s->handler_opaque, event);
+    if (s->chr_event) {
+        s->chr_event(s->handler_opaque, event);
+    }
+
+    if (s->exit_on_eof && event == CHR_EVENT_CLOSED) {
+        fprintf(stderr, "qemu: terminating due to eof on chardev '%s'\n",
+                s->label);
+        no_shutdown = 0;
+        qemu_system_shutdown_request();
+    }
 }
 
 void qemu_chr_be_generic_open(CharDriverState *s)
@@ -991,6 +998,7 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev 
*opts)
     int fd_in, fd_out;
     char filename_in[256], filename_out[256];
     const char *filename = opts->device;
+    CharDriverState *chr;
 
     if (filename == NULL) {
         fprintf(stderr, "chardev: pipe: no filename given\n");
@@ -1011,7 +1019,9 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev 
*opts)
             return NULL;
         }
     }
-    return qemu_chr_open_fd(fd_in, fd_out);
+    chr = qemu_chr_open_fd(fd_in, fd_out);
+    chr->exit_on_eof = opts->has_exit_on_eof && opts->exit_on_eof;
+    return chr;
 }
 
 /* init terminal so that we can grab keys */
@@ -2893,6 +2903,7 @@ static void tcp_chr_close(CharDriverState *chr)
 static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
                                                 bool is_listen, bool is_telnet,
                                                 bool is_waitconnect,
+                                                bool is_exit_on_eof,
                                                 Error **errp)
 {
     CharDriverState *chr = NULL;
@@ -2955,6 +2966,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, 
bool do_nodelay,
     chr->chr_update_read_handler = tcp_chr_update_read_handler;
     /* be isn't opened until we get a connection */
     chr->explicit_be_open = true;
+    chr->exit_on_eof = is_exit_on_eof;
 
     if (is_listen) {
         s->listen_fd = fd;
@@ -2991,6 +3003,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts 
*opts)
     bool is_telnet      = qemu_opt_get_bool(opts, "telnet", false);
     bool do_nodelay     = !qemu_opt_get_bool(opts, "delay", true);
     bool is_unix        = qemu_opt_get(opts, "path") != NULL;
+    bool is_exit_on_eof = qemu_opt_get_bool(opts, "exit-on-eof", false);
 
     if (is_unix) {
         if (is_listen) {
@@ -3013,7 +3026,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts 
*opts)
         qemu_set_nonblock(fd);
 
     chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet,
-                                  is_waitconnect, &local_err);
+                                  is_waitconnect, is_exit_on_eof, &local_err);
     if (local_err) {
         goto fail;
     }
@@ -3376,6 +3389,8 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, 
ChardevBackend *backend,
     backend->stdio = g_new0(ChardevStdio, 1);
     backend->stdio->has_signal = true;
     backend->stdio->signal = qemu_opt_get_bool(opts, "signal", true);
+    backend->stdio->has_exit_on_eof = true;
+    backend->stdio->exit_on_eof = qemu_opt_get_bool(opts, "exit-on-eof", 
false);
 }
 
 static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
@@ -3415,6 +3430,8 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, 
ChardevBackend *backend,
     }
     backend->pipe = g_new0(ChardevHostdev, 1);
     backend->pipe->device = g_strdup(device);
+    backend->pipe->has_exit_on_eof = true;
+    backend->pipe->exit_on_eof = qemu_opt_get_bool(opts, "exit-on-eof", false);
 }
 
 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
@@ -3848,6 +3865,9 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "chardev",
             .type = QEMU_OPT_STRING,
+        },{
+            .name = "exit-on-eof",
+            .type = QEMU_OPT_BOOL,
         },
         { /* end of list */ }
     },
@@ -3967,6 +3987,7 @@ static CharDriverState 
*qmp_chardev_open_socket(ChardevSocket *sock,
     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;
+    bool is_exit_on_eof = sock->has_exit_on_eof ? sock->exit_on_eof : false;
     int fd;
 
     if (is_listen) {
@@ -3978,7 +3999,8 @@ static CharDriverState 
*qmp_chardev_open_socket(ChardevSocket *sock,
         return NULL;
     }
     return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
-                                   is_telnet, is_waitconnect, errp);
+                                   is_telnet, is_waitconnect,
+                                   is_exit_on_eof, errp);
 }
 
 static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
diff --git a/qemu-options.hx b/qemu-options.hx
index 9e54686..4b4da4f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1927,8 +1927,9 @@ ETEXI
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off]\n"
     "-chardev 
socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay]\n"
-    "         [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
-    "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] 
(unix)\n"
+    "         [,server][,nowait][,telnet][,mux=on|off][,exit-on-eof] (tcp)\n"
+    "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off]\n"
+    "         [,exit-on-eof] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
     "-chardev msmouse,id=id[,mux=on|off]\n"
@@ -1936,13 +1937,13 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "         [,mux=on|off]\n"
     "-chardev ringbuf,id=id[,size=size]\n"
     "-chardev file,id=id,path=path[,mux=on|off]\n"
-    "-chardev pipe,id=id,path=path[,mux=on|off]\n"
+    "-chardev pipe,id=id,path=path[,mux=on|off][,exit-on-eof]\n"
 #ifdef _WIN32
     "-chardev console,id=id[,mux=on|off]\n"
     "-chardev serial,id=id,path=path[,mux=on|off]\n"
 #else
     "-chardev pty,id=id[,mux=on|off]\n"
-    "-chardev stdio,id=id[,mux=on|off][,signal=on|off]\n"
+    "-chardev stdio,id=id[,mux=on|off][,signal=on|off][,exit-on-eof]\n"
 #endif
 #ifdef CONFIG_BRLAPI
     "-chardev braille,id=id[,mux=on|off]\n"
@@ -2000,7 +2001,7 @@ Options to each backend are described below.
 A void device. This device will not emit any data, and will drop any data it
 receives. The null backend does not take any options.
 
address@hidden -chardev socket ,address@hidden address@hidden options} or 
@var{unix options}] [,server] [,nowait] [,telnet]
address@hidden -chardev socket ,address@hidden address@hidden options} or 
@var{unix options}] [,server] [,nowait] [,telnet] [,exit-on-eof]
 
 Create a two-way stream socket, which can be either a TCP or a unix socket. A
 unix socket will be created if @option{path} is specified. Behaviour is
@@ -2014,6 +2015,8 @@ connect to a listening socket.
 @option{telnet} specifies that traffic on the socket should interpret telnet
 escape sequences.
 
address@hidden specifies that QEMU should terminate upon disconnect
+
 TCP and unix socket options are given below:
 
 @table @option
@@ -2094,7 +2097,7 @@ Log all traffic received from the guest to a file.
 created if it does not already exist, and overwritten if it does. @option{path}
 is required.
 
address@hidden -chardev pipe ,address@hidden ,address@hidden
address@hidden -chardev pipe ,address@hidden ,address@hidden [,exit-on-eof]
 
 Create a two-way connection to the guest. The behaviour differs slightly 
between
 Windows hosts and other hosts:
@@ -2111,6 +2114,8 @@ be present.
 @option{path} forms part of the pipe path as described above. @option{path} is
 required.
 
address@hidden specifies that QEMU should terminate upon disconnect
+
 @item -chardev console ,address@hidden
 
 Send traffic from the guest to QEMU's standard output. @option{console} does 
not
@@ -2141,6 +2146,8 @@ Connect to standard input and standard output of the QEMU 
process.
 exiting QEMU with the key sequence @key{Control-c}. This option is enabled by
 default, use @option{signal=off} to disable it.
 
address@hidden specifies that QEMU should terminate upon EOF
+
 @option{stdio} is not available on Windows hosts.
 
 @item -chardev braille ,address@hidden
-- 
1.9.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]