From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:48094) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aM8Xt-00013A-HJ for qemu-devel@nongnu.org; Thu, 21 Jan 2016 01:16:41 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aM8Xm-0008E6-Nz for qemu-devel@nongnu.org; Thu, 21 Jan 2016 01:16:37 -0500 Received: from smtp2-g21.free.fr ([212.27.42.2]:61094) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aM8Xm-0008Dx-Bc for qemu-devel@nongnu.org; Thu, 21 Jan 2016 01:16:30 -0500 References: <1452873871-138914-1-git-send-email-pbonzini@redhat.com> <1452873871-138914-16-git-send-email-pbonzini@redhat.com> From: =?UTF-8?Q?Herv=c3=a9_Poussineau?= Message-ID: <56A077B9.3030209@reactos.org> Date: Thu, 21 Jan 2016 07:16:25 +0100 MIME-Version: 1.0 In-Reply-To: <1452873871-138914-16-git-send-email-pbonzini@redhat.com> Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PULL 14/15] qemu-char: add logfile facility to all chardev backends List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org, Paolo Bonzini , "Daniel P. Berrange\"" Hi, This patch (commit d0d7708ba29cbcc343364a46bff981e0ff88366f) regresses th= e following command line: qemu-system-i386 -nodefaults -chardev vc,id=3Dmon0 -mon chardev=3Dmon0 Before: [nothing is print on console] After: QEMU 2.5.50 monitor - type 'help' for more information (qemu) In both cases, a working vc is created in the GTK+ UI. Reverting the commit (and fixing the trivial conflict) makes things work = again. Regards, Herv=E9 Le 15/01/2016 17:04, Paolo Bonzini a =E9crit : > From: "Daniel P. Berrange" > > Typically a UNIX guest OS will log boot messages to a serial > port in addition to any graphical console. An admin user > may also wish to use the serial port for an interactive > console. A virtualization management system may wish to > collect system boot messages by logging the serial port, > but also wish to allow admins interactive access. > > Currently providing such a feature forces the mgmt app > to either provide 2 separate serial ports, one for > logging boot messages and one for interactive console > login, or to proxy all output via a separate service > that can multiplex the two needs onto one serial port. > While both are valid approaches, they each have their > own downsides. The former causes confusion and extra > setup work for VM admins creating disk images. The latter > places an extra burden to re-implement much of the QEMU > chardev backends logic in libvirt or even higher level > mgmt apps and adds extra hops in the data transfer path. > > A simpler approach that is satisfactory for many use > cases is to allow the QEMU chardev backends to have a > "logfile" property associated with them. > > $QEMU -chardev socket,host=3Dlocalhost,port=3D9000,\ > server=3Don,nowait,id-charserial0,\ > logfile=3D/var/log/libvirt/qemu/test-serial0.log > -device isa-serial,chardev=3Dcharserial0,id=3Dserial0 > > This patch introduces a 'ChardevCommon' struct which > is setup as a base for all the ChardevBackend types. > Ideally this would be registered directly as a base > against ChardevBackend, rather than each type, but > the QAPI generator doesn't allow that since the > ChardevBackend is a non-discriminated union. The > ChardevCommon struct provides the optional 'logfile' > parameter, as well as 'logappend' which controls > whether QEMU truncates or appends (default truncate). > > Signed-off-by: Daniel P. Berrange > Message-Id: <1452516281-27519-1-git-send-email-berrange@redhat.com> > [Call qemu_chr_parse_common if cd->parse is NULL. - Paolo] > Signed-off-by: Paolo Bonzini > --- > backends/baum.c | 7 +- > backends/msmouse.c | 6 +- > gdbstub.c | 3 +- > include/sysemu/char.h | 9 +- > qapi-schema.json | 49 +++++++--- > qemu-char.c | 248 +++++++++++++++++++++++++++++++++++++++++= +-------- > qemu-options.hx | 48 ++++++---- > spice-qemu-char.c | 20 +++- > ui/console.c | 6 +- > 9 files changed, 313 insertions(+), 83 deletions(-) > > diff --git a/backends/baum.c b/backends/baum.c > index 723c658..ba32b61 100644 > --- a/backends/baum.c > +++ b/backends/baum.c > @@ -566,6 +566,7 @@ static CharDriverState *chr_baum_init(const char *i= d, > ChardevReturn *ret, > Error **errp) > { > + ChardevCommon *common =3D qapi_ChardevDummy_base(backend->u.braill= e); > BaumDriverState *baum; > CharDriverState *chr; > brlapi_handle_t *handle; > @@ -576,8 +577,12 @@ static CharDriverState *chr_baum_init(const char *= id, > #endif > int tty; > > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > baum =3D g_malloc0(sizeof(BaumDriverState)); > - baum->chr =3D chr =3D qemu_chr_alloc(); > + baum->chr =3D chr; > > chr->opaque =3D baum; > chr->chr_write =3D baum_write; > diff --git a/backends/msmouse.c b/backends/msmouse.c > index 0126fa0..476dab5 100644 > --- a/backends/msmouse.c > +++ b/backends/msmouse.c > @@ -68,9 +68,13 @@ static CharDriverState *qemu_chr_open_msmouse(const = char *id, > ChardevReturn *ret, > Error **errp) > { > + ChardevCommon *common =3D qapi_ChardevDummy_base(backend->u.msmous= e); > CharDriverState *chr; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > chr->chr_write =3D msmouse_chr_write; > chr->chr_close =3D msmouse_chr_close; > chr->explicit_be_open =3D true; > diff --git a/gdbstub.c b/gdbstub.c > index 9c29aa0..1a84c1a 100644 > --- a/gdbstub.c > +++ b/gdbstub.c > @@ -1732,6 +1732,7 @@ int gdbserver_start(const char *device) > char gdbstub_device_name[128]; > CharDriverState *chr =3D NULL; > CharDriverState *mon_chr; > + ChardevCommon common =3D { 0 }; > > if (!device) > return -1; > @@ -1768,7 +1769,7 @@ int gdbserver_start(const char *device) > qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); > > /* Initialize a monitor terminal for gdb */ > - mon_chr =3D qemu_chr_alloc(); > + mon_chr =3D qemu_chr_alloc(&common, &error_abort); > mon_chr->chr_write =3D gdb_monitor_write; > monitor_init(mon_chr, 0); > } else { > diff --git a/include/sysemu/char.h b/include/sysemu/char.h > index aff193f..598dd2b 100644 > --- a/include/sysemu/char.h > +++ b/include/sysemu/char.h > @@ -77,6 +77,7 @@ struct CharDriverState { > void *opaque; > char *label; > char *filename; > + int logfd; > int be_open; > int fe_open; > int explicit_fe_open; > @@ -89,13 +90,15 @@ struct CharDriverState { > }; > > /** > - * @qemu_chr_alloc: > + * qemu_chr_alloc: > + * @backend: the common backend config > + * @errp: pointer to a NULL-initialized error object > * > * Allocate and initialize a new CharDriverState. > * > - * Returns: a newly allocated CharDriverState. > + * Returns: a newly allocated CharDriverState, or NULL on error. > */ > -CharDriverState *qemu_chr_alloc(void); > +CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp); > > /** > * @qemu_chr_new_from_opts: > diff --git a/qapi-schema.json b/qapi-schema.json > index 2e31733..dabb5ce 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -3093,6 +3093,21 @@ > ## > { 'command': 'screendump', 'data': {'filename': 'str'} } > > + > +## > +# @ChardevCommon: > +# > +# Configuration shared across all chardev backends > +# > +# @logfile: #optional The name of a logfile to save output > +# @logappend: #optional true to append instead of truncate > +# (default to false to truncate) > +# > +# Since: 2.6 > +## > +{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str', > + '*logappend': 'bool' } } > + > ## > # @ChardevFile: > # > @@ -3107,7 +3122,8 @@ > ## > { 'struct': 'ChardevFile', 'data': { '*in' : 'str', > 'out' : 'str', > - '*append': 'bool' } } > + '*append': 'bool' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevHostdev: > @@ -3120,7 +3136,8 @@ > # > # Since: 1.4 > ## > -{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' } } > +{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevSocket: > @@ -3147,7 +3164,8 @@ > '*wait' : 'bool', > '*nodelay' : 'bool', > '*telnet' : 'bool', > - '*reconnect' : 'int' } } > + '*reconnect' : 'int' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevUdp: > @@ -3160,7 +3178,8 @@ > # Since: 1.5 > ## > { 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddress', > - '*local' : 'SocketAddress' } } > + '*local' : 'SocketAddress' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevMux: > @@ -3171,7 +3190,8 @@ > # > # Since: 1.5 > ## > -{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' } } > +{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevStdio: > @@ -3184,7 +3204,9 @@ > # > # Since: 1.5 > ## > -{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' } } > +{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' }, > + 'base': 'ChardevCommon' } > + > > ## > # @ChardevSpiceChannel: > @@ -3195,7 +3217,8 @@ > # > # Since: 1.5 > ## > -{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } } > +{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevSpicePort: > @@ -3206,7 +3229,8 @@ > # > # Since: 1.5 > ## > -{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } } > +{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevVC: > @@ -3223,7 +3247,8 @@ > { 'struct': 'ChardevVC', 'data': { '*width' : 'int', > '*height' : 'int', > '*cols' : 'int', > - '*rows' : 'int' } } > + '*rows' : 'int' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevRingbuf: > @@ -3234,7 +3259,8 @@ > # > # Since: 1.5 > ## > -{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' } } > +{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' }, > + 'base': 'ChardevCommon' } > > ## > # @ChardevBackend: > @@ -3243,7 +3269,8 @@ > # > # Since: 1.4 (testdev since 2.2) > ## > -{ 'struct': 'ChardevDummy', 'data': { } } > +{ 'struct': 'ChardevDummy', 'data': { }, > + 'base': 'ChardevCommon' } > > { 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', > 'serial' : 'ChardevHostdev', > diff --git a/qemu-char.c b/qemu-char.c > index d7be185..11caa56 100644 > --- a/qemu-char.c > +++ b/qemu-char.c > @@ -159,10 +159,33 @@ static int sockaddr_to_str(char *dest, int max_le= n, > static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =3D > QTAILQ_HEAD_INITIALIZER(chardevs); > > -CharDriverState *qemu_chr_alloc(void) > +static void qemu_chr_free_common(CharDriverState *chr); > + > +CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp) > { > CharDriverState *chr =3D g_malloc0(sizeof(CharDriverState)); > qemu_mutex_init(&chr->chr_write_lock); > + > + if (backend->has_logfile) { > + int flags =3D O_WRONLY | O_CREAT; > + if (backend->has_logappend && > + backend->logappend) { > + flags |=3D O_APPEND; > + } else { > + flags |=3D O_TRUNC; > + } > + chr->logfd =3D qemu_open(backend->logfile, flags, 0666); > + if (chr->logfd < 0) { > + error_setg_errno(errp, errno, > + "Unable to open logfile %s", > + backend->logfile); > + g_free(chr); > + return NULL; > + } > + } else { > + chr->logfd =3D -1; > + } > + > return chr; > } > > @@ -188,12 +211,45 @@ void qemu_chr_be_generic_open(CharDriverState *s) > qemu_chr_be_event(s, CHR_EVENT_OPENED); > } > > + > +/* Not reporting errors from writing to logfile, as logs are > + * defined to be "best effort" only */ > +static void qemu_chr_fe_write_log(CharDriverState *s, > + const uint8_t *buf, size_t len) > +{ > + size_t done =3D 0; > + ssize_t ret; > + > + if (s->logfd < 0) { > + return; > + } > + > + while (done < len) { > + do { > + ret =3D write(s->logfd, buf + done, len - done); > + if (ret =3D=3D -1 && errno =3D=3D EAGAIN) { > + g_usleep(100); > + } > + } while (ret =3D=3D -1 && errno =3D=3D EAGAIN); > + > + if (ret <=3D 0) { > + return; > + } > + done +=3D ret; > + } > +} > + > int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len= ) > { > int ret; > > qemu_mutex_lock(&s->chr_write_lock); > ret =3D s->chr_write(s, buf, len); > + > + if (ret > 0) { > + qemu_chr_fe_write_log(s, buf, ret); > + } > + > qemu_mutex_unlock(&s->chr_write_lock); > return ret; > } > @@ -218,6 +274,10 @@ int qemu_chr_fe_write_all(CharDriverState *s, cons= t uint8_t *buf, int len) > > offset +=3D res; > } > + if (offset > 0) { > + qemu_chr_fe_write_log(s, buf, offset); > + } > + > qemu_mutex_unlock(&s->chr_write_lock); > > if (res < 0) { > @@ -365,8 +425,12 @@ static CharDriverState *qemu_chr_open_null(const c= har *id, > Error **errp) > { > CharDriverState *chr; > + ChardevCommon *common =3D qapi_ChardevDummy_base(backend->u.null); > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > chr->chr_write =3D null_chr_write; > chr->explicit_be_open =3D true; > return chr; > @@ -665,6 +729,7 @@ static CharDriverState *qemu_chr_open_mux(const cha= r *id, > ChardevMux *mux =3D backend->u.mux; > CharDriverState *chr, *drv; > MuxDriver *d; > + ChardevCommon *common =3D qapi_ChardevMux_base(backend->u.mux); > > drv =3D qemu_chr_find(mux->chardev); > if (drv =3D=3D NULL) { > @@ -672,7 +737,10 @@ static CharDriverState *qemu_chr_open_mux(const ch= ar *id, > return NULL; > } > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > d =3D g_new0(MuxDriver, 1); > > chr->opaque =3D d; > @@ -975,12 +1043,16 @@ static void fd_chr_close(struct CharDriverState = *chr) > } > > /* open a character device to a unix fd */ > -static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) > +static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, > + ChardevCommon *backend, Error= **errp) > { > CharDriverState *chr; > FDCharDriver *s; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(backend, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_new0(FDCharDriver, 1); > s->fd_in =3D io_channel_from_fd(fd_in); > s->fd_out =3D io_channel_from_fd(fd_out); > @@ -1005,6 +1077,7 @@ static CharDriverState *qemu_chr_open_pipe(const = char *id, > char filename_in[CHR_MAX_FILENAME_SIZE]; > char filename_out[CHR_MAX_FILENAME_SIZE]; > const char *filename =3D opts->device; > + ChardevCommon *common =3D qapi_ChardevHostdev_base(backend->u.pipe= ); > > snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename); > snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename)= ; > @@ -1021,7 +1094,7 @@ static CharDriverState *qemu_chr_open_pipe(const = char *id, > return NULL; > } > } > - return qemu_chr_open_fd(fd_in, fd_out); > + return qemu_chr_open_fd(fd_in, fd_out, common, errp); > } > > /* init terminal so that we can grab keys */ > @@ -1081,6 +1154,7 @@ static CharDriverState *qemu_chr_open_stdio(const= char *id, > ChardevStdio *opts =3D backend->u.stdio; > CharDriverState *chr; > struct sigaction act; > + ChardevCommon *common =3D qapi_ChardevStdio_base(backend->u.stdio)= ; > > if (is_daemonized()) { > error_setg(errp, "cannot use stdio with -daemonize"); > @@ -1102,7 +1176,7 @@ static CharDriverState *qemu_chr_open_stdio(const= char *id, > act.sa_handler =3D term_stdio_handler; > sigaction(SIGCONT, &act, NULL); > > - chr =3D qemu_chr_open_fd(0, 1); > + chr =3D qemu_chr_open_fd(0, 1, common, errp); > chr->chr_close =3D qemu_chr_close_stdio; > chr->chr_set_echo =3D qemu_chr_set_echo_stdio; > if (opts->has_signal) { > @@ -1324,6 +1398,7 @@ static CharDriverState *qemu_chr_open_pty(const c= har *id, > PtyCharDriver *s; > int master_fd, slave_fd; > char pty_name[PATH_MAX]; > + ChardevCommon *common =3D qapi_ChardevDummy_base(backend->u.pty); > > master_fd =3D qemu_openpty_raw(&slave_fd, pty_name); > if (master_fd < 0) { > @@ -1334,7 +1409,11 @@ static CharDriverState *qemu_chr_open_pty(const = char *id, > close(slave_fd); > qemu_set_nonblock(master_fd); > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + close(master_fd); > + return NULL; > + } > > chr->filename =3D g_strdup_printf("pty:%s", pty_name); > ret->pty =3D g_strdup(pty_name); > @@ -1557,12 +1636,14 @@ static void qemu_chr_close_tty(CharDriverState = *chr) > } > } > > -static CharDriverState *qemu_chr_open_tty_fd(int fd) > +static CharDriverState *qemu_chr_open_tty_fd(int fd, > + ChardevCommon *backend, > + Error **errp) > { > CharDriverState *chr; > > tty_serial_init(fd, 115200, 'N', 8, 1); > - chr =3D qemu_chr_open_fd(fd, fd); > + chr =3D qemu_chr_open_fd(fd, fd, backend, errp); > chr->chr_ioctl =3D tty_serial_ioctl; > chr->chr_close =3D qemu_chr_close_tty; > return chr; > @@ -1682,7 +1763,9 @@ static void pp_close(CharDriverState *chr) > qemu_chr_be_event(chr, CHR_EVENT_CLOSED); > } > > -static CharDriverState *qemu_chr_open_pp_fd(int fd, Error **errp) > +static CharDriverState *qemu_chr_open_pp_fd(int fd, > + ChardevCommon *backend, > + Error **errp) > { > CharDriverState *chr; > ParallelCharDriver *drv; > @@ -1697,7 +1780,10 @@ static CharDriverState *qemu_chr_open_pp_fd(int = fd, Error **errp) > drv->fd =3D fd; > drv->mode =3D IEEE1284_MODE_COMPAT; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(backend, errp); > + if (!chr) { > + return NULL; > + } > chr->chr_write =3D null_chr_write; > chr->chr_ioctl =3D pp_ioctl; > chr->chr_close =3D pp_close; > @@ -1748,11 +1834,16 @@ static int pp_ioctl(CharDriverState *chr, int c= md, void *arg) > return 0; > } > > -static CharDriverState *qemu_chr_open_pp_fd(int fd, Error **errp) > +static CharDriverState *qemu_chr_open_pp_fd(int fd, > + ChardevBackend *backend, > + Error **errp) > { > CharDriverState *chr; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > chr->opaque =3D (void *)(intptr_t)fd; > chr->chr_write =3D null_chr_write; > chr->chr_ioctl =3D pp_ioctl; > @@ -1978,12 +2069,16 @@ static int win_chr_poll(void *opaque) > } > > static CharDriverState *qemu_chr_open_win_path(const char *filename, > + ChardevCommon *backend, > Error **errp) > { > CharDriverState *chr; > WinCharState *s; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(backend, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_new0(WinCharState, 1); > chr->opaque =3D s; > chr->chr_write =3D win_chr_write; > @@ -1991,7 +2086,7 @@ static CharDriverState *qemu_chr_open_win_path(co= nst char *filename, > > if (win_chr_init(chr, filename, errp) < 0) { > g_free(s); > - g_free(chr); > + qemu_chr_free_common(chr); > return NULL; > } > return chr; > @@ -2086,8 +2181,12 @@ static CharDriverState *qemu_chr_open_pipe(const= char *id, > const char *filename =3D opts->device; > CharDriverState *chr; > WinCharState *s; > + ChardevCommon *common =3D qapi_ChardevHostdev_base(backend->u.pipe= ); > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_new0(WinCharState, 1); > chr->opaque =3D s; > chr->chr_write =3D win_chr_write; > @@ -2095,18 +2194,23 @@ static CharDriverState *qemu_chr_open_pipe(cons= t char *id, > > if (win_chr_pipe_init(chr, filename, errp) < 0) { > g_free(s); > - g_free(chr); > + qemu_chr_free_common(chr); > return NULL; > } > return chr; > } > > -static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) > +static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out, > + ChardevCommon *backend, > + Error **errp) > { > CharDriverState *chr; > WinCharState *s; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(backend, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_new0(WinCharState, 1); > s->hcom =3D fd_out; > chr->opaque =3D s; > @@ -2119,7 +2223,9 @@ static CharDriverState *qemu_chr_open_win_con(con= st char *id, > ChardevReturn *ret, > Error **errp) > { > - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); > + ChardevCommon *common =3D qapi_ChardevDummy_base(backend->u.consol= e); > + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), > + common, errp); > } > > static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, = int len) > @@ -2267,8 +2373,12 @@ static CharDriverState *qemu_chr_open_stdio(cons= t char *id, > WinStdioCharState *stdio; > DWORD dwMode; > int is_console =3D 0; > + ChardevCommon *common =3D qapi_ChardevStdio_base(backend->u.stdio)= ; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > stdio =3D g_new0(WinStdioCharState, 1); > > stdio->hStdIn =3D GetStdHandle(STD_INPUT_HANDLE); > @@ -2440,12 +2550,17 @@ static void udp_chr_close(CharDriverState *chr) > qemu_chr_be_event(chr, CHR_EVENT_CLOSED); > } > > -static CharDriverState *qemu_chr_open_udp_fd(int fd) > +static CharDriverState *qemu_chr_open_udp_fd(int fd, > + ChardevCommon *backend, > + Error **errp) > { > CharDriverState *chr =3D NULL; > NetCharDriver *s =3D NULL; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(backend, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_new0(NetCharDriver, 1); > > s->fd =3D fd; > @@ -2856,7 +2971,7 @@ static int tcp_chr_sync_read(CharDriverState *chr= , const uint8_t *buf, int len) > #ifndef _WIN32 > CharDriverState *qemu_chr_open_eventfd(int eventfd) > { > - CharDriverState *chr =3D qemu_chr_open_fd(eventfd, eventfd); > + CharDriverState *chr =3D qemu_chr_open_fd(eventfd, eventfd, NULL, = NULL); > > if (chr) { > chr->avail_connections =3D 1; > @@ -3138,10 +3253,14 @@ static CharDriverState *qemu_chr_open_ringbuf(c= onst char *id, > Error **errp) > { > ChardevRingbuf *opts =3D backend->u.ringbuf; > + ChardevCommon *common =3D qapi_ChardevRingbuf_base(backend->u.ring= buf); > CharDriverState *chr; > RingBufCharDriver *d; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > d =3D g_malloc(sizeof(*d)); > > d->size =3D opts->has_size ? opts->size : 65536; > @@ -3164,7 +3283,7 @@ static CharDriverState *qemu_chr_open_ringbuf(con= st char *id, > > fail: > g_free(d); > - g_free(chr); > + qemu_chr_free_common(chr); > return NULL; > } > > @@ -3408,6 +3527,18 @@ fail: > return NULL; > } > > +static void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backe= nd) > +{ > + const char *logfile =3D qemu_opt_get(opts, "logfile"); > + > + backend->has_logfile =3D logfile !=3D NULL; > + backend->logfile =3D logfile ? g_strdup(logfile) : NULL; > + > + backend->has_logappend =3D true; > + backend->logappend =3D qemu_opt_get_bool(opts, "logappend", false)= ; > +} > + > + > static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *b= ackend, > Error **errp) > { > @@ -3418,6 +3549,7 @@ static void qemu_chr_parse_file_out(QemuOpts *opt= s, ChardevBackend *backend, > return; > } > backend->u.file =3D g_new0(ChardevFile, 1); > + qemu_chr_parse_common(opts, qapi_ChardevFile_base(backend->u.file)= ); > backend->u.file->out =3D g_strdup(path); > > backend->u.file->has_append =3D true; > @@ -3428,6 +3560,7 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, = ChardevBackend *backend, > Error **errp) > { > backend->u.stdio =3D g_new0(ChardevStdio, 1); > + qemu_chr_parse_common(opts, qapi_ChardevStdio_base(backend->u.stdi= o)); > backend->u.stdio->has_signal =3D true; > backend->u.stdio->signal =3D qemu_opt_get_bool(opts, "signal", tr= ue); > } > @@ -3443,6 +3576,7 @@ static void qemu_chr_parse_serial(QemuOpts *opts,= ChardevBackend *backend, > return; > } > backend->u.serial =3D g_new0(ChardevHostdev, 1); > + qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(backend->u.se= rial)); > backend->u.serial->device =3D g_strdup(device); > } > #endif > @@ -3458,6 +3592,7 @@ static void qemu_chr_parse_parallel(QemuOpts *opt= s, ChardevBackend *backend, > return; > } > backend->u.parallel =3D g_new0(ChardevHostdev, 1); > + qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(backend->u.pa= rallel)); > backend->u.parallel->device =3D g_strdup(device); > } > #endif > @@ -3472,6 +3607,7 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, C= hardevBackend *backend, > return; > } > backend->u.pipe =3D g_new0(ChardevHostdev, 1); > + qemu_chr_parse_common(opts, qapi_ChardevHostdev_base(backend->u.pi= pe)); > backend->u.pipe->device =3D g_strdup(device); > } > > @@ -3481,6 +3617,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts= , ChardevBackend *backend, > int val; > > backend->u.ringbuf =3D g_new0(ChardevRingbuf, 1); > + qemu_chr_parse_common(opts, qapi_ChardevRingbuf_base(backend->u.ri= ngbuf)); > > val =3D qemu_opt_get_size(opts, "size", 0); > if (val !=3D 0) { > @@ -3499,6 +3636,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, Ch= ardevBackend *backend, > return; > } > backend->u.mux =3D g_new0(ChardevMux, 1); > + qemu_chr_parse_common(opts, qapi_ChardevMux_base(backend->u.mux)); > backend->u.mux->chardev =3D g_strdup(chardev); > } > > @@ -3527,6 +3665,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts,= ChardevBackend *backend, > } > > backend->u.socket =3D g_new0(ChardevSocket, 1); > + qemu_chr_parse_common(opts, qapi_ChardevSocket_base(backend->u.soc= ket)); > > backend->u.socket->has_nodelay =3D true; > backend->u.socket->nodelay =3D do_nodelay; > @@ -3588,6 +3727,7 @@ static void qemu_chr_parse_udp(QemuOpts *opts, Ch= ardevBackend *backend, > } > > backend->u.udp =3D g_new0(ChardevUdp, 1); > + qemu_chr_parse_common(opts, qapi_ChardevUdp_base(backend->u.udp)); > > addr =3D g_new0(SocketAddress, 1); > addr->type =3D SOCKET_ADDRESS_KIND_INET; > @@ -3687,7 +3827,12 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts= *opts, > error_propagate(errp, local_err); > goto qapi_out; > } > + } else { > + ChardevCommon *cc =3D g_new0(ChardevCommon, 1); > + qemu_chr_parse_common(opts, cc); > + backend->u.data =3D cc; > } > + > ret =3D qmp_chardev_add(bid ? bid : id, backend, errp); > if (!ret) { > goto qapi_out; > @@ -3819,17 +3964,25 @@ void qemu_chr_fe_release(CharDriverState *s) > s->avail_connections++; > } > > -void qemu_chr_free(CharDriverState *chr) > +static void qemu_chr_free_common(CharDriverState *chr) > { > - if (chr->chr_close) { > - chr->chr_close(chr); > - } > g_free(chr->filename); > g_free(chr->label); > qemu_opts_del(chr->opts); > + if (chr->logfd !=3D -1) { > + close(chr->logfd); > + } > g_free(chr); > } > > +void qemu_chr_free(CharDriverState *chr) > +{ > + if (chr->chr_close) { > + chr->chr_close(chr); > + } > + qemu_chr_free_common(chr); > +} > + > void qemu_chr_delete(CharDriverState *chr) > { > QTAILQ_REMOVE(&chardevs, chr, next); > @@ -3982,6 +4135,12 @@ QemuOptsList qemu_chardev_opts =3D { > },{ > .name =3D "append", > .type =3D QEMU_OPT_BOOL, > + },{ > + .name =3D "logfile", > + .type =3D QEMU_OPT_STRING, > + },{ > + .name =3D "logappend", > + .type =3D QEMU_OPT_BOOL, > }, > { /* end of list */ } > }, > @@ -3995,6 +4154,7 @@ static CharDriverState *qmp_chardev_open_file(con= st char *id, > Error **errp) > { > ChardevFile *file =3D backend->u.file; > + ChardevCommon *common =3D qapi_ChardevFile_base(backend->u.file); > HANDLE out; > > if (file->has_in) { > @@ -4008,7 +4168,7 @@ static CharDriverState *qmp_chardev_open_file(con= st char *id, > error_setg(errp, "open %s failed", file->out); > return NULL; > } > - return qemu_chr_open_win_file(out); > + return qemu_chr_open_win_file(out, common, errp); > } > > static CharDriverState *qmp_chardev_open_serial(const char *id, > @@ -4017,7 +4177,8 @@ static CharDriverState *qmp_chardev_open_serial(c= onst char *id, > Error **errp) > { > ChardevHostdev *serial =3D backend->u.serial; > - return qemu_chr_open_win_path(serial->device, errp); > + ChardevCommon *common =3D qapi_ChardevHostdev_base(backend->u.seri= al); > + return qemu_chr_open_win_path(serial->device, common, errp); > } > > #else /* WIN32 */ > @@ -4040,6 +4201,7 @@ static CharDriverState *qmp_chardev_open_file(con= st char *id, > Error **errp) > { > ChardevFile *file =3D backend->u.file; > + ChardevCommon *common =3D qapi_ChardevFile_base(backend->u.file); > int flags, in =3D -1, out; > > flags =3D O_WRONLY | O_CREAT | O_BINARY; > @@ -4063,7 +4225,7 @@ static CharDriverState *qmp_chardev_open_file(con= st char *id, > } > } > > - return qemu_chr_open_fd(in, out); > + return qemu_chr_open_fd(in, out, common, errp); > } > > #ifdef HAVE_CHARDEV_SERIAL > @@ -4073,6 +4235,7 @@ static CharDriverState *qmp_chardev_open_serial(c= onst char *id, > Error **errp) > { > ChardevHostdev *serial =3D backend->u.serial; > + ChardevCommon *common =3D qapi_ChardevHostdev_base(backend->u.seri= al); > int fd; > > fd =3D qmp_chardev_open_file_source(serial->device, O_RDWR, errp)= ; > @@ -4080,7 +4243,7 @@ static CharDriverState *qmp_chardev_open_serial(c= onst char *id, > return NULL; > } > qemu_set_nonblock(fd); > - return qemu_chr_open_tty_fd(fd); > + return qemu_chr_open_tty_fd(fd, common, errp); > } > #endif > > @@ -4091,13 +4254,14 @@ static CharDriverState *qmp_chardev_open_parall= el(const char *id, > Error **errp) > { > ChardevHostdev *parallel =3D backend->u.parallel; > + ChardevCommon *common =3D qapi_ChardevHostdev_base(backend->u.para= llel); > int fd; > > fd =3D qmp_chardev_open_file_source(parallel->device, O_RDWR, err= p); > if (fd < 0) { > return NULL; > } > - return qemu_chr_open_pp_fd(fd, errp); > + return qemu_chr_open_pp_fd(fd, common, errp); > } > #endif > > @@ -4142,8 +4306,12 @@ static CharDriverState *qmp_chardev_open_socket(= const char *id, > bool is_telnet =3D sock->has_telnet ? sock->telnet : false= ; > bool is_waitconnect =3D sock->has_wait ? sock->wait : false= ; > int64_t reconnect =3D sock->has_reconnect ? sock->reconnect : 0= ; > + ChardevCommon *common =3D qapi_ChardevSocket_base(backend->u.socke= t); > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_new0(TCPCharDriver, 1); > > s->fd =3D -1; > @@ -4182,8 +4350,7 @@ static CharDriverState *qmp_chardev_open_socket(c= onst char *id, > socket_try_connect(chr); > } else if (!qemu_chr_open_socket_fd(chr, errp)) { > g_free(s); > - g_free(chr->filename); > - g_free(chr); > + qemu_chr_free_common(chr); > return NULL; > } > > @@ -4203,13 +4370,14 @@ static CharDriverState *qmp_chardev_open_udp(co= nst char *id, > Error **errp) > { > ChardevUdp *udp =3D backend->u.udp; > + ChardevCommon *common =3D qapi_ChardevUdp_base(backend->u.udp); > int fd; > > fd =3D socket_dgram(udp->remote, udp->local, errp); > if (fd < 0) { > return NULL; > } > - return qemu_chr_open_udp_fd(fd); > + return qemu_chr_open_udp_fd(fd, common, errp); > } > > ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backen= d, > diff --git a/qemu-options.hx b/qemu-options.hx > index 215d00d..b4763ba 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -2089,40 +2089,43 @@ The general form of a character device option i= s: > ETEXI > > DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, > - "-chardev null,id=3Did[,mux=3Don|off]\n" > + "-chardev null,id=3Did[,mux=3Don|off][,logfile=3DPATH][,logappend=3D= on|off]\n" > "-chardev socket,id=3Did[,host=3Dhost],port=3Dport[,to=3Dto][,ipv= 4][,ipv6][,nodelay][,reconnect=3Dseconds]\n" > - " [,server][,nowait][,telnet][,reconnect=3Dseconds][,mux=3D= on|off] (tcp)\n" > - "-chardev socket,id=3Did,path=3Dpath[,server][,nowait][,telnet][,r= econnect=3Dseconds][,mux=3Don|off] (unix)\n" > + " [,server][,nowait][,telnet][,reconnect=3Dseconds][,mux=3D= on|off]\n" > + " [,logfile=3DPATH][,logappend=3Don|off] (tcp)\n" > + "-chardev socket,id=3Did,path=3Dpath[,server][,nowait][,telnet][,r= econnect=3Dseconds]\n" > + " [,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off] (u= nix)\n" > "-chardev udp,id=3Did[,host=3Dhost],port=3Dport[,localaddr=3Dloca= laddr]\n" > " [,localport=3Dlocalport][,ipv4][,ipv6][,mux=3Don|off]\n= " > - "-chardev msmouse,id=3Did[,mux=3Don|off]\n" > + " [,logfile=3DPATH][,logappend=3Don|off]\n" > + "-chardev msmouse,id=3Did[,mux=3Don|off][,logfile=3DPATH][,logappe= nd=3Don|off]\n" > "-chardev vc,id=3Did[[,width=3Dwidth][,height=3Dheight]][[,cols=3D= cols][,rows=3Drows]]\n" > - " [,mux=3Don|off]\n" > - "-chardev ringbuf,id=3Did[,size=3Dsize]\n" > - "-chardev file,id=3Did,path=3Dpath[,mux=3Don|off]\n" > - "-chardev pipe,id=3Did,path=3Dpath[,mux=3Don|off]\n" > + " [,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off]\n" > + "-chardev ringbuf,id=3Did[,size=3Dsize][,logfile=3DPATH][,logappen= d=3Don|off]\n" > + "-chardev file,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DPATH]= [,logappend=3Don|off]\n" > + "-chardev pipe,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DPATH]= [,logappend=3Don|off]\n" > #ifdef _WIN32 > - "-chardev console,id=3Did[,mux=3Don|off]\n" > - "-chardev serial,id=3Did,path=3Dpath[,mux=3Don|off]\n" > + "-chardev console,id=3Did[,mux=3Don|off][,logfile=3DPATH][,logappe= nd=3Don|off]\n" > + "-chardev serial,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DPAT= H][,logappend=3Don|off]\n" > #else > - "-chardev pty,id=3Did[,mux=3Don|off]\n" > - "-chardev stdio,id=3Did[,mux=3Don|off][,signal=3Don|off]\n" > + "-chardev pty,id=3Did[,mux=3Don|off][,logfile=3DPATH][,logappend=3D= on|off]\n" > + "-chardev stdio,id=3Did[,mux=3Don|off][,signal=3Don|off][,logfile=3D= PATH][,logappend=3Don|off]\n" > #endif > #ifdef CONFIG_BRLAPI > - "-chardev braille,id=3Did[,mux=3Don|off]\n" > + "-chardev braille,id=3Did[,mux=3Don|off][,logfile=3DPATH][,logappe= nd=3Don|off]\n" > #endif > #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ > || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__D= ragonFly__) > - "-chardev serial,id=3Did,path=3Dpath[,mux=3Don|off]\n" > - "-chardev tty,id=3Did,path=3Dpath[,mux=3Don|off]\n" > + "-chardev serial,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DPAT= H][,logappend=3Don|off]\n" > + "-chardev tty,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DPATH][= ,logappend=3Don|off]\n" > #endif > #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly= __) > - "-chardev parallel,id=3Did,path=3Dpath[,mux=3Don|off]\n" > - "-chardev parport,id=3Did,path=3Dpath[,mux=3Don|off]\n" > + "-chardev parallel,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DP= ATH][,logappend=3Don|off]\n" > + "-chardev parport,id=3Did,path=3Dpath[,mux=3Don|off][,logfile=3DPA= TH][,logappend=3Don|off]\n" > #endif > #if defined(CONFIG_SPICE) > - "-chardev spicevmc,id=3Did,name=3Dname[,debug=3Ddebug]\n" > - "-chardev spiceport,id=3Did,name=3Dname[,debug=3Ddebug]\n" > + "-chardev spicevmc,id=3Did,name=3Dname[,debug=3Ddebug][,logfile=3D= PATH][,logappend=3Don|off]\n" > + "-chardev spiceport,id=3Did,name=3Dname[,debug=3Ddebug][,logfile=3D= PATH][,logappend=3Don|off]\n" > #endif > , QEMU_ARCH_ALL > ) > @@ -2158,7 +2161,12 @@ A character device may be used in multiplexing m= ode by multiple front-ends. > The key sequence of @key{Control-a} and @key{c} will rotate the input= focus > between attached front-ends. Specify @option{mux=3Don} to enable this= mode. > > -Options to each backend are described below. > +Every backend supports the @option{logfile} option, which supplies the= path > +to a file to record all data transmitted via the backend. The @option{= logappend} > +option controls whether the log file will be truncated or appended to = when > +opened. > + > +Further options to each backend are described below. > > @item -chardev null ,id=3D@var{id} > A void device. This device will not emit any data, and will drop any = data it > diff --git a/spice-qemu-char.c b/spice-qemu-char.c > index e70e0f7..8951d7c 100644 > --- a/spice-qemu-char.c > +++ b/spice-qemu-char.c > @@ -271,13 +271,18 @@ static void spice_chr_accept_input(struct CharDri= verState *chr) > } > > static CharDriverState *chr_open(const char *subtype, > - void (*set_fe_open)(struct CharDriverState *, int)) > - > + void (*set_fe_open)(struct CharDriver= State *, > + int), > + ChardevCommon *backend, > + Error **errp) > { > CharDriverState *chr; > SpiceCharDriver *s; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(backend, errp); > + if (!chr) { > + return NULL; > + } > s =3D g_malloc0(sizeof(SpiceCharDriver)); > s->chr =3D chr; > s->active =3D false; > @@ -303,6 +308,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(con= st char *id, > { > const char *type =3D backend->u.spicevmc->type; > const char **psubtype =3D spice_server_char_device_recognized_sub= types(); > + ChardevCommon *common =3D qapi_ChardevSpiceChannel_base(backend->u= .spicevmc); > > for (; *psubtype !=3D NULL; ++psubtype) { > if (strcmp(type, *psubtype) =3D=3D 0) { > @@ -315,7 +321,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(con= st char *id, > return NULL; > } > > - return chr_open(type, spice_vmc_set_fe_open); > + return chr_open(type, spice_vmc_set_fe_open, common, errp); > } > > #if SPICE_SERVER_VERSION >=3D 0x000c02 > @@ -325,6 +331,7 @@ static CharDriverState *qemu_chr_open_spice_port(co= nst char *id, > Error **errp) > { > const char *name =3D backend->u.spiceport->fqdn; > + ChardevCommon *common =3D qapi_ChardevSpicePort_base(backend->u.sp= iceport); > CharDriverState *chr; > SpiceCharDriver *s; > > @@ -333,7 +340,10 @@ static CharDriverState *qemu_chr_open_spice_port(c= onst char *id, > return NULL; > } > > - chr =3D chr_open("port", spice_port_set_fe_open); > + chr =3D chr_open("port", spice_port_set_fe_open, common, errp); > + if (!chr) { > + return NULL; > + } > s =3D chr->opaque; > s->sin.portname =3D g_strdup(name); > > diff --git a/ui/console.c b/ui/console.c > index 4b65c34..fe950c6 100644 > --- a/ui/console.c > +++ b/ui/console.c > @@ -1953,12 +1953,16 @@ static void text_console_do_init(CharDriverStat= e *chr, DisplayState *ds) > > static CharDriverState *text_console_init(ChardevVC *vc, Error **errp= ) > { > + ChardevCommon *common =3D qapi_ChardevVC_base(vc); > CharDriverState *chr; > QemuConsole *s; > unsigned width =3D 0; > unsigned height =3D 0; > > - chr =3D qemu_chr_alloc(); > + chr =3D qemu_chr_alloc(common, errp); > + if (!chr) { > + return NULL; > + } > > if (vc->has_width) { > width =3D vc->width; >