qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Anthony Liguori <anthony@codemonkey.ws>
To: Chunyan Liu <cyliu@novell.com>
Cc: qemu-devel@nongnu.org, agraf@suse.de
Subject: Re: [Qemu-devel] [PATCH V2] Add "tee" option to qemu char device
Date: Sat, 23 Jul 2011 10:23:22 -0500	[thread overview]
Message-ID: <4E2AE76A.1050302@codemonkey.ws> (raw)
In-Reply-To: <1310633908-11520-1-git-send-email-cyliu@novell.com>

On 07/14/2011 03:58 AM, Chunyan Liu wrote:
> Add "tee" backend to char device. It could be used as follows:
>      -serial tee:filepath,pty
>      -chardev tee,tee_fpath=path,tee_backend=pty,,path=path,,[mux=on|off]
> With "tee" option, "pty" output would be duplicated to filepath.
> Related thread:
> http://lists.nongnu.org/archive/html/qemu-devel/2011-07/msg00105.html

I loathe adding even more complexity to the the char layer.  Can't you 
do this just as well with socat?

Regards,

Anthony Liguori

>
> V2 changes:
>      -implement "tee" as a new backend. V1 implemented "tee" as a option.
>      -add documentation in qemu-options.hx.
>
> Please review. Thanks.
>
> ---
>   qemu-char.c     |  168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   qemu-config.c   |    6 ++
>   qemu-options.hx |   25 ++++++++-
>   3 files changed, 197 insertions(+), 2 deletions(-)
>
> diff --git a/qemu-char.c b/qemu-char.c
> index fb13b28..99e49a9 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -228,6 +228,156 @@ static CharDriverState *qemu_chr_open_null(QemuOpts *opts)
>       return chr;
>   }
>
> +/* Tee driver */
> +typedef struct {
> +    CharDriverState *basechr; /* base io*/
> +    CharDriverState *filechr; /* duplicate output to file */
> +} TeeDriver;
> +
> +static void tee_init(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->init) {
> +        s->basechr->init(s->basechr);
> +    }
> +    if (s->filechr->init) {
> +        s->filechr->init(s->filechr);
> +    }
> +}
> +
> +static void tee_chr_update_read_handler(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    qemu_chr_add_handlers(s->basechr, chr->chr_can_read, chr->chr_read,
> +                              chr->chr_event, chr->handler_opaque);
> +}
> +
> +/* tee_chr_write will return the write result of basechr, write result to file
> + * will be ignored. FIX ME. */
> +static int tee_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->filechr->chr_write) {
> +        s->filechr->chr_write(s->filechr, buf, len);
> +    }
> +    if (s->basechr->chr_write) {
> +        return s->basechr->chr_write(s->basechr, buf, len);
> +    }
> +    return 0;
> +}
> +
> +static void tee_chr_close(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_close) {
> +        s->basechr->chr_close(s->basechr);
> +    }
> +    if (s->filechr->chr_close) {
> +        s->filechr->chr_close(s->filechr);
> +    }
> +    qemu_free(s);
> +}
> +
> +static int tee_chr_ioctl(CharDriverState *chr, int cmd, void *arg)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_ioctl) {
> +        return s->basechr->chr_ioctl(s->basechr, cmd, arg);
> +    }
> +    return 0;
> +}
> +
> +static int tee_get_msgfd(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->get_msgfd) {
> +        return s->basechr->get_msgfd(s->basechr);
> +    }
> +    return -1;
> +}
> +
> +static void tee_chr_send_event(CharDriverState *chr, int event)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_send_event) {
> +        s->basechr->chr_send_event(s->basechr, event);
> +    }
> +}
> +
> +static void tee_chr_accept_input(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_accept_input) {
> +        s->basechr->chr_accept_input(s->basechr);
> +    }
> +}
> +static void tee_chr_set_echo(CharDriverState *chr, bool echo)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_set_echo) {
> +        s->basechr->chr_set_echo(s->basechr, echo);
> +    }
> +}
> +static void tee_chr_guest_open(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_guest_open) {
> +        s->basechr->chr_guest_open(s->basechr);
> +    }
> +}
> +static void tee_chr_guest_close(CharDriverState *chr)
> +{
> +    TeeDriver *s = chr->opaque;
> +    if (s->basechr->chr_guest_close) {
> +        s->basechr->chr_guest_close(s->basechr);
> +    }
> +}
> +
> +static CharDriverState *qemu_chr_open_tee(QemuOpts *opts)
> +{
> +    CharDriverState *chr;
> +    TeeDriver *d;
> +    CharDriverState *basechr;
> +    CharDriverState *filechr;
> +    const char *label = qemu_opts_id(opts);
> +    const char *tee_fpath = qemu_opt_get(opts, "tee_fpath");
> +    const char *tee_backend = qemu_opt_get(opts, "tee_backend");
> +    char *new_label, *new_filename;
> +    int sz;
> +
> +    chr = qemu_mallocz(sizeof(CharDriverState));
> +    d = qemu_mallocz(sizeof(TeeDriver));
> +
> +    sz = strlen(label)+3;
> +    new_label = qemu_malloc(sz);
> +    snprintf(new_label, sz, "%s-0", label);
> +    basechr = qemu_chr_open(new_label, tee_backend, NULL);
> +
> +    snprintf(new_label, sz, "%s-1", label);
> +    sz = strlen(tee_fpath)+6;
> +    new_filename = qemu_malloc(sz);
> +    snprintf(new_filename, sz, "file:%s", tee_fpath);
> +    filechr = qemu_chr_open(new_label, new_filename, NULL);
> +    qemu_free(new_label);
> +    qemu_free(new_filename);
> +
> +    d->basechr = basechr;
> +    d->filechr = filechr;
> +    chr->opaque = d;
> +    chr->init = tee_init;
> +    chr->chr_write = tee_chr_write;
> +    chr->chr_close = tee_chr_close;
> +    chr->chr_update_read_handler = tee_chr_update_read_handler;
> +    chr->chr_ioctl = tee_chr_ioctl;
> +    chr->get_msgfd = tee_get_msgfd;
> +    chr->chr_send_event = tee_chr_send_event;
> +    chr->chr_accept_input = tee_chr_accept_input;
> +    chr->chr_set_echo = tee_chr_set_echo;
> +    chr->chr_guest_open = tee_chr_guest_open;
> +    chr->chr_guest_close = tee_chr_guest_close;
> +
> +    return chr;
> +}
>   /* MUX driver for serial I/O splitting */
>   #define MAX_MUX 4
>   #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
> @@ -2356,6 +2506,16 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
>           qemu_opt_set(opts, "mux", "on");
>       }
>
> +    if (strstart(filename, "tee:",&p)) {
> +        char tee_fpath[1024];
> +        p = get_opt_value(tee_fpath, sizeof(tee_fpath), p);
> +        filename = p+1;
> +        qemu_opt_set(opts, "tee_backend", filename);
> +        qemu_opt_set(opts, "tee_fpath", tee_fpath);
> +        qemu_opt_set(opts, "backend", "tee");
> +        return opts;
> +   }
> +
>       if (strcmp(filename, "null")    == 0 ||
>           strcmp(filename, "pty")     == 0 ||
>           strcmp(filename, "msmouse") == 0 ||
> @@ -2468,6 +2628,7 @@ static const struct {
>       const char *name;
>       CharDriverState *(*open)(QemuOpts *opts);
>   } backend_table[] = {
> +    { .name = "tee",       .open = qemu_chr_open_tee },
>       { .name = "null",      .open = qemu_chr_open_null },
>       { .name = "socket",    .open = qemu_chr_open_socket },
>       { .name = "udp",       .open = qemu_chr_open_udp },
> @@ -2536,7 +2697,12 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
>
>       if (!chr->filename)
>           chr->filename = qemu_strdup(qemu_opt_get(opts, "backend"));
> -    chr->init = init;
> +    if (strcmp(qemu_opt_get(opts, "backend"), "tee") == 0) {
> +        TeeDriver *s = chr->opaque;
> +        s->basechr->init = init;
> +    } else {
> +        chr->init = init;
> +    }
>       QTAILQ_INSERT_TAIL(&chardevs, chr, next);
>
>       if (qemu_opt_get_bool(opts, "mux", 0)) {
> diff --git a/qemu-config.c b/qemu-config.c
> index c63741c..b82516f 100644
> --- a/qemu-config.c
> +++ b/qemu-config.c
> @@ -151,6 +151,12 @@ static QemuOptsList qemu_chardev_opts = {
>           },{
>               .name = "debug",
>               .type = QEMU_OPT_NUMBER,
> +        },{
> +            .name = "tee_backend",
> +            .type = QEMU_OPT_STRING,
> +        },{
> +            .name = "tee_fpath",
> +            .type = QEMU_OPT_STRING,
>           },
>           { /* end of list */ }
>       },
> diff --git a/qemu-options.hx b/qemu-options.hx
> index e6d7adc..1496f34 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1428,6 +1428,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
>   #if defined(CONFIG_SPICE)
>       "-chardev spicevmc,id=id,name=name[,debug=debug]\n"
>   #endif
> +    "-chardev tee,id=id,tee_backend=dev_string,tee_fpath=path\n"
>       , QEMU_ARCH_ALL
>   )
>
> @@ -1453,7 +1454,8 @@ Backend is one of:
>   @option{braille},
>   @option{tty},
>   @option{parport},
> -@option{spicevmc}.
> +@option{spicevmc},
> +@option{tee}.
>   The specific backend will determine the applicable options.
>
>   All devices must have an id, which can be any string up to 127 characters long.
> @@ -1639,6 +1641,18 @@ required.
>   Connect to a spice virtual machine channel, such as vdiport.
>   #endif
>
> +@item -chardev tee ,id=@var{id} ,tee_backend=@var{dev_string} ,tee_fpath=@var{path}
> +
> +@option{tee_backend} any kind of char device specified above. Double each comma
> +in device string to ensure the whole device string is the option value, "id" in
> +device string could be ignored. For example:
> +tee_backend=stdio,,mux=on,,signal=on
> +
> +@option{tee_fpath} file path where char device output content is duplicated to.
> +
> +Connect to a device specified by dev_string, and duplicate output content of
> +that device to a file.
> +
>   @end table
>   ETEXI
>
> @@ -1878,6 +1892,15 @@ A unix domain socket is used instead of a tcp socket.  The option works the
>   same as if you had specified @code{-serial tcp} except the unix domain socket
>   @var{path} is used for connections.
>
> +@item tee:@var{path},@var{dev_string}
> +Tee can duplicate output content of a serial device to a file.
> +@var{path} is the file path where output content is duplicated to.
> +@var{dev_string} should be any one of the serial devices specified
> +above. An example would be:
> +@table @code
> +@item -serial tee:/var/log/test.log,pty
> +@end table
> +
>   @item mon:@var{dev_string}
>   This is a special option to allow the monitor to be multiplexed onto
>   another serial port.  The monitor is accessed with key sequence of

  reply	other threads:[~2011-07-23 15:23 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-14  8:58 [Qemu-devel] [PATCH V2] Add "tee" option to qemu char device Chunyan Liu
2011-07-23 15:23 ` Anthony Liguori [this message]
2011-07-23 18:31   ` Alexander Graf
2011-07-23 19:11     ` Anthony Liguori
2011-07-24  9:47       ` Alexander Graf
2011-07-24 13:25         ` Anthony Liguori
2011-07-24  9:44 ` Alexander Graf
  -- strict thread matches above, loose matches on Subject: below --
2011-08-05  2:26 Chun Yan Liu
2011-08-05 13:32 ` Anthony Liguori

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=4E2AE76A.1050302@codemonkey.ws \
    --to=anthony@codemonkey.ws \
    --cc=agraf@suse.de \
    --cc=cyliu@novell.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).