#include "configmake.h"
#include "threadpool.h"
#include "locking/lock_manager.h"
+#include "virkeycode.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
return ret;
}
+static int qemuDomainSendKey(virDomainPtr domain,
+ unsigned int codeset,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ int nkeycodes,
+ unsigned int flags)
+{
+ struct qemud_driver *driver = domain->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ virCheckFlags(0, -1);
+
+ /* translate the keycode to XT_KBD for qemu driver */
+ if (codeset != VIR_KEYCODE_SET_XT_KBD) {
+ int i;
+ int keycode;
+
+ for (i = 0; i < nkeycodes; i++) {
+ keycode = virKeycodeValueTranslate(codeset, VIR_KEYCODE_SET_XT_KBD,
+ keycodes[i]);
+ if (keycode < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot translate keycode %u of %s codeset to xt_kbd keycode"),
+ keycodes[i],
+ virKeycodeSetTypeToString(codeset));
+ return -1;
+ }
+ keycodes[i] = keycode;
+ }
+ }
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(domain->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ priv = vm->privateData;
+
+ if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ ignore_value(qemuDomainObjEnterMonitorWithDriver(driver, vm));
+ ret = qemuMonitorSendKey(priv->mon, holdtime, keycodes, nkeycodes);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (qemuDomainObjEndJob(driver, vm) == 0) {
+ vm = NULL;
+ goto cleanup;
+ }
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
static int qemudDomainGetInfo(virDomainPtr dom,
virDomainInfoPtr info)
{
.domainMigratePerform3 = qemuDomainMigratePerform3, /* 0.9.2 */
.domainMigrateFinish3 = qemuDomainMigrateFinish3, /* 0.9.2 */
.domainMigrateConfirm3 = qemuDomainMigrateConfirm3, /* 0.9.2 */
+ .domainSendKey = qemuDomainSendKey, /* 0.9.4 */
};
return ret;
}
+int qemuMonitorSendKey(qemuMonitorPtr mon,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ unsigned int nkeycodes)
+{
+ int ret;
+
+ VIR_DEBUG("mon=%p, holdtime=%u, nkeycodes=%u",
+ mon, holdtime, nkeycodes);
+
+ if (mon->json)
+ ret = qemuMonitorJSONSendKey(mon, holdtime, keycodes, nkeycodes);
+ else
+ ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes);
+ return ret;
+}
+
int qemuMonitorScreendump(qemuMonitorPtr mon,
const char *file)
{
int qemuMonitorScreendump(qemuMonitorPtr mon,
const char *file);
+int qemuMonitorSendKey(qemuMonitorPtr mon,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ unsigned int nkeycodes);
+
/**
* When running two dd process and using <> redirection, we need a
* shell that will not truncate files. These two strings serve that
return ret;
}
+int qemuMonitorJSONSendKey(qemuMonitorPtr mon,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ unsigned int nkeycodes)
+{
+ /*
+ * FIXME: qmp sendkey has not been implemented yet,
+ * and qmp API of it cannot be anticipated, so we use hmp temporary.
+ */
+ if (qemuMonitorCheckHMP(mon, "sendkey")) {
+ return qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes);
+ } else
+ return -1;
+}
+
int qemuMonitorJSONScreendump(qemuMonitorPtr mon,
const char *file)
{
int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon);
+int qemuMonitorJSONSendKey(qemuMonitorPtr mon,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ unsigned int nkeycodes);
+
int qemuMonitorJSONScreendump(qemuMonitorPtr mon,
const char *file);
return -1;
}
+int qemuMonitorTextSendKey(qemuMonitorPtr mon,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ unsigned int nkeycodes)
+{
+ int i;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char *cmd, *reply = NULL;
+
+ if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS || nkeycodes == 0)
+ return -1;
+
+ virBufferAddLit(&buf, "sendkey ");
+ for (i = 0; i < nkeycodes; i++) {
+ if (keycodes[i] > 0xffff) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("keycode %d is invalid: 0x%X"),
+ i, keycodes[i]);
+ virBufferFreeAndReset(&buf);
+ return -1;
+ }
+
+ if (i)
+ virBufferAddChar(&buf, '-');
+ virBufferAsprintf(&buf, "0x%02X", keycodes[i]);
+ }
+
+ if (holdtime)
+ virBufferAsprintf(&buf, " %u", holdtime);
+
+ if (virBufferError(&buf)) {
+ virReportOOMError();
+ return -1;
+ }
+
+ cmd = virBufferContentAndReset(&buf);
+ if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("failed to send key using command '%s'"),
+ cmd);
+ VIR_FREE(cmd);
+ return -1;
+ }
+
+ VIR_FREE(cmd);
+ VIR_FREE(reply);
+ return 0;
+}
+
/* Returns -1 on error, -2 if not supported */
int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file)
{
int qemuMonitorTextInjectNMI(qemuMonitorPtr mon);
+int qemuMonitorTextSendKey(qemuMonitorPtr mon,
+ unsigned int holdtime,
+ unsigned int *keycodes,
+ unsigned int nkeycodes);
+
int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file);
#endif /* QEMU_MONITOR_TEXT_H */