summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2011-06-23 13:31:42 +0100
committerAnthony Liguori <aliguori@us.ibm.com>2011-07-23 11:19:02 -0500
commit13661089810d3e59931f3e80d7cb541b99af7071 (patch)
tree56f507cd78ea65ca7cb8e500d7b9ce57e54f7981
parent7e7e2ebc942da8285931ceabf12823e165dced8b (diff)
downloadhqemu-13661089810d3e59931f3e80d7cb541b99af7071.zip
hqemu-13661089810d3e59931f3e80d7cb541b99af7071.tar.gz
Introduce a 'client_add' monitor command accepting an open FD
Allow client connections for VNC and socket based character devices to be passed in over the monitor using SCM_RIGHTS. One intended usage scenario is to start QEMU with VNC on a UNIX domain socket. An unprivileged user which cannot access the UNIX domain socket, can then connect to QEMU's VNC server by passing an open FD to libvirt, which passes it onto QEMU. { "execute": "get_fd", "arguments": { "fdname": "myclient" } } { "return": {} } { "execute": "add_client", "arguments": { "protocol": "vnc", "fdname": "myclient", "skipauth": true } } { "return": {} } In this case 'protocol' can be 'vnc' or 'spice', or the name of a character device (eg from -chardev id=XXXX) The 'skipauth' parameter can be used to skip any configured VNC authentication scheme, which is useful if the mgmt layer talking to the monitor has already authenticated the client in another way. * console.h: Define 'vnc_display_add_client' method * monitor.c: Implement 'client_add' command * qemu-char.c, qemu-char.h: Add 'qemu_char_add_client' method * qerror.c, qerror.h: Add QERR_ADD_CLIENT_FAILED * qmp-commands.hx: Declare 'client_add' command * ui/vnc.c: Implement 'vnc_display_add_client' method Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
-rw-r--r--console.h1
-rw-r--r--monitor.c32
-rw-r--r--qemu-char.c30
-rw-r--r--qemu-char.h2
-rw-r--r--qerror.c4
-rw-r--r--qerror.h3
-rw-r--r--qmp-commands.hx27
-rw-r--r--ui/vnc.c7
8 files changed, 100 insertions, 6 deletions
diff --git a/console.h b/console.h
index c09537b..67d1373 100644
--- a/console.h
+++ b/console.h
@@ -372,6 +372,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
void vnc_display_init(DisplayState *ds);
void vnc_display_close(DisplayState *ds);
int vnc_display_open(DisplayState *ds, const char *display);
+void vnc_display_add_client(DisplayState *ds, int csock, int skipauth);
int vnc_display_disable_login(DisplayState *ds);
char *vnc_display_local_addr(DisplayState *ds);
#ifdef CONFIG_VNC
diff --git a/monitor.c b/monitor.c
index 67ceb46..718935b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1185,6 +1185,38 @@ static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
return -1;
}
+static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *fdname = qdict_get_str(qdict, "fdname");
+ int skipauth = qdict_get_try_bool(qdict, "skipauth", 0);
+ CharDriverState *s;
+
+ if (strcmp(protocol, "spice") == 0) {
+ if (!using_spice) {
+ /* correct one? spice isn't a device ,,, */
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+ return -1;
+ }
+ qerror_report(QERR_ADD_CLIENT_FAILED);
+ return -1;
+ } else if (strcmp(protocol, "vnc") == 0) {
+ int fd = monitor_get_fd(mon, fdname);
+ vnc_display_add_client(NULL, fd, skipauth);
+ return 0;
+ } else if ((s = qemu_chr_find(protocol)) != NULL) {
+ int fd = monitor_get_fd(mon, fdname);
+ if (qemu_chr_add_client(s, fd) < 0) {
+ qerror_report(QERR_ADD_CLIENT_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ qerror_report(QERR_INVALID_PARAMETER, "protocol");
+ return -1;
+}
+
static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *protocol = qdict_get_str(qdict, "protocol");
diff --git a/qemu-char.c b/qemu-char.c
index 926987b..dcf7065 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -168,6 +168,11 @@ int qemu_chr_get_msgfd(CharDriverState *s)
return s->get_msgfd ? s->get_msgfd(s) : -1;
}
+int qemu_chr_add_client(CharDriverState *s, int fd)
+{
+ return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
+}
+
void qemu_chr_accept_input(CharDriverState *s)
{
if (s->chr_accept_input)
@@ -2146,6 +2151,22 @@ static void socket_set_nodelay(int fd)
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
}
+static int tcp_chr_add_client(CharDriverState *chr, int fd)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->fd != -1)
+ return -1;
+
+ socket_set_nonblock(fd);
+ if (s->do_nodelay)
+ socket_set_nodelay(fd);
+ s->fd = fd;
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ tcp_chr_connect(chr);
+
+ return 0;
+}
+
static void tcp_chr_accept(void *opaque)
{
CharDriverState *chr = opaque;
@@ -2178,12 +2199,8 @@ static void tcp_chr_accept(void *opaque)
break;
}
}
- socket_set_nonblock(fd);
- if (s->do_nodelay)
- socket_set_nodelay(fd);
- s->fd = fd;
- qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
- tcp_chr_connect(chr);
+ if (tcp_chr_add_client(chr, fd) < 0)
+ close(fd);
}
static void tcp_chr_close(CharDriverState *chr)
@@ -2256,6 +2273,7 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr)
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;
diff --git a/qemu-char.h b/qemu-char.h
index 892c6da..f361c6d 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -57,6 +57,7 @@ struct CharDriverState {
void (*chr_update_read_handler)(struct CharDriverState *s);
int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
int (*get_msgfd)(struct CharDriverState *s);
+ int (*chr_add_client)(struct CharDriverState *chr, int fd);
IOEventHandler *chr_event;
IOCanReadHandler *chr_can_read;
IOReadHandler *chr_read;
@@ -99,6 +100,7 @@ int qemu_chr_can_read(CharDriverState *s);
void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len);
int qemu_chr_get_msgfd(CharDriverState *s);
void qemu_chr_accept_input(CharDriverState *s);
+int qemu_chr_add_client(CharDriverState *s, int fd);
void qemu_chr_info_print(Monitor *mon, const QObject *ret_data);
void qemu_chr_info(Monitor *mon, QObject **ret_data);
CharDriverState *qemu_chr_find(const char *name);
diff --git a/qerror.c b/qerror.c
index 229d0d6..69c1bc9 100644
--- a/qerror.c
+++ b/qerror.c
@@ -198,6 +198,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Could not set password",
},
{
+ .error_fmt = QERR_ADD_CLIENT_FAILED,
+ .desc = "Could not add client",
+ },
+ {
.error_fmt = QERR_TOO_MANY_FILES,
.desc = "Too many open files",
},
diff --git a/qerror.h b/qerror.h
index 7ec0fc1..8058456 100644
--- a/qerror.h
+++ b/qerror.h
@@ -166,6 +166,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_SET_PASSWD_FAILED \
"{ 'class': 'SetPasswdFailed', 'data': {} }"
+#define QERR_ADD_CLIENT_FAILED \
+ "{ 'class': 'AddClientFailed', 'data': {} }"
+
#define QERR_TOO_MANY_FILES \
"{ 'class': 'TooManyFiles', 'data': {} }"
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 5d44edf..54e313c 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -919,6 +919,33 @@ Example:
EQMP
{
+ .name = "add_client",
+ .args_type = "protocol:s,fdname:s,skipauth:b?",
+ .params = "protocol fdname skipauth",
+ .help = "add a graphics client",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = add_graphics_client,
+ },
+
+SQMP
+add_client
+----------
+
+Add a graphics client
+
+Arguments:
+
+- "protocol": protocol name (json-string)
+- "fdname": file descriptor name (json-string)
+
+Example:
+
+-> { "execute": "add_client", "arguments": { "protocol": "vnc",
+ "fdname": "myclient" } }
+<- { "return": {} }
+
+EQMP
+ {
.name = "qmp_capabilities",
.args_type = "",
.params = "",
diff --git a/ui/vnc.c b/ui/vnc.c
index 39b5b51..8602adc 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2924,3 +2924,10 @@ int vnc_display_open(DisplayState *ds, const char *display)
}
return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
+
+void vnc_display_add_client(DisplayState *ds, int csock, int skipauth)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ return vnc_connect(vs, csock, skipauth);
+}
OpenPOWER on IntegriCloud