#include "qemu-common.h"
#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "sysemu/char.h"
#include "hw/usb.h"
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
-#include <dirent.h>
#include <netdb.h>
#include <sys/select.h>
#ifdef CONFIG_BSD
#define READ_BUF_LEN 4096
#define READ_RETRIES 10
#define CHR_MAX_FILENAME_SIZE 256
+#define TCP_MAX_FDS 16
/***********************************************************/
/* Socket address helpers */
-static void qapi_copy_SocketAddress(SocketAddress **p_dest,
- SocketAddress *src)
-{
- QmpOutputVisitor *qov;
- QmpInputVisitor *qiv;
- Visitor *ov, *iv;
- QObject *obj;
-
- *p_dest = NULL;
-
- qov = qmp_output_visitor_new();
- ov = qmp_output_get_visitor(qov);
- visit_type_SocketAddress(ov, &src, NULL, &error_abort);
- obj = qmp_output_get_qobject(qov);
- qmp_output_visitor_cleanup(qov);
- if (!obj) {
- return;
- }
-
- qiv = qmp_input_visitor_new(obj);
- iv = qmp_input_get_visitor(qiv);
- visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
- qmp_input_visitor_cleanup(qiv);
- qobject_decref(obj);
-}
static int SocketAddress_to_str(char *dest, int max_len,
const char *prefix, SocketAddress *addr,
return len;
}
-static CharDriverState *qemu_chr_open_null(void)
+static CharDriverState *qemu_chr_open_null(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
CharDriverState *chr;
"% h print this help\n\r",
"% x exit emulator\n\r",
"% s save disk data back to file (if -snapshot)\n\r",
- "% t toggle console timestamps\n\r"
+ "% t toggle console timestamps\n\r",
"% b send break (magic sysrq)\n\r",
"% c switch between console and monitor\n\r",
"% % sends %\n\r",
return d->drv->chr_add_watch(d->drv, cond);
}
-static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
+static CharDriverState *qemu_chr_open_mux(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret, Error **errp)
{
- CharDriverState *chr;
+ ChardevMux *mux = backend->mux;
+ CharDriverState *chr, *drv;
MuxDriver *d;
+ drv = qemu_chr_find(mux->chardev);
+ if (drv == NULL) {
+ error_setg(errp, "mux: base chardev %s not found", mux->chardev);
+ return NULL;
+ }
+
chr = qemu_chr_alloc();
- d = g_malloc0(sizeof(MuxDriver));
+ d = g_new0(MuxDriver, 1);
chr->opaque = d;
d->drv = drv;
}
if (now_active) {
- iwp->src = g_io_create_watch(iwp->channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
+ iwp->src = g_io_create_watch(iwp->channel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
g_source_attach(iwp->src, NULL);
} else {
CharDriverState *chr;
GIOChannel *fd_in, *fd_out;
int max_size;
- QTAILQ_ENTRY(FDCharDriver) node;
} FDCharDriver;
/* Called with chr_write_lock held. */
FDCharDriver *s;
chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(FDCharDriver));
+ s = g_new0(FDCharDriver, 1);
s->fd_in = io_channel_from_fd(fd_in);
s->fd_out = io_channel_from_fd(fd_out);
qemu_set_nonblock(fd_out);
return chr;
}
-static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
+static CharDriverState *qemu_chr_open_pipe(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
+ ChardevHostdev *opts = backend->pipe;
int fd_in, fd_out;
char filename_in[CHR_MAX_FILENAME_SIZE];
char filename_out[CHR_MAX_FILENAME_SIZE];
const char *filename = opts->device;
- if (filename == NULL) {
- fprintf(stderr, "chardev: pipe: no filename given\n");
- return NULL;
- }
-
snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename);
snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename);
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
close(fd_out);
TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
if (fd_in < 0) {
+ error_setg_file_open(errp, errno, filename);
return NULL;
}
}
static int old_fd0_flags;
static bool stdio_in_use;
static bool stdio_allow_signal;
+static bool stdio_echo_state;
+
+static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo);
static void term_exit(void)
{
fcntl(0, F_SETFL, old_fd0_flags);
}
+static void term_stdio_handler(int sig)
+{
+ /* restore echo after resume from suspend. */
+ qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
+}
+
static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
{
struct termios tty;
+ stdio_echo_state = echo;
tty = oldtty;
if (!echo) {
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
fd_chr_close(chr);
}
-static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
+static CharDriverState *qemu_chr_open_stdio(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
+ ChardevStdio *opts = backend->stdio;
CharDriverState *chr;
+ struct sigaction act;
if (is_daemonized()) {
- error_report("cannot use stdio with -daemonize");
+ error_setg(errp, "cannot use stdio with -daemonize");
return NULL;
}
if (stdio_in_use) {
- error_report("cannot use stdio by multiple character devices");
- exit(1);
+ error_setg(errp, "cannot use stdio by multiple character devices");
+ return NULL;
}
stdio_in_use = true;
qemu_set_nonblock(0);
atexit(term_exit);
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = term_stdio_handler;
+ sigaction(SIGCONT, &act, NULL);
+
chr = qemu_chr_open_fd(0, 1);
chr->chr_close = qemu_chr_close_stdio;
chr->chr_set_echo = qemu_chr_set_echo_stdio;
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|| defined(__GLIBC__)
-#define HAVE_CHARDEV_TTY 1
+#define HAVE_CHARDEV_SERIAL 1
+#define HAVE_CHARDEV_PTY 1
typedef struct {
GIOChannel *fd;
}
static CharDriverState *qemu_chr_open_pty(const char *id,
- ChardevReturn *ret)
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
CharDriverState *chr;
PtyCharDriver *s;
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
if (master_fd < 0) {
+ error_setg_errno(errp, errno, "Failed to create PTY");
return NULL;
}
close(slave_fd);
+ qemu_set_nonblock(master_fd);
chr = qemu_chr_alloc();
fprintf(stderr, "char device redirected to %s (label %s)\n",
pty_name, id);
- s = g_malloc0(sizeof(PtyCharDriver));
+ s = g_new0(PtyCharDriver, 1);
chr->opaque = s;
chr->chr_write = pty_chr_write;
chr->chr_update_read_handler = pty_chr_update_read_handler;
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_pp_fd(int fd)
+static CharDriverState *qemu_chr_open_pp_fd(int fd, Error **errp)
{
CharDriverState *chr;
ParallelCharDriver *drv;
if (ioctl(fd, PPCLAIM) < 0) {
+ error_setg_errno(errp, errno, "not a parallel port");
close(fd);
return NULL;
}
- drv = g_malloc0(sizeof(ParallelCharDriver));
+ drv = g_new0(ParallelCharDriver, 1);
drv->fd = fd;
drv->mode = IEEE1284_MODE_COMPAT;
return 0;
}
-static CharDriverState *qemu_chr_open_pp_fd(int fd)
+static CharDriverState *qemu_chr_open_pp_fd(int fd, Error **errp)
{
CharDriverState *chr;
#else /* _WIN32 */
+#define HAVE_CHARDEV_SERIAL 1
+
typedef struct {
int max_size;
HANDLE hcom, hrecv, hsend;
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
-static int win_chr_init(CharDriverState *chr, const char *filename)
+static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
{
WinCharState *s = chr->opaque;
COMMCONFIG comcfg;
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hsend) {
- fprintf(stderr, "Failed CreateEvent\n");
+ error_setg(errp, "Failed CreateEvent");
goto fail;
}
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hrecv) {
- fprintf(stderr, "Failed CreateEvent\n");
+ error_setg(errp, "Failed CreateEvent");
goto fail;
}
s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (s->hcom == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
+ error_setg(errp, "Failed CreateFile (%lu)", GetLastError());
s->hcom = NULL;
goto fail;
}
if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
- fprintf(stderr, "Failed SetupComm\n");
+ error_setg(errp, "Failed SetupComm");
goto fail;
}
CommConfigDialog(filename, NULL, &comcfg);
if (!SetCommState(s->hcom, &comcfg.dcb)) {
- fprintf(stderr, "Failed SetCommState\n");
+ error_setg(errp, "Failed SetCommState");
goto fail;
}
if (!SetCommMask(s->hcom, EV_ERR)) {
- fprintf(stderr, "Failed SetCommMask\n");
+ error_setg(errp, "Failed SetCommMask");
goto fail;
}
cto.ReadIntervalTimeout = MAXDWORD;
if (!SetCommTimeouts(s->hcom, &cto)) {
- fprintf(stderr, "Failed SetCommTimeouts\n");
+ error_setg(errp, "Failed SetCommTimeouts");
goto fail;
}
if (!ClearCommError(s->hcom, &err, &comstat)) {
- fprintf(stderr, "Failed ClearCommError\n");
+ error_setg(errp, "Failed ClearCommError");
goto fail;
}
qemu_add_polling_cb(win_chr_poll, chr);
return 0;
}
-static CharDriverState *qemu_chr_open_win_path(const char *filename)
+static CharDriverState *qemu_chr_open_win_path(const char *filename,
+ Error **errp)
{
CharDriverState *chr;
WinCharState *s;
chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(WinCharState));
+ s = g_new0(WinCharState, 1);
chr->opaque = s;
chr->chr_write = win_chr_write;
chr->chr_close = win_chr_close;
- if (win_chr_init(chr, filename) < 0) {
+ if (win_chr_init(chr, filename, errp) < 0) {
g_free(s);
g_free(chr);
return NULL;
return 0;
}
-static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
+static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
+ Error **errp)
{
WinCharState *s = chr->opaque;
OVERLAPPED ov;
s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hsend) {
- fprintf(stderr, "Failed CreateEvent\n");
+ error_setg(errp, "Failed CreateEvent");
goto fail;
}
s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!s->hrecv) {
- fprintf(stderr, "Failed CreateEvent\n");
+ error_setg(errp, "Failed CreateEvent");
goto fail;
}
PIPE_WAIT,
MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
if (s->hcom == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
+ error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
s->hcom = NULL;
goto fail;
}
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ret = ConnectNamedPipe(s->hcom, &ov);
if (ret) {
- fprintf(stderr, "Failed ConnectNamedPipe\n");
+ error_setg(errp, "Failed ConnectNamedPipe");
goto fail;
}
ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
if (!ret) {
- fprintf(stderr, "Failed GetOverlappedResult\n");
+ error_setg(errp, "Failed GetOverlappedResult");
if (ov.hEvent) {
CloseHandle(ov.hEvent);
ov.hEvent = NULL;
}
-static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
+static CharDriverState *qemu_chr_open_pipe(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
+ ChardevHostdev *opts = backend->pipe;
const char *filename = opts->device;
CharDriverState *chr;
WinCharState *s;
chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(WinCharState));
+ s = g_new0(WinCharState, 1);
chr->opaque = s;
chr->chr_write = win_chr_write;
chr->chr_close = win_chr_close;
- if (win_chr_pipe_init(chr, filename) < 0) {
+ if (win_chr_pipe_init(chr, filename, errp) < 0) {
g_free(s);
g_free(chr);
return NULL;
WinCharState *s;
chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(WinCharState));
+ s = g_new0(WinCharState, 1);
s->hcom = fd_out;
chr->opaque = s;
chr->chr_write = win_chr_write;
return chr;
}
-static CharDriverState *qemu_chr_open_win_con(void)
+static CharDriverState *qemu_chr_open_win_con(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
}
g_free(chr);
}
-static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
+static CharDriverState *qemu_chr_open_stdio(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
CharDriverState *chr;
WinStdioCharState *stdio;
int is_console = 0;
chr = qemu_chr_alloc();
- stdio = g_malloc0(sizeof(WinStdioCharState));
+ stdio = g_new0(WinStdioCharState, 1);
stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "cannot open stdio: invalid handle\n");
- exit(1);
+ error_setg(errp, "cannot open stdio: invalid handle");
+ return NULL;
}
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
if (is_console) {
if (qemu_add_wait_object(stdio->hStdIn,
win_stdio_wait_func, chr)) {
- fprintf(stderr, "qemu_add_wait_object: failed\n");
+ error_setg(errp, "qemu_add_wait_object: failed");
+ goto err1;
}
} else {
DWORD dwId;
stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
- chr, 0, &dwId);
-
- if (stdio->hInputThread == INVALID_HANDLE_VALUE
- || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
+ if (stdio->hInputReadyEvent == INVALID_HANDLE_VALUE
|| stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "cannot create stdio thread or event\n");
- exit(1);
+ error_setg(errp, "cannot create event");
+ goto err2;
}
if (qemu_add_wait_object(stdio->hInputReadyEvent,
win_stdio_thread_wait_func, chr)) {
- fprintf(stderr, "qemu_add_wait_object: failed\n");
+ error_setg(errp, "qemu_add_wait_object: failed");
+ goto err2;
+ }
+ stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread,
+ chr, 0, &dwId);
+
+ if (stdio->hInputThread == INVALID_HANDLE_VALUE) {
+ error_setg(errp, "cannot create stdio thread");
+ goto err3;
}
}
qemu_chr_fe_set_echo(chr, false);
return chr;
+
+err3:
+ qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
+err2:
+ CloseHandle(stdio->hInputReadyEvent);
+ CloseHandle(stdio->hInputDoneEvent);
+err1:
+ qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
+ return NULL;
}
#endif /* !_WIN32 */
NetCharDriver *s = NULL;
chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(NetCharDriver));
+ s = g_new0(NetCharDriver, 1);
s->fd = fd;
s->chan = io_channel_from_socket(s->fd);
guint reconnect_timer;
int64_t reconnect_time;
+ bool connect_err_reported;
} TCPCharDriver;
static gboolean socket_reconnect_timeout(gpointer opaque);
socket_reconnect_timeout, chr);
}
+static void check_report_connect_error(CharDriverState *chr,
+ Error *err)
+{
+ TCPCharDriver *s = chr->opaque;
+
+ if (!s->connect_err_reported) {
+ error_report("Unable to connect character device %s: %s",
+ chr->label, error_get_pretty(err));
+ s->connect_err_reported = true;
+ }
+ qemu_chr_socket_restart_timer(chr);
+}
+
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
#ifndef _WIN32
TCPCharDriver *s = chr->opaque;
int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
+ assert(num <= TCP_MAX_FDS);
+
if (to_copy) {
int i;
TCPCharDriver *s = chr->opaque;
/* clear old pending fd array */
- if (s->write_msgfds) {
- g_free(s->write_msgfds);
- }
+ g_free(s->write_msgfds);
if (num) {
- s->write_msgfds = g_malloc(num * sizeof(int));
+ s->write_msgfds = g_new(int, num);
memcpy(s->write_msgfds, fds, num * sizeof(int));
}
struct iovec iov[1];
union {
struct cmsghdr cmsg;
- char control[CMSG_SPACE(sizeof(int))];
+ char control[CMSG_SPACE(sizeof(int) * TCP_MAX_FDS)];
} msg_control;
int flags = 0;
ssize_t ret;
#ifdef MSG_CMSG_CLOEXEC
flags |= MSG_CMSG_CLOEXEC;
#endif
- ret = recvmsg(s->fd, &msg, flags);
+ do {
+ ret = recvmsg(s->fd, &msg, flags);
+ } while (ret == -1 && errno == EINTR);
+
if (ret > 0 && s->is_unix) {
unix_process_msgfd(chr, &msg);
}
static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
{
TCPCharDriver *s = chr->opaque;
- return qemu_recv(s->fd, buf, len, 0);
+ ssize_t ret;
+
+ do {
+ ret = qemu_recv(s->fd, buf, len, 0);
+ } while (ret == -1 && socket_error() == EINTR);
+
+ return ret;
}
#endif
uint8_t buf[READ_BUF_LEN];
int len, size;
- if (cond & G_IO_HUP) {
- /* connection closed */
- tcp_chr_disconnect(chr);
- return TRUE;
- }
-
if (!s->connected || s->max_size <= 0) {
return TRUE;
}
if (len > s->max_size)
len = s->max_size;
size = tcp_chr_recv(chr, (void *)buf, len);
- if (size == 0) {
+ if (size == 0 ||
+ (size < 0 &&
+ socket_error() != EAGAIN && socket_error() != EWOULDBLOCK)) {
/* connection closed */
tcp_chr_disconnect(chr);
} else if (size > 0) {
}
}
-static void qemu_chr_socket_connected(int fd, void *opaque)
+static void qemu_chr_socket_connected(int fd, Error *err, void *opaque)
{
CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
if (fd < 0) {
- qemu_chr_socket_restart_timer(chr);
+ check_report_connect_error(chr, err);
return;
}
+ s->connect_err_reported = false;
qemu_chr_finish_socket_connection(chr, fd);
}
chr->opaque = NULL;
}
-static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts,
+static CharDriverState *qemu_chr_open_ringbuf(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
Error **errp)
{
+ ChardevRingbuf *opts = backend->ringbuf;
CharDriverState *chr;
RingBufCharDriver *d;
opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_report_err(local_err);
return NULL;
}
if (strstart(filename, "mon:", &p)) {
filename = p;
- qemu_opt_set(opts, "mux", "on");
+ qemu_opt_set(opts, "mux", "on", &error_abort);
if (strcmp(filename, "stdio") == 0) {
/* Monitor is muxed to stdio: do not exit on Ctrl+C by default
* but pass it to the guest. Handle this only for compat syntax,
* for -chardev syntax we have special option for this.
* This is what -nographic did, redirecting+muxing serial+monitor
* to stdio causing Ctrl+C to be passed to guest. */
- qemu_opt_set(opts, "signal", "off");
+ qemu_opt_set(opts, "signal", "off", &error_abort);
}
}
strcmp(filename, "braille") == 0 ||
strcmp(filename, "testdev") == 0 ||
strcmp(filename, "stdio") == 0) {
- qemu_opt_set(opts, "backend", filename);
+ qemu_opt_set(opts, "backend", filename, &error_abort);
return opts;
}
if (strstart(filename, "vc", &p)) {
- qemu_opt_set(opts, "backend", "vc");
+ qemu_opt_set(opts, "backend", "vc", &error_abort);
if (*p == ':') {
if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) {
/* pixels */
- qemu_opt_set(opts, "width", width);
- qemu_opt_set(opts, "height", height);
+ qemu_opt_set(opts, "width", width, &error_abort);
+ qemu_opt_set(opts, "height", height, &error_abort);
} else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) {
/* chars */
- qemu_opt_set(opts, "cols", width);
- qemu_opt_set(opts, "rows", height);
+ qemu_opt_set(opts, "cols", width, &error_abort);
+ qemu_opt_set(opts, "rows", height, &error_abort);
} else {
goto fail;
}
return opts;
}
if (strcmp(filename, "con:") == 0) {
- qemu_opt_set(opts, "backend", "console");
+ qemu_opt_set(opts, "backend", "console", &error_abort);
return opts;
}
if (strstart(filename, "COM", NULL)) {
- qemu_opt_set(opts, "backend", "serial");
- qemu_opt_set(opts, "path", filename);
+ qemu_opt_set(opts, "backend", "serial", &error_abort);
+ qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
if (strstart(filename, "file:", &p)) {
- qemu_opt_set(opts, "backend", "file");
- qemu_opt_set(opts, "path", p);
+ qemu_opt_set(opts, "backend", "file", &error_abort);
+ qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
if (strstart(filename, "pipe:", &p)) {
- qemu_opt_set(opts, "backend", "pipe");
- qemu_opt_set(opts, "path", p);
+ qemu_opt_set(opts, "backend", "pipe", &error_abort);
+ qemu_opt_set(opts, "path", p, &error_abort);
return opts;
}
if (strstart(filename, "tcp:", &p) ||
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
goto fail;
}
- qemu_opt_set(opts, "backend", "socket");
- qemu_opt_set(opts, "host", host);
- qemu_opt_set(opts, "port", port);
+ qemu_opt_set(opts, "backend", "socket", &error_abort);
+ qemu_opt_set(opts, "host", host, &error_abort);
+ qemu_opt_set(opts, "port", port, &error_abort);
if (p[pos] == ',') {
- if (qemu_opts_do_parse(opts, p+pos+1, NULL) != 0)
+ qemu_opts_do_parse(opts, p+pos+1, NULL, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
goto fail;
+ }
}
if (strstart(filename, "telnet:", &p))
- qemu_opt_set(opts, "telnet", "on");
+ qemu_opt_set(opts, "telnet", "on", &error_abort);
return opts;
}
if (strstart(filename, "udp:", &p)) {
- qemu_opt_set(opts, "backend", "udp");
+ qemu_opt_set(opts, "backend", "udp", &error_abort);
if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
goto fail;
}
}
- qemu_opt_set(opts, "host", host);
- qemu_opt_set(opts, "port", port);
+ qemu_opt_set(opts, "host", host, &error_abort);
+ qemu_opt_set(opts, "port", port, &error_abort);
if (p[pos] == '@') {
p += pos + 1;
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
goto fail;
}
}
- qemu_opt_set(opts, "localaddr", host);
- qemu_opt_set(opts, "localport", port);
+ qemu_opt_set(opts, "localaddr", host, &error_abort);
+ qemu_opt_set(opts, "localport", port, &error_abort);
}
return opts;
}
if (strstart(filename, "unix:", &p)) {
- qemu_opt_set(opts, "backend", "socket");
- if (qemu_opts_do_parse(opts, p, "path") != 0)
+ qemu_opt_set(opts, "backend", "socket", &error_abort);
+ qemu_opts_do_parse(opts, p, "path", &local_err);
+ if (local_err) {
+ error_report_err(local_err);
goto fail;
+ }
return opts;
}
if (strstart(filename, "/dev/parport", NULL) ||
strstart(filename, "/dev/ppi", NULL)) {
- qemu_opt_set(opts, "backend", "parport");
- qemu_opt_set(opts, "path", filename);
+ qemu_opt_set(opts, "backend", "parport", &error_abort);
+ qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
if (strstart(filename, "/dev/", NULL)) {
- qemu_opt_set(opts, "backend", "tty");
- qemu_opt_set(opts, "path", filename);
+ qemu_opt_set(opts, "backend", "tty", &error_abort);
+ qemu_opt_set(opts, "path", filename, &error_abort);
return opts;
}
backend->stdio->signal = qemu_opt_get_bool(opts, "signal", true);
}
+#ifdef HAVE_CHARDEV_SERIAL
static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
backend->serial = g_new0(ChardevHostdev, 1);
backend->serial->device = g_strdup(device);
}
+#endif
+#ifdef HAVE_CHARDEV_PARPORT
static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
backend->parallel = g_new0(ChardevHostdev, 1);
backend->parallel->device = g_strdup(device);
}
+#endif
static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
const char *name;
ChardevBackendKind kind;
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
+ CharDriverState *(*create)(const char *id, ChardevBackend *backend,
+ ChardevReturn *ret, Error **errp);
} CharDriver;
static GSList *backends;
void register_char_driver(const char *name, ChardevBackendKind kind,
- void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp))
+ void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp),
+ CharDriverState *(*create)(const char *id, ChardevBackend *backend,
+ ChardevReturn *ret, Error **errp))
{
CharDriver *s;
s->name = g_strdup(name);
s->kind = kind;
s->parse = parse;
+ s->create = create;
backends = g_slist_append(backends, s);
}
chr = qemu_chr_new_from_opts(opts, init, &err);
if (err) {
- error_report("%s", error_get_pretty(err));
- error_free(err);
+ error_report_err(err);
}
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
qemu_chr_fe_claim_no_fail(chr);
s->avail_connections++;
}
-void qemu_chr_delete(CharDriverState *chr)
+void qemu_chr_free(CharDriverState *chr)
{
- QTAILQ_REMOVE(&chardevs, chr, next);
if (chr->chr_close) {
chr->chr_close(chr);
}
g_free(chr->filename);
g_free(chr->label);
- if (chr->opts) {
- qemu_opts_del(chr->opts);
- }
+ qemu_opts_del(chr->opts);
g_free(chr);
}
+void qemu_chr_delete(CharDriverState *chr)
+{
+ QTAILQ_REMOVE(&chardevs, chr, next);
+ qemu_chr_free(chr);
+}
+
ChardevInfoList *qmp_query_chardev(Error **errp)
{
ChardevInfoList *chr_list = NULL;
#ifdef _WIN32
-static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
+static CharDriverState *qmp_chardev_open_file(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
+ ChardevFile *file = backend->file;
HANDLE out;
if (file->has_in) {
return qemu_chr_open_win_file(out);
}
-static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
+static CharDriverState *qmp_chardev_open_serial(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
Error **errp)
{
- return qemu_chr_open_win_path(serial->device);
-}
-
-static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
- Error **errp)
-{
- error_setg(errp, "character device backend type 'parallel' not supported");
- return NULL;
+ ChardevHostdev *serial = backend->serial;
+ return qemu_chr_open_win_path(serial->device, errp);
}
#else /* WIN32 */
return fd;
}
-static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp)
+static CharDriverState *qmp_chardev_open_file(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
+ Error **errp)
{
+ ChardevFile *file = backend->file;
int flags, in = -1, out;
flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY;
return qemu_chr_open_fd(in, out);
}
-static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial,
+#ifdef HAVE_CHARDEV_SERIAL
+static CharDriverState *qmp_chardev_open_serial(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
Error **errp)
{
-#ifdef HAVE_CHARDEV_TTY
+ ChardevHostdev *serial = backend->serial;
int fd;
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
}
qemu_set_nonblock(fd);
return qemu_chr_open_tty_fd(fd);
-#else
- error_setg(errp, "character device backend type 'serial' not supported");
- return NULL;
-#endif
}
+#endif
-static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
+#ifdef HAVE_CHARDEV_PARPORT
+static CharDriverState *qmp_chardev_open_parallel(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
Error **errp)
{
-#ifdef HAVE_CHARDEV_PARPORT
+ ChardevHostdev *parallel = backend->parallel;
int fd;
fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
if (fd < 0) {
return NULL;
}
- return qemu_chr_open_pp_fd(fd);
-#else
- error_setg(errp, "character device backend type 'parallel' not supported");
- return NULL;
-#endif
+ return qemu_chr_open_pp_fd(fd, errp);
}
+#endif
#endif /* WIN32 */
+static void socket_try_connect(CharDriverState *chr)
+{
+ Error *err = NULL;
+
+ if (!qemu_chr_open_socket_fd(chr, &err)) {
+ check_report_connect_error(chr, err);
+ }
+}
+
static gboolean socket_reconnect_timeout(gpointer opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
- Error *err;
s->reconnect_timer = 0;
return false;
}
- if (!qemu_chr_open_socket_fd(chr, &err)) {
- error_report("Unable to connect to char device %s\n", chr->label);
- qemu_chr_socket_restart_timer(chr);
- }
+ socket_try_connect(chr);
return false;
}
-static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
+static CharDriverState *qmp_chardev_open_socket(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
Error **errp)
{
CharDriverState *chr;
TCPCharDriver *s;
+ ChardevSocket *sock = backend->socket;
SocketAddress *addr = sock->addr;
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_listen = sock->has_server ? sock->server : true;
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
chr = qemu_chr_alloc();
- s = g_malloc0(sizeof(TCPCharDriver));
+ s = g_new0(TCPCharDriver, 1);
s->fd = -1;
s->listen_fd = -1;
s->reconnect_time = reconnect;
}
- if (!qemu_chr_open_socket_fd(chr, errp)) {
- if (s->reconnect_time) {
- qemu_chr_socket_restart_timer(chr);
- } else {
- g_free(s);
- g_free(chr->filename);
- g_free(chr);
- return NULL;
- }
+ if (s->reconnect_time) {
+ socket_try_connect(chr);
+ } else if (!qemu_chr_open_socket_fd(chr, errp)) {
+ g_free(s);
+ g_free(chr->filename);
+ g_free(chr);
+ return NULL;
}
if (is_listen && is_waitconnect) {
return chr;
}
-static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
+static CharDriverState *qmp_chardev_open_udp(const char *id,
+ ChardevBackend *backend,
+ ChardevReturn *ret,
Error **errp)
{
+ ChardevUdp *udp = backend->udp;
int fd;
fd = socket_dgram(udp->remote, udp->local, errp);
Error **errp)
{
ChardevReturn *ret = g_new0(ChardevReturn, 1);
- CharDriverState *base, *chr = NULL;
+ CharDriverState *chr = NULL;
+ Error *local_err = NULL;
+ GSList *i;
+ CharDriver *cd;
chr = qemu_chr_find(id);
if (chr) {
return NULL;
}
- switch (backend->kind) {
- case CHARDEV_BACKEND_KIND_FILE:
- chr = qmp_chardev_open_file(backend->file, errp);
- break;
- case CHARDEV_BACKEND_KIND_SERIAL:
- chr = qmp_chardev_open_serial(backend->serial, errp);
- break;
- case CHARDEV_BACKEND_KIND_PARALLEL:
- chr = qmp_chardev_open_parallel(backend->parallel, errp);
- break;
- case CHARDEV_BACKEND_KIND_PIPE:
- chr = qemu_chr_open_pipe(backend->pipe);
- break;
- case CHARDEV_BACKEND_KIND_SOCKET:
- chr = qmp_chardev_open_socket(backend->socket, errp);
- break;
- case CHARDEV_BACKEND_KIND_UDP:
- chr = qmp_chardev_open_udp(backend->udp, errp);
- break;
-#ifdef HAVE_CHARDEV_TTY
- case CHARDEV_BACKEND_KIND_PTY:
- chr = qemu_chr_open_pty(id, ret);
- break;
-#endif
- case CHARDEV_BACKEND_KIND_NULL:
- chr = qemu_chr_open_null();
- break;
- case CHARDEV_BACKEND_KIND_MUX:
- base = qemu_chr_find(backend->mux->chardev);
- if (base == NULL) {
- error_setg(errp, "mux: base chardev %s not found",
- backend->mux->chardev);
+ for (i = backends; i; i = i->next) {
+ cd = i->data;
+
+ if (cd->kind == backend->kind) {
+ chr = cd->create(id, backend, ret, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out_error;
+ }
break;
}
- chr = qemu_chr_open_mux(base);
- break;
- case CHARDEV_BACKEND_KIND_MSMOUSE:
- chr = qemu_chr_open_msmouse();
- break;
-#ifdef CONFIG_BRLAPI
- case CHARDEV_BACKEND_KIND_BRAILLE:
- chr = chr_baum_init();
- break;
-#endif
- case CHARDEV_BACKEND_KIND_TESTDEV:
- chr = chr_testdev_init();
- break;
- case CHARDEV_BACKEND_KIND_STDIO:
- chr = qemu_chr_open_stdio(backend->stdio);
- break;
-#ifdef _WIN32
- case CHARDEV_BACKEND_KIND_CONSOLE:
- chr = qemu_chr_open_win_con();
- break;
-#endif
-#ifdef CONFIG_SPICE
- case CHARDEV_BACKEND_KIND_SPICEVMC:
- chr = qemu_chr_open_spice_vmc(backend->spicevmc->type);
- break;
- case CHARDEV_BACKEND_KIND_SPICEPORT:
- chr = qemu_chr_open_spice_port(backend->spiceport->fqdn);
- break;
-#endif
- case CHARDEV_BACKEND_KIND_VC:
- chr = vc_init(backend->vc);
- break;
- case CHARDEV_BACKEND_KIND_RINGBUF:
- case CHARDEV_BACKEND_KIND_MEMORY:
- chr = qemu_chr_open_ringbuf(backend->ringbuf, errp);
- break;
- default:
- error_setg(errp, "unknown chardev backend (%d)", backend->kind);
- break;
}
- /*
- * Character backend open hasn't been fully converted to the Error
- * API. Some opens fail without setting an error. Set a generic
- * error then.
- * TODO full conversion to Error API
- */
- if (chr == NULL && errp && !*errp) {
- error_setg(errp, "Failed to create chardev");
+ if (chr == NULL) {
+ assert(!i);
+ error_setg(errp, "chardev backend not available");
+ goto out_error;
}
- if (chr) {
- chr->label = g_strdup(id);
- chr->avail_connections =
- (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
- if (!chr->filename) {
- chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]);
- }
- if (!chr->explicit_be_open) {
- qemu_chr_be_event(chr, CHR_EVENT_OPENED);
- }
- QTAILQ_INSERT_TAIL(&chardevs, chr, next);
- return ret;
- } else {
- g_free(ret);
- return NULL;
+
+ chr->label = g_strdup(id);
+ chr->avail_connections =
+ (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
+ if (!chr->filename) {
+ chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]);
+ }
+ if (!chr->explicit_be_open) {
+ qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
+ QTAILQ_INSERT_TAIL(&chardevs, chr, next);
+ return ret;
+
+out_error:
+ g_free(ret);
+ return NULL;
}
void qmp_chardev_remove(const char *id, Error **errp)
static void register_types(void)
{
- register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL);
+ register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
+ qemu_chr_open_null);
register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
- qemu_chr_parse_socket);
- register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp);
+ qemu_chr_parse_socket, qmp_chardev_open_socket);
+ register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
+ qmp_chardev_open_udp);
register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
- qemu_chr_parse_ringbuf);
+ qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
- qemu_chr_parse_file_out);
+ qemu_chr_parse_file_out, qmp_chardev_open_file);
register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
- qemu_chr_parse_stdio);
+ qemu_chr_parse_stdio, qemu_chr_open_stdio);
+#if defined HAVE_CHARDEV_SERIAL
register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
- qemu_chr_parse_serial);
+ qemu_chr_parse_serial, qmp_chardev_open_serial);
register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
- qemu_chr_parse_serial);
+ qemu_chr_parse_serial, qmp_chardev_open_serial);
+#endif
+#ifdef HAVE_CHARDEV_PARPORT
register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
- qemu_chr_parse_parallel);
+ qemu_chr_parse_parallel, qmp_chardev_open_parallel);
register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
- qemu_chr_parse_parallel);
- register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL);
- register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL);
+ qemu_chr_parse_parallel, qmp_chardev_open_parallel);
+#endif
+#ifdef HAVE_CHARDEV_PTY
+ register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
+ qemu_chr_open_pty);
+#endif
+#ifdef _WIN32
+ register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
+ qemu_chr_open_win_con);
+#endif
register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
- qemu_chr_parse_pipe);
- register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux);
+ qemu_chr_parse_pipe, qemu_chr_open_pipe);
+ register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
+ qemu_chr_open_mux);
/* Bug-compatibility: */
register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
- qemu_chr_parse_ringbuf);
+ qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
/* this must be done after machine init, since we register FEs with muxes
* as part of realize functions like serial_isa_realizefn when -nographic
* is specified