Merge remote-tracking branch 'kraxel/chardev.1' into staging
# By Gerd Hoffmann # Via Gerd Hoffmann * kraxel/chardev.1: chardev: add pty chardev support to chardev-add (qmp) chardev: add socket chardev support to chardev-add (qmp) chardev: add parallel chardev support to chardev-add (qmp) chardev: add serial chardev support to chardev-add (qmp) chardev: add file chardev support to chardev-add (qmp) chardev: add hmp hotplug commands chardev: add qmp hotplug commands, with null chardev support chardev: reduce chardev ifdef mess a bit chardev: fix QemuOpts lifecycle chardev: add error reporting for qemu_chr_new_from_opts
This commit is contained in:
		
						commit
						47f4dac3fd
					
				@ -1482,6 +1482,38 @@ Password is invalidated at the given time.  @var{nsec} are the seconds
 | 
				
			|||||||
passed since 1970, i.e. unix epoch.
 | 
					passed since 1970, i.e. unix epoch.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@end table
 | 
					@end table
 | 
				
			||||||
 | 
					ETEXI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        .name       = "chardev-add",
 | 
				
			||||||
 | 
					        .args_type  = "args:s",
 | 
				
			||||||
 | 
					        .params     = "args",
 | 
				
			||||||
 | 
					        .help       = "add chardev",
 | 
				
			||||||
 | 
					        .mhandler.cmd = hmp_chardev_add,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STEXI
 | 
				
			||||||
 | 
					@item chardev_add args
 | 
				
			||||||
 | 
					@findex chardev_add
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					chardev_add accepts the same parameters as the -chardev command line switch.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ETEXI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        .name       = "chardev-remove",
 | 
				
			||||||
 | 
					        .args_type  = "id:s",
 | 
				
			||||||
 | 
					        .params     = "id",
 | 
				
			||||||
 | 
					        .help       = "remove chardev",
 | 
				
			||||||
 | 
					        .mhandler.cmd = hmp_chardev_remove,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STEXI
 | 
				
			||||||
 | 
					@item chardev_remove id
 | 
				
			||||||
 | 
					@findex chardev_remove
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Removes the chardev @var{id}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ETEXI
 | 
					ETEXI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										23
									
								
								hmp.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								hmp.c
									
									
									
									
									
								
							@ -1336,3 +1336,26 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
 | 
				
			|||||||
    qmp_nbd_server_stop(&errp);
 | 
					    qmp_nbd_server_stop(&errp);
 | 
				
			||||||
    hmp_handle_error(mon, &errp);
 | 
					    hmp_handle_error(mon, &errp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void hmp_chardev_add(Monitor *mon, const QDict *qdict)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *args = qdict_get_str(qdict, "args");
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
 | 
					    QemuOpts *opts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    opts = qemu_opts_parse(qemu_find_opts("chardev"), args, 1);
 | 
				
			||||||
 | 
					    if (opts == NULL) {
 | 
				
			||||||
 | 
					        error_setg(&err, "Parsing chardev args failed\n");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        qemu_chr_new_from_opts(opts, NULL, &err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    hmp_handle_error(mon, &err);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Error *local_err = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
 | 
				
			||||||
 | 
					    hmp_handle_error(mon, &local_err);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								hmp.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								hmp.h
									
									
									
									
									
								
							@ -80,5 +80,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict);
 | 
				
			|||||||
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
 | 
					void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
 | 
				
			||||||
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
 | 
					void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
 | 
				
			||||||
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
 | 
					void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
 | 
				
			||||||
 | 
					void hmp_chardev_add(Monitor *mon, const QDict *qdict);
 | 
				
			||||||
 | 
					void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -75,6 +75,7 @@ struct CharDriverState {
 | 
				
			|||||||
    char *filename;
 | 
					    char *filename;
 | 
				
			||||||
    int opened;
 | 
					    int opened;
 | 
				
			||||||
    int avail_connections;
 | 
					    int avail_connections;
 | 
				
			||||||
 | 
					    QemuOpts *opts;
 | 
				
			||||||
    QTAILQ_ENTRY(CharDriverState) next;
 | 
					    QTAILQ_ENTRY(CharDriverState) next;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -89,7 +90,8 @@ struct CharDriverState {
 | 
				
			|||||||
 * Returns: a new character backend
 | 
					 * Returns: a new character backend
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 | 
					CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 | 
				
			||||||
                                    void (*init)(struct CharDriverState *s));
 | 
					                                    void (*init)(struct CharDriverState *s),
 | 
				
			||||||
 | 
					                                    Error **errp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @qemu_chr_new:
 | 
					 * @qemu_chr_new:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										104
									
								
								qapi-schema.json
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								qapi-schema.json
									
									
									
									
									
								
							@ -3017,3 +3017,107 @@
 | 
				
			|||||||
# Since: 1.3.0
 | 
					# Since: 1.3.0
 | 
				
			||||||
##
 | 
					##
 | 
				
			||||||
{ 'command': 'nbd-server-stop' }
 | 
					{ 'command': 'nbd-server-stop' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @ChardevFile:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Configuration info for file chardevs.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @in:  #optional The name of the input file
 | 
				
			||||||
 | 
					# @out: The name of the output file
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'type': 'ChardevFile', 'data': { '*in' : 'str',
 | 
				
			||||||
 | 
					                                   'out' : 'str' } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @ChardevPort:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Configuration info for device chardevs.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @device: The name of the special file for the device,
 | 
				
			||||||
 | 
					#          i.e. /dev/ttyS0 on Unix or COM1: on Windows
 | 
				
			||||||
 | 
					# @type: What kind of device this is.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'enum': 'ChardevPortKind', 'data': [ 'serial',
 | 
				
			||||||
 | 
					                                       'parallel' ] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{ 'type': 'ChardevPort', 'data': { 'device' : 'str',
 | 
				
			||||||
 | 
					                                   'type'   : 'ChardevPortKind'} }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @ChardevSocket:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Configuration info for socket chardevs.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @addr: socket address to listen on (server=true)
 | 
				
			||||||
 | 
					#        or connect to (server=false)
 | 
				
			||||||
 | 
					# @server: #optional create server socket (default: true)
 | 
				
			||||||
 | 
					# @wait: #optional wait for connect (not used for server
 | 
				
			||||||
 | 
					#        sockets, default: false)
 | 
				
			||||||
 | 
					# @nodelay: #optional set TCP_NODELAY socket option (default: false)
 | 
				
			||||||
 | 
					# @telnet: #optional enable telnet protocol (default: false)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'type': 'ChardevSocket', 'data': { 'addr'     : 'SocketAddress',
 | 
				
			||||||
 | 
					                                     '*server'  : 'bool',
 | 
				
			||||||
 | 
					                                     '*wait'    : 'bool',
 | 
				
			||||||
 | 
					                                     '*nodelay' : 'bool',
 | 
				
			||||||
 | 
					                                     '*telnet'  : 'bool' } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @ChardevBackend:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Configuration info for the new chardev backend.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'type': 'ChardevDummy', 'data': { } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{ 'union': 'ChardevBackend', 'data': { 'file'   : 'ChardevFile',
 | 
				
			||||||
 | 
					                                       'port'   : 'ChardevPort',
 | 
				
			||||||
 | 
					                                       'socket' : 'ChardevSocket',
 | 
				
			||||||
 | 
					                                       'pty'    : 'ChardevDummy',
 | 
				
			||||||
 | 
					                                       'null'   : 'ChardevDummy' } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @ChardevReturn:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Return info about the chardev backend just created.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'type' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @chardev-add:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Add a file chardev
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @id: the chardev's ID, must be unique
 | 
				
			||||||
 | 
					# @backend: backend type and parameters
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Returns: chardev info.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'command': 'chardev-add', 'data': {'id'      : 'str',
 | 
				
			||||||
 | 
					                                     'backend' : 'ChardevBackend' },
 | 
				
			||||||
 | 
					  'returns': 'ChardevReturn' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# @chardev-remove:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Remove a chardev
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# @id: the chardev's ID, must exist and not be in use
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Returns: Nothing on success
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Since: 1.4
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					{ 'command': 'chardev-remove', 'data': {'id': 'str'} }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										453
									
								
								qemu-char.c
									
									
									
									
									
								
							
							
						
						
									
										453
									
								
								qemu-char.c
									
									
									
									
									
								
							@ -856,6 +856,8 @@ static void cfmakeraw (struct termios *termios_p)
 | 
				
			|||||||
    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
 | 
					    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
 | 
				
			||||||
    || defined(__GLIBC__)
 | 
					    || defined(__GLIBC__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_CHARDEV_TTY 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    int fd;
 | 
					    int fd;
 | 
				
			||||||
    int connected;
 | 
					    int connected;
 | 
				
			||||||
@ -1228,30 +1230,34 @@ static void qemu_chr_close_tty(CharDriverState *chr)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static CharDriverState *qemu_chr_open_tty(QemuOpts *opts)
 | 
					static CharDriverState *qemu_chr_open_tty_fd(int fd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const char *filename = qemu_opt_get(opts, "path");
 | 
					 | 
				
			||||||
    CharDriverState *chr;
 | 
					    CharDriverState *chr;
 | 
				
			||||||
    int fd;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK));
 | 
					 | 
				
			||||||
    if (fd < 0) {
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    tty_serial_init(fd, 115200, 'N', 8, 1);
 | 
					    tty_serial_init(fd, 115200, 'N', 8, 1);
 | 
				
			||||||
    chr = qemu_chr_open_fd(fd, fd);
 | 
					    chr = qemu_chr_open_fd(fd, fd);
 | 
				
			||||||
    chr->chr_ioctl = tty_serial_ioctl;
 | 
					    chr->chr_ioctl = tty_serial_ioctl;
 | 
				
			||||||
    chr->chr_close = qemu_chr_close_tty;
 | 
					    chr->chr_close = qemu_chr_close_tty;
 | 
				
			||||||
    return chr;
 | 
					    return chr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#else  /* ! __linux__ && ! __sun__ */
 | 
					
 | 
				
			||||||
static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
 | 
					static CharDriverState *qemu_chr_open_tty(QemuOpts *opts)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    const char *filename = qemu_opt_get(opts, "path");
 | 
				
			||||||
 | 
					    int fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK));
 | 
				
			||||||
 | 
					    if (fd < 0) {
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return qemu_chr_open_tty_fd(fd);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif /* __linux__ || __sun__ */
 | 
					#endif /* __linux__ || __sun__ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__linux__)
 | 
					#if defined(__linux__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_CHARDEV_PARPORT 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    int fd;
 | 
					    int fd;
 | 
				
			||||||
    int mode;
 | 
					    int mode;
 | 
				
			||||||
@ -1361,17 +1367,10 @@ static void pp_close(CharDriverState *chr)
 | 
				
			|||||||
    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
					    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
 | 
					static CharDriverState *qemu_chr_open_pp_fd(int fd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const char *filename = qemu_opt_get(opts, "path");
 | 
					 | 
				
			||||||
    CharDriverState *chr;
 | 
					    CharDriverState *chr;
 | 
				
			||||||
    ParallelCharDriver *drv;
 | 
					    ParallelCharDriver *drv;
 | 
				
			||||||
    int fd;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TFR(fd = qemu_open(filename, O_RDWR));
 | 
					 | 
				
			||||||
    if (fd < 0) {
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ioctl(fd, PPCLAIM) < 0) {
 | 
					    if (ioctl(fd, PPCLAIM) < 0) {
 | 
				
			||||||
        close(fd);
 | 
					        close(fd);
 | 
				
			||||||
@ -1395,6 +1394,9 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
 | 
				
			|||||||
#endif /* __linux__ */
 | 
					#endif /* __linux__ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
 | 
					#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_CHARDEV_PARPORT 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 | 
					static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int fd = (int)(intptr_t)chr->opaque;
 | 
					    int fd = (int)(intptr_t)chr->opaque;
 | 
				
			||||||
@ -1432,16 +1434,9 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
 | 
					static CharDriverState *qemu_chr_open_pp_fd(int fd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const char *filename = qemu_opt_get(opts, "path");
 | 
					 | 
				
			||||||
    CharDriverState *chr;
 | 
					    CharDriverState *chr;
 | 
				
			||||||
    int fd;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fd = qemu_open(filename, O_RDWR);
 | 
					 | 
				
			||||||
    if (fd < 0) {
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chr = g_malloc0(sizeof(CharDriverState));
 | 
					    chr = g_malloc0(sizeof(CharDriverState));
 | 
				
			||||||
    chr->opaque = (void *)(intptr_t)fd;
 | 
					    chr->opaque = (void *)(intptr_t)fd;
 | 
				
			||||||
@ -1663,9 +1658,8 @@ static int win_chr_poll(void *opaque)
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
 | 
					static CharDriverState *qemu_chr_open_win_path(const char *filename)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const char *filename = qemu_opt_get(opts, "path");
 | 
					 | 
				
			||||||
    CharDriverState *chr;
 | 
					    CharDriverState *chr;
 | 
				
			||||||
    WinCharState *s;
 | 
					    WinCharState *s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1684,6 +1678,11 @@ static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
 | 
				
			|||||||
    return chr;
 | 
					    return chr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return qemu_chr_open_win_path(qemu_opt_get(opts, "path"));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int win_chr_pipe_poll(void *opaque)
 | 
					static int win_chr_pipe_poll(void *opaque)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    CharDriverState *chr = opaque;
 | 
					    CharDriverState *chr = opaque;
 | 
				
			||||||
@ -2439,10 +2438,88 @@ static void tcp_chr_close(CharDriverState *chr)
 | 
				
			|||||||
    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
					    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
 | 
					static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
 | 
				
			||||||
 | 
					                                                bool is_listen, bool is_telnet,
 | 
				
			||||||
 | 
					                                                bool is_waitconnect,
 | 
				
			||||||
 | 
					                                                Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    CharDriverState *chr = NULL;
 | 
					    CharDriverState *chr = NULL;
 | 
				
			||||||
    TCPCharDriver *s = NULL;
 | 
					    TCPCharDriver *s = NULL;
 | 
				
			||||||
 | 
					    char host[NI_MAXHOST], serv[NI_MAXSERV];
 | 
				
			||||||
 | 
					    const char *left = "", *right = "";
 | 
				
			||||||
 | 
					    struct sockaddr_storage ss;
 | 
				
			||||||
 | 
					    socklen_t ss_len = sizeof(ss);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memset(&ss, 0, ss_len);
 | 
				
			||||||
 | 
					    if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) {
 | 
				
			||||||
 | 
					        error_setg(errp, "getsockname: %s", strerror(errno));
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chr = g_malloc0(sizeof(CharDriverState));
 | 
				
			||||||
 | 
					    s = g_malloc0(sizeof(TCPCharDriver));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s->connected = 0;
 | 
				
			||||||
 | 
					    s->fd = -1;
 | 
				
			||||||
 | 
					    s->listen_fd = -1;
 | 
				
			||||||
 | 
					    s->msgfd = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chr->filename = g_malloc(256);
 | 
				
			||||||
 | 
					    switch (ss.ss_family) {
 | 
				
			||||||
 | 
					#ifndef _WIN32
 | 
				
			||||||
 | 
					    case AF_UNIX:
 | 
				
			||||||
 | 
					        s->is_unix = 1;
 | 
				
			||||||
 | 
					        snprintf(chr->filename, 256, "unix:%s%s",
 | 
				
			||||||
 | 
					                 ((struct sockaddr_un *)(&ss))->sun_path,
 | 
				
			||||||
 | 
					                 is_listen ? ",server" : "");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    case AF_INET6:
 | 
				
			||||||
 | 
					        left  = "[";
 | 
				
			||||||
 | 
					        right = "]";
 | 
				
			||||||
 | 
					        /* fall through */
 | 
				
			||||||
 | 
					    case AF_INET:
 | 
				
			||||||
 | 
					        s->do_nodelay = do_nodelay;
 | 
				
			||||||
 | 
					        getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host),
 | 
				
			||||||
 | 
					                    serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
 | 
				
			||||||
 | 
					        snprintf(chr->filename, 256, "%s:%s:%s%s%s%s",
 | 
				
			||||||
 | 
					                 is_telnet ? "telnet" : "tcp",
 | 
				
			||||||
 | 
					                 left, host, right, serv,
 | 
				
			||||||
 | 
					                 is_listen ? ",server" : "");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chr->opaque = s;
 | 
				
			||||||
 | 
					    chr->chr_write = tcp_chr_write;
 | 
				
			||||||
 | 
					    chr->chr_close = tcp_chr_close;
 | 
				
			||||||
 | 
					    chr->get_msgfd = tcp_get_msgfd;
 | 
				
			||||||
 | 
					    chr->chr_add_client = tcp_chr_add_client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (is_listen) {
 | 
				
			||||||
 | 
					        s->listen_fd = fd;
 | 
				
			||||||
 | 
					        qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
 | 
				
			||||||
 | 
					        if (is_telnet) {
 | 
				
			||||||
 | 
					            s->do_telnetopt = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        s->connected = 1;
 | 
				
			||||||
 | 
					        s->fd = fd;
 | 
				
			||||||
 | 
					        socket_set_nodelay(fd);
 | 
				
			||||||
 | 
					        tcp_chr_connect(chr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (is_listen && is_waitconnect) {
 | 
				
			||||||
 | 
					        printf("QEMU waiting for connection on: %s\n",
 | 
				
			||||||
 | 
					               chr->filename);
 | 
				
			||||||
 | 
					        tcp_chr_accept(chr);
 | 
				
			||||||
 | 
					        socket_set_nonblock(s->listen_fd);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return chr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CharDriverState *chr = NULL;
 | 
				
			||||||
    Error *local_err = NULL;
 | 
					    Error *local_err = NULL;
 | 
				
			||||||
    int fd = -1;
 | 
					    int fd = -1;
 | 
				
			||||||
    int is_listen;
 | 
					    int is_listen;
 | 
				
			||||||
@ -2459,9 +2536,6 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
 | 
				
			|||||||
    if (!is_listen)
 | 
					    if (!is_listen)
 | 
				
			||||||
        is_waitconnect = 0;
 | 
					        is_waitconnect = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chr = g_malloc0(sizeof(CharDriverState));
 | 
					 | 
				
			||||||
    s = g_malloc0(sizeof(TCPCharDriver));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (is_unix) {
 | 
					    if (is_unix) {
 | 
				
			||||||
        if (is_listen) {
 | 
					        if (is_listen) {
 | 
				
			||||||
            fd = unix_listen_opts(opts, &local_err);
 | 
					            fd = unix_listen_opts(opts, &local_err);
 | 
				
			||||||
@ -2482,56 +2556,14 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
 | 
				
			|||||||
    if (!is_waitconnect)
 | 
					    if (!is_waitconnect)
 | 
				
			||||||
        socket_set_nonblock(fd);
 | 
					        socket_set_nonblock(fd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s->connected = 0;
 | 
					    chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet,
 | 
				
			||||||
    s->fd = -1;
 | 
					                                  is_waitconnect, &local_err);
 | 
				
			||||||
    s->listen_fd = -1;
 | 
					    if (error_is_set(&local_err)) {
 | 
				
			||||||
    s->msgfd = -1;
 | 
					        goto fail;
 | 
				
			||||||
    s->is_unix = is_unix;
 | 
					 | 
				
			||||||
    s->do_nodelay = do_nodelay && !is_unix;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    chr->opaque = s;
 | 
					 | 
				
			||||||
    chr->chr_write = tcp_chr_write;
 | 
					 | 
				
			||||||
    chr->chr_close = tcp_chr_close;
 | 
					 | 
				
			||||||
    chr->get_msgfd = tcp_get_msgfd;
 | 
					 | 
				
			||||||
    chr->chr_add_client = tcp_chr_add_client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (is_listen) {
 | 
					 | 
				
			||||||
        s->listen_fd = fd;
 | 
					 | 
				
			||||||
        qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
 | 
					 | 
				
			||||||
        if (is_telnet)
 | 
					 | 
				
			||||||
            s->do_telnetopt = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        s->connected = 1;
 | 
					 | 
				
			||||||
        s->fd = fd;
 | 
					 | 
				
			||||||
        socket_set_nodelay(fd);
 | 
					 | 
				
			||||||
        tcp_chr_connect(chr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* for "info chardev" monitor command */
 | 
					 | 
				
			||||||
    chr->filename = g_malloc(256);
 | 
					 | 
				
			||||||
    if (is_unix) {
 | 
					 | 
				
			||||||
        snprintf(chr->filename, 256, "unix:%s%s",
 | 
					 | 
				
			||||||
                 qemu_opt_get(opts, "path"),
 | 
					 | 
				
			||||||
                 qemu_opt_get_bool(opts, "server", 0) ? ",server" : "");
 | 
					 | 
				
			||||||
    } else if (is_telnet) {
 | 
					 | 
				
			||||||
        snprintf(chr->filename, 256, "telnet:%s:%s%s",
 | 
					 | 
				
			||||||
                 qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"),
 | 
					 | 
				
			||||||
                 qemu_opt_get_bool(opts, "server", 0) ? ",server" : "");
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        snprintf(chr->filename, 256, "tcp:%s:%s%s",
 | 
					 | 
				
			||||||
                 qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"),
 | 
					 | 
				
			||||||
                 qemu_opt_get_bool(opts, "server", 0) ? ",server" : "");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (is_listen && is_waitconnect) {
 | 
					 | 
				
			||||||
        printf("QEMU waiting for connection on: %s\n",
 | 
					 | 
				
			||||||
               chr->filename);
 | 
					 | 
				
			||||||
        tcp_chr_accept(chr);
 | 
					 | 
				
			||||||
        socket_set_nonblock(s->listen_fd);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return chr;
 | 
					    return chr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 fail:
 | 
					 fail:
 | 
				
			||||||
    if (local_err) {
 | 
					    if (local_err) {
 | 
				
			||||||
        qerror_report_err(local_err);
 | 
					        qerror_report_err(local_err);
 | 
				
			||||||
@ -2540,8 +2572,10 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
 | 
				
			|||||||
    if (fd >= 0) {
 | 
					    if (fd >= 0) {
 | 
				
			||||||
        closesocket(fd);
 | 
					        closesocket(fd);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    g_free(s);
 | 
					    if (chr) {
 | 
				
			||||||
 | 
					        g_free(chr->opaque);
 | 
				
			||||||
        g_free(chr);
 | 
					        g_free(chr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return NULL;
 | 
					    return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2737,6 +2771,22 @@ fail:
 | 
				
			|||||||
    return NULL;
 | 
					    return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_CHARDEV_PARPORT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *filename = qemu_opt_get(opts, "path");
 | 
				
			||||||
 | 
					    int fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fd = qemu_open(filename, O_RDWR);
 | 
				
			||||||
 | 
					    if (fd < 0) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return qemu_chr_open_pp_fd(fd);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct {
 | 
					static const struct {
 | 
				
			||||||
    const char *name;
 | 
					    const char *name;
 | 
				
			||||||
    CharDriverState *(*open)(QemuOpts *opts);
 | 
					    CharDriverState *(*open)(QemuOpts *opts);
 | 
				
			||||||
@ -2755,19 +2805,18 @@ static const struct {
 | 
				
			|||||||
#else
 | 
					#else
 | 
				
			||||||
    { .name = "file",      .open = qemu_chr_open_file_out },
 | 
					    { .name = "file",      .open = qemu_chr_open_file_out },
 | 
				
			||||||
    { .name = "pipe",      .open = qemu_chr_open_pipe },
 | 
					    { .name = "pipe",      .open = qemu_chr_open_pipe },
 | 
				
			||||||
    { .name = "pty",       .open = qemu_chr_open_pty },
 | 
					 | 
				
			||||||
    { .name = "stdio",     .open = qemu_chr_open_stdio },
 | 
					    { .name = "stdio",     .open = qemu_chr_open_stdio },
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef CONFIG_BRLAPI
 | 
					#ifdef CONFIG_BRLAPI
 | 
				
			||||||
    { .name = "braille",   .open = chr_baum_init },
 | 
					    { .name = "braille",   .open = chr_baum_init },
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
 | 
					#ifdef HAVE_CHARDEV_TTY
 | 
				
			||||||
    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
 | 
					 | 
				
			||||||
    || defined(__FreeBSD_kernel__)
 | 
					 | 
				
			||||||
    { .name = "tty",       .open = qemu_chr_open_tty },
 | 
					    { .name = "tty",       .open = qemu_chr_open_tty },
 | 
				
			||||||
 | 
					    { .name = "serial",    .open = qemu_chr_open_tty },
 | 
				
			||||||
 | 
					    { .name = "pty",       .open = qemu_chr_open_pty },
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) \
 | 
					#ifdef HAVE_CHARDEV_PARPORT
 | 
				
			||||||
    || defined(__FreeBSD_kernel__)
 | 
					    { .name = "parallel",  .open = qemu_chr_open_pp },
 | 
				
			||||||
    { .name = "parport",   .open = qemu_chr_open_pp },
 | 
					    { .name = "parport",   .open = qemu_chr_open_pp },
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef CONFIG_SPICE
 | 
					#ifdef CONFIG_SPICE
 | 
				
			||||||
@ -2779,36 +2828,37 @@ static const struct {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 | 
					CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 | 
				
			||||||
                                    void (*init)(struct CharDriverState *s))
 | 
					                                    void (*init)(struct CharDriverState *s),
 | 
				
			||||||
 | 
					                                    Error **errp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    CharDriverState *chr;
 | 
					    CharDriverState *chr;
 | 
				
			||||||
    int i;
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (qemu_opts_id(opts) == NULL) {
 | 
					    if (qemu_opts_id(opts) == NULL) {
 | 
				
			||||||
        fprintf(stderr, "chardev: no id specified\n");
 | 
					        error_setg(errp, "chardev: no id specified\n");
 | 
				
			||||||
        return NULL;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (qemu_opt_get(opts, "backend") == NULL) {
 | 
					    if (qemu_opt_get(opts, "backend") == NULL) {
 | 
				
			||||||
        fprintf(stderr, "chardev: \"%s\" missing backend\n",
 | 
					        error_setg(errp, "chardev: \"%s\" missing backend\n",
 | 
				
			||||||
                   qemu_opts_id(opts));
 | 
					                   qemu_opts_id(opts));
 | 
				
			||||||
        return NULL;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for (i = 0; i < ARRAY_SIZE(backend_table); i++) {
 | 
					    for (i = 0; i < ARRAY_SIZE(backend_table); i++) {
 | 
				
			||||||
        if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0)
 | 
					        if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0)
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (i == ARRAY_SIZE(backend_table)) {
 | 
					    if (i == ARRAY_SIZE(backend_table)) {
 | 
				
			||||||
        fprintf(stderr, "chardev: backend \"%s\" not found\n",
 | 
					        error_setg(errp, "chardev: backend \"%s\" not found\n",
 | 
				
			||||||
                   qemu_opt_get(opts, "backend"));
 | 
					                   qemu_opt_get(opts, "backend"));
 | 
				
			||||||
        return NULL;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chr = backend_table[i].open(opts);
 | 
					    chr = backend_table[i].open(opts);
 | 
				
			||||||
    if (!chr) {
 | 
					    if (!chr) {
 | 
				
			||||||
        fprintf(stderr, "chardev: opening backend \"%s\" failed\n",
 | 
					        error_setg(errp, "chardev: opening backend \"%s\" failed\n",
 | 
				
			||||||
                   qemu_opt_get(opts, "backend"));
 | 
					                   qemu_opt_get(opts, "backend"));
 | 
				
			||||||
        return NULL;
 | 
					        goto err;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!chr->filename)
 | 
					    if (!chr->filename)
 | 
				
			||||||
@ -2829,7 +2879,12 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 | 
				
			|||||||
        chr->avail_connections = 1;
 | 
					        chr->avail_connections = 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    chr->label = g_strdup(qemu_opts_id(opts));
 | 
					    chr->label = g_strdup(qemu_opts_id(opts));
 | 
				
			||||||
 | 
					    chr->opts = opts;
 | 
				
			||||||
    return chr;
 | 
					    return chr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					    qemu_opts_del(opts);
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
 | 
					CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
 | 
				
			||||||
@ -2837,6 +2892,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in
 | 
				
			|||||||
    const char *p;
 | 
					    const char *p;
 | 
				
			||||||
    CharDriverState *chr;
 | 
					    CharDriverState *chr;
 | 
				
			||||||
    QemuOpts *opts;
 | 
					    QemuOpts *opts;
 | 
				
			||||||
 | 
					    Error *err = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (strstart(filename, "chardev:", &p)) {
 | 
					    if (strstart(filename, "chardev:", &p)) {
 | 
				
			||||||
        return qemu_chr_find(p);
 | 
					        return qemu_chr_find(p);
 | 
				
			||||||
@ -2846,11 +2902,14 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in
 | 
				
			|||||||
    if (!opts)
 | 
					    if (!opts)
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chr = qemu_chr_new_from_opts(opts, init);
 | 
					    chr = qemu_chr_new_from_opts(opts, init, &err);
 | 
				
			||||||
 | 
					    if (error_is_set(&err)) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "%s\n", error_get_pretty(err));
 | 
				
			||||||
 | 
					        error_free(err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
 | 
					    if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
 | 
				
			||||||
        monitor_init(chr, MONITOR_USE_READLINE);
 | 
					        monitor_init(chr, MONITOR_USE_READLINE);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    qemu_opts_del(opts);
 | 
					 | 
				
			||||||
    return chr;
 | 
					    return chr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2878,10 +2937,14 @@ void qemu_chr_fe_close(struct CharDriverState *chr)
 | 
				
			|||||||
void qemu_chr_delete(CharDriverState *chr)
 | 
					void qemu_chr_delete(CharDriverState *chr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    QTAILQ_REMOVE(&chardevs, chr, next);
 | 
					    QTAILQ_REMOVE(&chardevs, chr, next);
 | 
				
			||||||
    if (chr->chr_close)
 | 
					    if (chr->chr_close) {
 | 
				
			||||||
        chr->chr_close(chr);
 | 
					        chr->chr_close(chr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    g_free(chr->filename);
 | 
					    g_free(chr->filename);
 | 
				
			||||||
    g_free(chr->label);
 | 
					    g_free(chr->label);
 | 
				
			||||||
 | 
					    if (chr->opts) {
 | 
				
			||||||
 | 
					        qemu_opts_del(chr->opts);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    g_free(chr);
 | 
					    g_free(chr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2996,3 +3059,199 @@ QemuOptsList qemu_chardev_opts = {
 | 
				
			|||||||
        { /* end of list */ }
 | 
					        { /* end of list */ }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    HANDLE out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (file->in) {
 | 
				
			||||||
 | 
					        error_setg(errp, "input file not supported");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out = CreateFile(file->out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
 | 
				
			||||||
 | 
					                     OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 | 
				
			||||||
 | 
					    if (out == INVALID_HANDLE_VALUE) {
 | 
				
			||||||
 | 
					        error_setg(errp, "open %s failed", file->out);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return qemu_chr_open_win_file(out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    switch (port->type) {
 | 
				
			||||||
 | 
					    case CHARDEV_PORT_KIND_SERIAL:
 | 
				
			||||||
 | 
					        return qemu_chr_open_win_path(port->device);
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        error_setg(errp, "unknown chardev port (%d)", port->type);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#else /* WIN32 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qmp_chardev_open_file_source(char *src, int flags,
 | 
				
			||||||
 | 
					                                        Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int fd = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TFR(fd = qemu_open(src, flags, 0666));
 | 
				
			||||||
 | 
					    if (fd == -1) {
 | 
				
			||||||
 | 
					        error_setg(errp, "open %s: %s", src, strerror(errno));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return fd;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int flags, in = -1, out = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
 | 
				
			||||||
 | 
					    out = qmp_chardev_open_file_source(file->out, flags, errp);
 | 
				
			||||||
 | 
					    if (error_is_set(errp)) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (file->in) {
 | 
				
			||||||
 | 
					        flags = O_RDONLY;
 | 
				
			||||||
 | 
					        in = qmp_chardev_open_file_source(file->in, flags, errp);
 | 
				
			||||||
 | 
					        if (error_is_set(errp)) {
 | 
				
			||||||
 | 
					            qemu_close(out);
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return qemu_chr_open_fd(in, out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int flags, fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (port->type) {
 | 
				
			||||||
 | 
					#ifdef HAVE_CHARDEV_TTY
 | 
				
			||||||
 | 
					    case CHARDEV_PORT_KIND_SERIAL:
 | 
				
			||||||
 | 
					        flags = O_RDWR;
 | 
				
			||||||
 | 
					        fd = qmp_chardev_open_file_source(port->device, flags, errp);
 | 
				
			||||||
 | 
					        if (error_is_set(errp)) {
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        socket_set_nonblock(fd);
 | 
				
			||||||
 | 
					        return qemu_chr_open_tty_fd(fd);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef HAVE_CHARDEV_PARPORT
 | 
				
			||||||
 | 
					    case CHARDEV_PORT_KIND_PARALLEL:
 | 
				
			||||||
 | 
					        flags = O_RDWR;
 | 
				
			||||||
 | 
					        fd = qmp_chardev_open_file_source(port->device, flags, errp);
 | 
				
			||||||
 | 
					        if (error_is_set(errp)) {
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return qemu_chr_open_pp_fd(fd);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        error_setg(errp, "unknown chardev port (%d)", port->type);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* WIN32 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
 | 
				
			||||||
 | 
					                                                Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    SocketAddress *addr = sock->addr;
 | 
				
			||||||
 | 
					    bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
 | 
				
			||||||
 | 
					    bool is_listen      = sock->has_server  ? sock->server  : true;
 | 
				
			||||||
 | 
					    bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
 | 
				
			||||||
 | 
					    bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
 | 
				
			||||||
 | 
					    int fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (is_listen) {
 | 
				
			||||||
 | 
					        fd = socket_listen(addr, errp);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        fd = socket_connect(addr, errp, NULL, NULL);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (error_is_set(errp)) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
 | 
				
			||||||
 | 
					                                   is_telnet, is_waitconnect, errp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
 | 
				
			||||||
 | 
					                               Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ChardevReturn *ret = g_new0(ChardevReturn, 1);
 | 
				
			||||||
 | 
					    CharDriverState *chr = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chr = qemu_chr_find(id);
 | 
				
			||||||
 | 
					    if (chr) {
 | 
				
			||||||
 | 
					        error_setg(errp, "Chardev '%s' already exists", id);
 | 
				
			||||||
 | 
					        g_free(ret);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (backend->kind) {
 | 
				
			||||||
 | 
					    case CHARDEV_BACKEND_KIND_FILE:
 | 
				
			||||||
 | 
					        chr = qmp_chardev_open_file(backend->file, errp);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case CHARDEV_BACKEND_KIND_PORT:
 | 
				
			||||||
 | 
					        chr = qmp_chardev_open_port(backend->port, errp);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case CHARDEV_BACKEND_KIND_SOCKET:
 | 
				
			||||||
 | 
					        chr = qmp_chardev_open_socket(backend->socket, errp);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					#ifdef HAVE_CHARDEV_TTY
 | 
				
			||||||
 | 
					    case CHARDEV_BACKEND_KIND_PTY:
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /* qemu_chr_open_pty sets "path" in opts */
 | 
				
			||||||
 | 
					        QemuOpts *opts;
 | 
				
			||||||
 | 
					        opts = qemu_opts_create_nofail(qemu_find_opts("chardev"));
 | 
				
			||||||
 | 
					        chr = qemu_chr_open_pty(opts);
 | 
				
			||||||
 | 
					        ret->pty = g_strdup(qemu_opt_get(opts, "path"));
 | 
				
			||||||
 | 
					        ret->has_pty = true;
 | 
				
			||||||
 | 
					        qemu_opts_del(opts);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    case CHARDEV_BACKEND_KIND_NULL:
 | 
				
			||||||
 | 
					        chr = qemu_chr_open_null(NULL);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        error_setg(errp, "unknown chardev backend (%d)", backend->kind);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (chr == NULL && !error_is_set(errp)) {
 | 
				
			||||||
 | 
					        error_setg(errp, "Failed to create chardev");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (chr) {
 | 
				
			||||||
 | 
					        chr->label = g_strdup(id);
 | 
				
			||||||
 | 
					        chr->avail_connections = 1;
 | 
				
			||||||
 | 
					        QTAILQ_INSERT_TAIL(&chardevs, chr, next);
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        g_free(ret);
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void qmp_chardev_remove(const char *id, Error **errp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CharDriverState *chr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chr = qemu_chr_find(id);
 | 
				
			||||||
 | 
					    if (NULL == chr) {
 | 
				
			||||||
 | 
					        error_setg(errp, "Chardev '%s' not found", id);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (chr->chr_can_read || chr->chr_read ||
 | 
				
			||||||
 | 
					        chr->chr_event || chr->handler_opaque) {
 | 
				
			||||||
 | 
					        error_setg(errp, "Chardev '%s' is busy", id);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    qemu_chr_delete(chr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1742,9 +1742,11 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
 | 
					#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
 | 
				
			||||||
        || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
 | 
					        || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
 | 
				
			||||||
 | 
					    "-chardev serial,id=id,path=path[,mux=on|off]\n"
 | 
				
			||||||
    "-chardev tty,id=id,path=path[,mux=on|off]\n"
 | 
					    "-chardev tty,id=id,path=path[,mux=on|off]\n"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
 | 
					#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
 | 
				
			||||||
 | 
					    "-chardev parallel,id=id,path=path[,mux=on|off]\n"
 | 
				
			||||||
    "-chardev parport,id=id,path=path[,mux=on|off]\n"
 | 
					    "-chardev parport,id=id,path=path[,mux=on|off]\n"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#if defined(CONFIG_SPICE)
 | 
					#if defined(CONFIG_SPICE)
 | 
				
			||||||
@ -1775,6 +1777,7 @@ Backend is one of:
 | 
				
			|||||||
@option{stdio},
 | 
					@option{stdio},
 | 
				
			||||||
@option{braille},
 | 
					@option{braille},
 | 
				
			||||||
@option{tty},
 | 
					@option{tty},
 | 
				
			||||||
 | 
					@option{parallel},
 | 
				
			||||||
@option{parport},
 | 
					@option{parport},
 | 
				
			||||||
@option{spicevmc}.
 | 
					@option{spicevmc}.
 | 
				
			||||||
@option{spiceport}.
 | 
					@option{spiceport}.
 | 
				
			||||||
@ -1910,8 +1913,8 @@ take any options.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Send traffic from the guest to a serial device on the host.
 | 
					Send traffic from the guest to a serial device on the host.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@option{serial} is
 | 
					On Unix hosts serial will actually accept any tty device,
 | 
				
			||||||
only available on Windows hosts.
 | 
					not only serial lines.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@option{path} specifies the name of the serial device to open.
 | 
					@option{path} specifies the name of the serial device to open.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1937,16 +1940,15 @@ Connect to a local BrlAPI server. @option{braille} does not take any options.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@item -chardev tty ,id=@var{id} ,path=@var{path}
 | 
					@item -chardev tty ,id=@var{id} ,path=@var{path}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Connect to a local tty device.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@option{tty} is only available on Linux, Sun, FreeBSD, NetBSD, OpenBSD and
 | 
					@option{tty} is only available on Linux, Sun, FreeBSD, NetBSD, OpenBSD and
 | 
				
			||||||
DragonFlyBSD hosts.
 | 
					DragonFlyBSD hosts.  It is an alias for -serial.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@option{path} specifies the path to the tty. @option{path} is required.
 | 
					@option{path} specifies the path to the tty. @option{path} is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@item -chardev parallel ,id=@var{id} ,path=@var{path}
 | 
				
			||||||
@item -chardev parport ,id=@var{id} ,path=@var{path}
 | 
					@item -chardev parport ,id=@var{id} ,path=@var{path}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@option{parport} is only available on Linux, FreeBSD and DragonFlyBSD hosts.
 | 
					@option{parallel} is only available on Linux, FreeBSD and DragonFlyBSD hosts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Connect to a local parallel port.
 | 
					Connect to a local parallel port.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2654,3 +2654,64 @@ EQMP
 | 
				
			|||||||
        .args_type  = "",
 | 
					        .args_type  = "",
 | 
				
			||||||
        .mhandler.cmd_new = qmp_marshal_input_query_target,
 | 
					        .mhandler.cmd_new = qmp_marshal_input_query_target,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        .name       = "chardev-add",
 | 
				
			||||||
 | 
					        .args_type  = "id:s,backend:q",
 | 
				
			||||||
 | 
					        .mhandler.cmd_new = qmp_marshal_input_chardev_add,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SQMP
 | 
				
			||||||
 | 
					chardev-add
 | 
				
			||||||
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add a chardev.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Arguments:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- "id": the chardev's ID, must be unique (json-string)
 | 
				
			||||||
 | 
					- "backend": chardev backend type + parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-> { "execute" : "chardev-add",
 | 
				
			||||||
 | 
					     "arguments" : { "id" : "foo",
 | 
				
			||||||
 | 
					                     "backend" : { "type" : "null", "data" : {} } } }
 | 
				
			||||||
 | 
					<- { "return": {} }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-> { "execute" : "chardev-add",
 | 
				
			||||||
 | 
					     "arguments" : { "id" : "bar",
 | 
				
			||||||
 | 
					                     "backend" : { "type" : "file",
 | 
				
			||||||
 | 
					                                   "data" : { "out" : "/tmp/bar.log" } } } }
 | 
				
			||||||
 | 
					<- { "return": {} }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-> { "execute" : "chardev-add",
 | 
				
			||||||
 | 
					     "arguments" : { "id" : "baz",
 | 
				
			||||||
 | 
					                     "backend" : { "type" : "pty", "data" : {} } } }
 | 
				
			||||||
 | 
					<- { "return": { "pty" : "/dev/pty/42" } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EQMP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        .name       = "chardev-remove",
 | 
				
			||||||
 | 
					        .args_type  = "id:s",
 | 
				
			||||||
 | 
					        .mhandler.cmd_new = qmp_marshal_input_chardev_remove,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SQMP
 | 
				
			||||||
 | 
					chardev-remove
 | 
				
			||||||
 | 
					--------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Remove a chardev.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Arguments:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- "id": the chardev's ID, must exist and not be in use (json-string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-> { "execute": "chardev-remove", "arguments": { "id" : "foo" } }
 | 
				
			||||||
 | 
					<- { "return": {} }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EQMP
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								vl.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								vl.c
									
									
									
									
									
								
							@ -2238,11 +2238,14 @@ static int device_init_func(QemuOpts *opts, void *opaque)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int chardev_init_func(QemuOpts *opts, void *opaque)
 | 
					static int chardev_init_func(QemuOpts *opts, void *opaque)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    CharDriverState *chr;
 | 
					    Error *local_err = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    chr = qemu_chr_new_from_opts(opts, NULL);
 | 
					    qemu_chr_new_from_opts(opts, NULL, &local_err);
 | 
				
			||||||
    if (!chr)
 | 
					    if (error_is_set(&local_err)) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "%s\n", error_get_pretty(local_err));
 | 
				
			||||||
 | 
					        error_free(local_err);
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user