qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Darrin M. Gorski" <darrin@gorski.net>
To: "Marc-André Lureau" <marcandre.lureau@gmail.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>, QEMU <qemu-devel@nongnu.org>
Subject: Re: [PATCH v1 1/1] chardev: enable guest socket status/crontrol via DTR and DCD
Date: Thu, 17 Dec 2020 12:54:21 -0500	[thread overview]
Message-ID: <CACdcev+VZ5xAgcdv_38QaZRrLv6Ev3LAa6MEeGy6eizjEQ-Hmg@mail.gmail.com> (raw)
In-Reply-To: <CAJ+F1C+moy=Bhq=gTGzbutxHCjyqcRKc5bCJLu8AUP4_B7+sYg@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 10142 bytes --]

> That sounds like a good idea to me, but others may have different
opinions.

My original idea was simply to allow DCD to track socket state, because
this is what I need.  The DTR idea came from the referenced bug.  The
feature defaults to disabled like many of the other edge cases (like telnet
and tn3270), and it's a reasonably small non-intrusive change.

> Can we make this generic over all chardevs by moving it to the base class?

I'll take a closer look at the base class. I admit I did not spend much
time studying it.  This seemed like a socket specific feature to me.  It
seems like it might conflict with other chardevs like pty and serial as
they are already using ioctl for interaction with real tty devices.

> This feature will need some new tests in tests/test-char.c.

I would be happy to add tests but would need some guidance.

- Darrin

On Thu, Dec 17, 2020 at 9:16 AM Marc-André Lureau <
marcandre.lureau@gmail.com> wrote:

> Hi
>
> On Thu, Dec 17, 2020 at 2:54 AM Darrin M. Gorski <darrin@gorski.net>
> wrote:
>
>>
>> This patch adds a 'modemctl' option to "-chardev socket" to enable
>> control of the socket via the guest serial port.
>> The default state of the option is disabled.
>>
>> 1. disconnect a connected socket when DTR transitions to low, also reject
>> new connections while DTR is low.
>> 2. provide socket connection status through the carrier detect line (CD
>> or DCD) on the guest serial port
>>
>
> That sounds like a good idea to me, but others may have different opinions.
>
>
>> Buglink: https://bugs.launchpad.net/qemu/+bug/1213196
>>
>> Signed-off-by: Darrin M. Gorski <darrin@gorski.net>
>>
>>
>> diff --git a/chardev/char-socket.c b/chardev/char-socket.c
>> index 213a4c8dd0..94dd28e0cd 100644
>> --- a/chardev/char-socket.c
>> +++ b/chardev/char-socket.c
>> @@ -36,6 +36,7 @@
>>  #include "qapi/qapi-visit-sockets.h"
>>
>>  #include "chardev/char-io.h"
>> +#include "chardev/char-serial.h"
>>  #include "qom/object.h"
>>
>>  /***********************************************************/
>> @@ -85,6 +86,9 @@ struct SocketChardev {
>>      bool connect_err_reported;
>>
>>      QIOTask *connect_task;
>> +
>> +    bool is_modemctl;
>> +    int modem_state;
>>
>
> Can we make this generic over all chardevs by moving it to the base class?
>
>  };
>>  typedef struct SocketChardev SocketChardev;
>>
>> @@ -98,12 +102,18 @@ static void tcp_chr_change_state(SocketChardev *s,
>> TCPChardevState state)
>>  {
>>      switch (state) {
>>      case TCP_CHARDEV_STATE_DISCONNECTED:
>> +        if(s->is_modemctl) {
>> +            s->modem_state &= ~(CHR_TIOCM_CAR);
>> +        }
>>          break;
>>      case TCP_CHARDEV_STATE_CONNECTING:
>>          assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED);
>>          break;
>>      case TCP_CHARDEV_STATE_CONNECTED:
>>          assert(s->state == TCP_CHARDEV_STATE_CONNECTING);
>> +        if(s->is_modemctl) {
>> +            s->modem_state |= CHR_TIOCM_CAR;
>> +        }
>>          break;
>>      }
>>      s->state = state;
>> @@ -947,6 +957,12 @@ static void tcp_chr_accept(QIONetListener *listener,
>>      tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING);
>>      tcp_chr_set_client_ioc_name(chr, cioc);
>>      tcp_chr_new_client(chr, cioc);
>> +
>> +    if(s->is_modemctl) {
>> +        if(!(s->modem_state & CHR_TIOCM_DTR)) {
>> +            tcp_chr_disconnect(chr); /* disconnect if DTR is low */
>> +        }
>> +    }
>>  }
>>
>>
>> @@ -1322,12 +1338,17 @@ static void qmp_chardev_open_socket(Chardev *chr,
>>      bool is_tn3270      = sock->has_tn3270  ? sock->tn3270  : false;
>>      bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
>>      bool is_websock     = sock->has_websocket ? sock->websocket : false;
>> +    bool is_modemctl     = sock->has_modemctl ? sock->modemctl : false;
>>      int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
>>      SocketAddress *addr;
>>
>>      s->is_listen = is_listen;
>>      s->is_telnet = is_telnet;
>>      s->is_tn3270 = is_tn3270;
>> +    s->is_modemctl = is_modemctl;
>> +    if(is_modemctl) {
>> +      s->modem_state = CHR_TIOCM_CTS | CHR_TIOCM_DSR;
>> +    }
>>      s->is_websock = is_websock;
>>      s->do_nodelay = do_nodelay;
>>      if (sock->tls_creds) {
>> @@ -1448,6 +1469,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts,
>> ChardevBackend *backend,
>>      sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
>>      sock->has_tls_authz = qemu_opt_get(opts, "tls-authz");
>>      sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
>> +    sock->has_modemctl = qemu_opt_get(opts, "modemctl");
>> +    sock->modemctl = qemu_opt_get_bool(opts, "modemctl", false);
>>
>>      addr = g_new0(SocketAddressLegacy, 1);
>>      if (path) {
>> @@ -1501,6 +1524,51 @@ char_socket_get_connected(Object *obj, Error
>> **errp)
>>      return s->state == TCP_CHARDEV_STATE_CONNECTED;
>>  }
>>
>> +/* ioctl support: allow guest to control/track socket state
>> + * via modem control lines (DCD/DTR)
>> + */
>> +static int
>> +char_socket_ioctl(Chardev *chr, int cmd, void *arg)
>> +{
>> +    SocketChardev *s = SOCKET_CHARDEV(chr);
>> +
>> +    if(!(s->is_modemctl)) {
>> +        return -ENOTSUP;
>> +    }
>> +
>> +    switch (cmd) {
>> +        case CHR_IOCTL_SERIAL_GET_TIOCM:
>> +            {
>> +                int *targ = (int *)arg;
>> +                *targ = s->modem_state;
>> +            }
>> +            break;
>> +        case CHR_IOCTL_SERIAL_SET_TIOCM:
>> +            {
>> +                int sarg = *(int *)arg;
>> +                if (sarg & CHR_TIOCM_RTS) {
>> +                    s->modem_state |= CHR_TIOCM_RTS;
>> +                } else {
>> +                    s->modem_state &= ~(CHR_TIOCM_RTS);
>> +                }
>> +                if (sarg & CHR_TIOCM_DTR) {
>> +                    s->modem_state |= CHR_TIOCM_DTR;
>> +                } else {
>> +                    s->modem_state &= ~(CHR_TIOCM_DTR);
>> +                    /* disconnect if DTR goes low */
>> +                    if(s->state == TCP_CHARDEV_STATE_CONNECTED) {
>> +                        tcp_chr_disconnect(chr);
>> +                    }
>> +                }
>> +            }
>> +            break;
>> +        default:
>> +            return -ENOTSUP;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>  static void char_socket_class_init(ObjectClass *oc, void *data)
>>  {
>>      ChardevClass *cc = CHARDEV_CLASS(oc);
>> @@ -1516,6 +1584,7 @@ static void char_socket_class_init(ObjectClass *oc,
>> void *data)
>>      cc->chr_add_client = tcp_chr_add_client;
>>      cc->chr_add_watch = tcp_chr_add_watch;
>>      cc->chr_update_read_handler = tcp_chr_update_read_handler;
>> +    cc->chr_ioctl = char_socket_ioctl;
>>
>>      object_class_property_add(oc, "addr", "SocketAddress",
>>                                char_socket_get_addr, NULL,
>> diff --git a/chardev/char.c b/chardev/char.c
>> index a9b8c5a9aa..601d818f81 100644
>> --- a/chardev/char.c
>> +++ b/chardev/char.c
>> @@ -928,6 +928,9 @@ QemuOptsList qemu_chardev_opts = {
>>          },{
>>              .name = "logappend",
>>              .type = QEMU_OPT_BOOL,
>> +        },{
>> +            .name = "modemctl",
>> +            .type = QEMU_OPT_BOOL,
>>  #ifdef CONFIG_LINUX
>>          },{
>>              .name = "tight",
>> diff --git a/qapi/char.json b/qapi/char.json
>> index 58338ed62d..f352bd6350 100644
>> --- a/qapi/char.json
>> +++ b/qapi/char.json
>> @@ -271,6 +271,9 @@
>>  #             then attempt a reconnect after the given number of seconds.
>>  #             Setting this to zero disables this function. (default: 0)
>>  #             (Since: 2.2)
>> +# @modemctl: allow guest to use modem control signals to control/monitor
>> +#            the socket state (CD follows is_connected, DTR influences
>> +#            connect/accept) (default: false) (Since: 5.2)
>>  #
>>  # Since: 1.4
>>  ##
>> @@ -284,6 +287,7 @@
>>              '*telnet': 'bool',
>>              '*tn3270': 'bool',
>>              '*websocket': 'bool',
>> +            '*modemctl': 'bool',
>>              '*reconnect': 'int' },
>>    'base': 'ChardevCommon' }
>>
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 459c916d3d..f09072afbf 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -2991,11 +2991,13 @@ DEFHEADING(Character device options:)
>>  DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
>>      "-chardev help\n"
>>      "-chardev
>> null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
>> -    "-chardev
>> socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
>> -    "
>> [,server][,nowait][,telnet][,websocket][,reconnect=seconds][,mux=on|off]\n"
>> -    "
>> [,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID] (tcp)\n"
>> -    "-chardev
>> socket,id=id,path=path[,server][,nowait][,telnet][,websocket][,reconnect=seconds]\n"
>> -    "
>> [,mux=on|off][,logfile=PATH][,logappend=on|off][,abstract=on|off][,tight=on|off]
>> (unix)\n"
>> +    "-chardev
>> socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay]\n"
>> +    "
>> [,reconnect=seconds][,server][,nowait][,telnet][,websocket]\n"
>> +    "
>> [,modemctl][,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
>> +    "         [,tls-creds=ID][,tls-authz=ID] (tcp)\n"
>> +    "-chardev
>> socket,id=id,path=path[,server][,nowait][,telnet][,websocket]\n"
>> +    "
>> [,reconnect=seconds][,modemctl][,mux=on|off][,logfile=PATH]\n"
>> +    "         [,logappend=on|off][,abstract=on|off][,tight=on|off]
>> (unix)\n"
>>      "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
>>      "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
>>      "         [,logfile=PATH][,logappend=on|off]\n"
>>
>
> This feature will need some new tests in tests/test-char.c.
>
> --
> Marc-André Lureau
>

[-- Attachment #2: Type: text/html, Size: 13129 bytes --]

  reply	other threads:[~2020-12-17 17:55 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-16 22:06 [PATCH v1 1/1] chardev: enable guest socket status/crontrol via DTR and DCD Darrin M. Gorski
2020-12-17 14:16 ` Marc-André Lureau
2020-12-17 17:54   ` Darrin M. Gorski [this message]
2020-12-18 11:57     ` Marc-André Lureau
2020-12-19  5:34       ` Darrin M. Gorski
2020-12-23 22:13 ` Eric Blake
2022-01-12 15:20 ` Aaro Koskinen
2022-01-12 18:50   ` Darrin M. Gorski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CACdcev+VZ5xAgcdv_38QaZRrLv6Ev3LAa6MEeGy6eizjEQ-Hmg@mail.gmail.com \
    --to=darrin@gorski.net \
    --cc=marcandre.lureau@gmail.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).