* THE SOFTWARE.
*/
#include "qemu-common.h"
-#include "net.h"
-#include "monitor.h"
-#include "console.h"
-#include "sysemu.h"
-#include "qemu-timer.h"
-#include "qemu-char.h"
+#include "monitor/monitor.h"
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "char/char.h"
#include "hw/usb.h"
#include "hw/baum.h"
#include "hw/msmouse.h"
#endif
#endif
-#include "qemu_socket.h"
+#include "qemu/sockets.h"
#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096
s->chr_event(s->handler_opaque, event);
}
-static void qemu_chr_generic_open_bh(void *opaque)
+static void qemu_chr_fire_open_event(void *opaque)
{
CharDriverState *s = opaque;
qemu_chr_be_event(s, CHR_EVENT_OPENED);
- qemu_bh_delete(s->bh);
- s->bh = NULL;
+ qemu_free_timer(s->open_timer);
+ s->open_timer = NULL;
}
void qemu_chr_generic_open(CharDriverState *s)
{
- if (s->bh == NULL) {
- s->bh = qemu_bh_new(qemu_chr_generic_open_bh, s);
- qemu_bh_schedule(s->bh);
+ if (s->open_timer == NULL) {
+ s->open_timer = qemu_new_timer_ms(rt_clock,
+ qemu_chr_fire_open_event, s);
+ qemu_mod_timer(s->open_timer, qemu_get_clock_ms(rt_clock) - 1);
}
}
if (stdio_nb_clients >= STDIO_MAX_CLIENTS) {
return NULL;
}
+ if (is_daemonized()) {
+ error_report("cannot use stdio with -daemonize");
+ return NULL;
+ }
if (stdio_nb_clients == 0) {
old_fd0_flags = fcntl(0, F_GETFL);
tcgetattr (0, &oldtty);
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|| defined(__GLIBC__)
+#define HAVE_CHARDEV_TTY 1
+
typedef struct {
int fd;
int connected;
CharDriverState *chr;
PtyCharDriver *s;
struct termios tty;
+ const char *label;
int master_fd, slave_fd, len;
#if defined(__OpenBSD__) || defined(__DragonFly__)
char pty_name[PATH_MAX];
chr->filename = g_malloc(len);
snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd));
qemu_opt_set(opts, "path", q_ptsname(master_fd));
- fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd));
+
+ label = qemu_opts_id(opts);
+ fprintf(stderr, "char device redirected to %s%s%s%s\n",
+ q_ptsname(master_fd),
+ label ? " (label " : "",
+ label ? label : "",
+ label ? ")" : "");
s = g_malloc0(sizeof(PtyCharDriver));
chr->opaque = s;
chr->chr_close = qemu_chr_close_tty;
return chr;
}
-#else /* ! __linux__ && ! __sun__ */
-static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
-{
- return NULL;
-}
#endif /* __linux__ || __sun__ */
#if defined(__linux__)
+
+#define HAVE_CHARDEV_PARPORT 1
+
typedef struct {
int fd;
int mode;
#endif /* __linux__ */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+
+#define HAVE_CHARDEV_PARPORT 1
+
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
{
int fd = (int)(intptr_t)chr->opaque;
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
+ Error *local_err = NULL;
int fd = -1;
chr = g_malloc0(sizeof(CharDriverState));
s = g_malloc0(sizeof(NetCharDriver));
- fd = inet_dgram_opts(opts);
+ fd = inet_dgram_opts(opts, &local_err);
if (fd < 0) {
- fprintf(stderr, "inet_dgram_opts failed\n");
goto return_err;
}
return chr;
return_err:
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
g_free(chr);
g_free(s);
if (fd >= 0) {
static void tcp_chr_accept(void *opaque);
-static void tcp_chr_connect(void *opaque);
-
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
if (s->connected) {
return send_all(s->fd, buf, len);
- } else if (s->listen_fd == -1) {
- /* (Re-)connect for unconnected writing */
- tcp_chr_connect(chr);
- return 0;
} else {
+ /* XXX: indicate an error ? */
return len;
}
}
TCPCharDriver *s = chr->opaque;
s->connected = 1;
- qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
- tcp_chr_read, NULL, chr);
+ if (s->fd >= 0) {
+ qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+ tcp_chr_read, NULL, chr);
+ }
qemu_chr_generic_open(chr);
}
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
+ Error *local_err = NULL;
int fd = -1;
int is_listen;
int is_waitconnect;
if (is_unix) {
if (is_listen) {
- fd = unix_listen_opts(opts);
+ fd = unix_listen_opts(opts, &local_err);
} else {
- fd = unix_connect_opts(opts);
+ fd = unix_connect_opts(opts, &local_err, NULL, NULL);
}
} else {
if (is_listen) {
- fd = inet_listen_opts(opts, 0, NULL);
+ fd = inet_listen_opts(opts, 0, &local_err);
} else {
- fd = inet_connect_opts(opts, NULL, NULL);
+ fd = inet_connect_opts(opts, &local_err, NULL, NULL);
}
}
if (fd < 0) {
return chr;
fail:
- if (fd >= 0)
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ }
+ if (fd >= 0) {
closesocket(fd);
+ }
g_free(s);
g_free(chr);
return NULL;
#else
{ .name = "file", .open = qemu_chr_open_file_out },
{ .name = "pipe", .open = qemu_chr_open_pipe },
- { .name = "pty", .open = qemu_chr_open_pty },
{ .name = "stdio", .open = qemu_chr_open_stdio },
#endif
#ifdef CONFIG_BRLAPI
{ .name = "braille", .open = chr_baum_init },
#endif
-#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
- || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
- || defined(__FreeBSD_kernel__)
+#ifdef HAVE_CHARDEV_TTY
{ .name = "tty", .open = qemu_chr_open_tty },
+ { .name = "pty", .open = qemu_chr_open_pty },
#endif
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) \
- || defined(__FreeBSD_kernel__)
+#ifdef HAVE_CHARDEV_PARPORT
{ .name = "parport", .open = qemu_chr_open_pp },
#endif
#ifdef CONFIG_SPICE
{ .name = "spicevmc", .open = qemu_chr_open_spice },
+#if SPICE_SERVER_VERSION >= 0x000c02
+ { .name = "spiceport", .open = qemu_chr_open_spice_port },
+#endif
#endif
};
CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
- void (*init)(struct CharDriverState *s))
+ void (*init)(struct CharDriverState *s),
+ Error **errp)
{
CharDriverState *chr;
int i;
if (qemu_opts_id(opts) == NULL) {
- fprintf(stderr, "chardev: no id specified\n");
- return NULL;
+ error_setg(errp, "chardev: no id specified\n");
+ goto err;
}
if (qemu_opt_get(opts, "backend") == NULL) {
- fprintf(stderr, "chardev: \"%s\" missing backend\n",
- qemu_opts_id(opts));
- return NULL;
+ error_setg(errp, "chardev: \"%s\" missing backend\n",
+ qemu_opts_id(opts));
+ goto err;
}
for (i = 0; i < ARRAY_SIZE(backend_table); i++) {
if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0)
break;
}
if (i == ARRAY_SIZE(backend_table)) {
- fprintf(stderr, "chardev: backend \"%s\" not found\n",
- qemu_opt_get(opts, "backend"));
- return NULL;
+ error_setg(errp, "chardev: backend \"%s\" not found\n",
+ qemu_opt_get(opts, "backend"));
+ goto err;
}
chr = backend_table[i].open(opts);
if (!chr) {
- fprintf(stderr, "chardev: opening backend \"%s\" failed\n",
- qemu_opt_get(opts, "backend"));
- return NULL;
+ error_setg(errp, "chardev: opening backend \"%s\" failed\n",
+ qemu_opt_get(opts, "backend"));
+ goto err;
}
if (!chr->filename)
chr->avail_connections = 1;
}
chr->label = g_strdup(qemu_opts_id(opts));
+ chr->opts = opts;
return chr;
+
+err:
+ qemu_opts_del(opts);
+ return NULL;
}
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
const char *p;
CharDriverState *chr;
QemuOpts *opts;
+ Error *err = NULL;
if (strstart(filename, "chardev:", &p)) {
return qemu_chr_find(p);
if (!opts)
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)) {
monitor_init(chr, MONITOR_USE_READLINE);
}
- qemu_opts_del(opts);
return chr;
}
void qemu_chr_delete(CharDriverState *chr)
{
QTAILQ_REMOVE(&chardevs, chr, next);
- if (chr->chr_close)
+ if (chr->chr_close) {
chr->chr_close(chr);
+ }
g_free(chr->filename);
g_free(chr->label);
+ if (chr->opts) {
+ qemu_opts_del(chr->opts);
+ }
g_free(chr);
}
return serial_hds[next_serial++];
}
+QemuOptsList qemu_chardev_opts = {
+ .name = "chardev",
+ .implied_opt_name = "backend",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
+ .desc = {
+ {
+ .name = "backend",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "host",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "port",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "localaddr",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "localport",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "to",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "ipv4",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "ipv6",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "wait",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "server",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "delay",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "telnet",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "width",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "height",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "cols",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "rows",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "mux",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "signal",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "name",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "debug",
+ .type = QEMU_OPT_NUMBER,
+ },
+ { /* 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);
+}
+
+#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);
+}
+
+#endif /* WIN32 */
+
+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_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);
+}