From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:36216) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qke35-0004gP-Kj for qemu-devel@nongnu.org; Sat, 23 Jul 2011 11:23:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qke33-0000pZ-Us for qemu-devel@nongnu.org; Sat, 23 Jul 2011 11:23:27 -0400 Received: from mail-yx0-f173.google.com ([209.85.213.173]:46395) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qke33-0000pU-R7 for qemu-devel@nongnu.org; Sat, 23 Jul 2011 11:23:25 -0400 Received: by yxt3 with SMTP id 3so2040977yxt.4 for ; Sat, 23 Jul 2011 08:23:24 -0700 (PDT) Message-ID: <4E2AE76A.1050302@codemonkey.ws> Date: Sat, 23 Jul 2011 10:23:22 -0500 From: Anthony Liguori MIME-Version: 1.0 References: <1310633908-11520-1-git-send-email-cyliu@novell.com> In-Reply-To: <1310633908-11520-1-git-send-email-cyliu@novell.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH V2] Add "tee" option to qemu char device List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Chunyan Liu Cc: qemu-devel@nongnu.org, agraf@suse.de 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