Ver código fonte

chardev: add path option for pty backend

Add path option to the pty char backend which will create a symbolic
link to the given path that points to the allocated PTY.

This avoids having to make QMP or HMP monitor queries to find out what
the new PTY device path is.

Based on patch from Paulo Neves:

https://patchew.org/QEMU/1548509635-15776-1-git-send-email-ptsneves@gmail.com/

Tested with the following invocations that the link is created and
removed when qemu stops:

  qemu-system-x86_64 -nodefaults -mon chardev=compat_monitor \
  -chardev pty,path=test,id=compat_monitor0

  qemu-system-x86_64 -nodefaults -monitor pty:test

  # check QMP invocation with path set
  qemu-system-x86_64 -nodefaults -qmp tcp:localhost:4444,server=on,wait=off
  nc localhost 4444
  > {"execute": "qmp_capabilities"}
  > {"execute": "chardev-add", "arguments": {"id": "bar", "backend": {
      "type": "pty", "data": {"path": "test" }}}}

  # check QMP invocation with path not set
  qemu-system-x86_64 -nodefaults -qmp tcp:localhost:4444,server=on,wait=off
  nc localhost 4444
  > {"execute": "qmp_capabilities"}
  > {"execute": "chardev-add", "arguments": {"id": "bar", "backend": {
      "type": "pty", "data": {}}}}

Also tested that when a link path is not passed invocations still work, e.g.:

  qemu-system-x86_64 -monitor pty

Co-authored-by: Paulo Neves <ptsneves@gmail.com>
Signed-off-by: Paulo Neves <ptsneves@gmail.com>
[OP: rebase and address original patch review comments]
Signed-off-by: Octavian Purdila <tavip@google.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20240806010735.2450555-1-tavip@google.com>
Octavian Purdila 1 ano atrás
pai
commit
b74cb8761c
4 arquivos alterados com 91 adições e 7 exclusões
  1. 33 0
      chardev/char-pty.c
  2. 5 0
      chardev/char.c
  3. 26 1
      qapi/char.json
  4. 27 6
      qemu-options.hx

+ 33 - 0
chardev/char-pty.c

@@ -29,6 +29,7 @@
 #include "qemu/sockets.h"
 #include "qemu/sockets.h"
 #include "qemu/error-report.h"
 #include "qemu/error-report.h"
 #include "qemu/module.h"
 #include "qemu/module.h"
+#include "qemu/option.h"
 #include "qemu/qemu-print.h"
 #include "qemu/qemu-print.h"
 
 
 #include "chardev/char-io.h"
 #include "chardev/char-io.h"
@@ -41,6 +42,7 @@ struct PtyChardev {
 
 
     int connected;
     int connected;
     GSource *timer_src;
     GSource *timer_src;
+    char *path;
 };
 };
 typedef struct PtyChardev PtyChardev;
 typedef struct PtyChardev PtyChardev;
 
 
@@ -204,6 +206,12 @@ static void char_pty_finalize(Object *obj)
     Chardev *chr = CHARDEV(obj);
     Chardev *chr = CHARDEV(obj);
     PtyChardev *s = PTY_CHARDEV(obj);
     PtyChardev *s = PTY_CHARDEV(obj);
 
 
+    /* unlink symlink */
+    if (s->path) {
+        unlink(s->path);
+        g_free(s->path);
+    }
+
     pty_chr_state(chr, 0);
     pty_chr_state(chr, 0);
     object_unref(OBJECT(s->ioc));
     object_unref(OBJECT(s->ioc));
     pty_chr_timer_cancel(s);
     pty_chr_timer_cancel(s);
@@ -330,6 +338,7 @@ static void char_pty_open(Chardev *chr,
     int master_fd, slave_fd;
     int master_fd, slave_fd;
     char pty_name[PATH_MAX];
     char pty_name[PATH_MAX];
     char *name;
     char *name;
+    char *path = backend->u.pty.data->path;
 
 
     master_fd = qemu_openpty_raw(&slave_fd, pty_name);
     master_fd = qemu_openpty_raw(&slave_fd, pty_name);
     if (master_fd < 0) {
     if (master_fd < 0) {
@@ -354,12 +363,36 @@ static void char_pty_open(Chardev *chr,
     g_free(name);
     g_free(name);
     s->timer_src = NULL;
     s->timer_src = NULL;
     *be_opened = false;
     *be_opened = false;
+
+    /* create symbolic link */
+    if (path) {
+        int res = symlink(pty_name, path);
+
+        if (res != 0) {
+            error_setg_errno(errp, errno, "Failed to create PTY symlink");
+        } else {
+            s->path = g_strdup(path);
+        }
+    }
+}
+
+static void char_pty_parse(QemuOpts *opts, ChardevBackend *backend,
+                           Error **errp)
+{
+    const char *path = qemu_opt_get(opts, "path");
+    ChardevPty *pty;
+
+    backend->type = CHARDEV_BACKEND_KIND_PTY;
+    pty = backend->u.pty.data = g_new0(ChardevPty, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevPty_base(pty));
+    pty->path = g_strdup(path);
 }
 }
 
 
 static void char_pty_class_init(ObjectClass *oc, void *data)
 static void char_pty_class_init(ObjectClass *oc, void *data)
 {
 {
     ChardevClass *cc = CHARDEV_CLASS(oc);
     ChardevClass *cc = CHARDEV_CLASS(oc);
 
 
+    cc->parse = char_pty_parse;
     cc->open = char_pty_open;
     cc->open = char_pty_open;
     cc->chr_write = char_pty_chr_write;
     cc->chr_write = char_pty_chr_write;
     cc->chr_update_read_handler = pty_chr_update_read_handler;
     cc->chr_update_read_handler = pty_chr_update_read_handler;

+ 5 - 0
chardev/char.c

@@ -428,6 +428,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
         qemu_opt_set(opts, "path", p, &error_abort);
         qemu_opt_set(opts, "path", p, &error_abort);
         return opts;
         return opts;
     }
     }
+    if (strstart(filename, "pty:", &p)) {
+        qemu_opt_set(opts, "backend", "pty", &error_abort);
+        qemu_opt_set(opts, "path", p, &error_abort);
+        return opts;
+    }
     if (strstart(filename, "tcp:", &p) ||
     if (strstart(filename, "tcp:", &p) ||
         strstart(filename, "telnet:", &p) ||
         strstart(filename, "telnet:", &p) ||
         strstart(filename, "tn3270:", &p) ||
         strstart(filename, "tn3270:", &p) ||

+ 26 - 1
qapi/char.json

@@ -444,6 +444,20 @@
   'base': 'ChardevCommon',
   'base': 'ChardevCommon',
   'if': 'CONFIG_SPICE_PROTOCOL' }
   'if': 'CONFIG_SPICE_PROTOCOL' }
 
 
+##
+# @ChardevPty:
+#
+# Configuration info for pty implementation.
+#
+# @path: optional path to create a symbolic link that points to the
+#     allocated PTY
+#
+# Since: 9.2
+##
+{ 'struct': 'ChardevPty',
+  'data': { '*path': 'str' },
+  'base': 'ChardevCommon' }
+
 ##
 ##
 # @ChardevBackendKind:
 # @ChardevBackendKind:
 #
 #
@@ -655,6 +669,17 @@
 { 'struct': 'ChardevRingbufWrapper',
 { 'struct': 'ChardevRingbufWrapper',
   'data': { 'data': 'ChardevRingbuf' } }
   'data': { 'data': 'ChardevRingbuf' } }
 
 
+
+##
+# @ChardevPtyWrapper:
+#
+# @data: Configuration info for pty chardevs
+#
+# Since: 9.2
+##
+{ 'struct': 'ChardevPtyWrapper',
+  'data': { 'data': 'ChardevPty' } }
+
 ##
 ##
 # @ChardevBackend:
 # @ChardevBackend:
 #
 #
@@ -675,7 +700,7 @@
             'pipe': 'ChardevHostdevWrapper',
             'pipe': 'ChardevHostdevWrapper',
             'socket': 'ChardevSocketWrapper',
             'socket': 'ChardevSocketWrapper',
             'udp': 'ChardevUdpWrapper',
             'udp': 'ChardevUdpWrapper',
-            'pty': 'ChardevCommonWrapper',
+            'pty': 'ChardevPtyWrapper',
             'null': 'ChardevCommonWrapper',
             'null': 'ChardevCommonWrapper',
             'mux': 'ChardevMuxWrapper',
             'mux': 'ChardevMuxWrapper',
             'msmouse': 'ChardevCommonWrapper',
             'msmouse': 'ChardevCommonWrapper',

+ 27 - 6
qemu-options.hx

@@ -3712,7 +3712,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev console,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
     "-chardev console,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
     "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
     "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
 #else
 #else
-    "-chardev pty,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
+    "-chardev pty,id=id[,path=path][,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
     "-chardev stdio,id=id[,mux=on|off][,signal=on|off][,logfile=PATH][,logappend=on|off]\n"
     "-chardev stdio,id=id[,mux=on|off][,signal=on|off][,logfile=PATH][,logappend=on|off]\n"
 #endif
 #endif
 #ifdef CONFIG_BRLAPI
 #ifdef CONFIG_BRLAPI
@@ -3951,12 +3951,22 @@ The available backends are:
 
 
     ``path`` specifies the name of the serial device to open.
     ``path`` specifies the name of the serial device to open.
 
 
-``-chardev pty,id=id``
-    Create a new pseudo-terminal on the host and connect to it. ``pty``
-    does not take any options.
+``-chardev pty,id=id[,path=path]``
+    Create a new pseudo-terminal on the host and connect to it.
 
 
     ``pty`` is not available on Windows hosts.
     ``pty`` is not available on Windows hosts.
 
 
+    If ``path`` is specified, QEMU will create a symbolic link at
+    that location which points to the new PTY device.
+
+    This avoids having to make QMP or HMP monitor queries to find out
+    what the new PTY device path is.
+
+    Note that while QEMU will remove the symlink when it exits
+    gracefully, it will not do so in case of crashes or on certain
+    startup errors. It is recommended that the user checks and removes
+    the symlink after QEMU terminates to account for this.
+
 ``-chardev stdio,id=id[,signal=on|off]``
 ``-chardev stdio,id=id[,signal=on|off]``
     Connect to standard input and standard output of the QEMU process.
     Connect to standard input and standard output of the QEMU process.
 
 
@@ -4314,8 +4324,19 @@ SRST
 
 
             vc:80Cx24C
             vc:80Cx24C
 
 
-    ``pty``
-        [Linux only] Pseudo TTY (a new PTY is automatically allocated)
+    ``pty[:path]``
+        [Linux only] Pseudo TTY (a new PTY is automatically allocated).
+
+        If ``path`` is specified, QEMU will create a symbolic link at
+        that location which points to the new PTY device.
+
+        This avoids having to make QMP or HMP monitor queries to find
+        out what the new PTY device path is.
+
+        Note that while QEMU will remove the symlink when it exits
+        gracefully, it will not do so in case of crashes or on certain
+        startup errors. It is recommended that the user checks and
+        removes the symlink after QEMU terminates to account for this.
 
 
     ``none``
     ``none``
         No device is allocated. Note that for machine types which
         No device is allocated. Note that for machine types which