graphics: add support for action_if_connected in qemu

This option accepts 3 values:
-keep, to keep current client connected (Spice+VNC)
-disconnect, to disconnect client (Spice)
-fail, to fail setting password if there is a client connected (Spice)
This commit is contained in:
Michal Privoznik 2011-05-26 16:15:54 +02:00
parent 2f4d2496a8
commit 30c43afd73
7 changed files with 99 additions and 14 deletions

View File

@ -1896,11 +1896,14 @@ qemu-kvm -net nic,model=? /dev/null
specifies the keymap to use. It is possible to set a limit specifies the keymap to use. It is possible to set a limit
on the validity of the password be giving an on the validity of the password be giving an
timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code> timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code>
assumed to be in UTC. NB, this may not be supported by all assumed to be in UTC. The <code>connected</code> attribute
hypervisors.<br/> <br/> Rather than using listen/port, allows control of connected client during password changes.
QEMU supports a <code>socket</code> attribute for VNC accepts <code>keep</code> value only.
listening on a unix domain socket <span class="since">since 0.9.3</span>
path.<span class="since">Since 0.8.8</span> NB, this may not be supported by all hypervisors.<br/> <br/>
Rather than using listen/port, QEMU supports a
<code>socket</code> attribute for listening on a unix
domain socket path.<span class="since">Since 0.8.8</span>
</dd> </dd>
<dt><code>"spice"</code></dt> <dt><code>"spice"</code></dt>
<dd> <dd>
@ -1918,8 +1921,14 @@ qemu-kvm -net nic,model=? /dev/null
attribute specifies the keymap to use. It is possible to attribute specifies the keymap to use. It is possible to
set a limit on the validity of the password be giving an set a limit on the validity of the password be giving an
timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code> timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code>
assumed to be in UTC. NB, this may not be supported by assumed to be in UTC. The <code>connected</code> attribute
all hypervisors.<span class="since">"spice" since 0.8.6</span>. allows control of connected client during password changes.
SPICE accepts <code>keep</code> to keep client connected,
<code>disconnect</code> to disconnect client and
<code>fail</code> to fail changing password.
<span class="since">Since 0.9.3</span>
NB, this may not be supported by all hypervisors.
<span class="since">"spice" since 0.8.6</span>.
</p> </p>
<p> <p>
When SPICE has both a normal and TLS secured TCP port When SPICE has both a normal and TLS secured TCP port

View File

@ -1294,6 +1294,13 @@
<data type="dateTime"/> <data type="dateTime"/>
</attribute> </attribute>
</optional> </optional>
<optional>
<attribute name="connected">
<choice>
<value>keep</value>
</choice>
</attribute>
</optional>
</group> </group>
<group> <group>
<attribute name="type"> <attribute name="type">
@ -1337,6 +1344,15 @@
<data type="dateTime"/> <data type="dateTime"/>
</attribute> </attribute>
</optional> </optional>
<optional>
<attribute name="connected">
<choice>
<value>fail</value>
<value>disconnect</value>
<value>keep</value>
</choice>
</attribute>
</optional>
<interleave> <interleave>
<zeroOrMore> <zeroOrMore>
<element name="channel"> <element name="channel">

View File

@ -317,6 +317,13 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST,
"desktop", "desktop",
"spice") "spice")
VIR_ENUM_IMPL(virDomainGraphicsAuthConnected,
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_LAST,
"default",
"fail",
"disconnect",
"keep")
VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName,
VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST,
"main", "main",
@ -3945,9 +3952,12 @@ error:
static int static int
virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr def) virDomainGraphicsAuthDefParseXML(xmlNodePtr node,
virDomainGraphicsAuthDefPtr def,
int type)
{ {
char *validTo = NULL; char *validTo = NULL;
char *connected = virXMLPropString(node, "connected");
def->passwd = virXMLPropString(node, "passwd"); def->passwd = virXMLPropString(node, "passwd");
@ -3988,6 +3998,28 @@ virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr de
def->expires = 1; def->expires = 1;
} }
if (connected) {
int action = virDomainGraphicsAuthConnectedTypeFromString(connected);
if (action <= 0) {
virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown connected value %s"),
connected);
VIR_FREE(connected);
return -1;
}
VIR_FREE(connected);
/* VNC supports connected='keep' only */
if (type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
action != VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP) {
virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("VNC supports connected='keep' only"));
return -1;
}
def->connected = action;
}
return 0; return 0;
} }
@ -4058,7 +4090,8 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, unsigned int flags)
!def->data.vnc.listenAddr[0]) !def->data.vnc.listenAddr[0])
VIR_FREE(def->data.vnc.listenAddr); VIR_FREE(def->data.vnc.listenAddr);
if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth,
def->type) < 0)
goto error; goto error;
} else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
char *fullscreen = virXMLPropString(node, "fullscreen"); char *fullscreen = virXMLPropString(node, "fullscreen");
@ -4194,7 +4227,8 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, unsigned int flags)
!def->data.spice.listenAddr[0]) !def->data.spice.listenAddr[0])
VIR_FREE(def->data.spice.listenAddr); VIR_FREE(def->data.spice.listenAddr);
if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth) < 0) if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth,
def->type) < 0)
goto error; goto error;
cur = node->children; cur = node->children;
@ -9280,6 +9314,10 @@ virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf,
strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm); strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm);
virBufferAsprintf(buf, " passwdValidTo='%s'", strbuf); virBufferAsprintf(buf, " passwdValidTo='%s'", strbuf);
} }
if (def->connected)
virBufferEscapeString(buf, " connected='%s'",
virDomainGraphicsAuthConnectedTypeToString(def->connected));
} }
static int static int

View File

@ -639,12 +639,22 @@ enum virDomainGraphicsType {
VIR_DOMAIN_GRAPHICS_TYPE_LAST, VIR_DOMAIN_GRAPHICS_TYPE_LAST,
}; };
enum virDomainGraphicsAuthConnectedType {
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_DEFAULT = 0,
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_FAIL,
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_DISCONNECT,
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP,
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_LAST
};
typedef struct _virDomainGraphicsAuthDef virDomainGraphicsAuthDef; typedef struct _virDomainGraphicsAuthDef virDomainGraphicsAuthDef;
typedef virDomainGraphicsAuthDef *virDomainGraphicsAuthDefPtr; typedef virDomainGraphicsAuthDef *virDomainGraphicsAuthDefPtr;
struct _virDomainGraphicsAuthDef { struct _virDomainGraphicsAuthDef {
char *passwd; char *passwd;
unsigned int expires: 1; /* Whether there is an expiry time set */ unsigned int expires: 1; /* Whether there is an expiry time set */
time_t validTo; /* seconds since epoch */ time_t validTo; /* seconds since epoch */
int connected; /* action if connected */
}; };
enum virDomainGraphicsSpiceChannelName { enum virDomainGraphicsSpiceChannelName {
@ -1582,6 +1592,7 @@ VIR_ENUM_DECL(virDomainHostdevSubsys)
VIR_ENUM_DECL(virDomainInput) VIR_ENUM_DECL(virDomainInput)
VIR_ENUM_DECL(virDomainInputBus) VIR_ENUM_DECL(virDomainInputBus)
VIR_ENUM_DECL(virDomainGraphics) VIR_ENUM_DECL(virDomainGraphics)
VIR_ENUM_DECL(virDomainGraphicsAuthConnected)
VIR_ENUM_DECL(virDomainGraphicsSpiceChannelName) VIR_ENUM_DECL(virDomainGraphicsSpiceChannelName)
VIR_ENUM_DECL(virDomainGraphicsSpiceChannelMode) VIR_ENUM_DECL(virDomainGraphicsSpiceChannelMode)
VIR_ENUM_DECL(virDomainGraphicsSpiceImageCompression) VIR_ENUM_DECL(virDomainGraphicsSpiceImageCompression)

View File

@ -270,6 +270,8 @@ virDomainFindByID;
virDomainFindByName; virDomainFindByName;
virDomainFindByUUID; virDomainFindByUUID;
virDomainGetRootFilesystem; virDomainGetRootFilesystem;
virDomainGraphicsAuthConnectedTypeFromString;
virDomainGraphicsAuthConnectedTypeToString;
virDomainGraphicsDefFree; virDomainGraphicsDefFree;
virDomainGraphicsSpiceChannelModeTypeFromString; virDomainGraphicsSpiceChannelModeTypeFromString;
virDomainGraphicsSpiceChannelModeTypeToString; virDomainGraphicsSpiceChannelModeTypeToString;

View File

@ -1065,10 +1065,12 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
return -1; return -1;
} }
/* If a password lifetime was, or is set, then we must always run, /* If a password lifetime was, or is set, or action if connected has
* even if new password matches old password */ * changed, then we must always run, even if new password matches
* old password */
if (olddev->data.vnc.auth.expires || if (olddev->data.vnc.auth.expires ||
dev->data.vnc.auth.expires || dev->data.vnc.auth.expires ||
olddev->data.vnc.auth.connected != dev->data.vnc.auth.connected ||
STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd,
dev->data.vnc.auth.passwd)) { dev->data.vnc.auth.passwd)) {
VIR_DEBUG("Updating password on VNC server %p %p", VIR_DEBUG("Updating password on VNC server %p %p",
@ -1084,6 +1086,7 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
dev->data.vnc.auth.passwd = NULL; dev->data.vnc.auth.passwd = NULL;
olddev->data.vnc.auth.validTo = dev->data.vnc.auth.validTo; olddev->data.vnc.auth.validTo = dev->data.vnc.auth.validTo;
olddev->data.vnc.auth.expires = dev->data.vnc.auth.expires; olddev->data.vnc.auth.expires = dev->data.vnc.auth.expires;
olddev->data.vnc.auth.connected = dev->data.vnc.auth.connected;
} else { } else {
ret = 0; ret = 0;
} }
@ -1116,6 +1119,7 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
* even if new password matches old password */ * even if new password matches old password */
if (olddev->data.spice.auth.expires || if (olddev->data.spice.auth.expires ||
dev->data.spice.auth.expires || dev->data.spice.auth.expires ||
olddev->data.spice.auth.connected != dev->data.spice.auth.connected ||
STRNEQ_NULLABLE(olddev->data.spice.auth.passwd, STRNEQ_NULLABLE(olddev->data.spice.auth.passwd,
dev->data.spice.auth.passwd)) { dev->data.spice.auth.passwd)) {
VIR_DEBUG("Updating password on SPICE server %p %p", VIR_DEBUG("Updating password on SPICE server %p %p",
@ -1131,6 +1135,7 @@ qemuDomainChangeGraphics(struct qemud_driver *driver,
dev->data.spice.auth.passwd = NULL; dev->data.spice.auth.passwd = NULL;
olddev->data.spice.auth.validTo = dev->data.spice.auth.validTo; olddev->data.spice.auth.validTo = dev->data.spice.auth.validTo;
olddev->data.spice.auth.expires = dev->data.spice.auth.expires; olddev->data.spice.auth.expires = dev->data.spice.auth.expires;
olddev->data.spice.auth.connected = dev->data.spice.auth.connected;
} else { } else {
VIR_DEBUG("Not updating since password didn't change"); VIR_DEBUG("Not updating since password didn't change");
ret = 0; ret = 0;
@ -1874,16 +1879,20 @@ qemuDomainChangeGraphicsPasswords(struct qemud_driver *driver,
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
time_t now = time(NULL); time_t now = time(NULL);
char expire_time [64]; char expire_time [64];
const char *connected = NULL;
int ret; int ret;
if (!auth->passwd && !driver->vncPassword) if (!auth->passwd && !driver->vncPassword)
return 0; return 0;
if (auth->connected)
connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected);
qemuDomainObjEnterMonitorWithDriver(driver, vm); qemuDomainObjEnterMonitorWithDriver(driver, vm);
ret = qemuMonitorSetPassword(priv->mon, ret = qemuMonitorSetPassword(priv->mon,
type, type,
auth->passwd ? auth->passwd : defaultPasswd, auth->passwd ? auth->passwd : defaultPasswd,
NULL); connected);
if (ret == -2) { if (ret == -2) {
if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) {

View File

@ -71,7 +71,7 @@
</console> </console>
<input type='tablet' bus='usb'/> <input type='tablet' bus='usb'/>
<input type='mouse' bus='ps2'/> <input type='mouse' bus='ps2'/>
<graphics type='spice' port='5900' autoport='no' passwd='sercet' passwdValidTo='2011-05-31T16:11:22'/> <graphics type='spice' port='5900' autoport='no' passwd='sercet' passwdValidTo='2011-05-31T16:11:22' connected='disconnect'/>
<sound model='ac97'> <sound model='ac97'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</sound> </sound>