Pass the TPM file descriptor to QEMU via command line. Instead of passing /dev/tpm0 we now pass /dev/fdset/10 and the additional parameters -add-fd set=10,fd=20. This addresses the use case when QEMU is started with non-root privileges and QEMU cannot open /dev/tpm0 for example. One problem is that for the passing of the file descriptor set to work, virCommandReorderFDs must not be called on the virCommand. This is prevented by setting a flag in the virCommandPassFDGetFDIndex that is checked to be clear when virCommandReorderFDs is run. Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxxxxxxxxxx> v2->v3: Fixed some memory leaks --- src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 136 ++++++++++++++++++++++++++++++++++++++++++++--- src/util/vircommand.c | 33 ++++++++++++ src/util/vircommand.h | 3 ++ 4 files changed, 166 insertions(+), 7 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index aeec440..3194e8b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1164,6 +1164,7 @@ virCommandNewArgList; virCommandNewArgs; virCommandNonblockingFDs; virCommandPassFD; +virCommandPassFDGetFDIndex; virCommandPassListenFDs; virCommandRawStatus; virCommandRequireHandshake; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 8ed7934..17debba 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -159,6 +159,58 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "interleave"); /** + * qemuVirCommandGetFDSet: + * @cmd: the command to modify + * @fd: fd to reassign to the child + * + * Get the parameters for the QEMU -add-fd command line option + * for the given file descriptor. The file descriptor must previously + * have been 'transferred' in a virCommandPassFD() call. + * This function for example returns "set=10,fd=20". + */ +static char * +qemuVirCommandGetFDSet(virCommandPtr cmd, int fd) +{ + char *result = NULL; + int idx = virCommandPassFDGetFDIndex(cmd, fd); + + if (idx >= 0) { + ignore_value(virAsprintf(&result, "set=%d,fd=%d", idx, fd) < 0); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("file descriptor %d has not been transferred"), fd); + } + + return result; +} + +/** + * qemuVirCommandGetDevSet: + * @cmd: the command to modify + * @fd: fd to reassign to the child + * + * Get the parameters for the QEMU path= parameter where a file + * descriptor is accessed via a file descriptor set, for example + * /dev/fdset/10. The file descriptor must previously have been + * 'transferred' in a virCommandPassFD() call. + */ +static char * +qemuVirCommandGetDevSet(virCommandPtr cmd, int fd) +{ + char *result = NULL; + int idx = virCommandPassFDGetFDIndex(cmd, fd); + + if (idx >= 0) { + ignore_value(virAsprintf(&result, "/dev/fdset/%d", idx) < 0); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("file descriptor %d has not been transferred"), fd); + } + return result; +} + + +/** * qemuPhysIfaceConnect: * @def: the definition of the VM (needed by 802.1Qbh and audit) * @driver: pointer to the driver instance @@ -5926,14 +5978,20 @@ qemuBuildRNGDeviceArgs(virCommandPtr cmd, static char *qemuBuildTPMBackendStr(const virDomainDef *def, + virCommandPtr cmd, virQEMUCapsPtr qemuCaps, - const char *emulator) + const char *emulator, + int *tpmfd, int *cancelfd) { const virDomainTPMDef *tpm = def->tpm; virBuffer buf = VIR_BUFFER_INITIALIZER; const char *type = virDomainTPMBackendTypeToString(tpm->type); - char *cancel_path; + char *cancel_path = NULL; const char *tpmdev; + char *devset = NULL, *cancel_devset = NULL; + + *tpmfd = -1; + *cancelfd = -1; virBufferAsprintf(&buf, "%s,id=tpm-%s", type, tpm->info.alias); @@ -5946,11 +6004,49 @@ static char *qemuBuildTPMBackendStr(const virDomainDef *def, if (!(cancel_path = virTPMCreateCancelPath(tpmdev))) goto error; - virBufferAddLit(&buf, ",path="); - virBufferEscape(&buf, ',', ",", "%s", tpmdev); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ADD_FD)) { + *tpmfd = open(tpmdev, O_RDWR); + if (*tpmfd < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not open TPM device %s"), tpmdev); + goto error; + } + + virCommandPassFD(cmd, *tpmfd, + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + devset = qemuVirCommandGetDevSet(cmd, *tpmfd); + if (devset == NULL) + goto error; + + *cancelfd = open(cancel_path, O_WRONLY); + if (*cancelfd < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not open TPM device's cancel path " + "%s"), cancel_path); + goto error; + } + + virCommandPassFD(cmd, *cancelfd, + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + cancel_devset = qemuVirCommandGetDevSet(cmd, *cancelfd); + if (cancel_devset == NULL) + goto error; + + virBufferAddLit(&buf, ",path="); + virBufferEscape(&buf, ',', ",", "%s", devset); + VIR_FREE(devset); - virBufferAddLit(&buf, ",cancel-path="); - virBufferEscape(&buf, ',', ",", "%s", cancel_path); + virBufferAddLit(&buf, ",cancel-path="); + virBufferEscape(&buf, ',', ",", "%s", cancel_devset); + VIR_FREE(cancel_devset); + } else { + /* all test cases will use this path */ + virBufferAddLit(&buf, ",path="); + virBufferEscape(&buf, ',', ",", "%s", tpmdev); + + virBufferAddLit(&buf, ",cancel-path="); + virBufferEscape(&buf, ',', ",", "%s", cancel_path); + } VIR_FREE(cancel_path); break; @@ -5970,6 +6066,10 @@ static char *qemuBuildTPMBackendStr(const virDomainDef *def, emulator, type); error: + VIR_FREE(devset); + VIR_FREE(cancel_devset); + VIR_FREE(cancel_path); + virBufferFreeAndReset(&buf); return NULL; } @@ -9223,13 +9323,35 @@ qemuBuildCommandLine(virConnectPtr conn, if (def->tpm) { char *optstr; + int tpmfd = -1; + int cancelfd = -1; + char *fdset; - if (!(optstr = qemuBuildTPMBackendStr(def, qemuCaps, emulator))) + if (!(optstr = qemuBuildTPMBackendStr(def, cmd, qemuCaps, emulator, + &tpmfd, &cancelfd))) goto error; virCommandAddArgList(cmd, "-tpmdev", optstr, NULL); VIR_FREE(optstr); + if (tpmfd >= 0) { + fdset = qemuVirCommandGetFDSet(cmd, tpmfd); + if (!fdset) + goto error; + + virCommandAddArgList(cmd, "-add-fd", fdset, NULL); + VIR_FREE(fdset); + } + + if (cancelfd >= 0) { + fdset = qemuVirCommandGetFDSet(cmd, cancelfd); + if (!fdset) + goto error; + + virCommandAddArgList(cmd, "-add-fd", fdset, NULL); + VIR_FREE(fdset); + } + if (!(optstr = qemuBuildTPMDevStr(def, qemuCaps, emulator))) goto error; diff --git a/src/util/vircommand.c b/src/util/vircommand.c index 6527d85..2616446 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -67,6 +67,7 @@ enum { VIR_EXEC_RUN_SYNC = (1 << 3), VIR_EXEC_ASYNC_IO = (1 << 4), VIR_EXEC_LISTEN_FDS = (1 << 5), + VIR_EXEC_FIXED_FDS = (1 << 6), }; typedef struct _virCommandFD virCommandFD; @@ -214,6 +215,12 @@ virCommandReorderFDs(virCommandPtr cmd) if (!cmd || cmd->has_error || !cmd->npassfd) return; + if ((cmd->flags & VIR_EXEC_FIXED_FDS)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The fds are fixed and cannot be reordered")); + goto error; + } + for (i = 0; i < cmd->npassfd; i++) maxfd = MAX(cmd->passfd[i].fd, maxfd); @@ -1019,6 +1026,32 @@ virCommandPassListenFDs(virCommandPtr cmd) cmd->flags |= VIR_EXEC_LISTEN_FDS; } +/* + * virCommandPassFDGetFDIndex: + * @cmd: pointer to virCommand + * @fd: FD to get index of + * + * Determine the index of the FD in the transfer set. + * + * Returns index >= 0 if @set contains @fd, + * -1 otherwise. + */ +int +virCommandPassFDGetFDIndex(virCommandPtr cmd, int fd) +{ + size_t i = 0; + + while (i < cmd->npassfd) { + if (cmd->passfd[i].fd == fd) { + cmd->flags |= VIR_EXEC_FIXED_FDS; + return i; + } + i++; + } + + return -1; +} + /** * virCommandSetPidFile: * @cmd: the command to modify diff --git a/src/util/vircommand.h b/src/util/vircommand.h index bf65de4..198da2f 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -62,6 +62,9 @@ void virCommandPassFD(virCommandPtr cmd, void virCommandPassListenFDs(virCommandPtr cmd); +int virCommandPassFDGetFDIndex(virCommandPtr cmd, + int fd); + void virCommandSetPidFile(virCommandPtr cmd, const char *pidfile) ATTRIBUTE_NONNULL(2); -- 1.9.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list