mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-22 12:35:17 +00:00
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:
parent
2f4d2496a8
commit
30c43afd73
@ -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
|
||||||
|
@ -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">
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -270,6 +270,8 @@ virDomainFindByID;
|
|||||||
virDomainFindByName;
|
virDomainFindByName;
|
||||||
virDomainFindByUUID;
|
virDomainFindByUUID;
|
||||||
virDomainGetRootFilesystem;
|
virDomainGetRootFilesystem;
|
||||||
|
virDomainGraphicsAuthConnectedTypeFromString;
|
||||||
|
virDomainGraphicsAuthConnectedTypeToString;
|
||||||
virDomainGraphicsDefFree;
|
virDomainGraphicsDefFree;
|
||||||
virDomainGraphicsSpiceChannelModeTypeFromString;
|
virDomainGraphicsSpiceChannelModeTypeFromString;
|
||||||
virDomainGraphicsSpiceChannelModeTypeToString;
|
virDomainGraphicsSpiceChannelModeTypeToString;
|
||||||
|
@ -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) {
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user